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

Bolt 102 - 시작하기

이 튜토리얼에서는 Bolt 로 첫 번째 멀티플레이어 게임을 어떻게 제작하는지 학습합니다. Bolt 로 간단한 씬을 셋업 하셨을 것이고 객체 스폰하기와 트랜스폼 복제와 같은 모든 기본적인 작업을 수행하셨을 것입니다. 명확성과 수업의 목적을 위해 코드는 Bolt 의 내장 자동화와 테스팅 툴이 선호됩니다.

이 튜토리얼은 Bolt 의 'DEBUG' 빌드를 사용하여 진행되며 다른 빌드에 대한 상세한 정보는 Debug vs. Release 페이지를 참고하시기 바랍니다. 처음 Bolt 를 설치하는 설명에 대해서는 설치하기 설명을 참고하세요. Bolt 업그레이드 설명에 대해서는 업그레이드하기 를 참고하세요. Bolt 를 설치 또는 업그레이드 후에 아래와 같은 폴더구조와 같아야 합니다:

bolt folder on unity assets
Bolt Folder on Unity Assets.

Bolt 와 다른 Unity 용 네트워킹 솔루션과 비교할 때 Bolt 에서는 직접 코딩을 해야 하는 실제 네트워킹 코드가 거의 없다는 점이 차이가 나게 됩니다. 모든 직렬화, 보간법, 보외법, 애니메이션 복제등은 Bolt 가 자동으로 처리해줍니다.

Bolt 에게 알려주여야 할 유일한 것은 "무엇"을 네트워크를 통해 복제할 것 인지만 알려주면됩니다. 이 과정은 Bolt Assets 윈도우를 통해 처리되며 Window/Bolt Engine/Assets 아래에 있습니다.

access bolt assets from window menu
Access Bolt Assets from Window Menu.

에셋은 아래의 네 카테고리로 분류됩니다:

  • 상태 상태(State)는 Bolt 로 게임을 만들때 골자이며, 상태는 이름, 체력, 트랜스폼, 애니메이션 파라미터등과 같은 것들을 정의 할 수 있습니다.
  • 객체 객체들은 C# 클래스와 동일한 방식으로 논리적으로 여러 프로퍼티들을 그룹으로 묶어놓는 방식입니다. 예를들어 RPG 게임에서 플레이어의 인벤토리를 생성하고 싶다고하면, "아이템" 이라고 하는 객체를 생성하고 ItemId, 내구성등과 같은 프로퍼티를 부여 할 수도 있습니다. 그리고나서 상태 에서 아이템 객체들의 배열을 만듭니다.
  • 명령어 명령어들은 Bolt의 신뢰성 높은 기능을 위해 독점적으로 사용되며 사용자 입력을 캡슐화하고 월드 캐릭터에게 입력 적용 결과를 캡슐화 하는데 사용됩니다. 명령어는 클라이언트측 고급 예측과 예측된 상태에 대한 자동 보정을 구현할 수 있도록 합니다.
  • 이벤트, Bolt 에서는 PRC 대신 이벤트로 의사결정을 합니다. 이벤트들은 게임의 다른 파트들과 더 쉽게 연관성을 제거해주기 때문에 동시에 단일 이벤트에 대하여 여러개의 리스너를 가질 수 있습니다.

Bolt Assets 윈도우 하단에 Bolt 의 버전 번호, 디버그/릴리즈 모드가 표시되어 있으며 컴파일하고 Bolt Assets을 저장하는 두개의 버튼이 있습니다.

Bolt Assets 윈도우의 빈 공간을 아무데나 우클릭을 하여 'New State'를 선택하세요.

bolt assets window - create a new state
Bolt Assets Window - Create a new State.

Bolt는 'NewState' 라고하는 새로운 상태를 생성하고 새로운 상태를 활성화 시킬 Bolt 에디터 윈도우가 팝업 됩니다.

bolt assets window - new state
Bolt Assets Window - New State.

상단의 텍스트 필드에 있는 상태의 이름을 'CubeState' 로 바꾸고 'New Property' 를 클릭하여 'CubeTransform' 으로 이름을 변경합니다. 그리고 타입을 'Transform' 으로 설정합니다. 나머지 설정들은 변경하지 않고 그대로 둡니다.

실수로 필요없이 하나 이상의 프로퍼티 또는 에셋을 추가했으면 키보드에서 Ctrl 키를 누르고 있으면 모든 에셋의 옆에 작은 붉은 X 가 팝업되어 프로퍼티를 삭제할 수 있습니다.

bolt assets window - edit new state
Bolt Assets Window - Edit New State.

Bolt 가 상태와 프로퍼티를 알 수 있도록 하기 위해 컴파일이 필요하며 Assets/Bolt Engine/Compile Assembly 메뉴 옵션 또는 Bolt Assets 윈도우안에 있는 작은 녹색 화살표 아이콘으로 컴파일 할 수 있습니다. 두 방식은 동일한 일을 수행합니다.

bolt compile menu
Bolt Compile Menu.

Bolt를 컴파일 할때는 Unity 에디터 콘솔에 몇개의 메시지가 출력됩니다.

bolt compile output
Bolt Compile Output.

Unity의 Project 탭으로 이동하여 'Tutorial' 폴더를 생성하고 두개의 하위 폴더 'Scripts' 와 'Prefabs' 를 생성합니다.

new tutorial folder
New Tutorial Folder.

우리가 첫 번째로 할 일은 표준 큐브로 Bolt용 프리팹을 생성하는 것 입니다. GameObject/Create Other/Cube (v5 GameObject/3D Object/Cube) 메뉴에서 큐브를 생성하여 계층구조에서 프로젝트 탭에 있는 'Tutorial/Prefabs' 폴더로 드래그해서 놓습니다. 완료되었으면 왼쪽 계층구조에서 큐브를 삭제할 수 있습니다.

new cube prefab
New Cube Prefab.

Bolt가 'Cube' 프리팹을 알 수 있도록 하기위해서 큐브 프리팹에 'Bolt Entity' 를 추가합니다. Add Component 를 클릭하고 이름으로 컴포넌트를 찾습니다.

bolt entity creation
Bolt Entity Creation.

Bolt가 처음으로 우리의 'Cube' 프리팹을 인지 했기 때문에, Bolt Entity 에디터에는 몇 개의 에러가 표시됩니다. 'Prefab Id' 필드 아래에 있는 두 개의 에러들은 Assets/Bolt Engine/Compile Assembly 또는 'Bolt Assets' 윈도우에서 녹색 화살표 아이콘을 이용하여 Bolt용 컴파일러를 수행하면 해결 됩니다. 'Serializer' (v5 State) 아래의 오류는 'NOT ASSIGNED' 가 있는 드롭다운 목록에서 ICubeState serializer (v5 state) 를 선택하면 해결 됩니다.

bolt entity setup
Bolt Entity Setup.

'Bolt Entity' 에는 우리가 변경할 수 있는 수 많은 설정항목들이 있지만, 거의 대부분 프리팹들과 게임 유형에 대하여 디폴트 값을 변경할 필요는 없습니다. 설정들은 특별한 경우에서만 다루어 지게되며 이 문서의 고급 섹션에 상세한 설명이 있습니다.

실제 C# 코드를 작성하기 전에 Unity 에서 큐브를 스폰할 간단한 씬을 설정하도록 하겠습니다. 새로운 씬을 생성하고 이름을 'Tutorial/Tutorial1'로 저장합니다.

tutorial scene creation
Tutorial Scene Creation.

씬에 GameObject/Create Other/Plane (v5 GameObject/3D Object/Plane) 에서 새로운 플레인을 생성하고 GameObject/Create Other/Directional Light 에서 Directional Light 를 생성합니다. 그리고 플레인에 신규 머티리얼을 생성하여 'Tutorial/Tutorial1_Plane' 로 저장해 큐브와 다른 색상을 주었습니다.

scene configuration for each game object
Scene Configuration for each Game Object.

씬에서 메인 카메라, 플레인, 디렉셔널 라이트의 설정이 변경된 사항에 대해서 강조 해 놓았습니다:

이제 코딩을 할 시간입니다. 'Tutorial/Scripts' 폴더에서 'NetworkCallbacks' 라고하는 새로운 스크립트를 생성하여 텍스트 편집기에서 기본 unity 메소드를 없애서 다음과 같이 편집합니다.

C#

using UnityEngine;
using System.Collections;

public class NetworkCallbacks : MonoBehaviour {

}

이제 기본 클래스를 MonoBehaviour 에서 Bolt.GlobalEventListener 로 변경하겠습니다. Bolt.GlobalEventListener 클래스 자체는 MonoBehaviour 로 부터 상속되었기 때문에 일반적인 Unity 처럼 사용할 수 있으나 상단에는 몇개의 Bolt 전용 메소드가 추가되었습니다.

C#

using UnityEngine;
using System.Collections;

public class NetworkCallbacks : Bolt.GlobalEventListener {

}

우리가 원하는 것은 씬이 로딩(이 경우는 우리의 'Tutorial1' 씬)될 때 콜백을 수신 하는 것으로 언제 우리의 'Cube' 프리팩 인스턴스화를 해야하는지 알기 위한 것 입니다. Bolt는 네트워크상에서 Unity 씬을 자동으로 로딩하는 것을 지원하여 씬 로드가 완료된 것을 후크하는 콜백은 SceneLoadLocalDone 입니다.

Bolt는 메소드 구현을 위해 오버라이드 메소드로 지정하지 않는 Unity 방식 대신 표준 C# 방식인 public override 를 사용한다는 점을 기억 해주셔야 합니다.

C#

using UnityEngine;
using System.Collections;

public class NetworkCallbacks : Bolt.GlobalEventListener {
  public override void SceneLoadLocalDone(string map) {

  }
}

Bolt에서 프리팹의 인스턴스생성을 하기위해 BoltNetwork.Instantiate 메소드를 사용합니다. 일반적으로 이 메소드는 Unity의 내장 GameObject.Instantiate 와 아주 똑같이 동작합니다. 이 메소드에는 여러가지의 파라미터들을 가진 여러개의 오버로드 메소드들이 있지만, 간단한 한가지 메소드에서만 사용할 것입니다: 파라미터는 Bolt 프리팹 레퍼런스, 위치와 회전을 받습니다.

C#

using UnityEngine;
using System.Collections;

public class NetworkCallbacks : Bolt.GlobalEventListener {
  public override void SceneLoadLocalDone(string map) {
    // randomize a position
    var pos = new Vector3(Random.Range(-16, 16), 0, Random.Range(-16, 16));

    // instantiate cube
    BoltNetwork.Instantiate(BoltPrefabs.Cube, pos, Quaternion.identity);
  }
}

XZ 평면에서 위치를 무작위로 놓게 하여 모든 것이 같은 위치에 스폰되지 않도록 하였고, 그 후 큐브에 대한 레퍼런스, 무작위 위치와 기본 회전을 가지고 BoltNetwork.Instantiate 를 호출 합니다.

BoltPrefabs.Cube 사용법은 Bolt를 처음 사용하신다면 약간 이상해 보일 수도 있습니다. BoltPrefabsAssets/Bolt Engine/Compile Assembly 를 실행할 때 Bolt에 의해 컴파일 되고 업데이트되는 정적 클래스입니다. 각각의 프리팹에 대해서 Bolt 엔티티를 가진 유일한 레퍼런스를 포함하고 있습니다. 필요하다면 프리팹에게 일반적인 게임오브젝트 레퍼런스를 전달할 수도 있습니다.

완료하기전에 NetworkCallbacks 스크립트에 마지막으로 처리해야할 것은 public class NetworkCallbacks ... 정의 위에 [BoltGlobalBehaviour] 라고 하는 속성을 추가해주는 것 입니다.

C#

using UnityEngine;
using System.Collections;

[BoltGlobalBehaviour]
public class NetworkCallbacks : Bolt.GlobalEventListener {
  public override void SceneLoadLocalDone(string map) {
    // randomize a position
    var pos = new Vector3(Random.Range(-16, 16), 0, Random.Range(-16, 16));

    // instantiate cube
    BoltNetwork.Instantiate(BoltPrefabs.Cube, pos, Quaternion.identity);
  }
}

이 속성이 하는 일은 Bolt가 자동적으로 이 스크립트를 감지하여 Bolt와 생명주기를 같이 하는 인스턴스를 생성하고 Bolt가 셧다운 될 때 파괴하는 것 입니다.

중요: 이 스크립트의 인스턴스를 Unity 내의 어떤 게임 오브젝트에 수동으로 붙이지 않아야 합니다. Bolt가 자동적으로 처리해줍니다. 어딘가에 Bolt.GlobalEventListener 를 수작업으로 붙이고 싶다면 [BoltGlobalBehaviour] 속성을 추가하지 않아야 합니다.

큐브가 스폰되는 것을 테스트 하기전 가장 마지막 단계로 수행해야 하는 것은 Bolt를 시작하는 메인 메뉴 스크립트를 설정해주는 것 입니다. 새로운 씬을 생성하고 'Tutorial/Tutorial1_Menu'로 저장합니다.

더 진행전에 메뉴에 대해 코딩을 해 줄 것입니다. 'Tutorial1' 과 'Tutorial1_Menu' 씬들이 Unity의 빌드 설정에 추가되어 있는지 확인해주세요.

configure scenes to build
Configure Scenes to build.

중요: 두 개의 씬을 추가한 이후 Assets/Bolt Engine/Compile Assembly 을 다시한번 실행하여 Bolt가 씬을 인지할 수 있도록 해 줍니다. 그리고 'Tutorial1_Menu' 씬이 첫 번째로 되어 있는지 확인하여 게임 시작시에 로드할 수 있도록 해 주세요.

이제 아주 간단한 메뉴 스크립트를 작성하여 'Tutorial1_Menu' 씬에 있는 MainCamera에 붙일 것 입니다. 'Tutorial/Scripts' 에서 'Menu' 라고 하는 스크립트를 생성합니다.

C#

using UnityEngine;
using System.Collections;

public class Menu : MonoBehaviour {
  void OnGUI() {

  }
}

Unity 기본 메소드를 없애고 OnGUI 메소드를 추가합니다. 화면상단에 펼쳐지는 두 개의 버튼을 생성할 것인데, 하나는 서버 시작을 해주는 버튼이고 다른 하나는 클라이언트를 시작시켜주는 버튼입니다.

C#

using UnityEngine;
using System.Collections;

public class Menu : Bolt.GlobalEventListener {
  void OnGUI() {
    GUILayout.BeginArea(new Rect(10, 10, Screen.width - 20, Screen.height - 20));

    if (GUILayout.Button("Start Server", GUILayout.ExpandWidth(true), GUILayout.ExpandHeight(true))) {
      // START SERVER
      BoltLauncher.StartServer(UdpKit.UdpEndPoint.Parse("127.0.0.1:27000"));
    }

    if (GUILayout.Button("Start Client", GUILayout.ExpandWidth(true), GUILayout.ExpandHeight(true))) {
      // START CLIENT
      BoltLauncher.StartClient();
    }

    GUILayout.EndArea();
  }

   public override void BoltStartDone() {
     if(BoltNetwork.isServer)
       BoltNetwork.LoadScene("Tutorial1");
     else  BoltNetwork.Connect(UdpKit.UdpEndPoint.Parse("127.0.0.1:27000"));

  }
}

모든 GUILayout.* 호출은 표준 Unity 이므로 건너 띄도록 하겠습니다. 코드에서 중요한 두 부분은 두 개의 if-블록 입니다.

서버는 BoltLauncher.StartServer 를 호출하여 시작됩니다. 그리고 나서 리슨하고 있는 엔드-포인트(ip-주소+포트)를 넘겨줍니다. 서버가 시작된 이후 우리는 BoltNetwork.LoadScene("Tutorial1") 를 호출하여 bolt가 'Tutorial1' 씬을 로드하도록 알려줍니다.

클라이언트의 프로세스는 서버와 매우 유사합니다. BoltLauncher.StartClient 를 호출하지만 일반적으로 특정 엔드-포인트를 클라이언트에게 지정할 필요는 없고 운영체제가 하나를 추측하도록 그냥 두게 할 수 있습니다. 그리고 서버에 BoltNetwork.Connect 로 접속하고 서버가 시작했을 때 BoltLauncher.StartServer 에 전달한것과 같이 동일 엔드-포인트를 넘겨 줍니다.

'Menu' 스크립트를 'Tutorial1_Menu' 씬에 있는 'MainCamera' 게임 오브젝트에 붙여줍니다.

tutorial menu scene setup
Tutorial Menu scene Setup.

중요: 'Menu' 스크립트를 붙이는 씬이 올바른지 확인해주세요.

게임을 시작하기 전에 Unity 의 'Player Settings' 설정에 가서 'Run In Background' 가 사용가능으로 되어 있는지 확인 해주세요.

스탠드얼론 데스크톱 게임 버전을 빌드하고 두 개의 인스턴스를 시작해주세요. 윈도우에서 'Windows 방화벽' 팝업이 뜨면 '접근 허용'을 클릭해주시면 됩니다.

'윈도우드' 모드에서 수행하고 있는지 확인해주시고, 1280x720과 같이 16:9 포맷의 해상도를 선택합니다.

start game configuration
Start game configuration.

첫 번째 인스턴스의 게임이 시작되면 'Start Server'를 클릭하고 두 번째 인스턴스에서는 'Start Client' 를 클릭합니다. 두 개의 인스턴스가 보여야 하며, 두 개의 큐브가 스폰되는 것이 보여야 합니다( 게임 인스턴스별로 하나).

game running.
Game running.

디버그 모드에서 수행할 때는 Bolt가 기본적으로 추가하는 'Console' 과 하단에 작은 'Bolt Performance' 표시도 보입니다. 작은 Bolt 아이콘에 마우스 커서를 올리면 Bolt는 객체의 상태(홈키를 누르세요)에 관한 디버그 정보를 표시해 줄 것입니다.

[다음 장 >>]에서 계속 (./bolt-103-properties-and-callbacks).

Back to top