Tutorial (1)

프로젝트 생성하기와 열기

 

작성 기준 버전 :: 2018.3.1f1

 

프로젝트 생성하기

 

유니티 엔진으로 게임을 제작하는 첫 번째 관문은 프로젝트를 생성하는 것이다. 유니티 엔진을 처음 실행하면 아래의 이미지와 같이 현재 생성된 프로젝트가 없다고 표시된다. 이미지에 빨간 사각형으로 표시된 New를 클릭하면 새로운 프로젝트를 생성할 수 있게 된다.

 


New 버튼을 누르고 나면, 아래의 이미지와 같이 새로 생성할 프로젝트의 이름과 몇 가지를 설정할 수 있는 화면으로 전환된다.

 


프로젝트를 생성할 때, 각 항목들의 내용은 다음과 같다.

 

 

Project Name은 생성할 프로젝트의 이름이다.

 

Template은 어떤 프로젝트를 생성할 것인지 설정하는 항목이다. 기본적으로 2D와 3D가 있는데, 평면상으로 보이는 2D 게임을 만들고 싶다면 2D 옵션을, 입체적인 3D 게임을 만들고 싶다면 3D 옵션을 선택하면 된다. 이 외에도 유니티를 활용하는 방법에 대한 몇 가지의 템플릿을 추가로 제공한다.

 

Location 항목은 프로젝트가 저장될 경로이다. 이를 통해서 사용자는 프로젝트를 원하는 위치에 저장할 수 있다.

 

Add Asset Package는 애셋 스토어에서 구매한 애셋을 프로젝트를 생성하는 단계에서 추가할 수 있게 해주는 버튼이다.

 

Enable Unity Analytics는 프로젝트에 관련된 통계나 비슷한 게임의 벤치마크 성능을 보여주는 애널리틱스 기능으로 프로젝트를 관히라는데 도움을 주는 기능을 활성화시키는 옵션이다.

 

필요한 항목들을 모두 정한 다음 Create Project 버튼을 누르면 새로운 프로젝트가 생성되고 유니티 에디터가 실행된다.

 

 

 

프로젝트 열기

 

프로젝트의 생성을 끝마쳤다면 이번에는 생성한 프로젝트를 불러와보자. 유니티 에디터를 닫은 후에 다시 유니티 엔진을 실행한다.

 

그렇게 하면 방금 전에 생성한 프로젝트가 프로젝트 목록에 있는 것을 확인할 수 있다. 프로젝트 이름을 클릭하면 간단하게 프로젝트를 불러올 수 있다.

 

 

간혹 프로젝트 폴더를 메신저나 이메일을 통해서 건네받은 경우에는 곧바로 프로젝트 목록에 해당 프로젝트가 뜨지 않을 수 있다. 이럴 때는 우측 상단에 Open 버튼을 클릭하면 열고자 하는 프로젝트의 폴더를 선택할 수 있는 대화상자가 열린다.

 

 

대화상자가 열리면 열고자 하는 프로젝트의 폴더를 클릭한 뒤 폴더 선택 버튼을 누르면 된다.

 

 

 

프로젝트 목록에 없던 프로젝트는 한 번 열고 나면 다음에 유니티 엔진을 실행할 때, 목록에 표시된다.

 

 

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

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

 

에셋스토어

여러분의 작업에 필요한 베스트 에셋을 찾아보세요. 유니티 에셋스토어가 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

 

반응형

로그(Log) 출력시 스택 트레이스(Stack trace) 관리하기

 

로그(Log)는 개발자들에게 없어서는 안될 중요한 동반자다. 개발자는 로그를 통해서 코드가 제대로 동작하는지, 데이터 값들이 정상인지 등을 확인할 수 있다. 만약 로그가 없다면, 개발자는 버그를 찾아내는데 더 많은 고생을 하게 될 것이다.

 

 

 

유니티에서는 이러한 로그를 출력할 때, 위의 이미지처럼 개발자가 출력하고자하는 로그의 내용과 함께 로그가 출력된 코드의 위치를 알려주는 스택 트레이스 역시 함께 보여준다. 로그를 출력하도록 설정해놓았다면 빌드한 어플이케이션에서도 로그가 찍힐 때 스택 트레이스 역시 함께 출력되도록 되어있다.

 

위의 이미지는 간단한 테스트 코드이기 때문에 스택 트레이스가 3줄 밖에 안되지만 본격적으로 개발에 들어간 이후에는 스택 트레이스가 기본적으로 4-5줄에서 많은 10여줄을 넘는 경우가 자주 발생한다.

 

에디터에서라면 스택 트레이스와 로그가 분리되어서 출력되기 때문에 로그를 읽는데는 큰 문제가 없지만 윈도우 빌드에서 나오는 로그 파일이나, 안드로이드로 빌드된 어플리케이션으로 로그캣에서 로그를 볼때는 로그 바로 아랫줄에 스택 트레이스가 바로 출력되기 때문에 로그를 제대로 읽기가 매우 어려워진다.

 

물론 코드의 어느 지점에서 에러 로그가 발생했는지 확인해야하는 로그라면 스택 트레이스가 출력되는게 좋지만, 로그가 출력된 위치보다는 출력되는 내용이 더 중요한 로그라면 스택 트레이스는 출력되지 않는 편이 로그의 가독성을 더 높혀줄 것이다.

 

Application.SetStackTraceLogType(LogType logType, StackTraceLogType stackTraceLogType);

 

Application 클래스의 SetStackTraceLogType() 함수를 통해서 스택 트레이스의 출력 수준을 결정할 수 있다.

 

LogType 열거형은 스택 트레이스 수준을 설정할 로그의 종류를 의미한다. 로그의 종류는 다음과 같다.

 

LogType.Error
LogType.Assert
LogType.Warning
LogType.Log
LogType.Exception

 

StackTraceLogType은 스택 트레이스의 출력 수준을 의미한다. 스택 트레이스 출력 수준의 종류는 다음과 같다.

 

StackTraceLogType.None;
StackTraceLogType.ScriptOnly;
StackTraceLogType.Full;

 

스택 트레이스 타입을 수정하지 않았을 때, 유니티의 기본 수준은 ScriptOnly이다. None으로 설정하면 스택 트레이스가 전혀 출력되지 않고 Full로 설정하면 기존의 스택 트레이스보다 더 자세한 정보를 제공하는 스택 트레이스가 출력된다.

 

 

 

 

응용

 

스택 트레이스 타입을 변경하는 순간부터 모든 로그에 스택 트레이스 타입이 변경되기 때문에, 제대로 다루지 않으면 스택 트레이스가 필요한 로그에서 스택 트레이스가 출력되지 않거나 혹은 그 반대의 경우가 발생할 수 있다.

 

필요한 상황에서만 스택 트레이스를 켜고 끄기 위한 방법으로는 Debug 클래스를 래핑해서 사용하는 방법이 있다.

 

using UnityEngine;

public static class Debug
{
    public static void Log(object message, StackTraceLogType stackTraceLogType = StackTraceLogType.ScriptOnly)
    {
        Application.SetStackTraceLogType(LogType.Log, stackTraceLogType);
        UnityEngine.Debug.Log(message);
    }
}

 

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

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

 

에셋스토어

여러분의 작업에 필요한 베스트 에셋을 찾아보세요. 유니티 에셋스토어가 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

 

반응형

네트워크 애니메이터(Network Animator)

 

네트워크 게임에서는 게임이나 유닛, 캐릭터 등의 상태를 동기화하는 것도 중요하지만 눈에 보이는 캐릭터들의 움직임, 즉 애니메이션 역시 동기화가 필요하다. 아무리 다른 동기화가 잘 되고 있다고 하더라도, 애니메이션 동기화가 진행되지 않아서 가만히 서있는 자세로 이동하거나 공격한다면 문제가 많을 것이다.

 

유니티 네트워크 시스템에서는 이러한 애니메이션 동기화를 위한 기본적인 기능을 제공하는데 그것이 바로 네트워크 애니메이터(Network Animator)다.

 

이번 섹션을 진행하기 위해서는 기본적인 유니티의 애니메이션 시스템과 애니메이터 컨트롤러에 대한 지식이 필요하다. 기반 지식이 부족하다면 유니티의 애니메이션 문서를 참조하여 공부를 해두는 것이 좋다.

 

그럼 이제부터 네트워크 애니메이터를 사용하는 방법을 알아보도록 하자.

 

 

네트워크 애니메이터 컴포넌트의 기본적인 요구사항

 

 

 

네트워크 애니메이터를 사용하기 위해서는 네트워크 애니메이터 컴포넌트가 있는 오브젝트와 같은 오브젝트에 에니메이터 컨트롤러와 네트워크 아이덴티티 컴포넌트가 있어야 한다.

 

 

네트워크 애니메이터의 작동 방식

 

 

네트워크 애니메이터의 작동 방식은 기본 애니메이터 컨트롤러의 파라메터가 변경되면 그 변경된 파라메터의 값을 네트워크 애니메이터를 통해서 클라이언트의 해당 네트워크 애니메이터로 전송하고 받은 측의 네트워크 애니메이터가 자신이 소유한 애니메이터 컨트롤러에 변경된 파라메터와 값을 알려서 애니메이션을 동작하게 한다.

 

 

 

 

 

네트워크 애니메이터를 사용하기 위한 준비

 

앞에서 네트워크 애니메이터의 기본적인 요구 사항과 작동 방식을 알아보았으니 이제 네트워크 애니메이터를 추가하고 사용하는 방법에 대해서 알아보도록 하자.

 

 

 

애니메이션 섹션의 단골인 박스맨이 이번 네트워크 애니메이터 섹션에서도 도움을 줄 것이다.

 

 

 

위의 이미지에 맞춰서 애니메이션 스테이트를 구성해보자. 박스맨 캐릭터는 대기(Idle) - 이동(Walk) - 공격(Attack), 세 가지 상태를 가지며, IsMove 파라메터의 상태에 따라서 대기와 이동 상태를 오가며, Attack 트리거를 받으면 공격 애니메이션을 재생하고 대기 상태로 돌아가는 아주 간단한 형태다.

 

 

 

위와 같이 게임 오브젝트와 컴포넌트를 세팅해주면 준비는 끝이다.

 

 

 

 

네트워크 애니메이터 추가하기

 

 

네트워크 애니메이터를 추가하는 방법은 간단하다. 애니메이션을 동기화하고자 하는 오브젝트에(애니메이터 컨트롤러 컴포넌트를 가지고 있어야 한다) 네트워크 애니메이터를 Add Component 해주면 된다. 그러면 네트워크 애니메이터와 함께 네트워크 통신에 필요한 Network Identity 컴포넌트가 자동으로 함께 추가된다.

 

 

 

네트워크 애니메이터를 추가한 후에는 네트워크 애니메이터 컴포넌트의 애니메이터 프로퍼티에 동기화되어야할 애니메이터를 추가해주면 된다.

 

 

그렇게하면 네트워크 애니메이터 컴포넌트에 동기화될 애니메이터의 파라메터들이 표시된다. 이 다음에는 네트워크 애니메이터를 컨트롤할 스크립트를 작성해야 한다.

 

using UnityEngine;
using UnityEngine.Networking;

public class PlayerCharacter : MonoBehaviour
{
    private NetworkAnimator netAnimator;

    void Start ()
    {
        netAnimator = GetComponent<NetworkAnimator>();
    }
   
    void Update ()
    {
        if (Input.GetKeyDown(KeyCode.W))
        {
            netAnimator.animator.SetBool("IsMove", true);
        }
        else if (Input.GetKeyUp(KeyCode.W))
        {
            netAnimator.animator.SetBool("IsMove", false);
        }

        if (Input.GetMouseButtonDown(0))
        {

            netAnimator.animator.SetTrigger("Attack");
            netAnimator.SetTrigger("Attack");
        }
    }
}

 

위와 같이 스크립트를 작성한 이후에 빌드하고 실행해서 한 쪽에서 서버를 열고 다른 쪽에서 클라이언트로 접속한 뒤 서버 측에서 W를 누르거나 마우스를 클릭했을때 애니메이션이 동기화됨을 확인할 수 있다.

 

 

위의 예시 코드에서 몇 가지 의문점이 있을 수 있는데, SetBool을 호출할 때는 networkAnimator.animator를 통해서 호출하고 SetTrigger를 호출할 때는 networkAnimator.SetTrigger()로 바로 호출하는가와 트리거를 사용하는 부분에서 왜 networkAnimator.animator.SetTrigger()와 networkAnimator.SetTrigger()를 중복해서 사용했는지가 그것이다.

 

첫 번째 의문점의 경우에는 Trigger는 NetworkAnimtor 클래스에서 호출할 수 있는 SetTrigger() 함수가 있지만, 다른 애니메이터의 파라메터(Int, Float, Bool)는 NetworkAnimator 클래스에서 바로 호출해서 사용하는 메서드가 따로 없고, NetworkAnimator의 멤버 변수인 animator를 통해서 SetBool(), SetInt(), SetFloat() 함수를 호출하도록 만들어져 있기 때문이다.

 

두 번째 문제는 트리거를 사용하는 부분에서 왜 networkAnimator.animator.SetTrigger()와 networkAnimator.SetTrigger()를 중복해서 사용했는지 인데, 이것은 networkAnimator.animator.SetTrigger() 함수를 통해서 동작하는 애니메이션은 서버에서만 재생되고, networkAnimator.SetTrigger() 함수를 통해서 동작하는 애니메이션은 클라이언트에서만 재생되기 때문이다. 즉, 서버에서도 애니메이션을 재생하고 클라이언트에서도 애니메이션을 재생하기를 원한다면 위의 예시 코드와 같이 작성되어야 한다. 특히 서버 측에서 애니메이션 이벤트를 통해서 애니메이션이 진행되는 도중에 특정한 동작이 발생되도록 로직이 짜여있다면 서버에서도 애니메이션이 동작되어야 하기 때문에 반드시 위의 코드처럼 작성되어야만 한다. 하지만 이후에도 설명하겠지만, 애니메이션을 재생하는 처리는 상당히 무거운 작업에 속하기 때문에, 클라이언트가 서버의 역할을 함께하는 P2P 방식이 아닌 순수한 서버라면 애니메이션 재생 도중 호출되는 애니메이션 이벤트는 반드시 배제해야 한다. 그리고 서버에서는 애니메이션이 완전히 동작하지 않도록 하는 것이 좋다.

 

 

 

 

추가로 : 네트워크 애니메이터에 대한 중요한 사실

 

유니티에서 기본적으로 제공하는 네트워크 애니메이터가 있기 때문에 우선은 소개하고 사용법에 대해서 알려주지만, 사실 기본 네트워크 애니메이터를 사용하는 것은 추천하지 않는다. 오히려 직접 커스텀 네트워크 애니메이터를 따로 구현해서 사용하도록 권장하고 싶다. 지금은 주요 작업을 2017.3.03f 버전의 유니티로 하고 있기 때문에 이후의 버전에서는 더 좋게 바뀌었는지 모르겠지만, 이 버전과 이전 버전에서는 기본 네트워크 애니메이터에 상당한 문제가 있기 때문이다.

 

첫 번째 문제는, 기본 네트워크 애니메이터가 소모하는 데이터량이 너무 많다. 커스텀 애니메이터를 잘 구현한다면 기본 네트워크 애니메이터를 사용할 때보다 훨씬 데이터 소모량을 많이 줄일 수 있다.

 

두 번째 문제는, 꽤나 심각한 문제인데, 기본 네트워크 애니메이터가 굉장히 많은 가비지(Garbage)를 발생시켜서 GC로 인한 프레임드랍이 심각하다고 여겨질 만큼 발생한다는 것이다. 이것에 대한 이슈는 구글링해보면 해외 개발자들도 상당히 심각하게 느끼도 있다는 것을 알 수 있다. 실제 개발에서 네트워크 애니메이터로 애니메이션을 동기화 했다가 이 문제 때문에 네트워크 애니메이터를 모두 제거하고 커스텀 네트워크 애니메이터를 구현해서 사용해야 했었다.

 

 

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

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

 

에셋스토어

여러분의 작업에 필요한 베스트 에셋을 찾아보세요. 유니티 에셋스토어가 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

 

반응형

마우스 입력 처리 총정리

 

[본 포스트의 내용은 유튜브 영상으로도 시청하실 수 있습니다]

 

마우스는 PC의 중요한 입력장치 중 하나로 PC를 타깃으로 하는 게임이라면 십중팔구는 마우스에 대한 입력처리가 필수적이다. 이번 섹션에서는 마우스 입력 처리하는 방법에 대해서 알아보자.

 

마우스를 통해서 들어오는 입력은 세 가지 정도로 나눌 수 있다. 버튼 입력, 위치 센서 입력, 스크롤휠 입력이 그것이다.

 

마우스 버튼 입력 처리

 

첫 번째로 알아볼 입력 처리는 버튼에 대한 입력 처리다.

 

마우스 버튼에 대한 입력은 일반적으로 Down, Hold, Up 이 세 가지 과정으로 나누어져서 처리된다. Down은 버튼을 누르는 순간을 의미하고 Hold는 누른 상태로 유지하는 것, Up은 눌려진 버튼을 떼는 것을 의미한다.

 

bool Input.GetMouseButtonDown(int button);
bool Input.GetMouseButton(int button);
bool Input.GetMouseButtonUp(int button);

 

유니티에서는 Input 클래스의 GetMouseButtonDown(), GetMouseButton(), GetMouseButtonUp() 함수를 통해서 버튼의 입력을 확인할 수 있는데, 이 함수들의 사용 예시는 다음과 같다.

 

void Update ()
{
    if (Input.GetMouseButtonDown(0))
    {
        // 마우스 왼쪽 버튼을 눌렀을 때의 처리
    }

    if (Input.GetMouseButton(0))
    {
        // 마우스 왼쪽 버튼을 누르고 있는 도중의 처리
    }

    if (Input.GetMouseButtonUp(0))
    {
        // 마우스 왼쪽 버튼을 뗄 때의 처리
    }
}

 

이 함수들의 매개변수에는 버튼의 번호가 들어가는데, 각 번호가 의미하는 마우스 버튼은 다음과 같다.

 

0 : 마우스 왼쪽 버튼

1 : 마우스 오른쪽 버튼

2 : 마우스 휠 버튼

3~6 : 마우스에 달린 추가 버튼

 

 

 

 

 

마우스 커서 위치 처리

 

게임에서는 마우스 커서 위치를 확인하는 것도 굉장히 많이 사용되는 편이다. 마우스 커서 위치는 다음과 같이 가져올 수 있다.

 

void Update()
{
    Vector3 mousePos = Input.mousePosition;
}

 

참고로 프로그램 화면의 왼쪽 아래가 (0, 0)이다.

 

 

 

마우스 휠 입력 처리

 

최근에 사용되는 마우스의 경우에는 기본으로 휠이 장착되어 있다. 휠 동작의 경우에는 휠을 돌리면 화면이 위/아래로 스크롤된다거나, 화면이 확대/축소 되는 방식으로 지원된다.

 

마우스 휠 입력을 처리하는 방법은 두 가지가 있는데 그 예시는 다음과 같다.

 

void Update()
{
    float wheelInput = Input.GetAxis("Mouse ScrollWheel");
    if (wheelInput > 0)
    {
        // 휠을 밀어 돌렸을 때의 처리 ↑
    }
    else if (wheelInput < 0)
    {
        // 휠을 당겨 올렸을 때의 처리 ↓
    }

    Vector2 wheelInput2 = Input.mouseScrollDelta;
    if (wheelInput2.y > 0)
    {
        // 휠을 밀어 돌렸을 때의 처리 ↑
    }
    else if (wheelInput2.y < 0)
    {
        // 휠을 당겨 올렸을 때의 처리 ↓
    }
}

 

추가로, 휠을 한 틱 돌렸을 때, 변경되는 값은 0.1이다.

 

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

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

 

에셋스토어

여러분의 작업에 필요한 베스트 에셋을 찾아보세요. 유니티 에셋스토어가 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

 

반응형

모바일 앱에서 디바이스의 배터리 잔량 표시하기

 

PC나 콘솔 같은 디바이스에서 동작하는 앱이라면 상관없는 문제이지만 모바일 같은 휴대기기에서 동작하는 앱이라면 현재 앱이 동작하고 있는 디바이스의 배터리 잔량에 대해서 알아야 하거나 확인해야 할 수도 있다. 사용자가 일일이 화면 상단을 드래그나 터치해서 상태바(Status bar)를 꺼내지 않고도 배터리 잔량을 할 수 있게 하는 방식으로 말이다.

 

유니티는 모바일을 타깃으로 하는 게임이나 앱들을 굉장히 폭넓게 지원하기 때문에 이와 관련된 기능 역시 제공하고 있다.

 

SystemInfo.batteryLevel;

SystemInfo.batteryStatus;

 

유니티에서 배터리의 상태와 관련된 정보는 SystemInfo 클래스의 정적 프로퍼티를 통해서 가져올 수 있다.

 

첫 번째로 batteryLevel은 현재 디바이스의 배터리 충전량으로 float 변수이며, 0-1사이의 값을 가지며, 이 프로퍼티를 호출한 디바이스가 배터리를 사용하지 않거나 batteryLevel을 지원하지 않는 디바이스라면 -1을 반환한다.

 

두 번째 프로퍼티는 batteryStatus로 현재 배터리의 상태를 가져올 수 있다. 이 프로퍼티는 BatteryStatus 타입의 열거형이며 열거형의 종류와 내용은 다음과 같다.

 

BatteryStatus.Unknown;       // 충전 상태를 알 수 없음. 배터리 상태를 지원하지 않는 플랫폼일 때 반환되는 값.
BatteryStatus.Discharging;   // 충전 케이블이 연결되지 않았고, 충전도 되지 않는 상태.
BatteryStatus.NotCharging;   // 충전 케이블은 연결되어 있지만, 충전은 되지 않는 상태.
BatteryStatus.Charging;      // 충전 케이블이 연결되어 있고, 충전되고 있는 상태.
BatteryStatus.Full;          // 충전 케이블이 연결되어 있고, 배터리가 가득 찬 상태.

 

 

 

 

 

간단한 사용 예시

 

using UnityEngine;
using UnityEngine.UI;

public class BatteryUI : MonoBehaviour
{
    Sprite chargeStateSprite;   // 배터리 충전중 표시 스프라이트
    Sprite fewStateSprite;      // 배터리가 부족하다는 표시 스프라이트

    Image batteryStateImg;      // 배터리 상태 표시 이미지
    Image batteryFrameImg;      // 배터리 모양 프레임 이미지
    Image batteryLevelImg;      // 배터리 잔량 표시 이미지

    public void UpdateBatteryUI()
    {
        float batteryLevel = SystemInfo.batteryLevel;
        switch (SystemInfo.batteryStatus)
        {
            case BatteryStatus.Full:
            case BatteryStatus.Charging:

                batteryLevelImg.color = batteryFrameImg.color = Color.white;
                batteryStateImg.sprite = chargeStateSprite;
                batteryStateImg.gameObject.SetActive(true);
                batteryLevelImg.fillAmount = 1f;
                break;

            case BatteryStatus.Discharging:
                if(batteryLevel < 0.1f) // 배터리가 부족하면 이미지를 빨갛게
                {
                    batteryLevelImg.color = batteryFrameImg.color = Color.red;
                    batteryStateImg.sprite = fewStateSprite;
                    batteryStateImg.gameObject.SetActive(true);
                }
                else
                {
                    batteryLevelImg.color = batteryFrameImg.color = Color.white;
                    batteryStateImg.gameObject.SetActive(false);
                }

                batteryLevelImg.fillAmount = batteryLevel;
                break;
        }
    }
}

 

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

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

 

에셋스토어

여러분의 작업에 필요한 베스트 에셋을 찾아보세요. 유니티 에셋스토어가 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

 

반응형

UI 리소스 최적화로 메모리와 용량 최적화 잡기

 

최근 게임을 제작할 때, PC를 타깃으로 한 평범한 수준의 게임은 유저들의 평균적인 메모리 사양이 4~8GB 가량 되기 때문에 메모리에 크게 구애받는 일은 적은 편이지만, 모바일을 타깃으로 하거나, PC나 콘솔 타깃이더라도 고사양의 게임은 메모리에 대한 최적화가 필요하다.
 
특히 모바일의 경우, 하이엔드 모델은 3~4GB 이상의 넉넉한 메모리를 지원하는 모델이지만, 대다수의 사용자들이 사용하는 보급형 모델은 1~2GB 수준으로 메모리 최적화를 고려하지 않고 게임을 만들었다면, 저사양의 유저들은 게임을 원활하게 게임을 플레이할 수 없을 것이다.
 
물론 애초에 고사양 타깃으로 만들어진 게임이면 어쩔 수 없는 일이지만, 중저사양의 모델 역시 타깃으로 잡았고 메모리를 제외하면 분명 중저사양에서도 돌아갈 수 있는 게임임에도 불구하고 메모리 최적화 때문에 중저사양 모델에서 돌리지 못한다면 문제가 있다.
 
이런 메모리 최적화 문제에 대한 해결책은 여러가지가 있지만 그 중에서도 제일 먼저 살펴보아야할 부분이 UI다. 그럼 UI 텍스처 최적화를 통한 메모리 최적화에 대해서 알아보도록 하자.
 

 

 

 

 

유니티의 텍스처 압축 지원 받기

 
유니티에서는 텍스처 리소스에 대해서 기본적인 압축 기능을 자체적으로 제공한다. 이러한 압축을 지원받는 것 만으로도 압축되지 않은 리소스에 비해서 상당한 수준의 용량을 아낄 수 있게 된다.
 
유니티의 텍스처 압축은 모든 텍스처에 적용되는 것은 아니고 한 가지 제약사항이 존재한다.
 

 

그것은 바로 텍스처의 너비와 높이 둘 다 4의 배수가 되어야 한다는 것이다. 만약 너비나 높이 둘 중 하나라도 4의 배수가 되지 못하면, 해당 텍스처는 무압축 상태로 빌드된다.

 

 

 

위의 이미지를 보면 텍스처 압축을 지원받지 못한 900x359 크기의 텍스처는 1.2MB지만 900x360 크기의 텍스처는 RGBA Compressed DXT5 포맷으로 압축되어 316.4KB로 엄청나게 크기가 줄어든 것을 확인할 수 있다.

 

단, DXT5 포맷의 압축 방식은 iOS에서는 지원되지 않기 때문에, 다른 압축 포맷을 직접 지정하는것이 좋다.

 

 

 

 

스프라이트 패킹(Sprite Packing)

 

UI 텍스처 압축을 통해 용량을 아꼈다면 이번에는 메모리를 아껴볼 차례다.

 

 

유니티에서 이미지는 메모리에 올라갈 때, 정사각형의 형태나 각 변의 길이가 POT(2의 승수, 128 256 512 같은..)인 형태로 올라가게 되는데, 만약 이미지가 정사각형이 아니거나 각 변의 길이가 POT인 형태라면 이미지의 가로변과 세로변의 길이 중 긴 변의 길이에 맞춰서 정사각형의 크기의 메모리를 할당받기 때문에 낭비되는 메모리 공간이 많아진다. 이 문제는 한 쪽변이 다른 한 쪽변의 길이에 비해서 매우 길어질 수록 심해진다.

 

 

 

이러한 문제를 해결하기 위한 것이 바로 스프라이트 패킹이다. 스프라이트 패킹이란 여러개의 이미지를 같은 패키지로 패킹해서 메모리에 함께 올리는 것으로 모든 UI 텍스처를 따로 메모리에 올렸을 때보다 메모리의 낭비를 많이 줄일 수 있게 된다.

 

유니티에서 스프라이트 패킹 방법으로 레거시 스프라이트 패커(Legary Sprite Packer)와 스프라이트 아틀라스(Sprite Atlas) 두 가지를 제공한다.

 

 

 

 

슬라이스드 이미지 사용하기(Sliced Image)

 

사실 UI 리소스의 경우에는 특별한 이미지가 많이 사용된다기 보다는 사용되는 이미지가 반복되어서 사용되는 경우가 많다. UI를 묶어주는 패널(Pannel), 입력을 받는 인풋 필드(Input Field), 버튼(Button) 등이 여기에 속한다.

 

이런 것에 사용되는 리소스는 재사용성을 높여야 되지만, 초보 개발자나 초보 UI 디자이너는 예쁘거나 멋지게 만들어야 되는다는 집착에 빠지거나, 최적화에 대한 신경을 못쓰고 만들어서, 패널이나 버튼에 들어갈 이미지를 크기에 맞춰서 필요한 모든 사이즈 별로 만드는 경우가 종종 있다.

 

 

예를 들어 760x960 크기의 UI가 필요해서 거기에 해당하는 리소스를 만들어 냈다고 해보자. 이걸 게임에 그대로 적용해버리면 패널을 꾸미기에 따라서는 UI가 예뻐보일 수는 있을 것이다. 하지만 압축된 리소스임에도 불구하고 0.7MB라는 엄청난 용량을 자랑하는 것을 볼 수 있다. 이게 겨우 하나여서 0.7MB지, 여러 종류의 많은 UI를 띄워야 하는 게임이어서 해당 UI의 크기 별로 패널 리소스를 새로 만들어서 적용한다면 그리고 그 와중에 몇몇 리소스가 압축되지 않는 불상사가 발생한다면 게임의 용량이나 메모리 소모는 엄청난 수준이 될 것이다.

 

버튼이나 패널 같은 UI의 리소스의 경우에는 일반적으로 리소스의 모서리 부분을 제외한 중심 부분은 반복되는 경우가 많다. 슬라이스드 이미지란 바로 이 점에서 착안한 아이디어로 모서리 부분과 반복될 중심 부분 조금만 있으면 유니티 엔진이 중심 부분을 자동으로 채워주는 기능이다. 그렇기 때문에 패널 리소스 중에서도 모서리 부분과 반복될 중심 부분 약간 만으로 리소스를 만들면 위 이미지와 같은 커다란 패널 UI 리소스는 아래 이미지와 같이 아주 작게 줄일 수 있다.

 

 

 

불필요한 중심 부분을 제거하는 것만으로도 이미지의 크기와 용량이 700분의 1로 줄어들었다.

 

 

 

 

리소스의 중심 부분을 제거한 뒤에는 유니티의 스프라이트 에디터(Sprite Editor)에서 원형을 유지할 부분과 크기가 늘어났을 때 자동으로 채워질 부분을 설정해주면된다. 위의 그림처럼 설정하면 UI의 크기가 늘어났을 때 모서리 부분은 원형을 유지하고 가운데 십자 부분만 자동으로 채워지는데 씬에서 UI Image를 추가할 때, Image Type를 Sliced로 변경해주면 작은 이미지가 전혀 확대되거나 이상한 모양으로 늘어지지 않음을 확인할 수 있다.

 

 

 

 

 

오른쪽의 패널이 슬라이스드 이미지를 사용한 UI이고 오른쪽은 그냥 패널 이미지를 통째로 사용한 것이다. 둘이 차이가 없음을 확인할 수 있다. 하지만 패널 리소스에 그라데이션이나 디자인이 들어갔다면 그 퀄리티는 다를 수도 있다. 하지만 그 부분은 저사양에서는 단순한 패널만 보여주고 유저가 고사양을 선택한다면 패널 UI 위에 디자인 이미지를 올려서 보여줄 수도 있다.

 

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

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

 

에셋스토어

여러분의 작업에 필요한 베스트 에셋을 찾아보세요. 유니티 에셋스토어가 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

 

반응형

'Unity3D > Optimize' 카테고리의 다른 글

[Unity] 유니티 공식 지원 오브젝트 풀링  (4) 2022.08.09

QoS 채널(Quality of Service)

 

유니티 네트워크에서는 메시지의 전송 품질을 QoS(Quality of Service)라고 하는데, 네트워크 매니저에서 채널을 추가하고, 그 채널의 전송 품질을 설정할 수 있다. 그리고 NetworkBehaviour를 상속받는 클래스를 정의할 때, NetworkSetting 어트리뷰트를 통해서 이 클래스의 객체가 네트워크 메시지를 보낼 때, 어떤 채널을 통해서 메시지를 보낼지, 그 채널이 어떤 수준의 전송 품질로 메시지를 전송할 것인지를 설정할 수 있다.

 

 

 

 

전송 품질 타입(QosType)

 

우선은 유니티 네트워크에서는 어떤 종류의 전송 품질의 정의하고 지원하는지 알아야 하는데, 이러한 전송 품질에 대한 타입을 QosType이라는 열거형으로 정의하고 있다.

 

유니티 네트워크에서 정의하는 전송 품질의 종류는 다음과 같다.

 

  • Unreliable - 전송되는 메시지의 도착이나 순서를 보장하지 않는다.
  • UnreliableFragmented - 전송되는 메시지의 도착이나 순서를 보장하지 않지만 메시지를 최대 32개로 분할된 메시지를 허용한다.
  • UnreliableSequenced - 메시지의 도착은 보장하지 않지만 순서는 보장한다. 만약 지금 도착한 메시지보다 이전에 전송된 메시지는 무시한다.
  • Reliable - 메시지의 도착은 보장하지만 순서는 보장하지 않는다.
  • ReliableFragmented - 메시지의 도착을 보장하며, 메시지 당 최대 32개로 분할된 메시지를 허용한다.
  • ReliableSequenced - 메시지의 도착과 순서를 보장한다.
  • StateUpdate - 기본적으로 메시지의 도착이나 순서를 보장하지 않고, 전송 버퍼에 쌓인 메시지 중에 가장 최근의 마지막 메시지만 전송한다.
  • ReliableStateUpdate - 메시지의 도착을 보장하며, 전송 버퍼에 쌓인 메시지 중에 가장 최근의 마지막 메시지만 전송한다.
  • AllCostDelivery - 상대가 수신을 받았다는 확인을 받을 때까지 높은 빈도로 재전송하는 가장 신뢰성이 높은 메시지.
  • UnreliableFragmentedSequenced - 전송되는 메시지의 도착은 보장하지 않지만, 순서를 보장하며, 최대 32개로 분할된 메시지를 허용한다.
  • ReliableFragmentedSequenced - 전송되는 메시지의 도착과 순서를 보장하며, 최대 32개로 분할된 메시지를 허용한다.

 

제일 기본적인 전송 품질은 Reliable과 Unreliable인데, 기본 네트워크로 따지면 Reliable은 TCP, Unreliable은 UDP와 같다. 그리고 여기에 추가적으로 순서를 보장할 것인지, 메시지의 분할을 허용할 것인지, 버퍼에 쌓인 메시지 중에 가장 마지막 메시지만 보낼 것인지에 따라서 전송 품질의 종류가 나누어진다.

 

 

 

 

채널 추가하기(Add Channel)

 

원하는 채널을 사용하기 위해서는 우선 네트워크 매니저에 채널을 추가 해주어야 한다.

 

Inspector 뷰에서 채널 추가하기

 

 

 

Inspector 뷰에서 추가하는 방법은 매우 간단하다. Inspector 뷰에서 네트워크 매니저의 내용들을 살펴보면 Advenced Configuration에 체크를 해주면 Qos Channels라는 것이 생기는데, 여기서 + 버튼을 눌러서 채널을 추가하고 드롭다운 메뉴에서 QosType을 선택하면 된다.

 

주의사항

 

네트워크 매니저에 채널을 추가할 때, 주의해야할 사항이 있다. 특히 서버와 클라이언트를 나누어서 빌드하는 경우에는, 서버와 클라이언트 간에 채널이 일치하지 않는 일이 발생하지 않도록 주의를 기울여야 한다.

 

만약에 서버와 클라이언트의 채널이 일치하지 않는다면, 클라이언트는 CRC Mismatch 오류를 발생시키며, 서버에 접속할 수 없게 된다.

 

 

 

 

채널 사용하기

 

채널을 사용하는 방법은 추가하는 것보다 더 간단하다.

 

using UnityEngine.Networking;

[NetworkSettings(channel = 0, sendInterval = 0.1f)]
public class Player : NetworkBehaviour
{
}

 

NetworkBehaviour를 상속받는 클래스에 NetworkSettings 어트리뷰트를 이용해서 channel 값에 그 네트워크 오브젝트가 사용하고자 하는 채널의 번호를 넣어주면 된다. 아무것도 넣지 않았을 경우에는 기본으로 0번 채널을 사용하고, 채널을 추가하지 않았다면 Reliable Sequenced 통신 채널을 기본으로 사용한다.

 

 

 

 

Qos 채널을 사용한 네트워크 사용량 최적화

 

각기 다른 전송 품질을 가진 전송 채널을 여러 개를 두고 각 메시지 타입의 특성에 알맞은 전송 품질을 가진 채널로 메시지를 전송하는 것만으로도 네트워크 전송량 최적화에 상당히 많은 도움이 된다.

 

 

 

 

 

 

네트워크 메시지 중에서 Rpc나 Command 같은 원격 액션은 무관하지만 SyncVar로 동기화 되는 멤버 변수의 경우, StateUpdate나 ReliableStateUpdate 채널을 사용하지 않는다면, 값이 업데이트되는 속도가 Send Interval보다 짧을 때, 변경된 메시지를 전송 버퍼에 쌓아두었다가 Send Interval이 끝나서 메시지를 전송하는 순간에 버퍼에 쌓여있던 메시지를 한꺼번에 전송해버린다. 즉, StateUpdate가 아닌 Reliable이나 Unreliabe 채널을 통해서 전송되는 SyncVar 메시지는 전송량이 StateUpdate 채널을 통해 전달되는 메시지보다 많을 수 밖에 없다.

 

그렇기 때문에 SyncVar를 사용할 때는 이 변수를 어떤 전송 품질로 전송하는게 적절한 지 충분히 고민한 후에 채널을 정해서 전송하는 것이 좋다.

 


 

참고

유니티 스크립트 API 레퍼런스 - QoS Type (https://docs.unity3d.com/ScriptReference/Networking.QosType.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

 

반응형

기본적인 네트워크 전송량 최적화

 

일반적으로 게임을 제작할 때 최적화라는 요소는 매우 중요하다. 적당한 성능의 컴퓨터에서 훌륭한 퍼포먼스를 보여주는 것은 얼마나 좋은 일인가. 하지만 고사양의 컴퓨터에서도 모자란 퍼포먼스를 보여주게 된다면 그 게임은 유저들에게 상당한 비평을 받게 될 것이다. 그와 마찬가지로 멀티플레이를 지원하는 게임의 경우에는 네트워크 전송량의 최적화가 필요하다. 월정액으로 사용되는 국내 인터넷 환경상 PC 멀티플레이 게임의 경우에는 그 중요성이 조금은 덜하겠지만, 데이터 사용량에 따라 요금이 달라지는 환경이나, 매월 사용할 수 있는 데이터량이 제한되어 있는 3G/LTE 같은 경우에 게임 중에 네트워크 전송량이 너무 많다면 성능이 발적화된 게임만큼이나 많은 유저들의 불만을 불러올 것이 틀림이 없다.

 

최근에 간단한 네트워크 게임을 프로토타입으로 만들면서 네트워크 전송량을 거의 최적화 하지 않은 상태로 테스트를 한 적이 있었다. 그 때 10분간 진행된 게임으로 무려 24MB나 되는 데이터를 사용했었다. 실시간으로 많은 수의 캐릭터가 움직이는 게임인 것을 감안하더라도 상당히 많은 데이터 소모였다. 그렇다면 이번 섹션에서는 네트워크를 사용하는 게임이 데이터를 과식하지 않도록 네트워크 사용량을 다이어트 하는 방법에 대해서 알아보자.

 

 

 

 

너무 자주 전송하고 있지는 않은가?

 

첫 번째로 확인해보아야 할 것은 전송 빈도가 너무 짧지 않은가 하는 것이다.

 

using UnityEngine.Networking;

[NetworkSettings(channel = 0, sendInterval = 0.1f)]
public class Player : NetworkBehaviour
{
    // Player 클래스 코드
}

 

유니티 네트워크의 네트워크 매니저에 의해 관리되는 NetworkBehaviour를 상속받는 모든 클래스는 NetworkSetting이라는 어트리뷰트를 통해서 동기화나 원격액션을 보내는 채널과 전송 빈도를 설정할 수 있는데, 기본적으로 이 전송 빈도는 0.1초당 한 번씩 전송되게 되어 있다. 0.1초라는 기본값은 얼핏 보기에는 나쁘지 않은 빈도로 보이는데, 여기서 사람의 욕심이 모든 문제를 발생시킨다.

 

1초에 10번을 전송하는 것은, 캐릭터의 상태나 체력, 공격력 같은 스탯을 전송하는데에는 나쁘지 않은 값이지만, 위치 동기화에는 조금 부족해 보일 것이다. 유니티 네트워크에서 제공하는 기본 NetworkTransform 클래스를 사용해보면 기본 전송 빈도가 초당 9회로 설정되어 있는데, 테스트를 해보면 동기화를 해주는 측에서는 부드러운 움직임을 보이겠지만, 동기화를 받는 측에서는 움직임이 뚝뚝 끊어져서 보이게 될 것이다. 그리고 기본 NetworkTransform에는 최대 전송 빈도가 초당 29회로 제한되어 있는데 이것 역시 테스트를 해보면 움직임이 미세하게 끊어져서 보이는 것을 확인할 수 있다.

 

이러한 문제를 해결하기 위해서, 단순한 해결책을 동원하게 되는데, 그것이 바로 전송 빈도를 매우 짧게 잡는 것이다. 초당 30프레임을 맞추기 위해서 sendInterval을 0.03333초로 잡거나, 더 과한 욕심으로 초당 60프레임의 동기화를 하기 위해 0.01666초로 맞추게 되는 것이다.

 

단순하게 계산해봐도, 0.1초의 전송 빈도에 비해서 0.03333초는 3배, 0.01666초는 6배로 네트워크 전송량이 증가하게 되는 것이다. 일반적으로 위치 동기화에는 Vector3 타입의 변수를 사용하게 되는데 4byte인 float 타입의 x, y, z 3개의 변수가 들어 있으니 1회 전송에 최소 12byte가 사용되는 것이니, 초당 120byte(사실 이것도 많은 편이다)면 되는 것이 360byte, 1080바이트까지 늘어나게 되는 것이다. 무려 1초에 1KB나 되는 데이터를 소모하게 된다.

 

무작정 전송 빈도를 짧게 설정하는 것은 좋지 않은 선택이라는 것을 알 수 있다. 그리고 전송 빈도의 마지노선인 30프레임도 아무런 처리 없이는 움직임이 미세하게 끊어져 보이는 현상이 있다.

 

그렇다면 전송 빈도를 길게 하면서 움직임을 부드럽게 하는 방법은 무엇인가?

 

 

 

 

추측항법(Dead Reckoning)

 

첫 번째로 제시되는 방법은 추측항법이다. 추측항법이란 최근에 확인한 실제 위치에 현재 움직이는 방향과 속력, 즉 속도를 이용해서 현재 위치를 추정해서 움직이는 것이다. 서버에선 새로 동기화 되어야할 위치와 속도(움직이는 방향과 속도)를 전송해주고 클라이언트에선 위치를 적용한 뒤 다음 동기화가 오기 전까지 오브젝트를 속도에 맞춰 이동시켜야 한다.

 

 

이렇게 서버가 다음 위치를 보내주기 전까지 그 사이의 움직임을 클라이언트가 계산해서 처리해주기 때문에 비교적 전송되는 텀이 길더라도 부드러운 움직임을 구현할 수 있게 된다. 위의 그림을 보면 첫 번째 빨간 원이 서버로부터 동기화 받은 위치이고 화살표가 오브젝트가 이동하는 방향과 속력을 의미한다. 처음 위치와 이동할 방향과 속력을 알고 있다면 클라이언트는 오브젝트가 이동해야할 위치를 알 수 있기 때문에 동기화 신호가 오지 않은 구간에서 클라이언트가 오브젝트를 이동시켜서 부드럽게 움직이는 것처럼 보이게 만든다.

 

이 방법의 경우에는, 처음 위치와 속도가 정확하게 동기화되었다면, 서버와 클라이언트 양쪽에 존재하는 오브젝트의 위치가 매우 정확한 수준으로 동기화 될 수 있다는 장점이 있다.

 

하지만 단점도 있는데, 이 섹션이 네트워크 전송량 최적화라는 것을 생각해본다면, 일반 위치 동기화와 같은 전송 빈도라고 비교했을 때, 더 많은 데이터를 소모한다는 것이다. 일반 위치 동기화라면 위치, 즉 Vector3 하나만 전송하면 되지만, 추측항법은 위치와 속도, Vector3 두 개를 전송해야 한다. 데이터 소모가 2배로 늘어난다는 뜻이다. 그렇기 때문에 추측항법을 사용하기 위해서는 일반 위치 동기화를 사용할 때와 비교해서 적절한 수준의 전송 빈도를 잘 계산해서 사용해야만 한다.

 

 

 

 

보간법(Interpolation)

 

네트워크 전송량을 줄이면서 부드러운 움직임을 보이는 두 번째 방법은 보간법이다. 보간법이란 두 위치 사이의 비어있는 위치를 알고 있는 두 위치를 이용하여 채워넣는 것이다. 일반적으로 두 위치 사이를 직선으로 채워넣는 선형 보간법이 사용된다.

 

 

보간법의 경우, 다음 이동할 위치를 받은 즉시 오브젝트를 그 위치로 이동시키지 않고 그 다음 위치 동기화가 오는 시간동안 현재 위치에서 동기화 받은 위치로 Lerp를 통해서 이동시킨다. 이동시키는 도중이나 그 다음에 다음 위치가 동기화 된다면 다시 현재 있는 위치에서부터 새로 동기화 받은 위치를 향해서 Lerp를 시키는 것이다.

 

보간법은 일반 위치 동기화와 같이 동기화할 위치만 전송하면 되기 때문에 전송 빈도만 일반 위치 동기화보다 길게 잡으면 데이터 전송량이 쉽게 줄어든다는 장점이 있지만, 보간법의 경우에는 서버가 위치를 알려주면 클라이언트의 오브젝트가 그 위치를 뒤늦게 따라가는 방식이기 때문에 서버와 클라이언트 간의 오브젝트의 실제 위치가 차이가 발생할 수 있다.

 

 

 

 

불필요한 데이터를 전송하고 있지는 않은가?

 

전송 빈도 다음으로 살펴볼 것은 정말로 필요한 데이터만 전송하고 있는가다. 가장 많이 동기화 되는게 위치 동기화기 때문에 이번에도 예시는 위치 동기화를 위주로 하게 될 것이다.

 

위치 동기화의 경우에 Vector3를 기본으로 사용한다는 것은 앞의 파트에서도 이야기했었다. Vector3라면 x, y, z 값 float 3개를 가지기 때문에 최소 12바이트를 전송하게 되는 것도 이야기를 했다. 그렇다면 만약 게임이 높낮이 없이 평면 상에서만 움직이는 게임이라면 과연 Vecter3를 이용해서 x, y, z의 모든 좌표를 동기화해야만 하는 것일까? 아니다. 높이 값이 필요없다면 그것을 Vector3를 이용하지 않고 Vector2를 이용해서 전송하는 것만으로도 단순 계산으로 데이터 전송량의 33%를 감소시킬 수 있다.

 

using UnityEngine;
using UnityEngine.Networking;

public class Player : NetworkBehaviour
{
    [SyncVar(hook = "ChangePosVect3")]
    Vector3 posV3;
    void ChangePosVect3(Vector3 pos)
    {
        posV3 = pos;
        transform.position = posV3;
    }

    [SyncVar(hook = "ChangePosVect2")]
    Vector2 posV2;
    void ChangePosVect2(Vector2 pos)
    {
        posV2 = pos;
        transform.position = new Vector3(pos.x, 0f, pos.y);
    }

    public void SyncPos()
    {
        posV3 = transform.position;
        posV2 = new Vector2(transform.position.x, transform.position.z);
    }
}

 

위의 코드는 Vector3를 이용하여 위치 동기화를 할 때와 Vector2를 이용하여 위치 동기화를 할 때의 차이를 보여준다. 분명 Vector3를 이용하여 동기화를 할 때에 비해서 무언가 처리해야할 것이 늘어나는 것은 사실이지만, 네트워크 최적화라는 것이 원래 네트워크의 부담을 줄이기 위해 그 부담을 서버나 클라이언트로 옮기는 것이다.

 

33% 감소의 효율을 보여주는 Vector2를 이용하는 위치 동기화만으로는 아직 만족스럽지 못할 수도 있다. 그렇다면 보다 좀 더 극단적인 효율을 보여주는 부분이 있는데, 바로 Rotation 동기화다.

 

만약 탑뷰 시점의 캐릭터가 마우스 방향을 바라보는 게임을 만든다고 가정해보자. 일반적으로 로테이션 동기화에는 유니티에서는 Quaternion 타입이 사용되는데 Quaternion 타입은 x, y, z, w로 무려 float 4개로 한 번 동기화 하는데 16byte가 사용된다. 탑뷰 시점에서 캐릭터가 마우스 방향으로 바라본다고 하면 y축의 각도만 전송하면 된다는 것을 생각해봤을 때, 무려 12byte가 낭비되고 있는 것이다.

 

using UnityEngine;
using UnityEngine.Networking;

public class Player : NetworkBehaviour
{
    [SyncVar(hook = "ChangeRotQuat")]
    Quaternion rotQuat;
    void ChangeRotQuat(Quaternion rot)
    {
        rotQuat = rot;
        transform.rotation = rotQuat;
    }

    [SyncVar(hook = "ChangeRotfloat")]
    float rotY;
    void ChangeRotfloat(float rot)
    {
        rotY = rot;
        transform.rotation = Quaternion.Euler(0f, rotY, 0f);
    }

    public void SyncRot()
    {
        rotQuat = transform.rotation;
        rotY = transform.rotation.eulerAngles.y;
    }
}

 

Quaternion을 사용하는 로테이션 동기화를 float 하나로 변경하는 것만으로도 데이터 사용량을 75%를 줄일 수 있게 된다.

 

이렇게 네트워크 통신에서 필요하지 않은 데이터를 배제하는 것만으로도 상당한 양의 네트워크 전송량을 감소시킬 수 있다.

 

 

 

 

그 외의 방법

 

위에서 언급한 방법 이 외에도 여러 가지의 아이디어나 테크닉이 있을 수 있다. 간단하게 예를 들자면 좌표(좌표를 하나의 변수에 압축해서 넣을 경우에는 일정 수준의 정밀도를 포기해야 한다)나 캐릭터의 여러 스탯을 하나의 변수에 압축하여 전송한 뒤 다시 분할해서 사용하는 방법을 사용하려 전송량을 감소시킬 수 있다.

 

 

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

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

 

에셋스토어

여러분의 작업에 필요한 베스트 에셋을 찾아보세요. 유니티 에셋스토어가 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