사전 구축 컴포넌트
소개
Fusion은 다양한 사전 빌드 된 NetworkBehaviour
를 제공하여 빠르게 작동할 수 있도록 지원합니다.
NetworkRunner
NetworkRunner
는 Fusion의 핵심입니다. NetworkRunner
는 네트워크에 대한 연결을 관리하고 입력 수집에서 스냅샷 병합, 콜백 및 라이프 사이클 메소드 호출에 이르기까지 시뮬레이션을 제어합니다. 각 클라이언트와 서버의 씬에 있는 NetworkRunner
는 하나뿐입니다.
이러한 작업을 수행하기 위해 NetworkRunner
는 모든 NetworkObject
s, NetworkBehaviour
그리고 SimulationBehaviour
를 추적합니다.
속성
모든 NetworkBehaviour
는 Runner
속성을 통해 현재의 NetworkRunner
에 접근할 수 있으며, Runner 자체가 일부 중요한 시스템 속성을 나타냅니다.
IsServer
: 러너가 권한 서버를 나타내는 경우 true.Stage
: 현재 시뮬레이션 단계입니다. 시뮬레이션이 현재 다음 상태를 예측하고 있는 경우Forward
가 되고, 서버 업데이트를 조정하기 위해 이전 상태를 다시 시뮬레이션하는 경우Resimulate
가 될 수 있습니다. 서버나 시뮬레이션이 항상Forward
인 Client-Authority 모드에서는Resimulate
가 발생하지 않습니다.
GameMode
GameMode
는 로컬 피어의 행동을 정의해 줍니다. GameMode
는 NetworkRunner.StartGame()
메소드에 파라미터로 전달됩니다.
client
: 클라이언트로 실행합니다. 클라이언트 호스트(클라우드-쉐어) 또는 전용(서버-기반)으로 연결하기 위하여 로컬 플레이어를 생성합니다.host
: 서버로 실행합니다. 로컬 플레이어를 생성합니다(서버 + 클라이언트)server
: 전용 서버로 실행합니다. 플레이어 없음shared
: 클라이언트로 실행합니다. Fusion 플러그인 서버에 연결하기 위해 로컬 플레이어를 생성합니다.single
: "서버"로 실행합니다. 로컬 플레이어를 생성하며, 연결이 허용되지 않습니다(Singleplayer)
NetworkTransform
NetworkTransform
은 실제 로컬 Transform
객체와 시뮬레이션 상태 사이를 보간하여 단순한 운동학적 객체를 관리합니다.
NetworkRigidbody
리기드 바디의 경우, NetworkRigidbody
는 단순히 이전 상태 사이를 보간하는 것보다 더 나은 기능을 하며 더 정확한 로컬 위치를 추정하기 위해 물리 객체의 예측 가능한 특성을 사용합니다.
NetworkMecanimAnimator
NetworkMecanimAnimator
는 관련 유니티 메커니즘 Animator
컴포넌트가 보유한 매개 변수의 상태와 값을 동기화합니다.
유니티의 Animator
컴포넌트는 다시 감거나 다시 시뮬레이션할 수 없으며(앞으로만 실행되도록 설계됨) 틱 상태로 정확하게 설정할 수 없기 때문에 틱 정확한 애니메이션에 의존할 수 없다는 점에 유의해야 합니다.
이러한 "포워드 전용" 제한 때문에 NetworkMecanimAnimator
는 재시뮬레이션을 시도하지 않으며 NetworkMecanimAnimator
은 상태 권한에서 프로시로 Animator
컴포넌트를 동기화만 합니다. 입력 권한은 앞으로 이동할 때 애니메이터에도 변경 사항을 적용해야 합니다.
C#
void FixedUpdateNetwork()
{
// Only apply changes to the Animator if input is available
// (which is true for StateAuthority and InputAuthority),
// and only on Forward ticks (resimulation should be ignored).
if (GetInput(out var input) && Runner.IsForward)
{
// Apply inputs to Animator
}
}
SetTrigger()
패스-쓰루 NetworkMecanimAnimator.SetTrigger()
메소드는 Animator.SetTrigger()
호출 대신에 사용되어야 합니다. 트리거들이 일시적이고 백업 부울값이 NetworkMecanimAnimator
가 Animator
컴포넌트의 값을 가져오기 이전에 false로 재설정될 수 있기 때문입니다. 편의상 입력 권한 에서 즉시 Animator.SetTrigger()
로 전달하는 NetworkMecanimAnimator.SetTrigger()
에 passThroughOnInputAuthority
옵션이 있습니다.
NetworkCharacterControllerPrototype
이 컴포넌트는 유니티의 CharacterController
를 NetworkObject
에서 동기화하는 방법을 보여주는 프로토타이핑 예입니다. 유니티의 CharacterController
는 본질적으로 예측 및 재시뮬레이션과 호환되지 않으므로 이 프로토타입은 적절한 시기에 CC를 활성화 및 비활성화하여 이러한 한계를 해결하는 방법을 보여줍니다. 이 컴포넌트를 기본 클래스로 사용하여 필요에 따라 확장하거나, 사용자 정의 구현을 위한 시작점으로 복사 및 이름을 변경할 수 있습니다.
NetworkCharacterController(이전 방식)
가장 불규칙한 객체는 플레이어가 직접 제어하는 객체(일반적으로 캐릭터 컨트롤러라고 함)이며, Fusion에는 특정 사용 사례에 대해서 NetworkCharacterController
가 있습니다.
사전 구축된 NetworkCharacterController
는 일반적으로 원하는 동작을 포함하므로 빠른 프로토 타이핑을 가능하게 합니다. 그러나 캐릭터 컨트롤러 구현이 게임마다 매우 다르기 때문에 모든 경우에 적합한 캐릭터 컨트롤러는 없습니다.
따라서 NetworkCharacterController
가 사용하는 두 가지 핵심 방식인 Move()
와 ComputeRawMovement()
의 소스 코드를 읽어 게임 제작에 필요한 대체 맞춤형 캐릭터 컨트롤러를 만드는 영감을 이끌어내는 것이 좋습니다.
ComputeRawSteer()
ComputeRawSteer()
는 캐릭터가 현재 수행하고 있는 동작 유형에 따라 동작 계산을 대부분 수행하는 내부 메소드입니다. 사전 구축된 NetworkCharacterController
에서 currently performing. In the pre-built NetworkCharacterController
, Move()
는 작성할 구조체에 대한 참조를 전달하여 ComputeRawMovement()
에서 movementPack
값을 요청합니다.
C#
void ComputeRawSteer(ref Movement movementPack, float dt) {
Grounded = movementPack.Grounded;
float minYSpeed = -100;
float maxYSpeed = 100;
var current = Velocity;
switch (movementPack.Type) {
case MovementType.FreeFall:
current.y -= Config._gravityStrength * dt;
if (!Config.AirControl || movementPack.Tangent == default(Vector3)) {
current.x = Mathf.Lerp(current.x, 0, dt * Config.Braking);
current.z = Mathf.Lerp(current.z, 0, dt * Config.Braking);
} else {
current += movementPack.Tangent * Config.Acceleration * dt;
}
break;
case MovementType.Horizontal:
// apply tangent velocity
current += movementPack.Tangent * Config.Acceleration * dt;
var tangentSpeed = Vector3.Dot(current, movementPack.Tangent);
// lerp current velocity to tangent
var tangentVel = tangentSpeed * movementPack.Tangent;
var lerp = Config.Braking * dt;
current.x = Mathf.Lerp(current.x, tangentVel.x, lerp);
current.z = Mathf.Lerp(current.z, tangentVel.z, lerp);
// we only lerp the vertical velocity if the character is not jumping in this exact frame,
// otherwise it will jump with a lower impulse
if (Jumped == false) {
current.y = Mathf.Lerp(current.y, tangentVel.y, lerp);
}
// clamp tangent velocity with max speed
if (tangentSpeed > MaxSpeed) {
current -= movementPack.Tangent * (tangentSpeed - MaxSpeed);
}
break;
case MovementType.SlopeFall:
current += movementPack.SlopeTangent * Config.Acceleration * dt;
minYSpeed = -Config.MaxSlopeSpeed;
break;
case MovementType.None:
var lerpFactor = dt * Config.Braking;
if (current.x != 0) {
current.x = Mathf.Lerp(current.x, default, lerpFactor);
if (Mathf.Abs(current.x) < float.Epsilon) {
current.x = 0;
}
}
if (current.z != 0) {
current.z = Mathf.Lerp(current.z, default, lerpFactor);
if (Mathf.Abs(current.z) < float.Epsilon) {
current.z = 0;
}
}
// we only lerp the vertical velocity back to 0 if the character is not jumping in this exact frame,
// otherwise it will jump with a lower impulse
if (current.y != 0 && Jumped == false) {
current.y = Mathf.Lerp(current.y, default, lerpFactor);
if (Mathf.Abs(current.y) < float.Epsilon) {
current.y = 0;
}
}
minYSpeed = 0;
break;
}
// horizontal is clamped elsewhere
if (movementPack.Type != MovementType.Horizontal) {
Vector2 h = new Vector2(current.x, current.z);
if (h.sqrMagnitude > MaxSpeed * MaxSpeed) {
h = h.normalized * MaxSpeed;
}
current.x = h.x;
current.y = Mathf.Clamp(current.y, minYSpeed, maxYSpeed);
current.z = h.y;
}
Velocity = current;
// set jump state
Jumped = false;
}
Move()
이것은 전체 Move()
함수의 기본 구현입니다. 이동 쿼리를 수행하고 그 결과를 사용하여 새 속도를 계산한 다음 침투 보정 + 속도 적분을 변환 위치에 적용합니다. 회전은 변경되지 않습니다.
direction
: 의도된 이동 방향, 이동 쿼리 + 가속callback
: 선택적인 사용자 지정 콜백 객체layerMask
: 선택적인 레이어 마스크. 전달되지 않으면, 구성의 기본값이 사용됩니다.
C#
public void Move(Vector3 direction, ICallbacks callback = null, LayerMask? layerMask = null) {
var dt = Runner.DeltaTime;
var movementPack = ComputeRawMovement(direction, callback, layerMask);
ComputeRawSteer(ref movementPack, dt);
var movement = Velocity * dt;
if (movementPack.Penetration > float.Epsilon) {
if (movementPack.Penetration > Config.AllowedPenetration) {
movement += movementPack.Correction;
} else {
movement += movementPack.Correction * Config.PenetrationCorrection;
}
}
_transform.position += movement;
#if DEBUG
LastMovement = movementPack;
#endif
}
NetworkEvents
이벤트 핸들러를 Unity Inspector에서 직접 연결하기 위해 NetworkRunner.AddCallbacks()
대신 NetworkEvents
를 사용할 수 있습니다. 컴포넌트를 NetworkObject
에 추가하고 끌어서 놓기를 사용하여 개별 이벤트 핸들러를 등록하기만 하면 됩니다.
NetworkEvents
는 사용할 필요가 없으며 대부분의 게임에서는 필요하지 않지만, 네트워크 이벤트를 유니티 씬에서 구성해야 하는 아주 드문 경우 편의를 위해 포함되어 있습니다.