유니티 스팀 멀티 연동하기 - 5. 게임 내 초대 구현
스팀에서 실행한 게임은, 게임 도중 Shift + Tab 키를 누르면 스팀 오버레이가 펼쳐지며 친구를 초대할 수 있다.
하지만, 스팀을 오래하지 않거나, 멀티가 익숙하지 않은 사람은 이런 초대 방법을 모를 수 있다.
그렇기 때문에 대부분의 게임에서는 스팀 오버레이 없이 게임 내에서 초대하는 기능을 따로 또 넣고는 한다.
이 기능은 스팀 구현은 정보가 거의 없어서 API 도큐먼트를 직접 읽으면서 찾을 수밖에 없었다.
아래는 스팀 친구 초대 UI 패널을 띄울 때 친구 목록을 가져오고 생성하는 부분을 잘라온 것이다.
private void UpdateFriendList()
{
var c = content.GetComponentsInChildren<Transform>();
c.ToList().ForEach(x =>
{
if(x != content)
Destroy(x.gameObject);
});
List<Steamworks.CSteamID> friendsList = new List<Steamworks.CSteamID>();
int friendCount = Steamworks.SteamFriends.GetFriendCount(Steamworks.EFriendFlags.k_EFriendFlagImmediate);
for(int i = 0; i < friendCount; i++)
{
var friend = Steamworks.SteamFriends.GetFriendByIndex(i, Steamworks.EFriendFlags.k_EFriendFlagImmediate);
if(Steamworks.SteamFriends.GetFriendPersonaState((Steamworks.CSteamID)friend.m_SteamID) != Steamworks.EPersonaState.k_EPersonaStateOffline)
{
GameObject obj = Instantiate(prefabPanelInviteItem, content, false);
var controller = obj.GetComponent<PanelInviteItemController>();
controller.PlayerSteamID = friend.m_SteamID;
controller.PlayerName = Steamworks.SteamFriends.GetFriendPersonaName((Steamworks.CSteamID)friend.m_SteamID);
controller.PlayerNameText.text = controller.PlayerName;
friendsList.Add(friend);
}
}
Debug.Log($"Steam friend Count : {friendsList.Count}");
}
Steamworks.SteamFriends.GetFriendCount를 통해 가져올 친구의 숫자를 받아올 수 있다.
물론 패러미터로 가져올 친구의 조건을 필터링할 수 있다.
위에 나온대로 EFriendFlags를 입맛에 맞게 변경해주면 된다.
본인의 스팀 이름이 아닌, 친구의 이름을 가져오는 방법도 찾기가 좀 어려웠는데, Steamworks.SteamFriends.GetFriendPersonaName()을 사용해서 가져올 수 있고, 해당 친구의 스팀ID 값을 패러미터로 넣어주면 된다.
구현시 친구의 설정해놓은 아바타 사진을 가져와서 보여주고 싶다면, 아래의 리스트 아이템에 넣은 스크립트 예시를 고쳐 사용하면 된다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using Steamworks;
using System;
public class PanelInviteItemController : MonoBehaviour
{
public string PlayerName;
public int ConnectionID;
public ulong PlayerSteamID;
private bool AvatarReceived;
public Text PlayerNameText;
public RawImage PlayerIcon;
[SerializeField]
private Button buttonInvite;
protected Callback<AvatarImageLoaded_t> ImageLoaded;
private void Start()
{
ImageLoaded = Callback<AvatarImageLoaded_t>.Create(OnImageLoaded);
buttonInvite.onClick.AddListener(() =>
{
//SteamFriends.InviteUserToGame((CSteamID)PlayerSteamID, SteamLobby.Instance.CurrentLobbyID.ToString());
SteamMatchmaking.InviteUserToLobby((CSteamID)SteamLobby.Instance.CurrentLobbyID, (CSteamID)PlayerSteamID);
});
SetPlayerValues();
}
private void OnImageLoaded(AvatarImageLoaded_t callback)
{
Debug.Log($"PlayerSteamID : {PlayerSteamID}");
if(callback.m_steamID.m_SteamID == PlayerSteamID)
{
PlayerIcon.texture = GetSteamImageAsTexture2D(callback.m_iImage);
}
else//another player
return;
}
void GetPlayerIcon()
{
int ImageID = SteamFriends.GetLargeFriendAvatar((CSteamID)PlayerSteamID);//SteamFriends.GetSmallFriendAvatar((CSteamID)PlayerSteamID); //
if (ImageID == -1)
{
Debug.Log($"no profile image : {(CSteamID)PlayerSteamID}");
return;
}
PlayerIcon.texture = GetSteamImageAsTexture2D(ImageID);
}
public void SetPlayerValues()
{
PlayerNameText.text = PlayerName;
if (!AvatarReceived)
GetPlayerIcon();
}
private Texture2D GetSteamImageAsTexture2D(int iImage)
{
Texture2D ret = null;
uint ImageWidth;
uint ImageHeight;
bool bIsValid = SteamUtils.GetImageSize(iImage, out ImageWidth, out ImageHeight);
if (bIsValid)
{
byte[] Image = new byte[ImageWidth * ImageHeight * 4];
bIsValid = SteamUtils.GetImageRGBA(iImage, Image, (int)(ImageWidth * ImageHeight * 4));
if (bIsValid)
{
ret = new Texture2D((int)ImageWidth, (int)ImageHeight, TextureFormat.RGBA32, false, true);
ret.LoadRawTextureData(Image);
ret.Apply();
}
}
return ret;
}
}
SteamFriends.GetLargeFriendAvatar()로 이미지ID값을 찾고, SteamUtils.GetImageRGBA()를 사용해서 가져올 수 있다.
byte[] 형식으로 가져오므로, 사이즈에 맞춰 RawTexture로 변환해 사용하면 된다.
테스트했을 때, 막상 구현해놨는데 이미지가 뒤집어져 나오면 이상한 게 아니고 정상이다.
데이터값 자체를 뒤집어주는 방법도 있겠지만, 복잡한 구현 없이 게임 내 Image를 rotation z값을 180도 돌리고, Scale x값을 -1로 변경해서 사용하면 편하다.
