오브젝트 스폰(Object Spawn)

 

유니티에서 게임 오브젝트를 생성할 때는, Instantiate() 함수를 사용해서 생성한다. 이것을 스폰(Spawn)이라고 한다. 하지만 이러한 스폰은 당연하게도 단순히 Instantiate() 함수만 호출해서 소환한다고 해서 자동으로 네트워크에서 스폰되고 동기화되는 것은 아니다. 평범하게 스폰된 게임 오브젝트를 네트워크에서 스폰하고 동기화하기 위해서는 유니티에서 정한 일련의 제약과 과정을 거쳐야 한다.

 

먼저 오브젝트가 네트워크로 스폰되기 위해서는 네트워크 매니저의 spawnPrefabs라는 리스트에 스폰 가능한 프리팹으로 등록되어 있어야 하는데 여기에 등록될 프리팹은 반드시 루트 게임 오브젝트에 NetworkIdentity 컴포넌트를 가지고 있어야 하며, Network Behaviour 스크립트는 반드시 이 Network Identity 컴포넌트와 동일한 게임 오브젝트에 있어야 한다.

 

 

 

정상적으로 Network Identity 컴포넌트를 가진 프리팹의 경우 Inspector 뷰에서 확인해보면 프리팹에 Asset ID가 고유한 값으로 할당되어 있는 것을 확인할 수 있다. 네트워크 매니저는 이 Asset ID를 통해 지금 네트워크 스폰하려는 오브젝트가 유효하게 spawnPrefabs에 등록되어 있는 프리팹인지 확인하고 스폰하게 된다.

 

 

 

 

이렇게 제대로 만들어진 프리팹을 spawnPrefabs 리스트에 등록하는 것은 스크립트에서도 가능하고 유니티 에디터에서도 가능하다.

 

 

 

 

using UnityEngine;
using UnityEngine.Networking;

public class SpawnObjTestNetManager : NetworkManager
{
    void Awake ()
    {
        // Resources 폴더에 있는 Character라는 이름을 가진 프리팹 파일을 가져온다.
        GameObject prefab = Resources.Load<GameObject>("Character");
        // spawnPrefabs 리스트에 스폰할 오브젝트를 추가
        spawnPrefabs.Add(prefab);
    }
}

 

위의 예시와 같이 spawnPrefabs에 등록된 프리팹들은 네트워크를 통해 스폰 가능해진다. 네트워크를 통해 스폰하는 예시 코드는 다음과 같다.

 

using UnityEngine;
using UnityEngine.Networking;

public class SpawnObjTestNetManager : NetworkManager
{

    // 서버측에서 클라이언트가 접속했을 때의 콜백
    public override void OnServerConnect(NetworkConnection conn)
    {
        base.OnServerConnect(conn);
        // spawnPrefabs에 등록된 프리팹을 스폰한다.
        GameObject charObj = Instantiate(spawnPrefabs[0]);

        // 네트워크를 통해서 이 오브젝트가 생성되었음을 클라이언트에 알린다.
        NetworkServer.Spawn(charObj);
    }
}

 

위의 예시들을 따라 작성하고 빌드하여 서버에 클라이언트를 접속시키면 Character 프리팹이 서버와 클라이언트 양측에 모두 소환되는 것을 확인할 수 있다.

 

 

 

 

스폰된 오브젝트는 유니티 네트워크의 스포닝 시스템이 관리하게 되며, 스포닝 시스템에 속하게 된 오브젝트가 서버에서 변화가 있으면 그것이 클라이언트에도 전송되고, 서버에서 오브젝트가 소멸하면 클라이언트에서도 소멸하게 된다. 그리고 스폰된 오브젝트는 서버가 관리하는 네트워크 오브젝트 집합에도 추가되기 때문에, 이후에 다른 클라이언트가 게임에 참여하더라도 프로그래머가 별도의 처리를 만들 필요없이 자동으로 오브젝트가 소환되고 동기화 되어야할 값들이 동기화된다.

 

 

 

또한 스폰된 오브젝트는 각각 서버와 클라이언트에서 동일한 "netId"라고 하는 고유한 네트워크 인스턴스 ID를 가지게 되는데, 이 ID를 통해서 각각의 오브젝트에 메시지를 보내고 식별할 수 있다.

 


 

네트워크를 통해 오브젝트가 생성되는 자세한 과정은 유니티 네트워크 문서를 통해 확인할 수 있다.

 

https://docs.unity3d.com/kr/current/Manual/UNetSpawning.html   

 

 

[유니티 어필리에이트 프로그램]

아래의 링크를 통해 에셋을 구매하시거나 유니티를 구독하시면 수익의 일부가 베르에게 수수료로 지급되어 채널의 운영에 도움이 됩니다.

 

에셋스토어

여러분의 작업에 필요한 베스트 에셋을 찾아보세요. 유니티 에셋스토어가 2D, 3D 모델, SDK, 템플릿, 툴 등 여러분의 콘텐츠 제작에 날개를 달아줄 다양한 에셋을 제공합니다.

assetstore.unity.com

 

Easy 2D, 3D, VR, & AR software for cross-platform development of games and mobile apps. - Unity Store

Have a 2D, 3D, VR, or AR project that needs cross-platform functionality? We can help. Take a look at the easy-to-use Unity Plus real-time dev platform!

store.unity.com

 

Create 2D & 3D Experiences With Unity's Game Engine | Unity Pro - Unity Store

Unity Pro software is a real-time 3D platform for teams who want to design cross-platform, 2D, 3D, VR, AR & mobile experiences with a full suite of advanced tools.

store.unity.com

[투네이션]

 

-

 

toon.at

[Patreon]

 

WER's GAME DEVELOP CHANNEL님이 Game making class videos 창작 중 | Patreon

WER's GAME DEVELOP CHANNEL의 후원자가 되어보세요. 아티스트와 크리에이터를 위한 세계 최대의 멤버십 플랫폼에서 멤버십 전용 콘텐츠와 체험을 즐길 수 있습니다.

www.patreon.com

[디스코드 채널]

 

Join the 베르의 게임 개발 채널 Discord Server!

Check out the 베르의 게임 개발 채널 community on Discord - hang out with 399 other members and enjoy free voice and text chat.

discord.com

 

반응형

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으로 등록한 함수의 매개 변수를 통해서 전달되기 때문에, 클라이언트 측의 멤버 변수에 매개 변수의 값을 직접 대입해주어야 한다.

 

 

[유니티 어필리에이트 프로그램]

아래의 링크를 통해 에셋을 구매하시거나 유니티를 구독하시면 수익의 일부가 베르에게 수수료로 지급되어 채널의 운영에 도움이 됩니다.

 

에셋스토어

여러분의 작업에 필요한 베스트 에셋을 찾아보세요. 유니티 에셋스토어가 2D, 3D 모델, SDK, 템플릿, 툴 등 여러분의 콘텐츠 제작에 날개를 달아줄 다양한 에셋을 제공합니다.

assetstore.unity.com

 

Easy 2D, 3D, VR, & AR software for cross-platform development of games and mobile apps. - Unity Store

Have a 2D, 3D, VR, or AR project that needs cross-platform functionality? We can help. Take a look at the easy-to-use Unity Plus real-time dev platform!

store.unity.com

 

Create 2D & 3D Experiences With Unity's Game Engine | Unity Pro - Unity Store

Unity Pro software is a real-time 3D platform for teams who want to design cross-platform, 2D, 3D, VR, AR & mobile experiences with a full suite of advanced tools.

store.unity.com

[투네이션]

 

-

 

toon.at

[Patreon]

 

WER's GAME DEVELOP CHANNEL님이 Game making class videos 창작 중 | Patreon

WER's GAME DEVELOP CHANNEL의 후원자가 되어보세요. 아티스트와 크리에이터를 위한 세계 최대의 멤버십 플랫폼에서 멤버십 전용 콘텐츠와 체험을 즐길 수 있습니다.

www.patreon.com

[디스코드 채널]

 

Join the 베르의 게임 개발 채널 Discord Server!

Check out the 베르의 게임 개발 채널 community on Discord - hang out with 399 other members and enjoy free voice and text chat.

discord.com

 

반응형

UNet Tutorial (5) - Command와 Client Rpc

 

유니티 네트워크에서는 클라이언트와 서버 간에 액션을 수행하는 방법이 있다. 이를 원격 프로시저 호출(RPC)라고 하는데 유니티 네트워크 시스템에는 두 가지 타입의 RPC가 있다. 클라이언트에서 호출되어서 서버에서 수행되는 커맨드(Command)와 서버에서 호출되어서 클라이언트에서 수행되는 클라이언트 RPC(Client Rpc)가 그것이다.

 

아래의 다이어그램을 통해서 유니티 네트워크에서 원격 액션이 동작하는 방향을 확인할 수 있다.

 

 

 

 

 

커맨드(Command)

 

커맨드는 클라이언트의 플레이어 오브젝트에서 서버의 플레이어 오브젝트로 보내진다. 커맨드는 클라이언트 자신의 플레이어 오브젝트에서만 보낼 수 있으며 타 클라이언트가 소유한 플레이어 오브젝트를 통해서는 보낼 수 없다. 또한 일반적으로 플레이어 오브젝트가 아닌 네트워크 오브젝트에서도 커맨드를 보낼 수 없지만 5.2 버전부터 플레이어가 없는 오브젝트(클라이언트 권한 있음)에서도 커맨드를 보낼 수 있다. 이러한 오브젝트는 NetworkServer.SpawnWithClientAuthority를 통해 스폰되거나 NetworkIdentity.AssignClientAuthority를 통해 권한을 설정한 상태여야 한다. 이러한 오브젝트에서 전송한 커맨드는 오브젝트의 서버 인스턴스에서 실행되고, 클라이언트에서의 해당 플레이어 오브젝트에서 실행되지 않는다.

 

함수를 커맨드로 바꾸려면 [Command] 커스텀 속성을 사용하고 함수의 이름에 "Cmd" 접두사를 붙여주어야 한다.

 

using System.Collections;
using UnityEngine;
using UnityEngine.Networking;

public class Player : NetworkBehaviour
{
    int count = 0;

    [Command]
    public void CmdTestFunction(int i)
    {
        Debug.Log("Command Function Call. " + i);
    }

    void Update ()
    {
        if (Input.GetKeyDown(KeyCode.A))
        {
            CmdTestFunction(count++);

            Debug.Log("Input A. " + count);
        }
    }
}

 

 

 

서버와 클라이언트를 실행하고 클라이언트 측에서 'A'를 입력하면 커맨드가 호출되서 서버에서 수행되는 것을 확인할 수 있다.

 

 

 

 

클라이언트 Rpc(Client Rpc)

 

클라이언트 Rpc는 커맨드와 반대로 서버의 오브젝트에서 호출되어서 클라이언트의 오브젝트에서 수행된다. 서버의 경우에는 모든 권한을 가지고 있기 때문에 Network Identity를 포함해서 모든 서버의 오브젝트에서 이 호출을 보낼 수 있고 이것은 보안 상의 문제가 되지 않는다. 함수를 ClientRpc 호출로 만들기 위해서는 커맨드와 같이 [ClientRpc] 커스텀 속성을 함수 앞에 추가하고 함수 이름에 "Rpc"라는 접두사를 추가해야 한다.

 

using UnityEngine;
using UnityEngine.Networking;

public class Player : NetworkBehaviour
{
    int count = 0;

    [ClientRpc]
    public void RpcPlayerConnected()
    {
        Debug.Log("Client Rpc Call.");
    }

    [Command]
    public void CmdTestFunction(int i)
    {
        Debug.Log("Command Function Call. " + i);
    }

    void Update ()
    {
        if(Input.GetKeyDown(KeyCode.A))
        {
            CmdTestFunction(count++);
            Debug.Log("Input A. " + count);
        }
    }
}

 

using UnityEngine;
using UnityEngine.Networking;

public class CustomNetworkManager : NetworkManager
{
    public override void OnServerAddPlayer(NetworkConnection conn, short playerControllerId)
    {
        base.OnServerAddPlayer(conn, playerControllerId);

        Debug.Log("Add Player.");

        var players = FindObjectsOfType<Player>();
        foreach(var player in players)
        {
            player.RpcPlayerConnected();
        }
    }
}

 

위의 예제 코드는 서버에 새로운 클라이언트가 접속해서 새 플레이어 오브젝트가 생성되었을 때, 서버에 접속된 모든 클라이언트의 플레이어 오브젝트에 알려주는 역할을 하는 코드이다.

 

 

 

 

 

 

 

원격 액션에서의 매개변수

 

커맨드와 클라이언트 Rpc의 호출로 전달되는 매개변수는 직렬화되어 네트워크로 전송된다. 매개변수로 전달가능한 타입은 다음과 같다.

  • 기본 타입(byte, int, float, string, UInt64 등)
  • 기본 타입 배열
  • 허용 가능 타입을 포함하는 구조체
  • 빌트인 Unity 수학 타입(Vector3, Quaternion 등)
  • NetworkIdentity
  • NetworkInstanceId
  • NetworkHash128
  • NetworkIdentity 컴포넌트가 부착된 게임 오브젝트

원격 액션으로 전달되는 매개변수는 스크립트 인스턴스나 트랜스폼과 같이 게임 오브젝트의 하위 요소여서는 안된다. 직렬화되어 네트워크로 보내질 수 없는 타입도 매개변수가 될 수 없다.

 

 

[유니티 어필리에이트 프로그램]

아래의 링크를 통해 에셋을 구매하시거나 유니티를 구독하시면 수익의 일부가 베르에게 수수료로 지급되어 채널의 운영에 도움이 됩니다.

 

에셋스토어

여러분의 작업에 필요한 베스트 에셋을 찾아보세요. 유니티 에셋스토어가 2D, 3D 모델, SDK, 템플릿, 툴 등 여러분의 콘텐츠 제작에 날개를 달아줄 다양한 에셋을 제공합니다.

assetstore.unity.com

 

Easy 2D, 3D, VR, & AR software for cross-platform development of games and mobile apps. - Unity Store

Have a 2D, 3D, VR, or AR project that needs cross-platform functionality? We can help. Take a look at the easy-to-use Unity Plus real-time dev platform!

store.unity.com

 

Create 2D & 3D Experiences With Unity's Game Engine | Unity Pro - Unity Store

Unity Pro software is a real-time 3D platform for teams who want to design cross-platform, 2D, 3D, VR, AR & mobile experiences with a full suite of advanced tools.

store.unity.com

[투네이션]

 

-

 

toon.at

[Patreon]

 

WER's GAME DEVELOP CHANNEL님이 Game making class videos 창작 중 | Patreon

WER's GAME DEVELOP CHANNEL의 후원자가 되어보세요. 아티스트와 크리에이터를 위한 세계 최대의 멤버십 플랫폼에서 멤버십 전용 콘텐츠와 체험을 즐길 수 있습니다.

www.patreon.com

[디스코드 채널]

 

Join the 베르의 게임 개발 채널 Discord Server!

Check out the 베르의 게임 개발 채널 community on Discord - hang out with 399 other members and enjoy free voice and text chat.

discord.com

 

반응형

UNet Tutorial (4) - Network Identity와 권한

 

지난 섹션에서 위치 동기화를 하면서, 게임 오브젝트에 Network Transform을 추가했을 때, Network Transform을 따라서 자동으로 추가된 컴포넌트를 기억하는가? 바로 Network Identity라는 컴포넌트다.

 

 

이름에서도 알 수 있듯이 이 컴포넌트는 오브젝트의 네트워크 ID를 관리하고 네트워킹 시스템에 알리는 역할을 한다. 즉, 이 오브젝트가 다른 서버나 클라이언트의 어느 오브젝트와 동기화되어야 하는지 알리는 역할을 한다

출처: http://wergia.tistory.com/99 [베르의 프로그래밍 노트]
이름에서도 알 수 있듯이 이 컴포넌트는 오브젝트의 네트워크 ID를 관리하고 네트워킹 시스템에 알리는 역할을 한다. 즉, 이 오브젝트가 다른 서버나 클라이언트의 어느 오브젝트와 동기화되어야 하는지 알리는 역할을 한다

출처: http://wergia.tistory.com/99 [베르의 프로그래밍 노트]
그것은 바로 Network Identity라는 컴포넌트인데, 이름에서도 알 수 있듯이 이 컴포넌트는 오브젝트의 네트워크 ID를 관리하고 네트워킹 시스템에 알리는 역할을 한다. 즉, 이 오브젝트가 다른 서버나 클라이언트의 어느 오브젝트와 동기화되어야 하는지 알리는 역할을 한다.

출처: http://wergia.tistory.com/99 [베르의 프로그래밍 노트]

지난 섹션에서도 언급했듯이 이 컴포넌트는 오브젝트의 네트워크 ID를 관리하고 네트워킹 시스템에 알리는 역할을 한다. 이 컴포넌트가 있어야 이 오브젝트가 다른 클라이언트의 어떤 오브젝트와 동기화 되어야 하는지 알 수 있는 것이다.

 

이번 섹션에서는 Inspector 뷰에서 표시되는 Server Only와 Local Player Authority라는 두 가지 옵션과 isServer, isClient, isLocalPlayer, hasAuthority라는 네 가지 프로퍼티에 대해서 알아볼 것이다.

 

우선은, Inspector 뷰에서 표시되는 두 가지 옵션에 대한 것이다.

 

Server Only - 여기에 체크된 오브젝트는 스폰될 때, 클라이언트에서는 스폰되지 않고 서버에서만 스폰된다.(네트워크 상에서의 스폰은 일반적인 Instantiate가 아닌 다른 방법을 통해서 스폰된다.)

Local Player Authority - 여기에 체크된 오브젝트는 이 오브젝트를 소유한 클라이언트가 오브젝트를 제어할 수 있다. 즉, 해당 클라이언트의 로컬 플레이어 오브젝트가 오브젝트에 대한 권한을 갖는다.

 

그 다음으로는, 네 가지 프로퍼티에 대한 것이다. 스크립트 상에서는 NetworkBehaviour를 상속받는 클래스의 멤버로 볼 수 있는데, 씬에서 스폰된 상태일 때는 Inspector 뷰 아래쪽에 Network Information으로 표시된다.

 

 

우선 설명할 것은 isClient와 isServer라는 프로퍼티이다. 이것은 간단히 말해서 지금의 네트워크가 서버이냐 아니면 클라이언트이냐를 알려주는 프로퍼티이다. 지난 섹션에서 본 Network Manager HUD를 이용하여 LAN Server Only(S)를 선택해서 서버를 열었다면 이 Network Identity의 isServer라는 프로퍼티는 true가 되는 것이고, LAN Client(C)를 선택해서 열린 서버에 접속했다면 isClient라는 프로퍼티가 true가 되는 것이다. 즉, 현재의 네트워크가 서버로서 다른 클라이언트의 접속을 받고 있는 것인지, 클라이언트로서 다른 서버에 접속하고 있는 것인지를 알 수 있다. 이것을 이용하면 서버에서만 동작하고 클라이언트에서는 동작하지 않는 방식의 코드를 작성할 수 있다.

 

일반적으로 이 isServer와 isClient는 둘 중에 하나만 true을 가지지만, LAN Host(H)를 선택하여 네트워크가 게임의 호스트가 되어서 다른 클라이언트들의 연결을 받게 되면 호스트 측에서는 이 두 프로퍼티 모두 true값을 가지게 되고, 호스트에 접속한 클라이언트들은 isClient 프로퍼티만 true값이 된다.

즉, isServer만 true라면 네트워크는 서버 역할만 맡고 있는 것이고, isClient만 true라면 클라이언트 역할만 맡고 있는 것이다. 그리고 isServer와 isClient가 모두 true라면 호스트로서 서버와 클라이언트의 역할을 동시에 하고 있는 것이다.

 

서버와 클라이언트가 분리된 방식은 일반적인 게임 서버나 데디케이트 서버에서 주로 사용되는 것이고, 하나의 클라이언트가 서버의 역할을 함께하는 호스트 방식은 주로 P2P 서버로 사용된다. 다만 P2P 서버 방식은 보안이나 해킹, 핵 등에 취약하다는 단점이 있다.

 

 

 

 

그 다음은 원래라면 HasAuthority 프로퍼티에 대한 설명을 할 차례지만 그전에 필요한 개념이 있기 때문에 isLocalPlayer에 대한 설명을 먼저 하겠다. 지난 섹션에서 클라이언트가 서버에 접속했을때, 이 클라이언트의 플레이어를 위한 하나의 프리팹을 Network Manager의 Player Prefab에 등록해준 것을 기억할 것이다. 이 플레이어 프리팹을 위해 사용되는 프로퍼티가 바로 isLocalPlayer이다.

 

 

 

플레이어 프리팹은 Auto Create Player의 설정을 통해서 하나의 클라이언트가 접속할 때마다, 자동으로 생성되거나, 클라이언트 측의 수동 플레이어 생성 요청을 받아 생성되는데, 이렇게 생성된 플레이어 오브젝트가 내 것인지를 알려주는 것이 바로 isLocalPlayer 프로퍼티이다. 클라이언트 측에서 작동할 코드를 만들때 그 클라이언트의 플레이어 오브젝트만 작동해야 하는 코드라면 isLocalPlayer 프로퍼티를 이용해 제한을 하는 것이 좋다.

 

 

 

 

그 다음은 HasAuthority 프로퍼티에 대한 내용을 알아보자. 이 프로퍼티는 이 네트워크 오브젝트의 권한을 가지고 있는가에 대한 프로퍼티이다.

 

 

 

위의 이미지에서는 Network Identity 컴포넌트의 Local Player Authority에 체크가 되어있는데, 이것이 체크되어 있지 않은 경우에는 그 오브젝트의 권한은 서버가 가지게 되고, 체크가 되어 있는 경우에는 위에서 설명한 isLocalPlayer가 true인 측에서 권한을 가지게 된다. isLocalPlayer가 아닌 쪽에도 이 권한을 줄 수 있지만, 그 방법에 대해서는 다른 섹션에서 설명하겠다.

 

이 hasAuthority 프로퍼티를 테스트하는 방법은 간단하다. 플레이어 프리팹에 Network Transform 컴포넌트를 추가하고 한 번은 Local Player Authority를 켜고, 또 한 번은 Local Player Authority를 끄고 움직여보자.

 

Local Player Authority를 켠 상태에서는 서버에서 아무리 플레이어 오브젝트를 움직여도 클라이언트 측으로 동기화 되지 않을 것이다. 하지만 그 플레이어 오브젝트를 isLocalPlayer로 가지는 클라이언트 측에서 플레이어 오브젝트를 움직이면 그 움직임이 서버와 다른 클라이언트에게 동기화 되는 것을 확인할 수 있다.

 

반대로 Local Player Authority를 끈 상태에서는 그 플레이어 오브젝트를 isLocalPlayer로 가지는 클라이언트는 물론 모든 클라이언트에서 움직여도 서버는 물론이고 다른 클라이언트로 동기화되지 않는다. 하지만 서버에서 플레이어 오브젝트를 움직이면 모든 클라이언트에 동기화된다.

 

네트워크 기능을 사용하는 커스텀 클래스를 만드는 경우, 위의 네 가지 프로퍼티들을 응용해서 각 상황에 따라 어떻게 동작하도록 할 것인지 정해줄 수 있다.

 

 

[유니티 어필리에이트 프로그램]

아래의 링크를 통해 에셋을 구매하시거나 유니티를 구독하시면 수익의 일부가 베르에게 수수료로 지급되어 채널의 운영에 도움이 됩니다.

 

에셋스토어

여러분의 작업에 필요한 베스트 에셋을 찾아보세요. 유니티 에셋스토어가 2D, 3D 모델, SDK, 템플릿, 툴 등 여러분의 콘텐츠 제작에 날개를 달아줄 다양한 에셋을 제공합니다.

assetstore.unity.com

 

Easy 2D, 3D, VR, & AR software for cross-platform development of games and mobile apps. - Unity Store

Have a 2D, 3D, VR, or AR project that needs cross-platform functionality? We can help. Take a look at the easy-to-use Unity Plus real-time dev platform!

store.unity.com

 

Create 2D & 3D Experiences With Unity's Game Engine | Unity Pro - Unity Store

Unity Pro software is a real-time 3D platform for teams who want to design cross-platform, 2D, 3D, VR, AR & mobile experiences with a full suite of advanced tools.

store.unity.com

[투네이션]

 

-

 

toon.at

[Patreon]

 

WER's GAME DEVELOP CHANNEL님이 Game making class videos 창작 중 | Patreon

WER's GAME DEVELOP CHANNEL의 후원자가 되어보세요. 아티스트와 크리에이터를 위한 세계 최대의 멤버십 플랫폼에서 멤버십 전용 콘텐츠와 체험을 즐길 수 있습니다.

www.patreon.com

[디스코드 채널]

 

Join the 베르의 게임 개발 채널 Discord Server!

Check out the 베르의 게임 개발 채널 community on Discord - hang out with 399 other members and enjoy free voice and text chat.

discord.com

 

반응형

UNet Tutorial (3) - 기초적인 위치 동기화 구현

 

Network Manager와 Network Manager HUD를 이용한 간단한 서버와 클라이언트 만들기

 

이번 섹션에서 우리는 유니티에서 제공하는 NetworkManager와 NetworkManager HUD, NetworkTransform 등을 이용해서 간단한 위치 동기화 구현을 만들어볼 것이다.

 

 

우선의 위의 화면과 같이 오브젝트의 위치를 파악할 수 있게 카메라가 바닥인 평면을 바라보게 만들자.

 

 

그 다음에는 게임 오브젝트를 하나 만들어서 Network Manager와 Network Manager HUD를 컴포넌트를 넣어주자. 여기서 Network Manager는 게임 내에서 네트워크 통신을 처리해줄 것이고, Network Manager HUD는 유니티 네트워킹 테스트용 UI를 제공한다.

 

 

게임 오브젝트에 Network Manager HUD를 추가하고 게임을 실행하면 위의 이미지처럼 몇 개의 버튼이 화면에 보이게 되고 보이는 버튼을 누르는 것 만으로 간단하게 서버를 열거나 열어놓은 서버에 접속할 수 있게 된다. 이전 섹션에서 설명한 것처럼 LAN Host 버튼을 누르면 클라이언트가 서버의 역할을 겸하게 되는 서버가 열리고, LAN Server Only 버튼을 누르면, 클라이언트의 기능은 배제된 서버만 열리게 된다. 그리고 LAN Client 버튼을 누르면 열린 서버에 접속하게 된다.

 

 

 

 

Network Transform을 이용한 위치 동기화 구현

 

위에서 Network Manager를 이용해서 서버를 열고 접속하는 방법을 확인했으니 이번에는 Network Transform를 이용해서 위치를 동기화하는 방법에 대해서 알아보자. 우선은 게임 씬에 큐브 오브젝트를 하나 만든 후에 아래의 이미지처럼 Network Transform을 컴포넌트에 추가해주자.

 

 

오브젝트에 Network Transform 컴포넌트를 추가하면 하나의 컴포넌트가 자동으로 생성된 것을 볼 수 있다.

 

 

그것은 바로 Network Identity라는 컴포넌트인데, 이름에서도 알 수 있듯이 이 컴포넌트는 오브젝트의 네트워크 ID를 관리하고 네트워킹 시스템에 알리는 역할을 한다. 즉, 이 오브젝트가 다른 서버나 클라이언트의 어느 오브젝트와 동기화되어야 하는지 알리는 역할을 한다. 자세한 옵션에 대해서는 이후에 설명할 것이다.

 

 

 

Network Transform 컴포넌트를 추가했다면, Hierachy 뷰에 있는 오브젝트를 Project 뷰로 드래그 앤 드롭해서 프리팹으로 만들자. 프리팹으로 만든 이후에는 Hierachy 뷰에 있는 오브젝트는 삭제해도 된다.

 

 

그 다음에는 아까 전에 만든 Network Manager의 항목 중에 Player Prefab이라는 곳에 방금 만든 TransformSyncObject를 넣어준다. 이 Player Prefab은 한 명의 플레이어가 접속했을 때마다 생성되는 프리팹을 의미한다.

 

여기까지 작업했다면 프로젝트를 빌드하고 서버를 에디터에서 켜고 빌드된 프로젝트를 통해 클라이언트로 접속해보자.

 

 

 

접속하면 우리가 만들어서 플레이어 프리팹으로 등록한 오브젝트가 생겨나고 서버 측 에디터의 뷰에서 피벗을 잡고 움직이면 클라이언트 측 화면에서도 함께 움직이는 것을 볼 수 있다. 다만, 서버에서 움직임과는 다르게 클라이언트의 움직임은 약간 끊어져서 보이게 되는데 이것은 여러가지 방법으로 해결할 수 있다.

 

 

 

 

 

첫 번째 방법으로는, Network Transform 옵션에서 Network Send Rate를 높여주는 방법이 있다. 이것을 올려주면 초당 위치 동기화 횟수를 늘려서 통신량이 많아지는 대신에 좀 더 부드러운 움직임이 가능하게 만들어 준다. 다만 Inspector 뷰에서 바꾸는 방법은 초당 29회가 한계로 더 짧은 주기로 동기화하기를 바란다면, 두 번째 방법으로, 별도의 커스텀 네트워크 트랜스폼을 만드는 것이 좋다. 또한 부드러운 움직임을 원하지만 통신량이 많지 않기를 원한다면 커스텀 네트워크 트랜스폼을 구현하고 보간이나 Lerp, 추측항법 등을 가미해야 한다.

 

 

[유니티 어필리에이트 프로그램]

아래의 링크를 통해 에셋을 구매하시거나 유니티를 구독하시면 수익의 일부가 베르에게 수수료로 지급되어 채널의 운영에 도움이 됩니다.

 

에셋스토어

여러분의 작업에 필요한 베스트 에셋을 찾아보세요. 유니티 에셋스토어가 2D, 3D 모델, SDK, 템플릿, 툴 등 여러분의 콘텐츠 제작에 날개를 달아줄 다양한 에셋을 제공합니다.

assetstore.unity.com

 

Easy 2D, 3D, VR, & AR software for cross-platform development of games and mobile apps. - Unity Store

Have a 2D, 3D, VR, or AR project that needs cross-platform functionality? We can help. Take a look at the easy-to-use Unity Plus real-time dev platform!

store.unity.com

 

Create 2D & 3D Experiences With Unity's Game Engine | Unity Pro - Unity Store

Unity Pro software is a real-time 3D platform for teams who want to design cross-platform, 2D, 3D, VR, AR & mobile experiences with a full suite of advanced tools.

store.unity.com

[투네이션]

 

-

 

toon.at

[Patreon]

 

WER's GAME DEVELOP CHANNEL님이 Game making class videos 창작 중 | Patreon

WER's GAME DEVELOP CHANNEL의 후원자가 되어보세요. 아티스트와 크리에이터를 위한 세계 최대의 멤버십 플랫폼에서 멤버십 전용 콘텐츠와 체험을 즐길 수 있습니다.

www.patreon.com

[디스코드 채널]

 

Join the 베르의 게임 개발 채널 Discord Server!

Check out the 베르의 게임 개발 채널 community on Discord - hang out with 399 other members and enjoy free voice and text chat.

discord.com

 

반응형

UNet Tutorial (2) - 간단한 개념

 

유니티에서 사용되는 개념들

 

1. 서버와 호스트

 

Unity 네트워크 시스템에서 게임은 한 개의 서버와 여러 개의 클라이언트를 가지게 된다. 이러한 방식은 클라이언트는 입력만 받고 서버에 전송해주고, 입력을 전송받은 서버가 모든 게임의 계산 처리를 한 이후 클라이언트에 전송해주는 데디케이트(Dedicate) 서버 방식에 사용된다. 전용 서버가 없는 P2P 방식의 경우에는 클라이언트 중 하나가 서버의 역할을 맡게 되며, 이 클라이언트는 “호스트”라고 부른다.

 

 

호스트는 동일 프로세스에서 서버이자 클라이언트이다. 호스트는 LocalClient라는 특수 클라이언트 유형을 사용하며, 다른 클라이언트는 RemoteClient를 사용한다. LocalClient는 동일 프로세스에 위치하고 있으므로 직접 함수 호출과 메시지 대기열을 통해 로컬 서버와 통신을 한다. RemoteClient는 서버와 일반적인 네트워크 연결을 통해 데이터를 주고 받는다.

네트워크 시스템의 목표 중 하나는 LocalClients와 RemoteClients 코드를 동일하게 하여 개발자가 되도록이면 한 종류의 클라이언트만 고려해도 되도록 하는 것이다.

 

 

 

 

2. 인스턴스화와 스폰

 

Unity에서는 GameObject.Instantiate() 함수를 통해서 새로운 Unity 게임 오브젝트를 생성한다. 하지만 네트워크 시스템의 경우 오브젝트는 NetworkServer를 통해서 “스폰”되어야만 네트워크에서 활성화가 된다. 이 과정은 서버에서만 할 수 있으며, 이를 통해 오브젝트가 연결된 클라이언트에서도 생성되도록 할 수 있다. 오브젝트가 스폰된 이후에는 스폰 시스템이 배포된 오브젝트의 생명주기 관리와 상태 동기화 원칙을 사용한다.

 

 

3. 플레이어, 로컬 플레이어, 권한

 

네트워크 시스템에서 플레이어 오브젝트는 특수하게 취급된다. 게임 플레이어 각각에 연관된 플레이어 오브젝트에 커맨드가 보내지게 된다. 자신이 아닌 다른 사람의 플레이어 오브젝트에는 커맨드를 호출할 수 없다. 플레이어가 추가되고 관계가 연결된 경우, 해당 플레이어 오브젝트는 플레이어 클라이언트의 “로컬 플레이어” 오브젝트가 된다. isLocalPlayer 프로퍼티는 true로 설정되며, 클라이언트 오브젝트에 호출된 OnStartLocalPlayer() 콜백이 발생한다. 아래 다이어그램은 두 클라이언트와 해당 로컬 플레이어의 예제를 보여준다.

 

 

오직 “본인” 플레이어 오브젝트에만 isLocalPlayer 플래그가 설정된다. 이 플래그를 통해 입력 프로세싱 필터링, 카메라 위치 조작, 또는 특정 플레이어에 대해서만 변경되어야 하는 클라이언트측 동작을 처리할 수 있다.

플레이어 오브젝트는 isLocalPlayer와 더불어 “로컬 권한”을 가질 수도 있다. 이는 소유자 클라이언트의 플레이어 오브젝트가 해당 오브젝트에 대한 권한이 있다는 의미입니다. 권한은 대부분 움직임을 제어하는 데 주로 활용되지만, 다른 용도로도 사용할 수 있습니다. NetworkTransform 컴포넌트는 이 권한을 이해하며 설정된 경우 클라이언트에서의 움직임을 보냅니다. NetworkIdentity는 LocalPlayerAuthority를 설정할 수 있는 체크박스가 있다.

적 오브젝트와 같이 비플레이어 오브젝트는 연관 클라이언트가 없으므로, 서버가 권한을 가지게 된다.

 

 

NetworkBehaviour에 있는 “hasAuthority” 프로퍼티를 사용하여 오브젝트에 권한이 있는지 파악할 수 있다. 따라서 비플레이어 오브젝트는 서버에 권한이 있고, 플레이어 오브젝트 중 localPlayerAuthority가 설정된 것은 각 소유자 클라이언트에 권한이 있다.

 

 

 

 

4. 비플레이어 오브젝트의 클라이언트 권한

 

Unity 5.2 릴리스 버전부터 클라이언트는 비플레이어 오브젝트에 권한을 가질 수 있으며, 두 가지 방법이 있다. 하나는 NetworkServer.SpawnWithClientAuthority로 오브젝트를 스폰한 후 클라이언트 네트워크 연결에 전달하여 소유권을 이전하는 방법이고 다른 하나는 NetworkIdentity.AssignClientAuthority를 클라이언트 네트워크 연결과 사용하여 소유권을 이전하는 방법이다.

클라이언트에 권한을 할당하면 오브젝트 NetworkBehaviours에 OnStartAuthority()가 호출되며, hasAuthority 프로퍼티는 true가 된다. 다른 클라이언트의 hasAuthority 프로퍼티는 계속 false가 된다. 클라이언트 권한이 있는 비플레이어 오브젝트는 플레이어와 같이 커맨드를 보낼 수 있다. 이 커맨드는 오브젝트 서버 인스턴스에서 실행되지, 연결에 관련된 플레이어 서버에서 실행되지 않는다.

클라이언트 권한이 있는 비플레이어 오브젝트는 NetworkIdentity에서 LocalPlayerAuthority가 체크되어 있어야 한다.

아래는 오브젝트를 스폰하고 스폰한 플레이어 클라이언트의 권한을 할당하는 예시이다.

 

[Command]
void CmdSpawn()
{
    var go = (GameObject)Instantiate(otherPrefab, transform.position + new Vector3(0,1,0), Quaternion.identity);
    NetworkServer.SpawnWithClientAuthority(go, connectionToClient);
}

 

 

 

 

5. 네트워크 컨텍스트 프로퍼티

 

NetworkBehaviour 클래스에는 언제든 네트워크된 오브젝트의 네트워크 컨텍스트가 무엇인지 알 수 있게 하는 프로퍼티가 있다.

  • isServer - 오브젝트가 스폰되었고 서버나 호스트에 있는 경우 true다.
  • isClient - 오브젝트가 서버에 의해 생성되었고 클라이언트에 있는 경우 true다.
  • isLocalPlayer - 오브젝트가 해당 클라이언트 플레이어 오브젝트인 경우 true다.
  • hasAuthority - 로컬 프로세스가 이 오브젝트를 소유하는 경우 true다.

이들 프로퍼티는 에디터 인스펙터 창의 오브젝트 미리보기 창을 통해 확인할 수 있다.

 

 

[유니티 어필리에이트 프로그램]

아래의 링크를 통해 에셋을 구매하시거나 유니티를 구독하시면 수익의 일부가 베르에게 수수료로 지급되어 채널의 운영에 도움이 됩니다.

 

에셋스토어

여러분의 작업에 필요한 베스트 에셋을 찾아보세요. 유니티 에셋스토어가 2D, 3D 모델, SDK, 템플릿, 툴 등 여러분의 콘텐츠 제작에 날개를 달아줄 다양한 에셋을 제공합니다.

assetstore.unity.com

 

Easy 2D, 3D, VR, & AR software for cross-platform development of games and mobile apps. - Unity Store

Have a 2D, 3D, VR, or AR project that needs cross-platform functionality? We can help. Take a look at the easy-to-use Unity Plus real-time dev platform!

store.unity.com

 

Create 2D & 3D Experiences With Unity's Game Engine | Unity Pro - Unity Store

Unity Pro software is a real-time 3D platform for teams who want to design cross-platform, 2D, 3D, VR, AR & mobile experiences with a full suite of advanced tools.

store.unity.com

[투네이션]

 

-

 

toon.at

[Patreon]

 

WER's GAME DEVELOP CHANNEL님이 Game making class videos 창작 중 | Patreon

WER's GAME DEVELOP CHANNEL의 후원자가 되어보세요. 아티스트와 크리에이터를 위한 세계 최대의 멤버십 플랫폼에서 멤버십 전용 콘텐츠와 체험을 즐길 수 있습니다.

www.patreon.com

[디스코드 채널]

 

Join the 베르의 게임 개발 채널 Discord Server!

Check out the 베르의 게임 개발 채널 community on Discord - hang out with 399 other members and enjoy free voice and text chat.

discord.com

 

반응형

 

UNet Tutorial (1) - Overview

 

네트워크 게임을 제작할 때, 클라이언트의 네트워크를 구현하는 방법은 여러가지가 있다. 대표적인 방법으로는 포톤 클라우드가 제공하는 Photon Unity Networking과 넷텐션의 ProudNet으로 구현하는 방법등이 있다. 이번 포스팅에서는 이런 외부에서 제공하는 방식이 아니라 유니티가 제공하는 Unity Networking을 이용하는 방법에 대해서 알아보게될 것이다.

 

사실 이전까지는 유니티에서 제공하는 Networking은 잦은 버그와 잘알려지지 않은 점, 사용 예가 많지 않은 점등을 이유로 잘 사용되지 않은 편인데, 계속된 버전업과 지난 2017년 12월 04일에 유니티 테크놀러지 측에서 게임 서버 호스팅 업체를 인수한 것으로 충분히 적용해 볼만 하다고 여겨져서 튜토리얼을 작성하게 되었다.

 

 

 

 

UNet의 구조

 

 

유니티의 네트워킹 API는 위의 이미지와 같은 레이어로 이루어져 있는데 두 가지 방식으로 사용할 수 있다.

 

1. 유니티에서 제공하는 "High-Level" 스크립팅 API - HLAPI라고 부르며, 이것을 이용하여 네트워킹 기능을 구현할 경우 Low-Level의 세부 사항에 대해 신경쓰지 않고도 멀티 유저 게임에 일반적으로 필요한 대부분의 요구 사항을 만족시킬 수 있는 커맨드에 접근할 수 있다.

2. Low-Level의 전송 레이어(Transport Layer) API - 네트워크 인프라 또는 커스텀화된 고급 멀티 플레이어 기능을 구현하고자 할 경우, 이 API를 통해 저수준에서 스스로의 네트워킹 시스템을 빌드할 수 있으며, 이는 게임 네트워킹에 있어서 구체적이거나 고급 요구 사항이 있는 경우 유용하다.

 

위의 두 가지 API를 통해서 개발자는 저수준에서 고수준까지 원하는 방법으로 게임의 네트워크를 구현할 수 있게 된다.

 

 

 

Unity가 제공하는 서비스

 

이러한 API 이 외에 유니티는 Unity Multiplayer라는 서비스를 통해서 다음과 같은 기능들을

  • 매치메이킹 서비스
  • 매치를 생성하고 홍보.
  • 참여 가능한 매치 리스트를 제공하고 매치에 참여.
  • 릴레이 서버
  • 전용 서버 없이 인터넷을 통한 게임플레이.
  • 매치 참여자에게 메시지 전송.

단, 이 Multiplayer 서비스는 유료 서비스로 이용하게 될 경우 별도의 비용을 지불해야 한다.

 

 

다음 섹션에서는 UNet의 몇몇 개념과 간단한 구현 튜토리얼을 알아보게 될 것이다.

 

 

[유니티 어필리에이트 프로그램]

아래의 링크를 통해 에셋을 구매하시거나 유니티를 구독하시면 수익의 일부가 베르에게 수수료로 지급되어 채널의 운영에 도움이 됩니다.

 

에셋스토어

여러분의 작업에 필요한 베스트 에셋을 찾아보세요. 유니티 에셋스토어가 2D, 3D 모델, SDK, 템플릿, 툴 등 여러분의 콘텐츠 제작에 날개를 달아줄 다양한 에셋을 제공합니다.

assetstore.unity.com

 

Easy 2D, 3D, VR, & AR software for cross-platform development of games and mobile apps. - Unity Store

Have a 2D, 3D, VR, or AR project that needs cross-platform functionality? We can help. Take a look at the easy-to-use Unity Plus real-time dev platform!

store.unity.com

 

Create 2D & 3D Experiences With Unity's Game Engine | Unity Pro - Unity Store

Unity Pro software is a real-time 3D platform for teams who want to design cross-platform, 2D, 3D, VR, AR & mobile experiences with a full suite of advanced tools.

store.unity.com

[투네이션]

 

-

 

toon.at

[Patreon]

 

WER's GAME DEVELOP CHANNEL님이 Game making class videos 창작 중 | Patreon

WER's GAME DEVELOP CHANNEL의 후원자가 되어보세요. 아티스트와 크리에이터를 위한 세계 최대의 멤버십 플랫폼에서 멤버십 전용 콘텐츠와 체험을 즐길 수 있습니다.

www.patreon.com

[디스코드 채널]

 

Join the 베르의 게임 개발 채널 Discord Server!

Check out the 베르의 게임 개발 채널 community on Discord - hang out with 399 other members and enjoy free voice and text chat.

discord.com

 

반응형

+ Recent posts