This document is about: PUN 2
SWITCH TO

PUN Classic (v1), PUN 2, Bolt는 휴업 모드입니다. Unity2022에 대해서는 PUN 2에서 서포트하지만, 신기능의 추가는 없습니다. 현재 이용중인 고객님의 PUN 및 Bolt 프로젝트는 중단되지 않고, 퍼포먼스나 성능이 떨어지는 일도 없습니다. 앞으로의 새로운 프로젝트에는 Photon Fusion 또는 Quantum을 사용해 주십시오.

2 - 로비 UI

이 파트에서는 로비의 사용자 인터페이스(UI) 생성에 집중 할 것 입니다. 아주 기본적인 것을 다루며 네트워킹에 관련된 것이 아닙니다.

플레이 버튼

현재 로비는 접속하면 룸을 자동으로 안내 해주는데, 초기 테스팅에 매우 편리하지만 우리는 사용자가 룸을 선택하여 플레이 하도록 하고 싶습니다. 따라서 이를 위해 간단히 버튼을 제공할 것 입니다.

  1. Launcher 씬을 오픈 합니다.
  2. 유니티 메뉴 'GameObject/UI/Button' 에서 UI 버튼을 생성하고 Play Button 으로 이름을 변경 합니다. 캔버스와 이벤트 시스템 게임오브젝트가 Hierarchy 에 생성되어 있다는 것을 확인 해보세요. 우리가 생성하지 않아도 되니 좋군요 :)
  3. Play Button 의 Child Text 를 "플레이" 로 변경 합니다.
  4. Play Button을 선택 하고 버튼 컴포넌트의 On Click () 섹션으로 이동합니다.
  5. 작은 '+' 버튼을 클릭하여 항목을 추가합니다.
  6. Hierarchy에서 Launcher 게임오브젝트를 드래그하여 필드로 이동시킵니다.
  7. 드롭다운 메뉴에서 Launcher.connect() 를 선택 합니다. 이제 Launcher 스크립트에 버튼을 연결 시켰으므로 사용자가 그 버튼을 누르게 되면 Launcher 스크립트의 "Connect()" 메소드를 호출 하게 됩니다.
  8. Launcher 스크립트를 오픈 합니다.
  9. Connect() 를 호출하는 Start() 내의 라인을 제거 합니다.
  10. Launcher 스크립트를 저장하고 씬도 같이 저장 합니다.

지금 플레이를 누르게 되면 버튼을 누르기 전까지는 접속하지 않게 될 것 입니다.

플레이어 이름

전형적인 게임에서 가장 중요한 것중의 하나는 사용자가 자신의 이름을 지을 수 있도록 하여 누구를 상대하고 있는 지 알수 있도록 하는 것 입니다. 이 작업을 약간 변형하여 PlayerPrefs 에 이름의 값을 저장하도록 하여 사용자가 게임을 오픈 했을 때 그 이름을 복구 할 수 있습니다. 구현하기 매우 쉽고 중요한 기능으로 사용자 경험을 최대로 주기 위하여 많은 영역에서 활용 할 수 있습니다.

플레이어의 이름을 관리하며 기억하고 관련된 UI를 생성하는 스크립트를 생성 하겠습니다.

PlayerNameInputField 생성하기

  1. 새로운 C# 스크립트를 생성하여 PlayerNameInputField 로 이름을 부여합니다.

  2. 아래는 소스의 전체 내용입니다. PlayerNameInputField 스크립트를 맞게 편집하고 저장해 주세요:

    C#

    using UnityEngine;
    using UnityEngine.UI;
    
    using Photon.Pun;
    using Photon.Realtime;
    
    using System.Collections;
    
    namespace Com.MyCompany.MyGame
    {
        /// <summary>
        /// Player name input field. Let the user input his name, will appear above the player in the game.
        /// </summary>
        [RequireComponent(typeof(InputField))]
        public class PlayerNameInputField : MonoBehaviour
        {
            #region Private Constants
    
            // Store the PlayerPref Key to avoid typos
            const string playerNamePrefKey = "PlayerName";
    
            #endregion
    
            #region MonoBehaviour CallBacks
    
            /// <summary>
            /// MonoBehaviour method called on GameObject by Unity during initialization phase.
            /// </summary>
            void Start () {
    
                string defaultName = string.Empty;
                InputField _inputField = this.GetComponent<InputField>();
                if (_inputField!=null)
                {
                    if (PlayerPrefs.HasKey(playerNamePrefKey))
                    {
                        defaultName = PlayerPrefs.GetString(playerNamePrefKey);
                        _inputField.text = defaultName;
                    }
                }
    
                PhotonNetwork.NickName =  defaultName;
            }
    
            #endregion
    
            #region Public Methods
    
            /// <summary>
            /// Sets the name of the player, and save it in the PlayerPrefs for future sessions.
            /// </summary>
            /// <param name="value">The name of the Player</param>
            public void SetPlayerName(string value)
            {
                // #Important
                if (string.IsNullOrEmpty(value))
                {
                    Debug.LogError("Player Name is null or empty");
                    return;
                }
                PhotonNetwork.NickName = value;
    
                PlayerPrefs.SetString(playerNamePrefKey,value);
            }
    
            #endregion
        }
    }
    

Let's analyze this script: 스크립트를 분석 해 보도록 하겠습니다:

  • RequireComponent(typeof(InputField)):
    이 스크립트에서는 InputField 가 필요 한데 스크립트를 어려움 없이 편리하고 빠른 방식을 제공 합니다.

  • PlayerPrefs.HasKey(), PlayerPrefs.GetString() and PlayerPrefs.SetString():
    PlayerPrefs 은 (엑셀에서 두 개의 컬럼으로 구성되어 있는 것과 같이) keyValue 의 쌍으로 묶여진 항목들의 리스트의 룩업 리스트 입니다. 키는 모든 값을 가질 수 있는 문자열이고 개발 전 과정에서 준수해야할 명명법을 결정 합니다. 이 때문에 PlayerPrefs 키들을 한 장소에 저장하는 것이 좋습니다. 편리한 방법으로 [Static] 변수로 선언하여 사용하시기 바랍니다. 왜냐하면 게임내에서 변하지 않고 언제나 동일 하기 때문입니다. 상수(Const)로도 선언하여 사용할 수 있지만 C# 언어에서 스코프를 벗어 나게 되면 귀찮은 일이 되는 것임을 C# 경험이 많으신 분은 알 수 있을 것 입니다.

로직은 매우 직관적입니다. PlayerPrefs 에 키가 주어 졌다면 그 키에 대한 값을 직접 넣을 수 있습니다. 시작할 때, 편집할 때 InputFiled 의 현재값을 PlayerPref 에 설정하고 사용자 단말에 나중(사용자가 이 게임을 다음에 다시 오픈 할때)에 가져오기 위하여 저장 하게 될 것 입니다.

  • PhotonNetwork.NickName:
    이 스크립트의 중요한 부분 이며 네트워크상의 플레이어 이름을 설정합니다. 스크립트에서는 이것을 두 부분에서 사용하고 있습니다. PlayerPrefs 에 저장된 이름이 있는지 체크한 후 Start() 부분과 public 메소드인 SetPlayerName() 내부에서 사용합니다. 지금 당장은 아무것도 이 메소드에서 호출하지 않습니다. InputField 의OnValueChange() 에서 SetPlayerName()을 호출하도록 바인드하여 사용자가 InputField 값을 편집했을 때 저장하도록 합니다. 이 사항은 플레이 버튼을 눌렀을 때 하도록 할 수도 있습니다. 이렇게 하면 추가적인 스크립트가 더 필요하기 때문에 좀 더 간단하고 명확하게 하기 위하여 이대로 유지 하겠습니다. 이렇게 하면 사용자가 무엇을 의도 하던간에 입력된 값은 항상 기억될 것이고 종종 우리가 원하는 방식일 것 입니다.

플레이어 이름을 위한 UI 생성

  1. Launcher 씬이 오픈되어 있는지 확인 합니다.
  2. 유니티 메뉴의 'GameObject/UI/InputField' 를 이용하여 UI InputFiled 를 생성하고 이 게임오브젝트의 이름을 Name InputField로 변경 합니다.
  3. RectTransform 에 있는 PosY 값을 35로 설정하여 Play Button 위에 위치 시킵니다.
  4. Name InputField를 선택합니다. Name InputField의 자식인 PlaceHolder 의 텍스트를 "Enter your Name..."로 값을 변경 합니다.
  5. Name Name InputField 게임 오브젝트를 선택 합니다.
  6. 방금전에 생성했던 PlayerNameInputField 를 추가 합니다.
  7. InputField 컴포넌트의 On Value Change (String)섹션으로 이동 합니다.
  8. 작은 '+' 버튼을 클릭하여 항목을 추가 합니다.
  9. PlayerNameInputField 를 필드로 드래그 합니다.
  10. Dynamic String 섹션의 드롭다운 메뉴에서 PlayerNameInputField.SetPlayerName을 선택 합니다.
  11. 씬을 저장 합니다.

이제 플레이를 누른 후 이름을 입력하고 플레이를 중지 합니다. 다시 플레이 하면 방금전에 입력했던 내용이 나타나게 됩니다. 점점 더 나아지고 있으나 사용자 경험 측면에서는 연결과정과 연결하고 룸에 참여하는 문제등에 대한 피드백을 주지는 않았습니다.

연결 진행과정

여기에서는 간단하도록 할 것이며 이름 필드와 플레이 버튼을 감추고 간단한 문자열인 "Connecting..."로 대체하고 필요시 다시 교체할 것 입니다.

이렇게 하기 위해서 플레이 버튼과 이름 필드를 그룹핑 하여 그룹단위로 활성 또는 비활성 시킬 것 입니다. 나중에 그룹에 추가적인 기능을 추가할 수 있고 로직에 영향은 주지 않을 것 입니다.

  1. Launcher 을 오픈하고 있어야 합니다.
  2. 'GameObject/UI/Panel' 메뉴를 통하여 UI Panel 을 생성하고 이름을 Control Panel로 이름을 변경 합니다.
  3. 이 panel 에서는 컨텐츠만을 다루고 시각적인 사항이 필요없기 때문에 Control PanelImageCanvas Renderer 를 삭제 합니다.
  4. Control PanelPlay ButtonName InputField 를 드래그 합니다.
  5. 유니티 메뉴 'GameObject/UI/Text' 에서 UI Text를 생성 후 Progress Label 로 이름을 부여 합니다. 시각적인 요소에 대해서는 크게 걱정하지 않아도 됩니다. 실행시에 상황에 따라 활성/비활성 해 줄 것입니다.
  6. Progress Label 의 Text 컴포넌트를 선택 합니다.
  7. Alignmentcenter alignmiddle align으로 설정 합니다.
  8. Text 값을 "Connecting..." 으로 설정 합니다.
  9. Color를 흰색으로 또는 배경에서 강조될 수 있는 색상으로 설정 합니다.
  10. 씬을 저장합니다.

현재까지 테스트로 여러 연결 단계가 어떻게 진행되고 있는지 보기 위해 Control PanelProgress Label을 사용가능/불가로 할 수 있습니다. 두 개의 게임오브젝트들을 어떻게 활성시키는지 스크립트를 작성하겠습니다.

  1. Launcher 스크립트를 편집합니다

  2. Public Fields 영역에 다음 두 개의 공용 프로퍼티를 추가합니다.

    C#

        [Tooltip("The Ui Panel to let the user enter name, connect and play")]
        [SerializeField]
        private GameObject controlPanel;
        [Tooltip("The UI Label to inform the user that the connection is in progress")]
        [SerializeField]
        private GameObject progressLabel;
    
  3. Start() 메소드에 다음 코드를 추가합니다

    C#

        progressLabel.SetActive(false);
        controlPanel.SetActive(true);
    
  4. Connect() 메소드의 시작에 다음 코드를 추가합니다

    C#

            progressLabel.SetActive(true);
            controlPanel.SetActive(false);
    
  5. OnDisconnected() 메소드의 처음에 다음을 추가합니다.

    C#

            progressLabel.SetActive(false);
            controlPanel.SetActive(true);
    
  6. Launcher 스크립트를 저장하고 유니티 컴파일이 완료 될때까지 기다립니다

  7. Launcher 씬에 있는지 확인합니다.

  8. Hierarchy 에서 Launcher 게임오브젝트를 선택합니다

  9. Control PanelProgress LabelLauncher 컴포넌트의 각각의 필드에 드래그 합니다.

  10. 씬을 저장합니다

이제 씬을 플레이 합니다. Control Panel 이 표시되고 플레이 버튼을 클릭하자마자 Progress 레이블이 표시 될 것 입니다.

지금까지 로비 파트까지 잘 해주셨습니다. 로비의 추가적인 기능을 위해서 게임 자체를 변환하고 다양한 씬을 생성하여 룸에 참여 했을 때 레벨에 맞는 화면을 로드 할 수 있어야 합니다. 다음 섹션에서는 이러한 사항을 다룰 것이며 로비 시스템에 대해 마치겠습니다.

다음 파트.
이전 파트.

Back to top