네트워크 게임에 대한 공부용으로 저장해두는 내용입니다.
sdk 설치 및 연동은 스킵!
입력 흐름: Unity Input System → Fusion OnInput → GetInput
이번 구조에서는 Unity Input System을 사용하고, 별도의 InputManager를 통해 Fusion에 입력을 전달하도록 구성했다.
1) InputManager: Unity 입력을 받아서 NetworkInputData로 래핑
using Fusion;
using UnityEngine;
using UnityEngine.InputSystem;
public class InputManager : MonoBehaviour, IManager
{
private PlayerInput inputAsset;
private Vector2 inputDirection;
public void Init()
{
inputAsset = new PlayerInput();
inputAsset.Enable();
inputAsset.KeyboardInput.Move.performed += OnMove;
inputAsset.KeyboardInput.Move.canceled += OnMoveCanceled;
}
private void OnMoveCanceled(InputAction.CallbackContext obj)
{
inputDirection = Vector2.zero;
}
private void OnMove(InputAction.CallbackContext context)
{
if (context.performed)
{
inputDirection = context.ReadValue<Vector2>();
}
}
public void OnInput(NetworkRunner runner, NetworkInput input)
{
var data = new NetworkInputData(inputDirection);
input.Set(data);
}
public void OnInputMissing(NetworkRunner runner, PlayerRef player, NetworkInput input)
{
input.Set(new NetworkInputData(Vector2.zero));
}
}
public struct NetworkInputData : INetworkInput
{
public Vector2 direction;
public NetworkInputData(Vector2 direction)
{
this.direction = direction;
}
}
핵심은 OnInput에서 현재 프레임의 방향 입력을 NetworkInputData에 담고, input.Set(data)로 Fusion에 넘겨주는 것이다.
2) NetworkManager에서 OnInput 콜백 연결
NetworkRunner에는 콜백을 등록해 두고, 거기서 GameManager.Input을 호출하도록 했다.
public class NetworkManager : MonoBehaviour, IManager, INetworkRunnerCallbacks
{
private NetworkRunner _runner;
public NetworkRunner Runner => _runner;
public void Init()
{
_runner = gameObject.AddComponent<NetworkRunner>();
_runner.AddCallbacks(this);
}
public void OnInput(NetworkRunner runner, NetworkInput input)
{
GameManager.Input.OnInput(runner, input);
}
public void OnInputMissing(NetworkRunner runner, PlayerRef player, NetworkInput input)
{
GameManager.Input.OnInputMissing(runner, player, input);
}
// … 생략 …
}
3) Player쪽에서 GetInput으로 입력 받기
using Fusion;
using Fusion.Addons.Physics;
using UnityEngine;
public class Player : NetworkBehaviour
{
private NetworkRigidbody2D _rigidbody;
public float moveSpeed = 5f;
private void Awake()
{
_rigidbody = GetComponent<NetworkRigidbody2D>();
}
public override void FixedUpdateNetwork()
{
if (GetInput(out NetworkInputData data))
{
data.direction.Normalize();
_rigidbody.RBPosition += Vector3.Lerp(Vector3.zero, new Vector3(data.direction.x, data.direction.y, 0), speed * Runner.DeltaTime);
}
}
}
현재 이동코드도 대충 추가했고, 모든 오브젝트가 같이 움직일것 같지만 그건 추후의 리팩토링으로 넘어가자.
여기서 중요한 점은 두 가지였다.
- GetInput이 true를 반환하려면
- 해당 오브젝트에 InputAuthority가 있어야 하고
- 그 Authority에 해당하는 플레이어가 OnInput에서 데이터를 넣어줘야 한다.
- 따라서 Spawn 시에 InputAuthority를 정확히 넘겨주지 않으면, GetInput은 계속 false를 반환하게 된다.
Spawn 시 InputAuthority를 넘기지 않아 생긴 문제
public void OnPlayerJoined(NetworkRunner runner, PlayerRef player)
{
Debug.Log("[NetworkManager] 플레이어가 방에 접속하였습니다.");
Player prefab = GameManager.Resource.LoadAsset<Player>("Prefab/player");
// 잘못된 버전: InputAuthority를 넘기지 않았다.
_runner.Spawn(prefab, Vector2.zero, Quaternion.identity);
// 수정된 버전: inputAuthority에 player를 넘긴다.
runner.Spawn(prefab, Vector2.zero, Quaternion.identity, player);
}
위처럼 하면 Player 오브젝트의 NetworkObject.InputAuthority가 None 상태로 남는다.
그 결과:
- InputManager → OnInput → input.Set(data) 까지는 잘 호출되는데
- Player.FixedUpdateNetwork()의 GetInput(out NetworkInputData data)는 항상 false
- 로그도 안 찍히고, 이동도 안 한다
이 문제를 해결하기 위해 Spawn 시 네 번째 인자로 PlayerRef를 넘겨주었다.
PlayerRef vs Network Runner
network runner는 Fusion의 중앙 유니티 컴포넌트로, 하나의 네트워크 피어를 나타낸다고 한다.
그런데 왜 PlayerRef를 넘기는게 아닌 Runner를 인자로써 넘겨야 하는걸까? runner또한 한 플레이어가 하나의 runner를 가지고 있을것이라 생각했다.
fusion 공식 문서에서는 이 runner를 여러개 생성하여 하나의 유니티 인스턴스에서 멀티 피어모드를 활성화 하는 방법을 설명해 주었다. 그렇다면 playerRef는 무엇일까. PlayerRef는 일종의 ID로 플레이어의 식별을 위한 것이라고 한다. 따라서 PlayerRef를 넘기는것이 플레이어가 제어할 수 있는 모든 runner에 대해 제어할 수 있으므로 플레이어 식별을 위한것이라면 PlayerRef를 사용하는 것이다.
'Unity' 카테고리의 다른 글
| Fusion 2D 이동 로직을 상태머신(FSM) 기반으로 리팩토링 (0) | 2025.12.12 |
|---|---|
| Unity 씬 전환 후 남아 있는 이벤트 구독 때문에 NullReference 오류가! (0) | 2025.12.02 |
| Unity UI - Atomic Design 패턴 (0) | 2025.12.01 |
| Unity ObjectPool로 만드는 제네릭 오브젝트 풀 매니저 (0) | 2025.11.28 |
| 싱글톤의 Awake vs SceneLoaded / 타일맵 타일마다 스크립트 작성하기 (0) | 2025.11.19 |