반응형

UNet Tutorial (6) - SyncVar와 Hook


지난 Unet Tutorial 섹션에서는 Command와 ClientRpc를 이용한 클라이언트와 서버 간의 액션 통신 방법에 대해서 알아보았다. 이것은 함수를 통한 동작 수행을 처리하는 것이었다. 하지만 이번 섹션에서 알아볼 SyncVar와 Hook은 NetworkBehaviour를 상속받는 클래스의 멤버 변수를 동기화하기 위한 것이다.


지난 섹션에서 배운 Command 와 ClientRpc를 이용해서도 멤버변수에 대한 동기화를 구현할 수는 있다. Command와 ClientRpc를 통한 멤버 변수 동기화를 구현한 코드는 다음과 같다.


using UnityEngine;
using UnityEngine.Networking;

public class Player : NetworkBehaviour
{
    // 플레이어의 체력
    public int hp;

    void Start()
    {
        // 서버에서 플레이어가 생성되면 hp를 100으로 만들고 Rpc를 통해 클라이언트에 알려준다.
        if (isServer)
        {
            hp = 100;
            RpcSyncHp(hp);
        }
    }
   
    [ClientRpc]
    // 클라이언트는 Rpc를 통해 현재 hp를 받으면 자신의 객체에 적용한다.
    public void RpcSyncHp(int hp)
    {
        this.hp = hp;
    }

    [Command]
    // 서버는 Cmd로 플레이어가 받아야될 데미지를 받으면 현재 hp에서 데미지를 빼고 남은 hp를 Rpc로 클라이언트에 알려준다.
    public void CmdDamaged(int dmg)
    {
        hp -= dmg;
        RpcSyncHp(hp);
    }

    void Update()
    {
        // 클라이언트에서 스페이스 버튼을 누르면 Cmd를 호출해서 서버에 플레이어가 입어야될 데미지를 알려준다.
        if (isClient && isLocalPlayer)
        {
            if (Input.GetKeyDown(KeyCode.Space))
            {
                CmdDamaged(10);
            }
        }
    }
}

위의 예시에서는 CmdDamaged를 호출할 때는 클라이언트에서 데미지 값을 넣도록 했지만 실제로 구현할 때는 저렇게 하지 않도록 하자. 클라이언트에서 중요한 값을 넣을 수 있도록하면 해킹에 취약해진다.


Command와 ClientRpc를 이용하여 멤버 변수 동기화 구현은 가능하지만 약간은 복잡하다.





SyncVar를 통한 멤버 변수 동기화


SyncVar를 통해 멤버 변수 동기화를 이용하면 아래의 예시 코드처럼 조금 더 간단한 코드가 작성 가능해진다.


using UnityEngine;
using UnityEngine.Networking;

public class Player : NetworkBehaviour
{
    [SyncVar]
    // 플레이어의 체력
    public int hp;

    void Start()
    {
        // 서버에서 플레이어가 생성되면 hp를 100으로 만든다.
        if (isServer)
        {
            hp = 100;
        }
    }

    [Command]
    // 서버는 Cmd로 플레이어가 받아야될 데미지를 받으면 현재 hp에서 데미지를 뺀다.
    public void CmdDamaged(int dmg)
    {
        hp -= dmg;
    }

    void Update()
    {
        // 클라이언트에서 스페이스 버튼을 누르면 Cmd를 호출해서 서버에 플레이어가 입어야될 데미지를 알려준다.
        if (isClient && isLocalPlayer)
        {
            if (Input.GetKeyDown(KeyCode.Space))
            {
                CmdDamaged(10);
            }
        }
    }
}


위의 예시 코드처럼 동기화하고자 하는 멤버 변수에 SyncVar 어트리뷰트를 붙여주면 서버에서 hp의 값이 변경되면 클라이언트의 해당 객체에 변경된 값을 알려주고 동기화하게 된다. 이러한 SyncVar의 동기화에서 알고있어야 할 점은 이 멤버 변수 동기화는 서버에서 클라이언트의 방향으로만 이루어진다는 점이다. 즉, 서버에서 변수가 수정되면 모든 클라이언트로 동기화되고, 클라이언트에서는 이 변수의 값을 아무리 수정해도 다른 클라이언트나 서버에서는 변경이 되지 않는다는 점이다.





Hook


멤버 변수가 변경된 것을 클라이언트에서 동기화 받고 난 뒤에 클라이언트에서 처리해야할 작업이 더 있는 경우가 더러 있다. 위의 코드를 예를 들자면 hp가 동기화된 이후에 클라이언트 측에서는 변경된 hp의 양에 맞춰서 hp ui를 변경해 주어야할 것이다. 그런 것을 처리하는 역할을 할 수 있는 것이 바로 hook이다. hook의 구현은 다음 예시 코드와 같이할 수 있다.


using UnityEngine;
using UnityEngine.Networking;

public class Player : NetworkBehaviour
{

    public delegate void OnChangeHp(int hp);

    public OnChangeHp onChangeHp;


    [SyncVar(hook = "ChangeHookHp")]
    // 플레이어의 체력
    public int hp;
    void ChangeHookHp(int hp)
    {

        // hook을 이용하는 경우에는 동기화해야할 값이 매개 변수로 넘어오고 그것을 직접 대입해주어야 한다.
        this.hp = hp;

        // 만약 delegate event에 hp ui의 함수를 등록해두었다면 hp ui가 갱신될 것이다.

        onChangeHp(this.hp);
    }

    void Start()
    {
        // 서버에서 플레이어가 생성되면 hp를 100으로 만들고 Rpc를 통해 클라이언트에 알려준다.
        if (isServer)
        {
            hp = 100;
        }
    }

    [Command]
    // 서버는 Cmd로 플레이어가 받아야될 데미지를 받으면 현재 hp에서 데미지를 빼고 남은 hp를 Rpc로 클라이언트에 알려준다.
    public void CmdDamaged(int dmg)
    {
        hp -= dmg;
    }

    void Update()
    {
        // 클라이언트에서 스페이스 버튼을 누르면 Cmd를 호출해서 서버에 플레이어가 입어야될 데미지를 알려준다.
        if (isClient && isLocalPlayer)
        {
            if (Input.GetKeyDown(KeyCode.Space))
            {
                CmdDamaged(10);
            }
        }
    }
}


위의 코드처럼 SyncVar의 어트리뷰트에 hook을 등록해두면 서버에서 hp값이 수정되고 그것이 클라이언트에 통지되었을 때, 자동으로 hook으로 등록해둔 함수가 호출된다. 즉, hp 값이 변경되었을 때, 클라이언트에서 추가적으로 처리해야 하는 일들을 처리할 수 있게 되는 것이다.


단, 이 hook을 사용할 때 알아두어야 할 것이 있다. hook은 서버에서 변경된 값이 클라이언트에 통지되었을때 동작하는 것임으로 클라이언트에서만 동작한다는 것과, hook을 사용한 경우에는 클라이언트의 멤버 변수에 동기화되어야하는 값이 바로 적용되지 않는다는 점이다. 멤버 변수에 바로 동기화되지 않고, hook으로 등록한 함수의 매개 변수를 통해서 전달되기 때문에, 클라이언트 측의 멤버 변수에 매개 변수의 값을 직접 대입해주어야 한다.

반응형
  1. HJ 2018.05.04 03:06

    hook 사용시 왜 클라이언트에 값이 바로 동기화 되지않는지 헤메고 있었는데 그 이유를 드디어 찾았네요!
    감사합니다 ^^!

+ Recent posts