[Unity] 포톤 멀티 숨바꼭질 만들기 01

이정현·2021년 9월 30일
6

📌 목표

포톤2 서버를 사용하여 여러명이서 즐길 수 있는 숨바꼭질 게임을 제작한다.

게임에 접속하면 플레이어와 같은 외형의 캐릭터들이 맵을 걸어다닌다. 이 중 다른 플레이어를 찾아 공격하면 공격을 당한 플레이어는 게임 오버되고, 최종으로 남는 플레이어가 승리하는 게임을 만드는 것이 목표이다.

🔧 포톤 기본 환경 구성

포톤 홈페이지에서 회원가입을 진행한 후, 새 어플리케이션을 만들고 어플리케이션 ID를 복사한다.

포톤 홈페이지에서 어플리케이션 생성을 완료했다면, 유니티로 돌아와 필요한 에셋을 다운로드 받는다.
Asset Store에 들어가 맵 제작에 사용할 무료 에셋과 포톤을 사용하기 위한 에셋을 다운 받는다.

필자는 이미 프로젝트에 포톤 임포트와 적용이 완료되었기 때문에 Update로 뜨지만, 원래는 Import 또는 Download 버튼이 보인다. 이를 클릭한 후, Pun Setup 창이 뜨면 포톤 홈페이지에서 복사하였던 어플리케이션 ID를 붙여넣기 하면된다.


맵과 캐릭터로 사용할 무료 에셋으로 Lowpoly Medieval Plague Doctor-Free Pack을 임포트하였다.

🎞 테스트 Scene 세팅

여러명의 플레이어가 접속할 수 있으며, 캐릭터 조작 및 카메라 세팅을 테스트해보기 위한 "TestScene"을 생성한다.
TestScene은 아래와 같이 구성하였다.

NetworkManager

멀티 접속을 위한 "NetworkManager.cs" 스크립트를 실행시킨다.

using UnityEngine;
using Photon.Pun;
using Photon.Realtime;
using UnityEngine.UI;

public class NetworkManager : MonoBehaviourPunCallbacks
{
  public Text StatusText;
  public InputField NickNameInput;
  public GameObject Cube;
  public string gameVersion = "1.0";


  void Awake()
  {
      PhotonNetwork.AutomaticallySyncScene = true;
  }

  void Start()
  {
      PhotonNetwork.GameVersion = this.gameVersion;
      PhotonNetwork.ConnectUsingSettings();
  }

  void Connect() => PhotonNetwork.ConnectUsingSettings();

  override void OnConnectedToMaster()
  {
      PhotonNetwork.JoinRandomRoom();
  }

  override void OnJoinedRoom()
  {
      PhotonNetwork.Instantiate("Player3", Cube.transform.position, Quaternion.identity);
  }

  override void OnJoinRandomFailed(short returnCode, string message)
  {
      this.CreateRoom();
  }

  void CreateRoom()
  {
      PhotonNetwork.CreateRoom(null, new RoomOptions { MaxPlayers = 4 });
  }
}

StatusText: 각 사용자의 접속상태를 저장하기위한 변수이며, 아직은 사용하지 않는다.
NickNameImput: 사용자가 닉네임을 입력하는 입력 폼이며, 아직은 사용하지 않는다.
gameVersion: 포톤 게임 버전을 설정하기 위한 변수, 같은 게임 버전끼리 공유가 된다.
Cube: 플레이어를 생성할 위치를 나타낸다.

PhotonNetwork.AutomaticallySyncScene: 마스터 client와 일반 client의 레벨을 동기화 할지 결정한다. true일 시, 마스터 client에서 레벨 변경 시 모든 client들이 자동으로 동일한 레벨을 로드한다.
PhotonNetwork.GameVersion: 포톤 게임 버전을 gameVersion으로 설정한다.
PhotonNetwork.ConnectUsingSettings: 온라인 연결 수행. 연결이 성공하면 콜백 메소드인 OnConnectedToMaster가 실행된다.
PhotonNetwork.JoinRandomRoom: 생성된 방에 랜덤하게 접속한다. 접속 실패 시, 콜백 메소드인 OnJoinRandomFailed가 실행되고 PhotonNetwork.CreateRoom을 통해 새로운 방을 생성한다. 접속 성공 시, OnJoinedRoom가 실행되고, PhotonNetwork.Instantiate을 통해 플레이어를 생성한다.

카메라 세팅

포톤 연결 수행이나 플레이어 스크립트 작성에는 큰 어려움이 없었는데 카메라 부분에서 은근 시간이 걸렸다. 각 플레이어마다 백뷰 시점으로 플레이가 가능하도록 카메라를 설정하고자 했는데, 카메라 간섭이 일어나는 것인지 자꾸만 문제가 발생했다. 포톤 카메라 관련 많은 포스팅을 참고하여 다음과 같이 문제를 해결하였다.

Smooth Follow


우선 에셋 스토어에서 무료로 제공하는 Standard Assets의 SmoothFollow를 임포트한다. 해당 패키지는 많은 에셋을 포함하고 있는데 "SmoothFollow.cs"만 찾아 임포트 해주면 된다.

스크롤을 많이 내려야 해당 스크립트를 찾을 수 있다.
임포트를 완료했다면, 씬의 Main Camera에 스크립트를 추가한 후, 스크립트 내 target 변수의 접근지정자를 public으로 변경한다.

카메라는 플레이어를 쫓아다녀야 하므로 Smooth Flollow 스크립트의 Target을 플레이어로 설정하였다.

그외의 Distance, Height와 같은 변수들은 자신의 입맛대로 조정하면 된다.

카메라 피벗 설정

설정을 완료한 후 게임을 실행시켰을 때, 카메라의 위치가 묘하게 마음에 들지 않았다. 그래서 플레이어 프리팹 내에 피벗을 설정하여 카메라의 시야를 조정하고자 하였다.

플레이어 프리팹에 CamPivot이라는 이름의 빈 오브젝트를 생성한 후, 위치를 y +2로 설정해주었다.

플레이어 스크립트 내 카메라 설정 추가

using UnityEngine;
using System.Collections.Generic;
using Photon.Pun;
using UnityStandardAssets.Utility;
public class TestPlayerScript : MonoBehaviourPunCallbacks
{
   public PhotonView PV;
   private Transform tr;
   void Start()
   {
       tr = GetComponent<Transform>();
       if (PV.IsMine)
           Camera.main.GetComponent<SmoothFollow>().target = tr.Find("CamPivot").transform;
   }
}

플레이어 스크립트의 자세한 내용은 아래에서 설명하도록 하고, 카메라 설정을 위해 추가한 부분은 다음과 같다.

using UnityStandardAssets.Utility;
private Transform tr;
tr = GetComponent<Transform>();
if (PV.IsMine)
            Camera.main.GetComponent<SmoothFollow>().target = tr.Find("CamPivot").transform;

Player 설정

플레이어에게 Photone View, Photone Transform View, Photone Animator View 스크립트를 추가한다. Photone View는 객체를 식별 및 상태 동기화하는데 사용이 되며, Observed Components로 Photone Transform View, Photne Animator View를 추가하면 해당 객체의 transform, animation 값 공유도 가능해진다.

Photone Transform View에서는 위치와 회전 값을 동기화 시켰으며, Photone Animator View에선 Hi 파라미터만 disable하고, 나머지 파라미터는 discrete로 설정하였다.

Player Script 작성

using UnityEngine;
using System.Collections.Generic;
using Photon.Pun;
using UnityStandardAssets.Utility;

public class TestPlayerScript : MonoBehaviourPunCallbacks
{

    public PhotonView PV;
    public Rigidbody rb;
    private Transform tr;

    public float m_moveSpeed = 1;
    public float m_turnSpeed = 200;
    public Animator m_animator;
    
    private float m_currentV = 0; //현재 가상 수직선 위치
    private float m_currentH = 0; //현재 가상 수평선 위치
    private readonly float m_interpolation = 10;
    private readonly float m_backwardRunScale = 0.66f;


    private void Start()
    {
        rb = GetComponent<Rigidbody>();
        rb.centerOfMass = new Vector3(0, -1.5f, 0); // 무게 중심점을 변경
        tr = GetComponent<Transform>();
        if (PV.IsMine)
            Camera.main.GetComponent<SmoothFollow>().target = tr.Find("CamPivot").transform;
    }
  

    void Update()
    {//메인 캐릭터 업데이트

        if (!PV.IsMine && PhotonNetwork.IsConnected)
            return;

        float v = Input.GetAxis("Vertical"); //상하 이동 W키 : 0~1, S키: -1~0
        float h = Input.GetAxis("Horizontal"); //좌우 이동 D키: 0~1, A키: -1~0
        if (Input.GetMouseButton(0)) //마우스 왼쪽 버튼을 눌렀을 때
        {
            float y_rot = Input.GetAxisRaw("Mouse X");
            transform.Rotate(0, y_rot * m_turnSpeed * Time.deltaTime, 0); //로테이션
        }
        if (v < 0)
        { //s일때 뒤로 걷는 속도 적용
            v *= m_backwardRunScale;
        }

        m_currentV = Mathf.Lerp(m_currentV, v, Time.deltaTime * m_interpolation); //상하 갱신
        m_currentH = Mathf.Lerp(m_currentH, h, Time.deltaTime * m_interpolation); //좌우 갱신

        tr.Translate(Vector3.forward * v * m_moveSpeed * Time.deltaTime);
        tr.Rotate(Vector3.up * h * m_turnSpeed * Time.deltaTime);
        m_animator.SetFloat("Speed", m_currentV); //애니메이션 갱신
    }
}

아직 코드 내의 수정할 부분이 많지만, 일단 플레이어 스크립트를 다음과 같이 작성하고 플레이어에게 적용하였다.

스크립트에 필요한 PV, Rb, Animator를 추가하였다.

🎮 실행화면


게임을 실행하였을 때, 백뷰 시점으로 카메라가 세팅됐음을 확인할 수 있다.

씬에도 캐릭터가 잘 나타난다. 이제 새로운 플레이어를 한명 더 접속시켜 보자.


새로운 창에서 접속을 시도했다. 이미 접속 중이던 플레이어가 맞은편에 잘 나타남을 확인할 수 있다.

이미 접속 중이던 캐릭터 시점에서도 새로 접속한 플레이어의 모습이 잘 나타나며, 게임 씬을 확인하니 두 명의 플레이어가 생성됨을 확인할 수 있다.

profile
개발자 꿈나무

0개의 댓글