Tanks, Multiplayer with PUN! (Part 3)
이번 편에서는 PUN에서 아주 중요한 2개의 네트워크 요소인 Photon View와 Photon Transform View를 소개하려고 합니다. 이는 네트워크 중 각종 오브젝트의 위치/회전/축소확대 또는 기타 정보의 변동을 동기화하는 데 사용됩니다.
Tanks! Multiplayer (Unity Tutorial)
먼저 Unity 공식 학습에서 아주 유명한 게임 범례 Tanks!를 보도록 하겠습니다.
이것은 콘솔 멀티플레이어의 탱크사격게임으로 2명이 동일한 키보드를 사용해 한 대의 메인 기기 상에서 함께 플레이할 수 있는 게임입니다. 처음 접하시는 분이라면 아래 웹페이지에서 상세한 Unity 공식 자습서을 확인해주세요~
게임 장면 구축(Game Scene)
우리의 Unity 프로젝트 TanksPUN은 현재 다음 Asset을 임포트하려고 하는데, 이는 플레이어 캐릭터 및 멀티플레이어 접속의 게임 장면 구축에 상당히 적합합니다. Tanks! Tutorial의 Asset Store 는 아래 페이지에서 접속하세요.
Tanks! Tutorial by Unity Technologies
Asset Store에서 Tanks!를 찾고 임포트를 완료한 후, 이어서 새로운 Scene을 구축하고, 이것을 GameRoomScene이라고 부릅시다!
이어서 Assets→Complete→Scenes에서 하나의 CompleteLevelArt의 Prefab을 찾고, 곧바로 이것을 우리의 GameRoomScene 내로 끌어옵니다.
Assets→Complete→Scenes →CompleteLevelArt
다시 기존 GameRoomScene 내에 사전 설정한 Directional Light를 제거하여 이 장면의 컬러가 광원의 직접적으로 영향을 받지 않도록 합니다. 이어서 다음 화면이 나타납니다. 성취감이 느껴지지 않나요?
CompleteLevelArt
아, 위에서 한 작업은 너무 쉬우니 이어서 프로그래밍 코드를 보시죠~
아래 프로그램은 현재까지 우리가 구축한 PhotonManager.cs의 내용입니다.
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class PhotonManager : Photon.PunBehaviour {
public static PhotonManager instance;
public static GameObject localPlayer;
void Awake()
{
if(instance != null)
{
DestroyImmediate(gameObject);
return;
}
DontDestroyOnLoad(gameObject);
instance = this;
PhotonNetwork.automaticallySyncScene = true;
}
void Start()
{
PhotonNetwork.ConnectUsingSettings("TanksPUN_v1.0");
}
public void JoinGame()
{
RoomOptions options= new RoomOptions();
options.MaxPlayers = 4;
PhotonNetwork.JoinOrCreateRoom("Fighting Room",options, TypedLobby.Default);
}
public override void OnJoinedRoom ()
{
Debug.Log("당신은 이미 게임 룸에 진입했습니다!!");
if(PhotonNetwork.isMasterClient)
{
PhotonNetwork.LoadLevel("GameRoomScene");
}
}
void OnLevelWasLoaded(int levelNumber)
{
// Photon 룸 안이 아니라면 네트워크에 문제가 있을지도..
if (!PhotonNetwork.inRoom)
return;
Debug.Log("게임에 들어왔어요, yay~");
}
}
이전 편(Part2)의 PhotonManager에서 우리는 이미 Photon Server와 소통할 수 있으며, 게임 룸 내에 이미 진입한 경우 PunBehaviour가 우리의 OnJoinedRoom에 통지한다고 앞에서 언급했습니다. 이 때 PhotonNetwork.LoadLevel을 사용해 게임 장면에 로딩할 수 있습니다.
장면 로딩 완료 후 OnLevelWasLoaded가 호출될 수 있고, 이 때 장면 내에 일부 초기값 또는 정보 설정을 구축할 수 있습니다. 여기에서 간단하게 플레이어 캐릭터(Player Character)를 하나 만들어 테스트해 봅시다~
플레이어 캐릭터 참여(The Player)
OnLevelWasLoaded에서 우리는 PhotonNetwork.Instantiate를 사용해 네트워크 상에 동기화된 오브젝트를 구축할 수 있고, 이렇게 하면 이 Game Room에 접속한 모든 사람이 이 오브젝트를 볼 수 있습니다.
void OnLevelWasLoaded(int levelNumber)
{
if (!PhotonNetwork.inRoom) return;
Debug.Log("게임에 들어왔어요, yay~");
localPlayer = PhotonNetwork.Instantiate(
"Player",
new Vector3(0,0.5f,0),
Quaternion.identity, 0);
}
}
따라서 장면에 진입하면 우리는 지정한 위치에서 동적으로 Character를 생성할 수 있고, Player의 프로그래밍 코드는 다음과 같이 간단하게 먼저 설정할 수 있습니다.
public class Player : Photon.PunBehaviour
{
Camera playerCam;
void Awake () {
DontDestroyOnLoad(gameObject);
playerCam = GetComponentInChildren<Camera>();
if(!photonView.isMine)
{
playerCam.gameObject.SetActive(false);
}
}
}
이어서 중요한 Photon Network 요소를 소개해드리겠습니다. 아주 중요합니다!!
네트워크 상에서 동기화해야 하는 각 오브젝트는 모두 이 요소가 있어야 정보 동기화가 가능합니다!
Photon View 요소 소개
이 요소는 Unity Networking 내 NetworkView 요소의 기능과 상당히 유사합니다. 주로 게임 중 다른 관찰 컨포넌트를 동기화하는 데 사용하며, 그 내부에는 ViewID가 있고 Photon Network가 이를 기반으로 장면 중 다른 컨포넌트를 식별하며, 정보 변동이 있을 경우 네트워크 메시지에서 각 Client 터미널에서 동기화 처리를 진행하는데, 그 동기화 규칙은 Observe Option에서 설정할 수도 있습니다. 여기에서 우리는 먼저 Unreliable On Change를 설정하면 됩니다.
Photon Transform View 요소 소개
이 요소는 Photon에 맞춰 제작된 Transform View 요소이며, PUN은 우리가 선택한 방식에 따라 네트워크 상에서 변동되는 값을 동기화하는데, 마치 우리가 특정 오브젝트를 이동시킬 때 네트워크 상에서 다른 Client가 보는 것은 튀어오르거나 미끄러져 이동하는 유형의 것과 같습니다. 동기화할 수 있는 값은 Position, Rotation, Scale의 단일 옵션 또는 다수 옵션입니다. 여기에서 우리는 먼저 Position+Rotation을 설정하면 됩니다.
Project 내에서 우리는 CompleteTank의 Prefab을 찾고, 이것이 Inspector 중에 표시되도록 선택, Photon View 및 Photon Transform View의 이 두 요소를 추가합니다.
이어서 Photon Transform View를 Photon View로 끌어오면, 그 Observed Components 하에 컨포넌트가 하나 더 추가됩니다. 이는 다음 화면에 보이는 것과 같습니다.
PhotonTransView를 PhotonView의 ObservedComponents 중으로 끌어오기
플레이어 이동 처리의 프로그래밍 코드는 다음과 같습니다.
using UnityEngine;
namespace TanksPUN
{
public class PlayerMovement : Photon.PunBehaviour
{
// How fast the tank moves forward and back.
public float m_Speed = 12f;
// How fast the tank turns in degrees per second.
public float m_TurnSpeed = 180f;
// Reference used to move the tank.
private Rigidbody m_Rigidbody;
// The current value of the movement input.
private float m_MovementInputValue;
// The current value of the turn input.
private float m_TurnInputValue;
private void Awake ()
{
m_Rigidbody = GetComponent<Rigidbody> ();
if(!photonView.isMine)
{
enabled = false;
}
}
private void OnEnable ()
{
m_Rigidbody.isKinematic = false;
m_MovementInputValue = 0f;
m_TurnInputValue = 0f;
}
private void OnDisable ()
{
m_Rigidbody.isKinematic = true;
}
private void Update ()
{
m_MovementInputValue = Input.GetAxis ("Vertical");
m_TurnInputValue = Input.GetAxis ("Horizontal");
}
private void FixedUpdate ()
{
Move ();
Turn ();
m_Rigidbody.velocity = Vector3.zero;
m_Rigidbody.angularVelocity = Vector3.zero;
}
private void Move ()
{
Vector3 movement = transform.forward * m_MovementInputValue * m_Speed * Time.deltaTime;
m_Rigidbody.MovePosition(m_Rigidbody.position + movement);
}
private void Turn ()
{
float turn = m_TurnInputValue * m_TurnSpeed * Time.deltaTime;
Quaternion turnRotation = Quaternion.Euler (0f, turn, 0f);
m_Rigidbody.MoveRotation (m_Rigidbody.rotation * turnRotation);
}
}
}
다음 편에서는 다른 플레이어의 진입점(부활점)에 대한 관리, 플레이어 이동 처리의 방식 및 강력한 Tank의 탄약사격과 충돌을 설명하도록 하겠습니다!!
다음에 봐요!
댓글
댓글 0개
댓글을 남기려면 로그인하세요.