유니티/Tutorial

Unity Fishnet[2] Ownership과 Player Move

어나더원 2025. 7. 8. 16:46
 

Unity Fishnet[1] 네트워크 연결 및 플레이어 스폰

Unity Fishnet[0] 소개 및 설치에셋스토어 FishNet: Networking Evolved | 네트워크 | Unity Asset StoreGet the FishNet: Networking Evolved package from FirstGearGames and speed up your game development process. Find this & other 네트워크 optio

gamecoke.tistory.com

저번 강의에 이어서 계속됩니다.

Unity 6000.0.51f 환경에서 진행했고 멀티를 테스트할 수 있는 에셋은 ParrelSync를 사용했습니다.


Player Move

멀티에서 자신의 플레이어를 움직이려면 2가지 과정이 필요합니다.

첫 번째는 움직이는 키를 입력받아 클라이언트가 특정 Player Object의 Owner면 해당 Player Object를 움직이게 하기

두 번째는 서버와 다른 클라이언트에 Player Object 움직임 동기화하기

 

 Player Object 움직이기

첫 번째부터 구현해 보겠습니다.

움직이는 키를 입력받아야 하는데 Old Input System, New Input System 두 방식 중 New Input System 방식을 써보겠습니다.

 

New Input System 설치는 아래 링크를 따라 하시면 됩니다.

 

Unity New Input System

New Input System은 기존의 Input Manager의 문제점인플랫폼 간의 키 연동 문제를 개선하기 위해 나온 툴입니다. 또한, 위 사진처럼 키 매핑을 보기 쉽게 만들어서 개발자가 한눈에 어떤 키가 있는지 파

gamecoke.tistory.com

 

Network Object 컴포넌트가 붙어 있는 Player Prefab에 Player Input 컴포넌트를 추가해 줍니다.

그리고 Player Input을 비활성화시켜 줍니다.

그리고 Input Value는 Vector2를 받아오게끔 했습니다.

 

public class Player : NetworkBehaviour
{
    public float MoveSpeed = 5f;
    private Vector2 _moveDirection;

    /// <summary>
    /// 클라이언트에 접속했을 때 실행되는 함수
    /// </summary>
    public override void OnStartClient()
    {
        // 클라이언트가 이 스크립트가 붙어 있는 플레이어 오브젝트의 소유권을 가지고 있으면
        // PlayerInput 컴포넌트 활성화
        if (IsOwner)
        {
            GetComponent<PlayerInput>().enabled = true;
        }
    }

    public void OnMove(InputValue value)
    {
        _moveDirection = value.Get<Vector2>();
    }
    
    
    private void Update()
    {
        if (!IsOwner) return;
        
        // 소유권을 가지고 있을 떄만 캐릭터가 움직임
        Vector3 moveDirection = new Vector3(_moveDirection.x, 0f, _moveDirection.y);
        transform.Translate(moveDirection * (MoveSpeed * Time.deltaTime));
    }
}

 

Player.cs 스크립트를 만들어서 플레이어 오브젝트에 넣고 움직이게 하면 되는데 NetworkBehaivour를 상속받아야 합니다.

NetworkBehaviour는 오브젝트의 소유권 확인, OnStartNetwork, OnStartServer, OnStartClient 같은 콜백 함수 오버라이드, RPC, Broadcast 등 네트워크 기능들을 사용하려면 NetworkBehaviour를 상속해서 사용해야 합니다.

 

OnStartClient : 플레이어 오브젝트의 소유권을 확인한 후 아까 비활성화시켜 놨던 PlayerInput을 활성화시킵니다.

OnMove : New Input System을 통해 입력된 값을 가져옵니다.

Update : 소유권이 없으면 해당 오브젝트를 움직이지 못하게 하고 소유권이 있으면 움직이게 합니다.

 

여기서 소유권 (Ownership)은 아래에서 더 설명하겠지만 멀티에서 어떤 오브젝트를 제어할 수 있는 권한을 말합니다.

 

왼쪽 클라이언트2, 오른쪽 클라이언트1

 

이렇게 해서 멀티 테스트를 해보면 좀 이상합니다.

내 클라이언트에서 플레이어를 움직이면 다른 클라이언트에서도 내 플레이어 오브젝트가 움직여야 하는데 안 움직입니다.

이건 네트워크 동기화가 안되어있어서 그렇습니다.

 

 Transform 동기화

Transform 동기화는 매우 간단합니다.

걍 Player Prefab에 NetworkTransform을 붙이면 끝납니다.

 

여기서 Client Authoritative가 true면 클라이언트가 오브젝트의 Transform을 조절하면 서버와

다른 클라이언트에도 동기화하고 false면 서버에서 직접 오브젝트의 Transform을 변경해야 클라이언트들에게 동기화가 됩니다.

 


Ownership

소유권 (Ownership)은 클라이언트가 어떤 Network Object를 컨트롤할 수 있는 권한을 말합니다.

위 예시에선 스폰된 하나의 플레이어 오브젝트에 소유권을 부여하고 소유권 여부를 확인해서 자신의 플레이어를 움직이게 했습니다.

public override void OnStartClient()
{
    if (base.IsOwner)
    {
        GetComponent<PlayerInput>().enabled = true;
    }
}

소유권을 확인하는 방법은 IsOwner를 쓰면 됩니다.

하지만 IsOwner를 쓸려면 NetworkBehaviour를 상속받아야 합니다.

 

만약 소유권 없이 플레이어를 움직이면 어떻게 될까요?

위 Player 스크립트에서 IsOwner 체크하는 부분을 제거해 봤습니다.

public class Player : NetworkBehaviour
{
    public float MoveSpeed = 5f;
    private Vector2 _moveDirection;
    
    public override void OnStartClient()
    {
        GetComponent<PlayerInput>().enabled = true;
    }

    public void OnMove(InputValue value)
    {
        _moveDirection = value.Get<Vector2>();
    }
    
    
    private void Update()
    {
        Vector3 moveDirection = new Vector3(_moveDirection.x, 0f, _moveDirection.y);
        transform.Translate(moveDirection * (MoveSpeed * Time.deltaTime));
    }
}

 

 

내 클라이언트에서 키를 눌러 움직이면 내 화면에선 두 플레이어 오브젝트가 움직이고 다른 클라이언트 화면에선 정상적으로 상대 플레이어 오브젝트만 움직이게 됩니다.

 

내 화면에서 두 플레이어 오브젝트가 모두 움직이는 이유는 현재 내 씬에 두 플레이어 오브젝트 모두 Player Input이 켜지기 때문입니다.

public override void OnStartClient()
{
    GetComponent<PlayerInput>().enabled = true;
}

 

클라이언트에 연결될 때 내 씬에 있는 두 플레이어 오브젝트 안에 들어 있는 Player 스크립트의 OnStartClient가 실행되면서 둘 다 PlayerInput이 활성화됩니다.

그래서 두 플레이어 오브젝트 모두 키를 입력받게 되어 내 화면에선 둘 다 움직이는 것처럼 보이게 됩니다.

내 플레이어 오브젝트
상대 플레이어 오브젝트

 

근데 왜 다른 클라이언트 화면엔 상대방 오브젝트만 움직여서 정상처럼 보이는 걸까요?

상대방의 오브젝트는 네트워크를 통해 받은 데이터로 움직이기 때문입니다. 즉, 남의 플레이어는 아까 NetworkTransform로 위치를 동기화시켰기 때문에 정상적으로 보이는 겁니다.

 

이렇게 플레이어를 움직이는 경우 내 플레이어만 움직이도록 Owner를 확인하기도 하지만 RPC, 플레이어 관련 값들 (플레이어 이름, 아이템 등) 등 다양하게 사용합니다.

 

 Owner 부여

오브젝트에다가 Owner를 부여하는 방법은 2가지가 있습니다.

이는 나중에 다룰 NetworkConnection 가져오는 방법이 필요합니다.

그리고 Owner는 Server를 통해서만 부여해야 합니다.

Gameobject go = Instantiate(_yourPrefab);
InstanceFinder.ServerManager.Spawn(go, ownerConnection);

먼저 스폰을 할 때 사용하는데 Spawn의 두 번째 인수에 Client의 NetworkConnection을 집어넣으면 해당 Client는 스폰된 오브젝트의 소유권을 가지게 됩니다.

참고로 InstanceFinder는 ClientManager, NetworkManager, ServerManager, SceneManager 등 어디서나 다양하게 접근할 수 있는 만능 클래스입니다.

networkObject.GiveOwnership(newOwnerConnection);

특정 NetworkObject에 직접 GiveOwnership 함수를 써서 넣을 수도 있습니다.

이 함수를 통해 내가 아니라 다른 클라이언트에게도 소유권을 줄 수 있다는 걸 알 수 있습니다.

networkObject.RemoveOwnership();

소유권을 없애기 위해선 RemoveOwnership으로 없앨 수 있습니다.

 

 

실제로 fishnet에서 제공해 준 PlayerSpawner 스크립트를 보면 Spawn을 통해 접속한 클라이언트에게 소유권을 주는 것을 볼 수 있습니다.