내일배움캠프 Unity 41일차 TIL - 심화 주차 개인과제 - Photon

Wooooo·2023년 12월 25일
0

내일배움캠프Unity

목록 보기
43/94

[오늘의 키워드]

심화주차 개인과제에 Photon을 이용해 멀티플레이 게임을 구현해보기로 했다.
1개의 Room만 이용하고, 방에 들어오는 유저에게 캐릭터를 1개씩 할당시켜 줄 생각이다.
호스트와 상호작용이 제대로 동기화되는지 테스트하기 위해 건들면 움직이는 공도 하나 넣어줄 생각이다.


[PhotonConnector class]

photon master서버에 접속 후 Room에 들어가도록 해주는 싱글톤 클래스다. (사실 싱글톤으로 작성해도 되는지는 아직 잘 모르겠다.)
SlotCar-Scene이라는 데모 씬이 있는데, 여기서 제공해주는 ConnectAndJoinRandom이라는 컴포넌트를 가져와서 살짝 수정해봤다.

기존의 ConnectAndJoinRandom 컴포넌트는 마스터 서버에 접속하고나면, 자동으로 랜덤한 Room에 입장하게 해주는 방식이었는데, 나는 방을 1개만 사용할 것이라서 랜덤한 방을 입장하는 대신 master라는 이름을 가진 방이 있다면 그 방에 입장하고, 없다면 방을 만들어서 입장하도록 수정했다.

    public override void OnConnectedToMaster()
    {
        Debug.Log("OnConnectedToMaster() was called by PUN. This client is now connected to Master Server in region [" + PhotonNetwork.CloudRegion +
            "] and can join a room.");
        RoomOptions roomOptions = new RoomOptions() { MaxPlayers = this.MaxPlayers };
        if (playerTTL >= 0)
            roomOptions.PlayerTtl = playerTTL;
        PhotonNetwork.JoinOrCreateRoom("master", roomOptions, null); // "master" 방 입장
    }

또, 유저가 방에 입장 했을 때 캐릭터를 생성하기 위해 OnComplete라는 event를 만들어두고, 방에 입장 했을 때 실행되는 메서드인 OnJoinedRoom() 메서드에서 OnComplete를 호출하게 했다.

    public override void OnJoinedRoom()
    {
        Debug.Log("OnJoinedRoom() called by PUN. Now this client is in a room in region [" + PhotonNetwork.CloudRegion + "]. Game is now running.");
        OnComplete?.Invoke();
    }

참고로, 이 클래스는 MonoBehaviourPunCallbacks를 상속받고 있어서, photon에서 제공하는 이벤트 콜백 인터페이스를 구현하고 있기 때문에 위 메서드들은 photon에서 알맞은 상황에 호출해준다.


[GameManager class]

[게임 초기화 순서]

게임매니저에서는 포톤 서버 접속, 어드레서블 리소스 로드, 게임 시작을 순서대로 진행 할 수 있도록 해줬다.

    protected override void Start()
    {
        base.Start();

        // 1. 포톤 서버 연결
        PhotonConnect(() =>
        {
            // 2. 리소스 로드
            ResourceLoad((key, count, total) =>
            {
                if (count == total)
                {
                    // 3. 맵, 캐릭터, 공 생성 (게임 시작)
                    GameStart();
                }
            });
        });
    }

각 메서드에서 PhotonConnector, ResourceManager와 같은 매니저들에게 작업을 요청한다.

[PhotonNetwork.Instantiate]

멀티플레이 환경에서 플레이어 객체나 상호작용 객체를 생성할 때, 방에 있는 모든 유저에게 Instantiate를 해줘야한다.
이걸 도와주는 메서드를 이미 Photon에서 PhotonNetwork.Instantiate로 제공하고 있다. 근데 prefab을 찾을 때 내가 직접 prefab을 넣어 주는게 아니라 string으로 prefab name을 매개변수로 받고 있다.

public static GameObject Instantiate(string prefabName, Vector3 position, Quaternion rotation, byte group = 0, object[] data = null)
{
	if (CurrentRoom == null)
    {
    	Debug.LogError("Can not Instantiate before the client joined/created a room. State: "+PhotonNetwork.NetworkClientState);
        return null;
	}

	Pun.InstantiateParameters netParams = new InstantiateParameters(prefabName, position, rotation, group, data, currentLevelPrefix, null, LocalPlayer, ServerTimestamp);
	return NetworkInstantiate(netParams, false);
}

string으로 받아온 prefabName은 PhotonNetwork.PrefabPool이란 곳에 캐싱된 리소스를 불러오기 위한 key였던 거다.

Unity Forum : DefaultPool을 이용해 PrefabPool에 리소스 캐싱하기

[PhotonNetwork.PrefabPool]

PrefabPool 프로퍼티로 직접 접근하면 IPunPrefabPool이라는 인터페이스로 참조하게 된다.
실제 리소스가 캐싱된 Dictionary에 접근하기 위해선 DefaultPool이라는 클래스로 형변환을 해줘야한다.

    private void SetPhotonPrefabPool()
    {
        // 1. 플레이어 prefab 등록
        var playerPrefab = ResourceManager.Instance.GetCache<GameObject>("Player.prefab");
        var pool = PhotonNetwork.PrefabPool as DefaultPool;
        pool.ResourceCache.Add("Player.prefab", playerPrefab);

        // 2. 공 prefab 등록

    }

ResourceManager에 캐싱된 플레이어 캐릭터 prefab을 PrefabPool에 등록해줬다.

이제 에디터에서 게임을 실행하면 마스터 서버 접속을 기다린 다음, 리소스 로드를 거쳐 PrefabPool에 prefab을 등록하고, Instantiate까지 잘 동작한다. (아직 멀티 플레이에서는 테스트를 해보진 않았지만...)


마치며

마스터 서버 접속, 리소스 캐싱까지는 된 것 같긴 한데, 아직 갈 길이 멀다.
PhotonView나 RPC를 이용해 오브젝트를 동기화하는 작업도 해야하고
플레이어 캐릭터는 다른 유저의 캐릭터가 아니라 나에게 할당된 캐릭터만 입력을 받아 움직여야한다.

방에 들어왔을 때 생성되는 캐릭터가 제대로 입장한 유저에게 할당된 캐릭터인지도 테스트해봐야한다.

Photon 데이터 동기화 방법

profile
game developer

0개의 댓글