This document is about: QUANTUM 2
SWITCH TO

플레이어

소개

Quantum은 플레이어 엔티티 개념에 구애받지 않습니다. 시뮬레이션의 관점에서 모든 엔티티는 동일합니다. 따라서 본 문서에서 "플레이어"를 언급할 때, 우리는 플레이어가 제어하는 엔티티를 의미합니다.

플레이어 식별

플레이어는 2 방식으로 식별할 수 있습니다:

  • 플레이어 인덱스와
  • 플레이어의 PlayerRef.

플레이어 인덱스 지정

Quantum은 player indexSession.Join() 메시지가 도달하는 순서에 의해 서버가 지정합니다. 이 값은 Photon room에 참여한 플레이어들의 순서에 기반한 Photon Id와 혼동해서는 안 됩니다. Photon 플레이어에 "원하는 Quantum Id"를 설정하는 것은 불가능합니다.

중요: 연결 해제되는 이벤트에서, 동일한 ClientId 로 다시 연결하는 경우 Photon Id - public static QuantumRunner StartGame(String clientId, Int32 playerCount, StartParameters param)에 관계없이 동일한 플레이어 인덱스를 얻는 것을 보장합니다.

Player 인덱스 vs PlayerRef

PlayerRef는 Quantum ECS에서 player 인덱스를 감싸는 것입니다. PlayerRef 1로 시작하는 반면, player index는 0부터 시작합니다. 그 이유는 default(PlayerRef) 이 편의를 위해 "null/nalvalid" 플레이어 ref 구조체를 반환하기 때문입니다.

정수형을 PlayerRef 및 그 반대로 자동 캐스트 해주는 연산자들이 있습니다.

  • default(PlayerRef), 내부적으로 0이며 의미는 아무도 없음
  • PlayerRef, 내부적으로 1이며 플레이어 인덱스 0과 같습니다
  • PlayerRef, 내부적으로 2이며 플레이어 인덱스 1과 같습니다

Photon Id

Frame API를 통해 플레이어에 상응하는 Photon Id를 식별할 수 있습니다:

  • Frame.PlayerToActorId(PlayerRef player) 는 Quantum PlayerRef를 ActorId (Photon client id)로 변환하며 또는
  • Frame.ActorIdToAllPlayers(Int32 actorId) 는 이전 메소드의 반대 프로세스입니다.

예를 들어 PhotonPlayer.Nickname을 통해 플레이어 이름을 표시하고 싶은 경우에 사용하세요.

중요: Photon Id 는 Quantum 시뮬레이션과는 관계없습니다.

게임 참여

게임을 시작할 때, 다음 사항이 연속적으로 발생합니다:

1 - QuantumRunner.Session.Join는 원하는 플레이어 수를 사용하여 참가 요청을 서버로 보냅니다. 2 - 검증 및 확인을 하는 서버는 요청을 받고 사용자에게 확인 메시지가 다시 전송됩니다. 요청에 붙여진 정보가 올바르지 않으면 요청이 거부됩니다. 3 - 클라이언트가 게임 시작 메시지를 수신합니다. 4 - 플레이어가 이제 입력을 보내고 받을 수 있습니다. 5 - (선택) - 레이트 조인인 경우 클라이언트는 스냅샷 재동기화를 받을 수 있습니다. 이 경우 4단계에서는 스냅샷을 기다리는 동안 입력을 보내지 않습니다. 6 - (선택) - SendPlayerData를 이제 사용할 수 있습니다. 게임 세션(게임 시작 시 및/또는 세션이 유지되는 동안)에 필요한 횟수만큼 사용할 수 있습니다. SendPlayerData가 호출될 때마다 RuntimePlayer의 직렬화된 버전을 서버로 전송합니다. 플레이어가 서버에 연결되면 틱 입력 세트 확인에 연결되어 신호가 결정론적으로 트리거됩니다.

Config Sequence Diagram
시퀀스 다이어그램

포함된 구성 파일에 대한 상세한 정보는 매뉴얼의 구성 파일 문서를 참고하세요.

SendPlayerData

QuantumGame.SendPlayerData(RuntimePlayer features)는 특별한 종류의 데이터(직렬화된 RuntimePlayer)를 입력 스트림에 결정론적으로 주입하기 위한 데이터 경로입니다. SendPlayerData는 일반적으로 게임 시작 시 모든 플레이어를 설정하기 위해 호출되지만, 데이터를 업데이트해야 하는 경우 게임 세션 중에 호출할 수도 있습니다.

시작 후 Quantum 게임에 참여하여 CallbackGameStarted 콜백이 발생합니다. 각 플레이어가 다른 모든 플레이어의 시뮬레이션에서 플레이어로 추가하기 위해 SendPlayerData 메서드를 호출할 수 있는 것은 바로 이 순간입니다. 이를 명시적으로 호출하면 늦게 참여한 플레이어의 프로세스가 크게 간소화됩니다.

C#

public class MyCallbacks : MonoBehaviour {
  private void OnEnable() {
    QuantumCallback.Subscribe<CallbackGameStarted>(this, OnGameStart);
  }

  private void OnGameStart(CallbackGameStarted callback) {
    // paused on Start means waiting for Snapshot
    if (callback.Game.Session.IsPaused) return;

  // It needs to be sent for each local player.
    foreach (var lp in callback.Game.GetLocalPlayers()) {
      Debug.Log("CustomCallbacks - sending player: " + lp);
      callback.Game.SendPlayerData(lp, new Quantum.RuntimePlayer { });
    }
  }
}

로컬 모드에서는 플레이어 엔티티들이 인스턴스화되지만 멀티플레이어 모드에서는 그렇지 않습니다

대부분의 경우 QuantumGame.SendPlayerData()는 각 플레이어에 실행되지 않습니다. 데모 메뉴를 사용하여 게임을 시작하는 경우 메뉴 장면에 스크립트 CustomCallbacks.cs을 아무 곳에나 추가합니다.

PlayerConnectedSystem

Quantum 세션에 대한 플레이어의 연결을 추적하기 위해 입력 & 연결 플래그를 사용합니다. PlayerConnectedSystem은 절차를 자동화하여 플레이어가 세션에 연결되어 있거나 세션에서 연결이 끊어졌을 경우 시뮬레이션에 알립니다. 시스템을 활용하려면 SystemSetup에 추가해야 합니다.

C#

public static class SystemSetup {
  public static SystemBase[] CreateSystems(RuntimeConfig gameConfig, SimulationConfig simulationConfig) {
    return new SystemBase[] {
      // pre-defined core systems
      ...
  
      new PlayerConnectedSystem(),
      
      // custom systems
      ...
    }
  }
}

연결 및 연결 해제 콜백을 수신하려면 시스템에 ISignalOnPlayerConnectedISignalOnPlayerDisconnected를 구현해야 합니다.

ISignalOnPlayerDataSet

시스템에서 ISignalOnPlayerDataSet을 구현하여 public void OnPlayerDataSet(Frame f, PlayerRef playerRef)에 접근합니다. 직렬화된 RuntimePlayer이 특정 틱 입력의 일부일 때마다 OnPlayerDataSet이 호출됩니다.

RuntimePlayer

RuntimePlayer 클래스는 선택한 캐릭터와 같은 플레이어별 정보를 보유하기 위한 것입니다. RuntimePlayer는 플레이어가 사용자 정의 요구에 맞게 작업하려면 직렬화 메소드를 구현해야 합니다. 에셋 링크의 경우 GUID를 직렬화해야 합니다.

RuntimePlayerquantum.code 프로젝트의 일부 클래스이니다. SDK 업그레이드 및 향후 증빙 작업을 용이하게 하기 위해 사용자 지정 구현을 RuntimePlayer.User.cs에서 수행해야 합니다. 여기서 각 플레이어에 대해 지정하려는 매개 변수를 추가하고 해당 직렬화를 SerializeUserData 메도스에서 구현할 수 있습니다. 결과는 다음과 유사합니다.

C#

namespace Quantum {
  partial class RuntimePlayer {
    public AssetRefCharacterSpec CharacterSpec;

    partial void SerializeUserData(BitStream stream)
    {
      stream.Serialize(ref CharacterSpec.Guid);
    }
  }
}

런타임에 접근

RuntimePlayer와 관련된 플레이어의 에셋 프레임을 PlayerRef를 사용하여 Frame.GetPlayerData()로 쿼리하여 검색할 수 있습니다.

C#

public void OnPlayerDataSet(Frame f, PlayerRef player){
    var data = f.GetPlayerData(player);
}

플레이어 엔티티 초기화

플레이어에 의해 제어되는 엔티티는 시뮬레이션 중 어느 시점에서도 초기화될 수 있습니다. 일반적인 초기화 방법은 플레이어가 연결할 때 초기화(ISignalOnPlayerConnected) 하거나 플레이어 데이터가 수신될 때((ISignalOnPlayerDataSet)입니다.

  • ISignalOnPlayerConnected: 시뮬레이션 또는 에셋 데이터베이스에서 이미 사용 가능한 모든 정보를 사용하여 플레이어 엔티티를 초기화할 수 있습니다.

  • ISignalOnPlayerDataSet: 플레이어 엔티티는 플레이어의 RuntimePlayer 특정 정보와 관련된 정보로 초기화할 수 있습니다. 이것은 선택한 캐릭터 모델/스킨 또는 인벤토리 로드아웃과 같은 경우에 편리합니다.

Simulation vs View

먼저, 다음 사항을 명백화합니다:

  1. 시뮬레이션의 관점(Quantum)에서 플레이어가 제어하는 엔티티는 플레이어 입력을 가진 엔티티이다. 로컬 또는 원격 플레이어에 대해서는 알지 못합니다.
  2. 뷰의 관점(유니티)에서 로컬 클라이언트의 플레이어로부터 입력된 내용을 폴링 합니다.

다시 복구하기 위해 시뮬레이션에는 "로컬" 또는 "원격" 플레이어가 없지만, 뷰에 있는 플레이어는 "로컬"이거나 그렇지 않습니다.

C#

Photon.Deterministic.DeterministicGameMode.Local
Photon.Deterministic.DeterministicGameMode.Multiplayer
Photon.Deterministic.DeterministicGameMode.Replay

최대 플레이어 수

최대 플레이어 수는 각 프레임에 대해 메모리 청크 내부에서 할당해야 하는 공간을 정의하기 위해 미리 알아야 합니다. 기본적으로 플레이어의 최대 수는 6명입니다. 이를 변경하려면 qtn-파일에 다음 줄을 추가합니다:

Unknown

#define PLAYER_COUNT 8
#pragma max_players PLAYER_COUNT
  • define은 정의 기능을 수행하며 DSL 내에서 사용될 수 있습니다(예, 플레이어 수로 배열 할당).
  • pragma는 몇 명의 플레이어 시뮬레이션을 처리하는지 정의합니다.

로컬 플레이어

Quantum 은 뷰에서 플레이어가 로컬인지를 확인하는 API를 제공합니다:

  • QuantumRunner.Default.Game.Session.IsLocalPlayer(int player); 그리고,
  • QuantumRunner.Default.Game.PlayerIsLocal(PlayerRef playerRef).

복수 로컬 플레이어

QuantumRunner.Default.Game.GetLocalPlayers()는 각 클라이언트에서 고유한 배열을 반환하고 Quantum 시뮬레이션에서 로컬 컴퓨터가 제어하는 플레이어의 인덱스를 나타냅니다.

  • 로컬 플레이어가 한 명일 경우 인덱스를 한 개 반환합니다. 여러 플레이어가 동일한 로컬 컴퓨터 컨트롤에 있는 경우 배열의 크기는 로컬 플레이어 수입니다.
  • QuantumInput.Instance.PollInput(int player) 에 전달되는 인덱스와 정확히 동일합니다.
  • 인덱스는 서버에서 정의됩니다(로컬 게임이 아닌 경우).
  • 인덱스는 항상 [0, PlayerCount-1] 범위입니다. PlayerCount는 해당 경기의 총 플레이어 수를 나타냅니다. QuantumRunner.StartGame으로 전달됩니다.
  • 인덱스 값은 임의로 지정되며(이 세션의 0부터 최대 플레이어 범위 이내) 여러 플레이어의 연결 및 연결 해제 순서와 해당 메시지가 서버에 도달하는 시간에 따라 달라집니다.
  • 로컬 컴퓨터에 둘 이상의 플레이어가 있는 경우 값이 연속적이지 않을 수 있습니다.
  • 동일한 GUID를 가지고 Session.Join()으로 게임에 다시 참여하고 연결된 이후 새 플레이어가 들어가지 않았다면 동일한 플레이어 인덱스를 할당받을 수 있습니다.

런타임 플레이어 데이터를 전송하기 위해 위에서 설명한 함수에 로컬 플레이어 인덱스를 사용합니다:QuantumGame.SendPlayerData(int player, RuntimePlayer data). 한 기기에 있는 모든 플레이어에 대해 이렇게 수행합니다.

Back to top