플레이어
소개
Quantum은 플레이어 엔티티 개념에 구애받지 않습니다. 시뮬레이션의 관점에서 모든 엔티티는 동일합니다. 따라서 본 문서에서 "플레이어"를 언급할 때, 우리는 플레이어가 제어하는 엔티티를 의미합니다.
플레이어 식별
플레이어는 2 방식으로 식별할 수 있습니다:
- 플레이어 인덱스와
- 플레이어의 PlayerRef.
플레이어 인덱스 지정
Quantum은 player index
를 Session.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
의 직렬화된 버전을 서버로 전송합니다. 플레이어가 서버에 연결되면 틱 입력 세트 확인에 연결되어 신호가 결정론적으로 트리거됩니다.

포함된 구성 파일에 대한 상세한 정보는 매뉴얼의 구성 파일 문서를 참고하세요.
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
...
}
}
}
연결 및 연결 해제 콜백을 수신하려면 시스템에 ISignalOnPlayerConnected
및 ISignalOnPlayerDisconnected
를 구현해야 합니다.
ISignalOnPlayerDataSet
시스템에서 ISignalOnPlayerDataSet
을 구현하여 public void OnPlayerDataSet(Frame f, PlayerRef playerRef)
에 접근합니다. 직렬화된 RuntimePlayer
이 특정 틱 입력의 일부일 때마다 OnPlayerDataSet
이 호출됩니다.
RuntimePlayer
RuntimePlayer
클래스는 선택한 캐릭터와 같은 플레이어별 정보를 보유하기 위한 것입니다. RuntimePlayer
는 플레이어가 사용자 정의 요구에 맞게 작업하려면 직렬화 메소드를 구현해야 합니다. 에셋 링크의 경우 GUID를 직렬화해야 합니다.
RuntimePlayer
는 quantum.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
먼저, 다음 사항을 명백화합니다:
- 시뮬레이션의 관점(Quantum)에서 플레이어가 제어하는 엔티티는 플레이어 입력을 가진 엔티티이다. 로컬 또는 원격 플레이어에 대해서는 알지 못합니다.
- 뷰의 관점(유니티)에서 로컬 클라이언트의 플레이어로부터 입력된 내용을 폴링 합니다.
다시 복구하기 위해 시뮬레이션에는 "로컬" 또는 "원격" 플레이어가 없지만, 뷰에 있는 플레이어는 "로컬"이거나 그렇지 않습니다.
C#
Photon.Deterministic.DeterministicGameMode.Local
Photon.Deterministic.DeterministicGameMode.Multiplayer
Photon.Deterministic.DeterministicGameMode.Replay
최대 플레이어 수
최대 플레이어 수는 각 프레임에 대해 메모리 청크 내부에서 할당해야 하는 공간을 정의하기 위해 미리 알아야 합니다. 기본적으로 플레이어의 최대 수는 6명입니다.
이를 변경하려면 qtn
-파일에 다음 줄을 추가합니다:
#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)
. 한 기기에 있는 모든 플레이어에 대해 이렇게 수행합니다.