This document is about: FUSION 1
SWITCH TO

수정중인 페이지 입니다.

사전 구축 컴포넌트

소개

Fusion은 다양한 사전 빌드 된 NetworkBehaviour를 제공하여 빠르게 작동할 수 있도록 지원합니다.

NetworkRunner

NetworkRunner는 Fusion의 핵심입니다. NetworkRunner는 네트워크에 대한 연결을 관리하고 입력 수집에서 스냅샷 병합, 콜백 및 라이프 사이클 메소드 호출에 이르기까지 시뮬레이션을 제어합니다. 각 클라이언트와 서버의 씬에 있는 NetworkRunner는 하나뿐입니다.

이러한 작업을 수행하기 위해 NetworkRunner는 모든 NetworkObjects, NetworkBehaviour 그리고 SimulationBehaviour를 추적합니다.

속성

모든 NetworkBehaviourRunner 속성을 통해 현재의 NetworkRunner에 접근할 수 있으며, Runner 자체가 일부 중요한 시스템 속성을 나타냅니다.

  • IsServer: 러너가 권한 서버를 나타내는 경우 true.
  • Stage: 현재 시뮬레이션 단계입니다. 시뮬레이션이 현재 다음 상태를 예측하고 있는 경우 Forward가 되고, 서버 업데이트를 조정하기 위해 이전 상태를 다시 시뮬레이션하는 경우 Resimulate가 될 수 있습니다. 서버나 시뮬레이션이 항상 Forward인 Client-Authority 모드에서는 Resimulate가 발생하지 않습니다.

GameMode

GameMode는 로컬 피어의 행동을 정의해 줍니다. GameModeNetworkRunner.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() 호출 대신에 사용되어야 합니다. 트리거들이 일시적이고 백업 부울값이 NetworkMecanimAnimatorAnimator 컴포넌트의 값을 가져오기 이전에 false로 재설정될 수 있기 때문입니다. 편의상 입력 권한 에서 즉시 Animator.SetTrigger()로 전달하는 NetworkMecanimAnimator.SetTrigger()passThroughOnInputAuthority 옵션이 있습니다.

NetworkCharacterControllerPrototype

이 컴포넌트는 유니티의 CharacterControllerNetworkObject에서 동기화하는 방법을 보여주는 프로토타이핑 예입니다. 유니티의 CharacterController는 본질적으로 예측 및 재시뮬레이션과 호환되지 않으므로 이 프로토타입은 적절한 시기에 CC를 활성화 및 비활성화하여 이러한 한계를 해결하는 방법을 보여줍니다. 이 컴포넌트를 기본 클래스로 사용하여 필요에 따라 확장하거나, 사용자 정의 구현을 위한 시작점으로 복사 및 이름을 변경할 수 있습니다.

NetworkCharacterController(이전 방식)

가장 불규칙한 객체는 플레이어가 직접 제어하는 객체(일반적으로 캐릭터 컨트롤러라고 함)이며, Fusion에는 특정 사용 사례에 대해서 NetworkCharacterController가 있습니다.

사전 구축된 NetworkCharacterController는 일반적으로 원하는 동작을 포함하므로 빠른 프로토 타이핑을 가능하게 합니다. 그러나 캐릭터 컨트롤러 구현이 게임마다 매우 다르기 때문에 모든 경우에 적합한 캐릭터 컨트롤러는 없습니다.

따라서 NetworkCharacterController 가 사용하는 두 가지 핵심 방식인 Move()ComputeRawMovement()의 소스 코드를 읽어 게임 제작에 필요한 대체 맞춤형 캐릭터 컨트롤러를 만드는 영감을 이끌어내는 것이 좋습니다.

ComputeRawSteer()

This is only a reference implementation.

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는 사용할 필요가 없으며 대부분의 게임에서는 필요하지 않지만, 네트워크 이벤트를 유니티 씬에서 구성해야 하는 아주 드문 경우 편의를 위해 포함되어 있습니다.

Back to top