주의 사항:Photon TrueSync와 Thunder는 서비스 종료되었으며 앞으로도 업데이트 및 릴리즈는 없을 예정입니다. 기존 어플리케이션에 영향은 없습니다만 현재 개발중인 어플리케이션에 대해서는 마이그레이션을 장려합니다.

TrueSync 튜토리얼 파트 3

튜토리얼 내용

이 튜토리얼 시리즈에서는 Photon TrueSync 를 이용하여 Unity 로 간단한 멀티플레이어 게임을 만드는 방법에 대해서 설명합니다. 파트 1과 2에서는 기본 설정, Photon 연결 코드 및 다른 게임 클라이언트 간에 움직임이 결정론적인 물리기반의 플레이어-제어 게임 오브젝트 생성 방법에 대해 학습 하였습니다.

파트3에서 학습 할 내용:

멀티플레이어 게임 클라이언트는 총알을 동적으로 인스턴스화하고 파괴할 수 있습니다. 플레이어들은 상자를 움직이고 서로 쏘며 인터넷 상에서 완벽하게 점수 동기화를 유지할 수 있습니다.

동기화 프리팹 인스턴스생성/파괴

Player 프리팹은 좋은 시작점이긴 하지만, 게임에서 발생한 상황에 따라 실시간으로 객체들을 생성할 필요가 있을 것 입니다. 예측 가능한 완전 동기화 시뮬레이션에서는 코드의 결과는 모든 단일 프레임 종료시점에서 같아야 TrueSync가 게임 오브젝트들의 인스턴스 생성과 파괴를 제어할 수 있습니다.

예를 들어 플레이어가 Unity의 디폴트 입력인 "Fire1" 를 누를 때마다 총알 프리팹을 생성할 것 입니다.

우선, 게임 신에 TrueSync Sphere(Game Object/TrueSync/Sphere)를 생성하고 "Tutorial" 폴더로 드래그 하여 새로운 프리팹을 생성합니다.

인스펙터에서 프리팹의 TSRigidbody 컴포넌트를 제거하고 TSSphereCollider 트리거로 만들어 줍니다(아래 그림 참조).

Sphere inspector
인스펙터의 Sphere 컴포넌트.

프리팹 이름을 "ProjectilePrefab" 으로 변경 합니다. 항상 신에서 게임 오브젝트를 제거하고 저장하는 것을 기억해주세요(프리팹만 필요하기 때문입니다).

이제 새로운 C# 스크립트를 생성하고 "PlayerWeapon.cs"로 이름을 부여합니다. 플레이어 움직임을 제어했던 것과 같이 이 스크립트를 편집하여 "TrueSyncBehavior" 에서 상속을 받습니다. 새로운 스크립트에서 이전에 생성했던 총알 프리팹 레퍼런스를 추가하여 아래와 같은 코드로 작성합니다.

C#

using UnityEngine;
using System.Collections;
using TrueSync;

public class PlayerWeapon : TrueSyncBehaviour {

    public GameObject projectilePrefab;

}

이 스크립트를 이전 튜토리얼에서 생성해 놓은 "PlayerBox" 플레이어 프리팹에 붙여줍니다. 이제 이 프리팹에 두 개의 커스텀 스크립트가 있으며, 새 스크립트는 무기 제어를 위한 것 입니다.

프로젝트 탭에서 "ProjectilePrefab" 를 "PlayerWeapon" 스크립트의 슬롯으로 드래그 합니다. 이제 플레이어 프리팹의 인스펙터는 다음 그림과 같아야 합니다.

Player weapon on inspector
인스펙터의 플레이어 무기.

"PlayerWeapon.cs" 스크립트는 "PlayerMovement.cs" 와 유사한 구조를 가지고 있으므로 "OnSyncedInput" 메소드내에서 발사 입력을 수집하여 큐에 넣고 "OnSyncedUpdate" 메소드에서 무기를 쏘고 cooldown 변수를 업데이트 합니다.

input 에 대해서 단순하게 플레이어가 "Fire1" 버튼을 눌렀는지를 알려주는 바이트를 큐에 넣습니다( 0 이면 누르지 않은것, 1이면 눌렀을 때 큐에 넣습니다). 아래의 코드를 "PlayerWeapon.cs" 에 추가합니다.

C#

public override void OnSyncedInput() {
    if (Input.GetButton("Fire1"))
        TrueSyncInput.SetByte (2, 1);
    else
        TrueSyncInput.SetByte (2, 0);
}

이 입력값 키를 2로 등록했다는 것에 주의해주세요. 이미 움직임 입력값으로 0과 1을 이미 사용했기 때문입니다(가속과 방향조정).

아래의 코드는 플레이어가 발사 버튼을 누를때마다 TrueSync가 cooldown 변수의 값에 의해서 총알 프리팹의 복사본 인스턴스를 생성시켜줍니다. 이 코드를 "PlayerWeapon.cs" 스크립트에 추가합니다.

C#

private FP cooldown = 0;

public override void OnSyncedUpdate () {
    byte fire = TrueSyncInput.GetByte (2);
    if (fire == 1 && cooldown <= 0) {
        TrueSyncManager.SyncedInstantiate (projectilePrefab, tsTransform.position, TSQuaternion.identity);
        cooldown = 1;
    }
    cooldown -= TrueSyncManager.DeltaTime;
}

"cooldown" 변수가 FixedPoint 숫자라는 것에 주의 해 주시고, TrueSync 버전의 "DeltaTime" 에 의해 감소되므로 전체 코드는 결정론적입니다.

총알 움직이기

이제 총알을 앞으로 이동시켜야 하며 상대방 플레이어에 맞았거나 일정 시간이 지난 후에는 총알을 없애주어야 합니다. 이를 위해 새로운 C# 스크립트를 생성하고 "Projectile.cs" 로 이름을 부여 합니다. 아래처럼 다른 것과 같이 "TrueSyncBehaviour" 에서 상속을 받습니다.

C#

using UnityEngine;
using System.Collections;
using TrueSync;

public class Projectile : TrueSyncBehaviour {

}

이 스크립트를 총알 프리팹에 붙여주세요. 이제 우리는 몇 가지 속성을 추가하려고 합니다:

  • speed: 총알이 움직이는 속도를 원하는 대로 설정 할 수 있습니다 (FixedPoint 숫자이어야 합니다);
  • direction: 현재 진행하는 방향을 저장하기 위한 TSVector로 총알이 그 방향으로 움직이도록 유지하게 됩니다;
  • destroyTime (private): 총알이 계속 움직이는 시간을 기록하기 위한 다른 FP 로 특정 시간이 지난 후에 파괴 할 수 있습니다;

이러한 속성이 있는 "Projectile.cs" 스크립트와 이동 코드는 다음과 같습니다:

C#

using UnityEngine;
using System.Collections;
using TrueSync;

public class Projectile : TrueSyncBehaviour {

    public FP speed = 15;
    public TSVector direction;
    private FP destroyTime = 3;

    public override void OnSyncedUpdate () {
        if (destroyTime <= 0) {
            TrueSyncManager.SyncedDestroy (this.gameObject);
        }
        tsTransform.Translate (direction * speed * TrueSyncManager.DeltaTime);
        destroyTime -= TrueSyncManager.DeltaTime;
    }

}

"PlayerWeapon.cs" 코드는 총알 인스턴스 생성해주는 코드로 다음과 같이 변경합니다:

C#

public override void OnSyncedUpdate () {
    byte fire = TrueSyncInput.GetByte (2);
    if (fire == 1 && cooldown <= 0) {
        GameObject projectileObject = TrueSyncManager.SyncedInstantiate (projectilePrefab, tsTransform.position, TSQuaternion.identity);

        Projectile projectile = projectileObject.GetComponent<Projectile> ();
        projectile.direction = tsTransform.forward;
        projectile.owner; = owner;

        cooldown = 1;
    }
    cooldown -= TrueSyncManager.DeltaTime;
}

이제 우리는 총알이 움직여야할 방향을 설정했고 이 총알이 어떤 플레이어(TrueSync의 커스텀 ownerID)에 속했는지 표시했습니다(총알의 목표를 식별하기 위하여 다음 섹션에서 사용 될 것 입니다).

물리 콜백과 점수

총알이 플레이어 상자와 부딪혔을 때를 식별하여 맞게 반응할 수 있도록 만들어주어야 합니다. TrueSync 물리 엔진에는 "OnSyncedCollision[Enter/Stay/Exit]" 와 "OnTrigger[Enter/Stay/Exit]"와 같이 Unity 와 유사한 콜백들이 있습니다.

총알 프리팹 콜라이더를 트리거로 해 놓았기 때문에 "OnSyncedTriggerEnter"를 사용할 것 입니다. "Projectile.cs" 에 다음의 코드를 추가해주세요.

C#

public void OnSyncedTriggerEnter(TSCollision other) {
    if (other.gameObject.tag == "Player") {
        PlayerMovement hitPlayer = other.gameObject.GetComponent<PlayerMovement> ();
        if (hitPlayer.owner != owner) {
            TrueSyncManager.SyncedDestroy (this.gameObject);
            hitPlayer.Respawn ();
        }
    }
}

상단의 코드는 "Player" 태그를 가지고 있고, 소유자가 다른, 즉, 적 플레이어 상자를 의미하는 다른 TrueSync 물리 오브젝트에만 영향을 줄 것 입니다.

총알을 없애는 것 뿐만 아니라 부딪히는 상자의 "PlayerMovement.cs" 스크립트 레퍼런스를 얻어 새로운 메소드인 ("Respawn") 호출 한다는 것을 주의해서 보세요.

아래의 메소드는 맞은 플레이어 상자를 무작위 장소에서 다시 스폰을 하고 죽은 수를 증가시켜주는 코드입니다. "PlayerMovement.cs" 에 이 코드를 추가해주세요.

C#

public int deaths = 0;

public override void OnSyncedStart () {
    tsTransform.position = new TSVector (TSRandom.Range(-5,5), 0, TSRandom.Range(-5,5));
}

public void Respawn() {
    tsTransform.position = new TSVector (TSRandom.Range(-5,5), 0, TSRandom.Range(-5,5));
    deaths++;
}

동일한 무작위 스폰 코드를 "OnSyncedStart" 콜백에도 추가하여, 시작시에 모든 플레이어가 서로 다른 장소에 위치 할 수 있도록 하였습니다.

TrueSync 게임은 플레이어의 입력 값을 제외한 모든 계산은 네트워크를 통해 동기화되지 않고 모든 컴퓨터에서 정확하게 같다는 것에 의존하다는 것에 주목해주세요.

게임 점수를 보기 위하여 "PlayerMovement.cs" 에 다음의 GUI 코드를 추가해 주세요.

C#

void OnGUI() {
    GUI.Label (new Rect(10, 100 + 30 * owner.Id, 300, 30), "player: " + owner.Id + ", deaths: " + deaths);
}

테스팅

두 신(Tutorial/Menu 와 Tutorial/Game)이 빌드 설정에 포함되어 있고, Tutorial/Menu 가 첫 번째로 되어 있는지 확인 해 주세요(Unity 가 시작 시 로드할 신).

원하시는 빌드를 선택하여 두 개의 복사본을 실행시키거나, 하나의 복사본을 실행하고 Unity 에디어테서 Menu 신을 실행합니다.

두 클라이언트가 Photon 에 접속된 후 각 클라이언트가 자신의 상자를 제어하는 game 신이 로드되는 것을 볼 수 있어야 합니다.

이제 (link?) 파트4에서 롤백으로 플레이어에서 발생되는 레이턴시를 없애는 방법을 학습 하도록 하겠습니다.

Back to top