This document is about: FUSION 2
SWITCH TO

3 - 이동 & 카메라

개요

파트 3에서는 플레이어 입력에 따른 플레이어 움직임과 올바른 플레이어 객체를 따라가는 1인칭 카메라를 추가하여 기존 씬을 확장합니다.

플레이어 이동

Fusion에서는 움직임과 같은 모든 틱을 업데이트하는 게임 플레이 코드가 Update / FixedUpdate에서 실행되어서는 안 됩니다. 대신 FixedUpdateNetwork를 사용해야 합니다. 이를 통해 모든 클라이언트에서 움직임이 원활하고 올바르게 보간 됩니다.

새 스크립트를 만들고 이름을 PlayerMovement로 지정한 다음 스크립트에 다음 코드를 추가합니다:

C#

using Fusion;
using UnityEngine;

public class PlayerMovement : NetworkBehaviour
{
    private CharacterController _controller;

    public float PlayerSpeed = 2f;

    private void Awake()
    {
        _controller = GetComponent<CharacterController>();
    }

    public override void FixedUpdateNetwork()
    {
        // Only move own player and not every other player. Each player controls its own player object.
        if (HasStateAuthority == false)
        {
            return;
        }

        Vector3 move = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical")) * Runner.DeltaTime * PlayerSpeed;

        _controller.Move(move);

        if (move != Vector3.zero)
        {
            gameObject.transform.forward = move;
        }
    }
}

이는 FixedUpdateNetwork를 제공하고 나중에 Networked Properties을 사용할 수 있는 MonoBehaviour 대신 NetworkBehaviour를 상속하는 것에 주목하세요.

HasStateAuthority를 사용하면 클라이언트가 객체를 제어하는지 확인할 수 있습니다. NetworkTransformStateAuthority의 변경 내용만 동기화합니다. 다른 클라이언트가 객체의 위치를 변경하면 로컬 변경에 불과하고 향후 네트워크의 데이터를 무시합니다.

FixedUpdateNetwork에서 코드 실행 시에 항상 Runner.DeltaTime을 사용하세요!

버튼 눌림 처리

Update 시 유니티에서는 GetButtonDown과 up과 같은 입력이 한 번씩 발생합니다. FixedUpdateNetwork에서는 움직임과 같은 게임 플레이 코드가 실행되어야 합니다. 버튼 상태를 정확하게 추적하기 위해서는 특별한 취급이 필요합니다.

FixedUpdate에서 버튼 누름을 확인할 때도 동일한 문제가 발생합니다.

FixedUpdateNetwork에서 다음과 같은 입력을 캡처하는 여러 가지 접근 방식이 있습니다:

  1. 업데이트에서 버튼 다운을 요청합니다. 결과를 부울 변수에 저장한 후 게임 로직에 사용합니다. FixedUpdateNetwork의 마지막 부분에서 삭제합니다.
  2. Fusion의 NetworkInputNetworkButtons와 함께 사용합니다.
  3. 새로운 유니티 입력 시스템을 사용하여 Update ModeManual Update로 설정하고 FixedUpdateNetwork에서 InputSystem.Update를 호출합니다.
위의 옵션은 공유 모드에만 적용됩니다. 서버/호스트 모드에서 Fusion을 실행할 때는 항상 NetworkInput을 사용합니다.

점프

플레이어를 위한 점프 액션을 구현하여 버튼 누르기를 처리하는 예를 구현해 보겠습니다. 이 경우 이전 섹션에 나열된 옵션 중에서 옵션 1이 사용됩니다.

플레이어 이동 스크립트의 코드를 다음으로 바꿉니다:

C#

using Fusion;
using UnityEngine;

public class PlayerMovement : NetworkBehaviour
{
    private Vector3 _velocity;
    private bool _jumpPressed;
    
    private CharacterController _controller;

    public float PlayerSpeed = 2f;
    
    public float JumpForce = 5f;
    public float GravityValue = -9.81f;

    private void Awake()
    {
        _controller = GetComponent<CharacterController>();
    }
    
    void Update()
    {
        if (Input.GetButtonDown("Jump"))
        {
            _jumpPressed = true;
        }
    }

    public override void FixedUpdateNetwork()
    {
        // Only move own player and not every other player. Each player controls its own player object.
        if (HasStateAuthority == false)
        {
            return;
        }
        
        if (_controller.isGrounded)
        {
            _velocity = new Vector3(0, -1, 0);
        }
        
        Vector3 move = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical")) * Runner.DeltaTime * PlayerSpeed;

        _velocity.y += GravityValue * Runner.DeltaTime;
        if (_jumpPressed && _controller.isGrounded)
        {
            _velocity.y += JumpForce;
        }
        _controller.Move(move + _velocity * Runner.DeltaTime);

        if (move != Vector3.zero)
        {
            gameObject.transform.forward = move;
        } 
        
        _jumpPressed = false;
    }
}

이 코드는 KCC에 점프 기능과 속도/중력 처리 기능을 추가합니다.

버튼 상태를 추적하기 위해 _jumpPressed를 사용합니다. 이 버튼은 매 업데이트마다 폴링 되어 FixedUpdateNetwork의 끝부분에 놓치는 키가 없는지 확인하고 재설정됩니다.

C#

if (_controller.isGrounded)
{
    _velocity = new Vector3(0, -1, 0);
}

KCC가 작은 경사면을 걸을 때도 그라운드에 고정된 상태를 유지하고 점프만 하면 플레이어가 그라운드를 떠날 수 있도록 하는 데 사용됩니다.

플레이 테스트

PlayerMovement 스크립트가 준비된 상태에서 유니티로 돌아가 PlayerCharacter 프리팹에 PlayerMovement 컴포넌트를 추가합니다.

이제 플레이어의 움직임을 테스트할 시간입니다. 두 명의 클라이언트를 사용할 수 있도록 하는 것은 유니티 빌드를 만드는 것부터 시작합니다.

파일 > 빌드 설정으로 이동하여 현재 씬을 목록에 추가한 다음 창의 왼쪽 하단에 있는 플레이어 설정 버튼을 누릅니다.

Resolution and Presentation플레이어 설정에서 Fullscreen ModeWindowed로 설정하고 Run In Background가 선택되어 있는지 확인합니다.

SDK 다운로드
App Id 복사.

빌드 설정 창으로 돌아가서 Build and Run을 선택하여 빌드를 만듭니다. 빌드가 완료되면 유니티에서 플레이 모드로 전환하여 2개의 클라이언트를 사용할 수 있습니다.

두 클라이언트 모두 공유 클라이언트 시작 버튼을 누릅니다. 두 명의 플레이어가 등장하고, 각각 한 명의 클라이언트가 제어합니다. 걸어 다니기와 점프가 효과가 있는지 테스트합니다.

카메라

플레이어 설정을 완료하려면 카메라를 추가합니다.

카메라를 설정하는 많은 방법(제3자, 제1자 등)과 이를 구현하는 다른 방법이 있습니다. 멀티플레이어 게임에서는 종종 로컬 플레이어 객체를 따르는 단일 카메라가 필요합니다. 이를 달성하기 위한 두 가지 일반적인 방법이 있습니다.

  1. 로컬 플레이어 객체가 생성되면 카메라를 인스턴스화합니다.
  2. 카메라를 씬의 일부로 합니다. 로컬 플레이어 객체가 생성되면 카메라를 자신에게 부착합니다.

이 튜토리얼에서는 두 번째 접근 방식을 사용합니다.

새 스크립트를 만들고 이름을 FirstPersonCamera로 지정합니다. 여기에 다음 코드를 추가합니다:

C#

using UnityEngine;

public class FirstPersonCamera : MonoBehaviour
{
    public Transform Target;
    public float MouseSensitivity = 10f;

    private float verticalRotation;
    private float horizontalRotation;
    
    void LateUpdate()
    {
        if (Target == null)
        {
            return;
        }

        transform.position = Target.position;
        
        float mouseX = Input.GetAxis("Mouse X");
        float mouseY = Input.GetAxis("Mouse Y");

        verticalRotation -= mouseY * MouseSensitivity;
        verticalRotation = Mathf.Clamp(verticalRotation, -70f, 70f);

        horizontalRotation += mouseX * MouseSensitivity;

        transform.rotation = Quaternion.Euler(verticalRotation, horizontalRotation, 0);
    }
}

이것은 매우 간단한 1인칭 카메라 구현입니다. 코드에 멀티플레이어 요소가 포함되어 있지 않습니다. 이 카메라를 시네마 머신을 포함한 대상 객체와 함께 작동하는 다른 카메라 설정으로 대체할 수 있습니다.

FirstPersonCamera 동작을 `MainCamera 게임 객체에 올려놓습니다. 타깃을 드래그할 필요가 없습니다. 코드별로 런타임에 타깃을 설정합니다.

네트워킹 추가

타깃을 지정하기 위해서, PlayerMovement 스크립트를 엽니다. 카메라를 저장할 변수를 추가합니다.

C#

public Camera Camera;

Then when the player object gets spawned find and set up the camera if it is the local player.

C#

public override void Spawned()
{
    if (HasStateAuthority)
    {
        Camera = Camera.main;
        Camera.GetComponent<FirstPersonCamera>().Target = transform;
    }
}

NetworkObjects를 초기화할 때는 반드시 Awake/Start 대신 Spawned를 사용해야 합니다. Awake/Start에서는 NetworkObject를 사용할 준비가 되지 않았을 수 있습니다.

HasStateAuthority는 플레이어가 제어하는 객체에만 해당하므로 로컬 플레이어 객체만 해당되고 다른 플레이어 객체는 해당되지 않습니다.

마지막으로 1인칭 게임에서는 플레이어 캐릭터가 룩 방향으로 이동하기 때문에 움직임을 조금 조절합니다. Vector3 move가 계산되는 선을 다음과 같이 바꿉니다:

C#

Quaternion cameraRotationY = Quaternion.Euler(0, Camera.transform.rotation.eulerAngles.y, 0);
Vector3 move = cameraRotationY * new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical")) * Runner.DeltaTime * PlayerSpeed;

다음 공유 모드 기초 4 - 네트워크 속성

Back to top