네트워크 게임상 각 플레이어를 식별하기 위해 유저명을 입력받아야 한다. 먼저 UI를 구성하자. 로비 UI구현에 사용할 텍스트는 TextMeshPro를 사용해보자. 메뉴에서 [Window][TextMeshPro][Import TMP Essential Resources]를 선택해 초기화한다.

Canvas를 생성한 후 다음과 같이 로비 UI에서 유저명과 생성할 룸의 이름을 입력받을 Text 항목과 Button을 각각 추가한다. Canvas Scaler의 UI Scale Mode 속성은 Scale With Screen Size로 설정하고 진행한다. UI가 너무 커서 Resolution을 조정후

하이라키뷰의 Canvas를 더블클릭하고 씬뷰를 2D로 만든후 보기 좋게 하고 다음과 같이 배치한다.

로그인 UI구성을 완료한 후 유저명을 입력받아 설정하고 룸을 생성하는 로직을 구현해 본다. PhotoneManager스크립트를 다음과 같이 수정한다.

using TMpro 스페이스를 추가한다.

using System.Collections.Generic;
using UnityEngine;
using TMPro;  //TextMeshPro UI를 위한 네임스페이스
using Photon.Pun;  //포톤 API를 위한 네임스페이스
using Photon.Realtime; //포톤 API를 위한 네임스페이스

 UI 필드를 참조할 변수를 설정한다.

public TMP_InputField userIF; // 유저명을 입력할 TextMeshPro Input Field
public TMP_InputField roomNameIF; // 룸 이름을 입력할 TextMeshPro Input Field

Awake()내의 NickName은 참조처리한다.

//PhotonNetwork.NickName = userId;  //접속 유저의 닉네임 설정

처음 실행했을때 유저명을 자동으로 InputField에 표시하기 위해 PlayerPrefab을 이용 유저명을 로드하고 표시한다. 저장된 유저명이 없다면 랜덤으로 지정한다.

void Start()
{
    // 저장된 유저명을 로드
    userId = PlayerPrefs.GetString("USER_ID", $"USER_{Random.Range(1,21):00}");
    userIF.text = userId;
    // 접속 유저의 닉네임 등록
    PhotonNetwork.NickName = userId;  
}

로그인 UI의 [Login]버튼 또는 [Make Room]버튼을 클릭했을때, 유저명의 변경 사항을 최종확인하고 PlayerPrefs를 사용해 유저명을 저장한후PhotoneNetwork.NickName을 설정한다.

// 유저명을 설정하는 로직
public void SetUserId()
{
    if (string.IsNullOrEmpty(userIF.text))
    {
        userId = $"USER_{Random.Range(1,21):00}";
    }
    else
    {
        userId = userIF.text;
    }

    // 유저명 저장
    PlayerPrefs.SetString("USER_ID", userId);
    // 접속 유저의 닉네임 등록
    PhotonNetwork.NickName = userId;
}

게임 실행시 자동 입장을 방지하기 위해 PhotonNetwork.JoinRandomRoom() 주석처리한다

// 로비에 접속 후 호출되는 콜백 함수
public override void OnJoinedLobby() {
    Debug.Log($"PhotonNetwork.InLobby = {PhotonNetwork.InLobby}");
    //PhotonNetwork.JoinRandomRoom();     수동입장을 위해 주석처리
}

무작위 룸에 입장을 시도했다가 실패했을때 호출되는 OnJoinedRandomFailed 콜백 함수도 룸을 생성하는 로직을 주석 처리하고 OnMakeRoomClick 함수를 호출하도록 수정한다.

// 랜덤한 룸 입장이 실패했을 경우 호출되는 콜백 함수
public override void OnJoinRandomFailed(short returnCode, string message) {
    Debug.Log($"JoinRandom Filed {returnCode}:{message}");
    OnMakeRoomClick();
      // 룸의 속성 정의
    //RoomOptions ro = new RoomOptions();
    //ro.MaxPlayers = 20;     // 룸에 입장할 수 있는 최대 접속자 수
    //ro.IsOpen = true;       // 룸의 오픈 여부
    //ro.IsVisible = true;    // 로비에서 룸 목록에 노출시킬 여부

    // 룸 생성
    //PhotonNetwork.CreateRoom("My Room", ro);
}

룸에 입장한후 호출되는 OnJoinedRoom 콜백함수에서 캐릭터를 생성하는 로직은 주석처리하고 BattleField씬을 호출한다. Phtoneview가 추가된 주인공 캐릭터는 BattleField씬에서 생성한다. 씬을 로딩하는 함수는 유니티가 제공하는 SceneManagement.SceneManager.LoadScene함수대신 PhotoneNetwork.LoadLevel함수를 이용한다.  이 함수는 씬을 로딩전 데이터 송수신을 잠시 멈추고 재개하는 로직이 포함되어 있다. 만일 수동으로 처리한다면 PhotoneNetwork.IsMessageQueueRunning 속성을 false로 지정한후 로딩후 true로 변경해야한다.

씬의 로딩은 마스터 클라이언트만 호출해야 한다. 룸에 입장한 다른 네트워크유저는 PhotonNetwork.AutomaticallySyncScene을 true로 설정했기 때문에 마스터 클라이언트가 다른 씬을 로딩하면 자동으로 씬이 로딩된다.

// 룸에 입장한 후 호출되는 콜백 함수
public override void OnJoinedRoom() {
    Debug.Log($"PhotonNetwork.InRoom = {PhotonNetwork.InRoom}");
    Debug.Log($"Player Count = {PhotonNetwork.CurrentRoom.PlayerCount}");
    foreach (var player in PhotonNetwork.CurrentRoom.Players) {
        Debug.Log($"{player.Value.NickName} , {player.Value.ActorNumber}");
    }
    //출연위치를 배열에 저장
    //Transform[] points = GameObject.Find("SpawnPointGroup").GetComponentsInChildren<Transform>();
    //int idx = Random.Range(1, points.Length);
    //네트워크에 캐릭터 생성
    //PhotonNetwork.Instantiate("Player", points[idx].position, points[idx].rotation, 0);
    // 마스터 클라이언트인 경우에 룸에 입장한 후 전투 씬을 로딩한다.
    if (PhotonNetwork.IsMasterClient) {
        PhotonNetwork.LoadLevel("BattleField");
    }
}

OnLoginClick()함수는 UI의[Login]버튼의 클릭 이벤트에 연결할 함수로 유저명을 설정하고 무작위로 선택한 룸에 입장한다.

public void OnLoginClick() {
    // 유저명 저장
    SetUserId();

    // 무작위로 추출한 룸으로 입장
    PhotonNetwork.JoinRandomRoom();
}

OnMakeRoomClick() 함수 역시 로그인 UI [Maker Room] 버튼에 연결할 함수 이며 입력한 룸 이름으로 룸을 생성한다.

public void OnMakeRoomClick() {
    // 유저명 저장
    SetUserId();

    // 룸의 속성 정의
    RoomOptions ro = new RoomOptions();
    ro.MaxPlayers = 20;     // 룸에 입장할 수 있는 최대 접속자 수
    ro.IsOpen = true;       // 룸의 오픈 여부
    ro.IsVisible = true;    // 로비에서 룸 목록에 노출시킬 여부

    // 룸 생성
    PhotonNetwork.CreateRoom(SetRoomName(), ro);
}

PhotonManager.cs
0.00MB

전체코드

using System.Collections.Generic;
using UnityEngine;
using TMPro;  //TextMeshPro UI를 위한 네임스페이스
using Photon.Pun;  //포톤 API를 위한 네임스페이스
using Photon.Realtime; //포톤 API를 위한 네임스페이스


public class PhotonManager : MonoBehaviourPunCallbacks {
    private readonly string version = "1.0"; // 게임의 버전
    private string userId = "Zack"; // 유저의 닉네임  
                                    
    public TMP_InputField userIF; // 유저명을 입력할 TextMeshPro Input Field
    public TMP_InputField roomNameIF; // 룸 이름을 입력할 TextMeshPro Input Field

    void Awake() {      
        PhotonNetwork.AutomaticallySyncScene = true;  // 마스터 클라이언트의 씬 자동 동기화 옵션
        PhotonNetwork.GameVersion = version; // 게임 버전 설정
        //PhotonNetwork.NickName = userId;  //접속 유저의 닉네임 설정
        Debug.Log(PhotonNetwork.SendRate);// 포톤 서버와의 데이터의 초당 전송 횟수
        PhotonNetwork.ConnectUsingSettings(); // 포톤 서버 접속
    }
    void Start() {
        // 저장된 유저명을 로드
        userId = PlayerPrefs.GetString("USER_ID", $"USER_{Random.Range(1, 21):00}");
        userIF.text = userId;
        // 접속 유저의 닉네임 등록
        PhotonNetwork.NickName = userId;
    }

    // 유저명을 설정하는 로직
    public void SetUserId() {
        if (string.IsNullOrEmpty(userIF.text)) {
            userId = $"USER_{Random.Range(1, 21):00}";
        } else {
            userId = userIF.text;
        }

        // 유저명 저장
        PlayerPrefs.SetString("USER_ID", userId);
        // 접속 유저의 닉네임 등록
        PhotonNetwork.NickName = userId;
    }
    // 룸 명의 입력여부를 확인하는 로직
    string SetRoomName() {
        if (string.IsNullOrEmpty(roomNameIF.text)) {
            roomNameIF.text = $"ROOM_{Random.Range(1, 101):000}";
        }

        return roomNameIF.text;
    }
    public override void OnConnectedToMaster() {
        Debug.Log("Connected to Master!");
        Debug.Log($"PhotonNetwork.InLobby = {PhotonNetwork.InLobby}");
        PhotonNetwork.JoinLobby();
    }

    // 로비에 접속 후 호출되는 콜백 함수
    public override void OnJoinedLobby() {
        Debug.Log($"PhotonNetwork.InLobby = {PhotonNetwork.InLobby}");
        //PhotonNetwork.JoinRandomRoom();     수동입장을 위해 주석처리
    }

    // 랜덤한 룸 입장이 실패했을 경우 호출되는 콜백 함수
    public override void OnJoinRandomFailed(short returnCode, string message) {
        Debug.Log($"JoinRandom Filed {returnCode}:{message}");
        OnMakeRoomClick();
          // 룸의 속성 정의
        //RoomOptions ro = new RoomOptions();
        //ro.MaxPlayers = 20;     // 룸에 입장할 수 있는 최대 접속자 수
        //ro.IsOpen = true;       // 룸의 오픈 여부
        //ro.IsVisible = true;    // 로비에서 룸 목록에 노출시킬 여부

        // 룸 생성
        //PhotonNetwork.CreateRoom("My Room", ro);
    }

    // 룸 생성이 완료된 후 호출되는 콜백 함수
    public override void OnCreatedRoom() {
        Debug.Log("Created Room");
        Debug.Log($"Room Name = {PhotonNetwork.CurrentRoom.Name}");
    }

    // 룸에 입장한 후 호출되는 콜백 함수
    public override void OnJoinedRoom() {
        Debug.Log($"PhotonNetwork.InRoom = {PhotonNetwork.InRoom}");
        Debug.Log($"Player Count = {PhotonNetwork.CurrentRoom.PlayerCount}");
        foreach (var player in PhotonNetwork.CurrentRoom.Players) {
            Debug.Log($"{player.Value.NickName} , {player.Value.ActorNumber}");
        }
        //출연위치를 배열에 저장
        //Transform[] points = GameObject.Find("SpawnPointGroup").GetComponentsInChildren<Transform>();
        //int idx = Random.Range(1, points.Length);
        //네트워크에 캐릭터 생성
        //PhotonNetwork.Instantiate("Player", points[idx].position, points[idx].rotation, 0);
        // 마스터 클라이언트인 경우에 룸에 입장한 후 전투 씬을 로딩한다.
        if (PhotonNetwork.IsMasterClient) {
            PhotonNetwork.LoadLevel("BattleField");
        }
    }
    #region UI_BUTTON_EVENT

    public void OnLoginClick() {
        // 유저명 저장
        SetUserId();

        // 무작위로 추출한 룸으로 입장
        PhotonNetwork.JoinRandomRoom();
    }

    public void OnMakeRoomClick() {
        // 유저명 저장
        SetUserId();

        // 룸의 속성 정의
        RoomOptions ro = new RoomOptions();
        ro.MaxPlayers = 20;     // 룸에 입장할 수 있는 최대 접속자 수
        ro.IsOpen = true;       // 룸의 오픈 여부
        ro.IsVisible = true;    // 로비에서 룸 목록에 노출시킬 여부

        // 룸 생성
        PhotonNetwork.CreateRoom(SetRoomName(), ro);
    }

    #endregion
}

스크립트를 작성 저장후  로그인 UI의 InputField(TMP)-UserID와 InputField(TMP)-Room을 PhtonManager의 User IF, Room Name IF에 각각 드래그해 연결한다.

하이라키뷰의 [Canvas][Button-Login]을 선택하고 인스펙터의 OnClick에서 +를 눌러 이벤트를 하나 만들어 주고 하이라키뷰에서 PhotonManager를 끌어다 놔주면 활성화되는데 

No Fuction을 선택해 OnLoginClick()을 지정해준다.

같은 방법으로 Room Button도 OnMakerRoomClick을 지정해준다.

게임룸 입장

이제 실행해보면 씬이 로드되지 않았다는 에러가 나서

File-BuildSetting에서 우리가 만든 2개의 씬을 추가해주었다

이제 실행해보면 User ID 입력필드에 무작위 이름이 표시된다.

여러분의 이름을 직접입력하고 [Login]버튼을 누르면 입장되고 콘솔뷰에 유저명과 룸에 접속한 접속자 수가 표시된다.

또한 씬이 BattleField로 변경된것을 확인할 수 있다.

룸에 입장한후 로드된 BattleField씬에서는 아무런 동작도 없이 단순히 씬만 전환된것 뿐이다 .Lobby씬에서 유저명을 입력받아 네트워크상 식별자를 부여한 후 무작위 추출된 룸에 입장하고 BattleField씬으로 전환된것 이다. 따라서 BattleField씬으로 넘어왔다는 것은 이미 룸에 입장한 상태이므로 주인공 캐릭터를 생성하는 로직을 구현해야 한다.

+ Recent posts