당신을 고통에 빠뜨릴 문제 (1)

코루틴과 Instantiate 그리고 Start

 

게임을 개발하면서 생겨나는 많은 버그들은 개발자들을 고통에 빠뜨리지만, 그 중에서도 엔진의 특성과 맞물려서 발생하는 버그는 깊이가 다른 고통을 느끼게 해준다. 이런 종류의 버그는 개발자가 생각한 로직으로는 완벽하지만 엔진의 특성으로 인해서 그 로직이 완벽하게 동작하지 않는 것이기 때문에 해당 엔진의 동작과 특성을 확실하게 알고 있지 않으면 어디서 버그가 발생했는지, 원인이 무엇인지 알기 어렵다.

 

이제부터 이러한 버그들에 대해서 알아보도록 하자. 첫 번째 내용은 코루틴과 Instantiate 그리고 Start에 얽힌 문제에 관한 내용이다.

 

 

코루틴(Coroutine)의 특성

 

코루틴은 유니티로 게임을 개발할 때, 자주 사용되는 기능 중에 하나다. 주로 비동기 처리나, 프로그램을 멈추지 않고 다른 동작을 기다리거나, 메인 로직 뒤에서 돌아가는 서브 루틴을 구현할 때 사용된다.

 

using System.Collections;
using UnityEngine;

public class B : MonoBehaviour
{
    public void CallCoroutine()
    {
        StartCoroutine(SomeCoroutine());
    }

    private IEnumerator SomeCoroutine()
    {
        Debug.Log("Start Coroutine");
        yield return null;
        Debug.Log("End Coroutine");
    }
}

 

using UnityEngine;

public class A : MonoBehaviour
{
    public B b;

    void Start()
    {
        b.CallCoroutine();
    }
}

 

위의 코드를 보면 A라는 클래스가 B라는 클래스의 CallCoroutine() 함수를 호출하여 B 클래스의 SomeCoroutine()을 동작하게 만들고 있다.

 

 

이 코드는 만약 B 오브젝트가 활성화(enabled)되어 있다면 정상적으로 동작하겠지만, 비활성화(disabled)된 상태라면 위의 이미지처럼 오브젝트가 활성화되지 않은 상태에서 코루틴을 호출했다는 에러 로그가 발생하고 해당 코루틴의 코드가 하나도 실행되지 않는다.

 

이것은 코루틴을 사용할 때 기본적인 주의 사항으로 해당 오브젝트가 활성화(Enabled)된 상태에서 코루틴을 호출해야 한다는 것을 의미한다.

 

이런 문제는 에러 로그가 명확하기 때문에 코루틴을 호출하기 전에 게임 오브젝트의 액티브를 확인해주거나 게임 오브젝트가 활성화된 이후에만 코루틴을 호출하도록 수정하면 간단하게 해결된다.

 

 

 

 

진짜 문제

 

이전의 예시처럼 이미 생성되어 있는 게임 오브젝트의 코루틴을 호출하는 문제는 활성화 상태만 잘 체크하면 된다. 진짜 문제는 이 코루틴과 Start, Instantiate가 엮였을 때부터 발생한다.

 

using UnityEngine;

public class A : MonoBehaviour
{
    public GameObject bPrefab;

    void Start()
    {
        var b = Instantiate(bPrefab).GetComponent<B>();
        b.CallCoroutine();
    }
}

 

이미 생성되어 있는 B 오브젝트를 참조하는 이전의 예시와 다르게 이번에는 B 오브젝트의 프리팹을 소유하고 있다가 A 오브젝트가 Start() 되면 B 오브젝트를 새로 생성해서 B 오브젝트의 코루틴을 호출하는 상황을 가정해보자.

 

using System.Collections;
using UnityEngine;

public class B : MonoBehaviour
{
    private void Start()
    {
        Debug.Log("Start");
    }

    public void CallCoroutine()
    {
        StartCoroutine(SomeCoroutine());
    }

    private IEnumerator SomeCoroutine()
    {
        // yield return null;
        Debug.Log("Start Coroutine");
      
        Debug.Log("Ing Coroutine");
       
        Debug.Log("End Coroutine");
    }
}

 

그리고 B 클래스는 Start() 함수가 호출되면 "Start"라는 로그를 띄우고 SomeCoroutine()에서 각 과정에 맞는 로그를 띄울 것이다.

 

자, 그러면 아래와 같이 여러 상황의 코루틴들을 가정해보자.

 

private IEnumerator SomeCoroutine()
{
    yield return null;
    Debug.Log("Start Coroutine");
      
    Debug.Log("Ing Coroutine");
       
    Debug.Log("End Coroutine");


}

 


private IEnumerator SomeCoroutine()
{
   
    Debug.Log("Start Coroutine");
    yield return null;
    Debug.Log("Ing Coroutine");
       
    Debug.Log("End Coroutine");


}

 


private IEnumerator SomeCoroutine()
{

    Debug.Log("Start Coroutine");
      
    Debug.Log("Ing Coroutine");
    yield return null;
    Debug.Log("End Coroutine");


}

 


private IEnumerator SomeCoroutine()
{

    Debug.Log("Start Coroutine");
      
    Debug.Log("Ing Coroutine");
       
    Debug.Log("End Coroutine");

    yield return null;
}

 

여러 상황의 코루틴에서 과연 Start() 함수는 같은 시점에 호출될까? 아니다. 유니티에서 게임 오브젝트의 Start() 함수는 게임 오브젝트가 Instantiate()로 생성되고 한 프레임 뒤에 호출되기 때문에 코루틴이 돌다가 만난 첫 번째, yield return 시점에 Start() 함수가 호출되게 된다. 만약 Start() 함수가 별다른 역할을 하지 않는다거나, 호출순서에 상관없는 일을 처리한다면 큰 문제는 없겠지만, 만약 코루틴의 동작과 연관된 중요한 작업을 Start()가 처리하고 있다면, 이 yield return의 위치에 따라서 심각하고도 감지하기 어려운 난감한 문제를 만나게 될 확률이 높아진다.

 

그리고 이것보다 심각한 문제로, Instantiate() 직후에 코루틴을 호출한 경우, 희박한 확률로 코루틴의 앞 부분 코드는 정상 동작을 하다가 첫 번째 yield return을 만나는 순간, 그 이후의 코드를 실행하지 않는 문제도 발생한다. 게다가 이 경우에는 어떠한 에러나 경고 로그도 발생하지 않기 때문에, 이 문제의 원인이나 해결법을 찾아내기가 아주 어렵다.

 

using System.Collections;
using UnityEngine;

public class B : MonoBehaviour
{
    private void Start()
    {
        Debug.Log("Start");
        StartCoroutine(SomeCoroutine());
    }

    private IEnumerator SomeCoroutine()
    {
        Debug.Log("Start Coroutine");
        yield return null;
        Debug.Log("Ing Coroutine");
       
        Debug.Log("End Coroutine");
    }
}

 

그렇기 때문에 만약 Instantiate()와 Start() 그리고 코루틴이 긴밀하게 엮여있거나, Instantiate()로 게임 오브젝트를 생성한 직후에 코루틴을 호출해서 어떤 작업을 처리해야 하는 경우라면, 별도로 코루틴을 호출하는 것보다는, 위의 코드처럼 Start() 함수에서 코루틴을 실행하는 것을 권장한다.

 

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

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

 

에셋스토어

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

 

반응형

Tutorial (6)

게임 오브젝트와 컴포넌트

 

작성 기준 버전 :: 2018.3.2f1

 

이번 섹션에서는 게임 오브젝트와 컴포넌트에 대해서 알아보자.

 

게임 오브젝트(Game Object)

 

게임 오브젝트는 유니티 엔진에서 가장 기본이 되는 개념으로 캐릭터와 바닥에 떨어진 아이템, 배경으로 배치된 건물이나 소품, 폭발하면서 발생하는 이펙트, 빛을 밝히는 광원, 모든 장면을 찍는 카메라까지 씬에 배치되는 모든 것은 게임 오브젝트이다.

 

기본적인 게임 오브젝트에 나중에 설명할 컴포넌트를 추가로 붙임으로써 게임 오브젝트가 캐릭터가 되느냐, 아이템이 되느냐, 아니면 간단한 배경이 되느냐를 결정하게 된다.

 

 

게임 오브젝트는 기본적으로 이름(Name - "GameObject"), 태그(Tag - "Untagged"), 레이어(Layer - "Default")를 가지고 있다. 이를 응용해서 나중에 원하는 오브젝트를 찾거나 할 수 있다.

 

 

컴포넌트(Component)

 

컴포넌트는 게임 오브젝트에 붙일 수 있는 다양한 기능을 가진 구성요소들로, 비어있는 게임 오브젝트에 어떤 컴포넌트를 붙이느냐에 따라서 그 게임 오브젝트의 역할이 달라진다.

 

트랜스폼 컴포넌트(Transform Component)

 

모든 컴포넌트 중에서 트랜스폼(Transform) 컴포넌트는 모든 게임 오브젝트에 기본으로 부착되며 제거할 수 없다.

 

 

이 트랜스폼 컴포넌트는 해당 게임 오브젝트가 씬에 어느 위치에, 얼마나 회전되어서, 어떤 크기로 배치되어 있는지를 정하는 컴포넌트이다.

 

기타 컴포넌트

 

게임 오브젝트에는 여러 컴포넌트를 붙일 수 있고 어떤 컴포넌트를 붙이느냐에 따라서 게임 오브젝트의 역할이 달라진다.

 

 

게임 오브젝트에 Light 컴포넌트를 붙이면 광원이 되고 Camera 컴포넌트를 붙이면 게임 내에서 씬을 보여주는 카메라가 된다. 이렇게 유니티에서 기본적으로 제공하는 컴포넌트 이외에도 개발자가 유니티 스크립트 API를 이용해서 직접 컴포넌트를 만들어서 게임 오브젝트에 붙일 수도 있다.

 

컴포넌트 추가하기

 

컴포넌트를 추가할 게임 오브젝트를 선택하고 인스펙터 창에서 Add Component 버튼을 누르면 게임 오브젝트에 추가할 수 있는 컴포넌트들이 보여진다. 이 중에서 게임 오브젝트에 추가할 컴포넌트를 선택하면 된다.

 

 

커스텀 컴포넌트

 

유니티에서 기본적으로 제공하는 기본적인 컴포넌트 이외에도 개발자가 직접 컴포넌트를 만들어서 게임 오브젝트에 붙일 수도 있는데 대부분의 실제적인 게임 기능을 하는 시스템들은 커스텀 컴포넌트로 만들어지고 게임 오브젝트에 추가될 것이다.

 

 

커스텀 컴포넌트를 만드는 방법은 프로젝트 뷰에서 우클릭한 뒤 Create>C# Script 항목을 선택하면 된다.

 

 

그러면 NewBehaviourScript라는 이름으로 C# 스크립트 파일이 생성된다.

 

 

이렇게 생성한 컴포넌트는 다른 컴포넌트와 마찬가지로 컴포넌트를 붙일 게임 오브젝트를 선택하고 Add Component 버튼을 누른 뒤 추가한 컴포넌트의 이름을 검색해서 붙일 수 있다.

 

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

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

 

에셋스토어

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

 

반응형

인스펙터 커스텀 버튼 만들기

 

작성 기준 버전 :: 2018.3.2f1

 

유니티 엔진에서 게임을 제작할 때, 모든 작업을 일일이 수작업으로 진행하면 개발 시간이 길어진다. 특히 같은 오브젝트를 여러 개 생성하고 각각 다른 수치를 입력하는 반복적인 세팅 작업의 경우에는 꽤나 큰 시간 낭비를 초래한다.

 

여러 개의 오브젝트를 생성하는데, 그 오브젝트들에 입력되어야 하는 설정이 일정한 규칙을 가지고 있거나, 설정될 값들에 대한 테이블을 미리 가지고 있다면, 오브젝트를 일일이 생성하고 설정 값을 입력하는 것보다, 버튼을 누르면 자동으로 모든 오브젝트들을 생성하고 일정한 규칙에 따라서 설정 값을 세팅하거나 테이블에서 설정 값을 가져와서 세팅하도록 만드는 것이 많은 시간을 절약할 수 있다.

 

이런 커스텀 버튼을 만드는 데도 작업 시간이 소모되겠지만, 일일이 오브젝트를 생성하고 값을 세팅하는 작업 시간이 누적되면 커스텀 버튼을 만드는 데 드는 누적 시간을 빠르게 추월할 것이다. 그리고 이런 종류의 버튼은 만들어두면 다른 프로젝트에서도 충분히 재활용할 수 있기 때문에 인스펙터 커스텀 버튼을 만드는데 시간을 투자할 가치가 있다.

 

 

예제

 

이번 예제에서는 한 오브젝트를 기준으로 그 오브젝트의 forward 방향으로 distance 거리마다 cubeCount 개의 큐브 오브젝트를 배치하는 인스펙터 커스텀 버튼을 만드는 작업을 해볼 것이다.

 

 

위의 작업은 수작업으로 진행할 경우, cubeCount 횟수만큼 반복 작업을 해야하며, 나중에 중심 오브젝트를 추가로 배치할 계획이면 다시 그 추가배치 횟수 * cubeCount 만큼 작업 횟수가 폭발적으로 증가한다.

 

이런 큐브 생성 작업을 버튼 클릭 한 번에 자동으로 처리해주는 인스펙터 커스텀 버튼을 만들어 보자.

 

우선 CubeGenerator 클래스를 생성하고 다음과 같은 코드를 작성한다.

 

public class CubeGenerator : MonoBehaviour
{
    [SerializeField]
    private GameObject cubePrefab;
    [SerializeField]
    private float distance;
    [SerializeField]
    private int cubeCount;

    public void GenerateCubes()
    {

        if (transform.childCount != 0)
        {
            for (int i = transform.childCount - 1; i >= 0; i--)
            {
                DestroyImmediate(transform.GetChild(i).gameObject);
            }
        }


        for (int i = 0; i < cubeCount; i++)
        {
            var newCube = Instantiate(cubePrefab);
            newCube.transform.SetParent(gameObject.transform);
            newCube.transform.localPosition = new Vector3(0f, 0f, i * distance);
            newCube.transform.localRotation = Quaternion.identity;
        }
    }
}

 

코드를 모두 작성한 뒤에는 CubeGenerator 클래스를 CubeStandard 오브젝트에 컴포넌트로 추가하고 큐브 오브젝트를 프리팹화하여 Cube Prefab 프로퍼티에 추가해준다.

 

 

이 다음에는 커스텀 버튼을 추가하기 위해 CubeGenerteButton 클래스를 새로 추가한다. 인스펙터 창에 커스텀 버튼을 추가하는 기능은 유니티 에디터를 수정하는 것이기 때문에 UnityEditor 네임스페이스에 들어가는 기능을 사용해야한다. UnityEditor 네임스페이스의 기능을 사용하는 클래스는 반드시 Editor 폴더 아래에 들어가야 되기 때문에 Editor 폴더를 만들어서 그 안에 넣어준다.

 

 

그리고 아래와 같이 CubeGenerateButton의 코드를 작성한다.

 

using UnityEngine;
using UnityEditor;

[CustomEditor(typeof(CubeGenerator))]
public class CubeGenerateButton : Editor
{
    public override void OnInspectorGUI()
    {
        base.OnInspectorGUI();

        CubeGenerator generator = (CubeGenerator)target;
        if (GUILayout.Button("Generate Cubes"))
        {
            generator.GenerateCubes();
        }
    }
}

 

코드를 저장하고 에디터로 돌아가면 CubeGenerator 컴포넌트 하단에 "Generate Cubes" 버튼이 생겨난 것을 확인할 수 있다.

 

 

distanse와 cubeCount 값을 수정하고 Generate Cubes 버튼을 누르면 설정된 값에 맞춰서 큐브가 생겨나는 것을 확인할 수 있다.

 

 

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

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

 

에셋스토어

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

 

반응형

유니티에서 JSON 사용하기(Newtonsoft JSON)

작성 기준 버전 :: 2018.3.1f1

 

JSON은 웹이나 네트워크에서 서버와 클라이언트 사이에서 데이터를 주고 받을 때 사용하는 개방형 표준 포멧으로, 텍스트를 사용하기 때문에 사람이 이해하기 쉽다는 장점이 있다.

 

이런 JSON 포멧을 유니티에서도 많이 사용하는 편이다. 네트워크 게임을 개발할 때 게임에 필요한 데이터를 주고 받거나, 게임 진행 상황을 저장하거나, 게임 설정을 저장하는 방식으로도 사용할 수 있다.

 

유니티에서 XML을 사용하는 것과 사용 범위가 거의 일치하는데, XML은 가독성이 매우 떨어지고 데이터를 넣거나 꺼내기 위해 파싱(Parsing)하는 과정이 까다로운데 반해서, JSON은 XML에 비해서 가독성이 좋고 직렬화(Serialize)와 비직렬화(Deserialize) 함수를 통해서 데이터에서 JSON 데이터로, JSON 데이터에서 데이터로 편하게 변환할 수 있다는 장점을 가지고 있다.

 

Newtonsoft의 JSON 라이브러리는 다양한 전체 기능을 제공하는 라이브러리로 시리얼라이즈 및 디시리얼라이즈하는 컴팩트한 기능만을 사용하기를 원한다면 유니티 엔진에 내장된 JsonUtility를 사용할 것을 권장한다.

 

 

JSON 라이브러리 다운로드 및 프로젝트에 임포트(Download JSON & Import JSON to project)

 

우선 JSON 라이브러리를 다운로드받기 위해 아래 링크에 접속한다.

 

Newtonsoft JSON Library

 

그리고 릴리즈된 애셋 중에 원하는 버전의 Json(버전).zip 파일을 다운로드받는다.

 

 

다운로드 받은 파일을 압축을 해제하고 폴더를 열어보면 위와 같은 폴더와 파일들이 보일텐데 그 중에서 Bin 폴더를 연다.

 

 

 

Bin 폴더 안에는 사용하는 .NET 버전에 따라 라이브러리 파일들이 폴더에 나눠져 담겨있는데, 일반적으로는 net35 폴더 안에 있는 dll을 사용하면 되지만, 유니티에서 .NET 4.x 기능을 사용하거나 최신 버전의 기능이 필요하다면 net45 폴더 안에 있는 dll을 사용해도 된다. 이번 섹션에서는 간단하게 JSON 사용법을 익힐 것이기 때문에 net35 버전을 사용한다.

 

 

net35 폴더 안에서 Newtonsoft.Json.dll 파일을 프로젝트 창에 드래그해서 프로젝트에 포함시킨다.

 

 

JSON의 기본구조

 

기본적인 JSON 데이터의 구조는 다음과 같다.

 

{

    "id":"wergia",

    "level":10,

    "exp":33.3,

    "hp":400,

    "items":

    [

        "Sword",

        "Armor",

        "Hp Potion",

        "Hp Potion",

        "Hp Potion"

    ]

}

 

JSON의 데이터는 키(Key)와 값(Value) 쌍(Pair)로 이루어진 데이터를 저장하는데, items와 같이 배열로 된 데이터 역시 저장이 가능하고 객체 안에 객체를 넣는 것도 가능하며 위의 데이터 내용이 문자열로 이루어져 있기 때문에 사람이 알아보기가 매우 쉽다.

 

JSON 데이터에서 { } 는 객체를 의미하고, [ ] 는 순서가 있는 배열을 나타낸다. 그리고 JSON은 정수, 실수, 문자열, 불리언, null 타입의 데이터 타입을 지원한다.

 

JSON은 주석을 지원하지 않기 때문에, JSON 파일을 사람이 읽고 수정할 수 있도록 할 예정이라면, 키의 이름을 명확하게 정해서 이 값이 무엇을 의미하는지 확실히 표현하는게 좋다.

 

 

JSON의 단점은 작은 문법 오류에도 매우 민감하다는 점이다. 중간에 중괄호나 대괄호, 콜론, 쉼표가 하나라도 빠지면 JSON 파일이 깨져버리고 파일을 읽어들일 수 없게 된다. 이런 문제 때문에 구글에서 JSON 검사기를 검색하면 JSON 데이터가 유효한지 검사해주는 웹페이지들이 많다. JSON 데이터를 작성하고 난 뒤에는 JSON 데이터 파일의 깨짐으로 인한 버그를 막기 위해서 이런 JSON 검사기로 검사하고 사용하는 것이 좋다.

 

 

 

 

 

유니티에서 JSON 사용하기

 

JSON에 대해서 간단하게 알아보았으니 이제 유니티에서 JSON을 사용하는 방법에 대해서 알아보자.

 

기본적인 JSON <-> Object 변환하기

 

우선 Json 예제를 작성할 JsonExample 클래스를 하나 생성한다.

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

 

public class JsonExample : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {

    }

    // Update is called once per frame
    void Update()
    {
       
    }
}

 

JSON과 관련된 기능을 사용하기 위해서 상단의 using 지시문 파트에 다음 using 지시문을 추가한다.

 

using Newtonsoft.Json;

 

using 지시문을 추가하지 않아도 기능을 사용할 수는 있지만, 그렇게 하면 Newtonsoft.Json 네임스페이스를 계속해서 타이핑해야하기 때문에 using 지시문을 추가한다.

 

그리고 JSON 데이터와 오브젝트 간에 시리얼라이즈, 디시리얼라이즈 테스트를 위해 다음과 같은 클래스를 정의한다.

 

public class JTestClass
{
    public int i;
    public float f;
    public bool b;
    public string str;
    public int[] iArray;
    public List<int> iList = new List<int>();
    public Dictionary<string, float> fDictionary = new Dictionary<string, float>();


    public JTestClass() { }

 

    public JTestClass(bool isSet)
    {

        if (isSet)

        {
            i = 10;
            f = 99.9f;
            b = true;
            str = "JSON Test String";
            iArray = new int[] { 1, 1, 3, 5, 8, 13, 21, 34, 55 };

            for (int idx = 0; idx < 5; idx++)
            {
                iList.Add(2 * idx);
            }

 

            fDictionary.Add("PIE", Mathf.PI);
            fDictionary.Add("Epsilon", Mathf.Epsilon);
            fDictionary.Add("Sqrt(2)", Mathf.Sqrt(2));

        }
    }


    public void Print()
    {
        Debug.Log("i = " + i);
        Debug.Log("f = " + f);
        Debug.Log("b = " + b);
        Debug.Log("str = " + str);

        for (int idx = 0; idx < iArray.Length; idx++)
        {
            Debug.Log(string.Format("iArray[{0}] = {1}", idx, iArray[idx]));
        }

        for (int idx = 0; idx < iList.Count; idx++)
        {
            Debug.Log(string.Format("iList[{0}] = {1}", idx, iList[idx]));
        }

        foreach(var data in fDictionary)
        {
            Debug.Log(string.Format("iDictionary[{0}] = {1}", data.Key, data.Value));
        }
    }
}

 

여러 가지 타입과 배열, 리스트, 딕셔너리를 가지고 있는 클래스이기 때문에 오브젝트를 JSON 데이터로 변환하기에 좋은 클래스이다.

 

JSON 테스트용 클래스를 모두 작성했으면 JsonExample 클래스에 다음 함수 두 개를 구현한다.

 

string ObjectToJson(object obj)
{
    return JsonConvert.SerializeObject(obj);
}

T JsonToOject<T>(string jsonData)
{
    return JsonConvert.DeserializeObject<T>(jsonData);
}

 

ObjectToJson() 함수는 JsonConvert 클래스의 SerializeObject() 함수를 이용해서 오브젝트를 문자열로 된 JSON 데이터로 변환하여 반환하는 처리를 하고 JsonToObject() 함수는 DeserializeObject() 함수를 이용해서 문자열로 된 JSON 데이터를 받아서 원하는 타입의 객체로 반환하는 처리를 한다.

 

함수들을 모두 작성했다면 Start() 함수에 우선 ObjectToJson() 함수를 테스트하는 코드를 작성한다.

 

void Start()
{
    JTestClass jtc = new JTestClass(true);
    string jsonData = ObjectToJson(jtc);
    Debug.Log(jsonData);
}

 

코드를 저장한 뒤 에디터로 돌아가서 JsonExample을 게임 오브젝트에 붙이고 플레이 버튼을 눌러보면 JTestClass 객체가 JSON 데이터로 변환되어 로그로 출력되는 것을 확인할 수 있다.

 

 

그 다음에는 Start() 함수 아래에 JsonToObject() 함수를 테스트하는 다음 코드를 작성한다.

 

var jtc2 = JsonToOject<JTestClass>(jsonData);
jtc2.Print();

 

그리고 코드를 저장하고 에디터로 가서 플레이 버튼을 눌러보면 문자열인 JSON 데이터가 JTestClass 객체로 변환되어 정상적으로 작동하는 것을 확인할 수 있다.

 

 

 

JSON 데이터 파일로 저장하고 불러오기

 

JSON 데이터를 파일로 저장하거나 파일에 저장된 JSON 데이터 파일을 불러올 일이 있을 수 있다. 이번에는 이것에 대해서 배워보자.

 

우선은 문자열로 만든 JSON 데이터를 파일로 저장하는 코드의 예시는 다음과 같다.

 

void CreateJsonFile(string createPath, string fileName, string jsonData)
{
    FileStream fileStream = new FileStream(string.Format("{0}/{1}.json", createPath, fileName), FileMode.Create);
    byte[] data = Encoding.UTF8.GetBytes(jsonData);
    fileStream.Write(data, 0, data.Length);
    fileStream.Close();
}

 

CreateJsonFile() 함수를 작성한 뒤 Start() 함수를 아래와 같이 CreateJsonFile() 함수를 호출하도록 수정한다.

 

void Start()
{
    JTestClass jtc = new JTestClass(true);
    string jsonData = ObjectToJson(jtc);
    CreateJsonFile(Application.dataPath, "JTestClass", jsonData);
}

 

코드를 저장하고 에디터에서 플레이 해보면 dataPath인 Assets 폴더 안에 JTestClass.json 파일이 생성되고 그 내용이 제대로 쓰여져 있는 것을 확인할 수 있다.

 

 

이번에는 방금 저장한 JSON 파일을 읽어들여서 오브젝트로 변환하는 코드를 작성한다. 예시 코드는 아래와 같다.

 

T LoadJsonFile<T>(string loadPath, string fileName)
{
    FileStream fileStream = new FileStream(string.Format("{0}/{1}.json", loadPath, fileName), FileMode.Open);
    byte[] data = new byte[fileStream.Length];
    fileStream.Read(data, 0, data.Length);
    fileStream.Close();
    string jsonData = Encoding.UTF8.GetString(data);
    return JsonConvert.DeserializeObject<T>(jsonData);
}

 

LoadJsonFile() 함수를 모두 작성했으면 Start() 함수를 다음과 같이 수정한다.

 

void Start()
{
    var jtc2 = LoadJsonFile<JTestClass>(Application.dataPath, "JTestClass");
    jtc2.Print();
}

 

코드를 저장하고 에디터에서 플레이해보면 정상적으로 파일의 JSON 데이터가 로드되어서 오브젝트로 변환되어 로그가 출력된 것을 확인할 수 있다.

 

 

 

 

 

 

유니티에서 JSON 사용시 주의점

 

유니티에서 JSON을 사용할 때, 몇가지 주의점이 있다.

 

우선 유니티에서 클래스를 만들 때, 일반적으로 대다수의 클래스는 모노비헤이비어(Monobehaviour)를 상속받는다.

 

public class JsonExample : MonoBehaviour
{
    void Start()
    {
        GameObject obj = new GameObject();
        obj.AddComponent<TestMono>();
        Debug.Log(JsonConvert.SerializeObject(obj.GetComponent<TestMono>()));
    }

}

 

위의 예시 코드에 TestMono 클래스는 int 타입 변수 하나를 가지고 모노비헤이비어를 상속받는 클래스이다. 빈 게임 오브젝트에 TestMono 클래스를 컴포넌트로 붙여서 JSON데이터로 시리얼라이즈해서 로그로 출력하는 테스트인데 이를 플레이해서 테스트해보면 아래와 같이 에러가 발생한다.

 

 

 

이 예외는 gameObject에서 gameObject를 호출할 수 있는 순환구조 때문에 생기는 것인데 이것을 해결할 수는 있지만, 이후에도 다른 예외를 많이 발생시키기고 몇몇 문제는 해결책이 없기 때문에 Newtonsoft의 JSON 라이브러리로는 모노비헤이비어를 상속받는 클래스의 오브젝트를 JSON 데이터로 시리얼라이즈할 수는 없다. 그렇기 때문에 모노비헤이비어를 상속받는 클래스의 오브젝트를 시리얼라이즈하는 대신에 스크립트가 가지고 있는 프로퍼티를 클래스로 묶어서 해당 클래스만 시리얼라이즈하거나 유니티가 제공하는 JsonUntility 기능을 사용해서 시리얼라이즈하는 것을 추천한다.

 

다음은 Vector3를 시리얼라이즈하는 문제인데, Vector3를 그냥 시리얼라이즈 하려고 하면 모노비헤이비어를 시리얼라이즈하려고 할 때처럼 Self referencing loop 문제가 발생한다. 이것은 Vector3의 프로퍼티인 normalized에서 다시 normalized를 호출할 수 있기 때문에 발생하는 문제이다.

 

public class UJsonTester
{
    public Vector3 v3;

    public UJsonTester() { }

    public UJsonTester(float f)
    {
        v3 = new Vector3(f, f, f);
    }
}

 

public class JsonExample : MonoBehaviour
{
    void Start()
    {
        JsonSerializerSettings setting = new JsonSerializerSettings(); ;
        setting.Formatting = Formatting.Indented;
        setting.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;

        UJsonTester jt = new UJsonTester(3f);
        Debug.Log(JsonConvert.SerializeObject(jt, setting));
    }

}

 

이를 해결하기 위해서는 위의 코드처럼 JsonSerializerSetting을 만들어서 ReferenceLoopHandling을 Ignore로 설정하고 시리얼라이즈를 해야한다.

 

 

하지만 이런 방식으로 레퍼런스 반복을 무시하게 만들어도 normalized 벡터나 벡터의 길이 등의 불필요한 값들이 시리얼라이즈되기 때문에, 불필요하게 JSON 데이터의 길이가 늘어나는 문제가 발생한다.

 

public class JVector3
{
    [JsonProperty("x")]
    public float x;
    [JsonProperty("y")]
    public float y;
    [JsonProperty("z")]
    public float z;

    public JVector3()
    {
        x = y = z = 0f;
    }

    public JVector3(Vector3 v)
    {
        x = v.x;
        y = v.y;
        z = v.z;
    }

    public JVector3(float f)
    {
        x = y = z = f;
    }
}

public class UJsonTester
{
    public JVector3 v3;

    public UJsonTester() { }

    public UJsonTester(float f)
    {
        v3 = new JVector3(f);
    }

    public UJsonTester(Vector3 v)
    {
        v3 = new JVector3(v);
    }
}

public class JsonExample : MonoBehaviour
{
    void Start()
    {
        UJsonTester jt = new UJsonTester(transform.position);
        Debug.Log(JsonConvert.SerializeObject(jt));
    }

}

 

외부 라이브러리를 이용해서 Vector3 중에서 x, y, z 좌표값만을 JSON 데이터로 시리얼라이즈하기를 원한다면 위의 예시 코드와 같이 별도의 시리얼라이즈용 Vector 클래스를 만들어서 시리얼라이즈를 해야한다.

 

 

이러한 번거로운 과정이 불편하다면, 유니티가 기본 제공하는 JsonUtility를 혼용해서 사용하는 방법도 있다. 유니티가 제공하는 JsonUtility로 Vector3를 시리얼라이즈하면 x, y, z 좌표값만을 JSON 데이터로 변환한다.

 

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

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

 

에셋스토어

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

 

반응형

유니티에서 JSON 사용하기(Unity JSON Utility)

 

작성 기준 버전 :: 2018.3.1f1

 

JSON은 웹이나 네트워크에서 서버와 클라이언트 사이에서 데이터를 주고 받을 때 사용하는 개방형 표준 포멧으로, 텍스트를 사용하기 때문에 사람이 이해하기 쉽다는 장점이 있다.

 

이런 JSON 포멧을 유니티에서도 많이 사용하는 편이다. 네트워크 게임을 개발할 때 게임에 필요한 데이터를 주고 받거나, 게임 진행 상황을 저장하거나, 게임 설정을 저장하는 방식으로도 사용할 수 있다.

 

유니티에서 XML을 사용하는 것과 사용 범위가 거의 일치하는데, XML은 가독성이 매우 떨어지고 데이터를 넣거나 꺼내기 위해 파싱(Parsing)하는 과정이 까다로운데 반해서, JSON은 XML에 비해서 가독성이 좋고 직렬화(Serialize)와 비직렬화(Deserialize) 함수를 통해서 데이터에서 JSON 데이터로, JSON 데이터에서 데이터로 편하게 변환할 수 있다는 장점을 가지고 있다.

 

유니티에서 기본 제공하는 JsonUtility는 컴팩트한 최소한의 기능만을 제공하기 때문에 JSON 라이브러리의 모든 기능을 사용하고 싶다면 다른 JSON 라이브러리를 사용할 것을 권장한다.

 

 

JSON의 기본구조

 

기본적인 JSON 데이터의 구조는 다음과 같다.

 

{

    "id":"wergia",

    "level":10,

    "exp":33.3,

    "hp":400,

    "items":

    [

        "Sword",

        "Armor",

        "Hp Potion",

        "Hp Potion",

        "Hp Potion"

    ]

}

 

JSON의 데이터는 키(Key)와 값(Value) 쌍(Pair)로 이루어진 데이터를 저장하는데, items와 같이 배열로 된 데이터 역시 저장이 가능하고 객체 안에 객체를 넣는 것도 가능하며 위의 데이터 내용이 문자열로 이루어져 있기 때문에 사람이 알아보기가 매우 쉽다.

 

JSON 데이터에서 { } 는 객체를 의미하고, [ ] 는 순서가 있는 배열을 나타낸다. 그리고 JSON은 정수, 실수, 문자열, 불리언, null 타입의 데이터 타입을 지원한다.

 

JSON은 주석을 지원하지 않기 때문에, JSON 파일을 사람이 읽고 수정할 수 있도록 할 예정이라면, 키의 이름을 명확하게 정해서 이 값이 무엇을 의미하는 지 하는게 좋다.

 

 

 

JSON의 단점은 작은 문법 오류에도 매우 민감하다는 점이다. 중간에 중괄호나 대괄호, 콜론, 쉼표가 하나라도 빠지면 JSON 파일이 깨져버리고 파일을 읽어들일 수 없게 된다. 이런 문제 때문에 구글에서 JSON 검사기를 검색하면 JSON 데이터가 유효한지 검사해주는 웹페이지들이 많다. JSON 데이터를 작성하고 난 뒤에는 JSON 데이터 파일의 깨짐으로 인한 버그를 막기 위해서 이런 JSON 검사기로 검사하고 사용하는 것이 좋다.

 

 

유니티에서 JSON 사용하기

 

JSON에 대해서 간단하게 알아보았으니 이제 유니티에서 JSON을 사용하는 방법에 대해서 알아보자.

 

기본적인 JSON <-> Object 변환하기

 

우선 Json 예제를 작성할 JsonExample 클래스를 하나 생성한다.

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

 

public class JsonExample : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {

    }

    // Update is called once per frame
    void Update()
    {
       
    }
}

 

유니티가 제공하는 JsonUtility는 UnityEngine 네임스페이스에 포함되어 있기 때문에, 다른 using 지시문을 추가할 필요가 없다.

그리고 JSON 데이터와 오브젝트 간에 시리얼라이즈, 디시리얼라이즈 테스트를 위해 다음과 같은 클래스를 정의한다.

 

[System.Serializable]

public class JTestClass
{
    public int i;
    public float f;
    public bool b;

    public Vector3 v;
    public string str;
    public int[] iArray;
    public List<int> iList = new List<int>();

 

    public JTestClass() { }

 

    public JTestClass(bool isSet)
    {

        if (isSet)

        {
            i = 10;
            f = 99.9f;
            b = true;

            v = new Vector3(39.56f, 21.2f, 6.4f);
            str = "JSON Test String";
            iArray = new int[] { 1, 1, 3, 5, 8, 13, 21, 34, 55 };

 

            for (int idx = 0; idx < 5; idx++)
            {
                iList.Add(2 * idx);
            }
        }
    }


    public void Print()
    {
        Debug.Log("i = " + i);
        Debug.Log("f = " + f);
        Debug.Log("b = " + b);

        Debug.Log("v = " + v);
        Debug.Log("str = " + str);

        for (int idx = 0; idx < iArray.Length; idx++)
        {
            Debug.Log(string.Format("iArray[{0}] = {1}", idx, iArray[idx]));
        }

        for (int idx = 0; idx < iList.Count; idx++)
        {
            Debug.Log(string.Format("iList[{0}] = {1}", idx, iList[idx]));
        }
    }
}

 

여러 가지 타입과 배열, 리스트를 가지고 있는 클래스이기 때문에 오브젝트를 JSON 데이터로 변환하기에 좋은 클래스이다.

 

JSON 테스트용 클래스를 모두 작성했으면 JsonExample 클래스에 다음 함수 두 개를 구현한다.

 

string ObjectToJson(object obj)
{
    return JsonUtility.ToJson(obj);
}

T JsonToOject<T>(string jsonData)
{
    return JsonUtility.FromJson<T>(jsonData);
}

 

ObjectToJson() 함수는 JsonUtility 클래스의 ToJson() 함수를 이용해서 오브젝트를 문자열로 된 JSON 데이터로 변환하여 반환하는 처리를 하고 JsonToObject() 함수는 FromJson() 함수를 이용해서 문자열로 된 JSON 데이터를 받아서 원하는 타입의 객체로 반환하는 처리를 한다.

 

함수들을 모두 작성했다면 Start() 함수에 우선 ObjectToJson() 함수를 테스트하는 코드를 작성한다.

 

void Start()
{
    JTestClass jtc = new JTestClass(true);
    string jsonData = ObjectToJson(jtc);
    Debug.Log(jsonData);
}

 

코드를 저장한 뒤 에디터로 돌아가서 JsonExample을 게임 오브젝트에 붙이고 플레이 버튼을 눌러보면 JTestClass 객체가 JSON 데이터로 변환되어 로그로 출력되는 것을 확인할 수 있다.

 

 

그 다음에는 Start() 함수 아래에 JsonToObject() 함수를 테스트하는 다음 코드를 작성한다.

 

var jtc2 = JsonToOject<JTestClass>(jsonData);
jtc2.Print();

 

그리고 코드를 저장하고 에디터로 가서 플레이 버튼을 눌러보면 문자열인 JSON 데이터가 JTestClass 객체로 변환되어 정상적으로 작동하는 것을 확인할 수 있다.

 

 

 

 

 

 

JSON 데이터 파일로 저장하고 불러오기

 

JSON 데이터를 파일로 저장하거나 파일에 저장된 JSON 데이터 파일을 불러올 일이 있을 수 있다. 이번에는 이것에 대해서 배워보자.

 

우선은 문자열로 만든 JSON 데이터를 파일로 저장하는 코드의 예시는 다음과 같다.

 

void CreateJsonFile(string createPath, string fileName, string jsonData)
{
    FileStream fileStream = new FileStream(string.Format("{0}/{1}.json", createPath, fileName), FileMode.Create);
    byte[] data = Encoding.UTF8.GetBytes(jsonData);
    fileStream.Write(data, 0, data.Length);
    fileStream.Close();
}

 

CreateJsonFile() 함수를 작성한 뒤 Start() 함수를 아래와 같이 CreateJsonFile() 함수를 호출하도록 수정한다.

 

void Start()
{
    JTestClass jtc = new JTestClass(true);
    string jsonData = ObjectToJson(jtc);
    CreateJsonFile(Application.dataPath, "JTestClass", jsonData);
}

 

코드를 저장하고 에디터에서 플레이 해보면 dataPath인 Assets 폴더 안에 JTestClass.json 파일이 생성되고 그 내용이 제대로 쓰여져 있는 것을 확인할 수 있다.

 

 

이번에는 방금 저장한 JSON 파일을 읽어들여서 오브젝트로 변환하는 코드를 작성한다. 예시 코드는 아래와 같다.

 

T LoadJsonFile<T>(string loadPath, string fileName)
{
    FileStream fileStream = new FileStream(string.Format("{0}/{1}.json", loadPath, fileName), FileMode.Open);
    byte[] data = new byte[fileStream.Length];
    fileStream.Read(data, 0, data.Length);
    fileStream.Close();
    string jsonData = Encoding.UTF8.GetString(data);
    return JsonUtility.FromJson<T>(jsonData);
}

 

LoadJsonFile() 함수를 모두 작성했으면 Start() 함수를 다음과 같이 수정한다.

 

void Start()
{
    var jtc2 = LoadJsonFile<JTestClass>(Application.dataPath, "JTestClass");
    jtc2.Print();
}

 

코드를 저장하고 에디터에서 플레이해보면 정상적으로 파일의 JSON 데이터가 로드되어서 오브젝트로 변환되어 로그가 출력된 것을 확인할 수 있다.

 

 

 

유니티의 JsonUtility가 제공하는 특수한 기능들

 

유니티의 JsonUtility는 유니티 엔진을 위한 특수한 기능들을 몇 가지 제공한다.

 

Vector3 시리얼라이즈

 

Vector3는 유니티에 내장된 클래스로써 위치나 방향을 표시하는데 자주 사용되는 클래스이다. 그렇기 때문에 이전에 접속했을 때의 캐릭터의 마지막 위치같은 데이터로 저장될 수 있다. 하지만 Vector3는 유니티의 JsonUtility가 아닌 다른 JSON 라이브러리를 사용해서 시리얼라이즈를 하면 normalized 프로퍼티로 인해서 Self reference loop 문제를 발생시키고 이 문제를 해결해도 아래의 이미지와 같이 x, y, z 좌표값 이외에 정규화된 벡터와 그 길이와 길이의 제곱등 불필요한 정보들을 많이 포함하게 된다.

 

 

이런 문제는 Vector3를 시리얼라이즈할 때 JsonUtility를 사용하면 간단하게 해결된다.

 

[System.Serializable]
public class UJsonTester
{
    public Vector3 v3;

    public UJsonTester() { }

    public UJsonTester(float f)
    {
        v3 = new Vector3(f, f, f);
    }

    public UJsonTester(Vector3 v)
    {
        v3 = v;
    }
}

public class JsonExample : MonoBehaviour
{
    void Start()
    {
        UJsonTester jt = new UJsonTester(transform.position);
        Debug.Log(JsonUtility.ToJson(jt));
    }

}

 

테스트 코드를 저장하고 테스트해보면 불필요한 값 없이 x, y, z 좌표값만 저장된 것을 확인할 수 있다.

 

 

 

모노비헤이비어를 상속받는 클래스의 오브젝트 시리얼라이즈

 

다른 JSON 라이브러리를 사용해서 모노비헤이비어를 상속받는 클래스의 오브젝트를 시리얼라이즈하려고 하면 여러 문제가 발생하며 시리얼라이즈가 되지 않는다.

 

[System.Serializable]
public class TestMono : MonoBehaviour
{
    public int i = 10;
    public Vector3 pos = new Vector3(1f, 2f, 3f);
}

 

모노비헤이비어를 상속받는 TestMono 클래스를 선언했으면 JsonUtility로 시리얼라이즈하는 코드를 작성한다. 모노비헤이비어를 상속받는 클래스의 오브젝트를 시리얼라이즈할 때, 주의할 점은 반드시 클래스가 컴포넌트로 붙어있는 게임 오브젝트가 아니라 GetComponent() 등으로 직접 가져온 클래스로 시리얼라이즈를 해야한다.

 

GameObject obj = new GameObject();
obj.name = "TestMono 01";
var jd = JsonUtility.ToJson(obj.GetComponent<TestMono>());
Debug.Log(jd);

 

JsonUtility로 모노비헤이비어를 상속받는 클래스의 오브젝트를 시리얼라이즈하면 문제없이 깔끔하게 오브젝트가 JSON 데이터로 변환되는 것을 확인할 수 있다.

 

 

 

이렇게 JSON 데이터로 변환한 모노비헤이비어를 상속받는 클래스의 오브젝트는 디시리얼라이즈를 할 때 FromJson() 함수를 사용하면 아래와 같이 새로운 인스턴스를 생성하지 못했다고 에러가 발생하고 시리얼라이즈에 실패한다.

 

 

이런 문제를 해결하기 위해서는 FromJson() 함수 대신에 FromJsonOverwrite() 함수를 사용해야 한다. 이 함수는 JSON 데이터를 오브젝트로 변환할 때, 새로운 오브젝트를 만들지 않고 기존에 있는 오브젝트에 클래스의 변수 값을 덮어씌우는 처리를 한다.

 

FromJsonOverwrite() 함수의 테스트를 위해서 Start() 함수의 내용을 아래와 같이 수정한다.

 

GameObject obj = new GameObject();
obj.name = "TestMono 01";
var t = obj.AddComponent<TestMono>();
t.i = 333;
t.pos = new Vector3(-939, -33, -22);
var jd = JsonUtility.ToJson(obj.GetComponent<TestMono>());
Debug.Log(jd);

GameObject obj2 = new GameObject();
obj2.name = "TestMono 02";
var t2 = obj2.AddComponent<TestMono>();
JsonUtility.FromJsonOverwrite(jd, t2);

 

에디터로 가서 테스트를 진행해보면 TestMono 02 오브젝트가 생성되고, 이 오브젝트가 가진 TestMono 컴포넌트의 프로퍼티의 값이 TestMono 01 오브젝트를 JSON 데이터로 변환한 값이 덮어씌워져 있는 것을 확인할 수 있다.

 

 

 

 

 

 

JsonUtility와 딕셔너리(Dictionary)

 

유니티에 내장된 JsonUtility를 통해서 JSON을 다룰 때 알아두어야 할 점은 JsonUtility는 딕셔너리에 대한 시리얼라이즈 및 디시리얼라이즈를 지원하지 않는다는 것이다. JsonUtility는 JSON을 다루기 위한 가장 최소한의 기능만 제공하기 때문에 딕셔너리를 JSON으로 다루려면 Newtonsoft나 다른 JSON 라이브러리를 사용하거나 이에 대한 기능을 직접 구현해야 할 것이다.

 

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

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

 

에셋스토어

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

 

반응형

Tutorial (5)

 

유니티의 좌표계

 

작성 기준 버전 :: 2018.3.1f1

 

[본 튜토리얼의 내용은 유튜브 영상으로도 시청할 수 있습니다]

 

이번 섹션에서는 유니티의 좌표계에 대해서 알아보자.

 

 

좌표계란?

 

좌표계란 공간 내에서 특정한 위치를 나타내기 위한 방식이다.

 

어떤 공간에서 위치를 찾고자 하는 것인지 기준을 잡기 위해서 축이라는 것을 사용하는데, X라는 하나의 축을 사용하면 수직선 상에서의 점의 위치를 찾아낼 수 있게 된다.

 

 

X축과 Y축, 2개의 축을 이용하면 평면 상의 중심으로부터의 점의 위치를 알 수 있다.

 

 

X축과 Y축 그리고 Z축까지 3개의 축을 사용하면 3차원 공간 상의 점의 위치를 알아낼 수 있게 된다.

 

 유니티 엔진에서는 씬이라는 공간 안에서 오브젝트의 위치를 표현하기 위해서 좌표계를 이용한다.

 

수직선을 이용한 1차원 상의 공간을 사용하는 게임은 별로 없고 대부분은 2D 좌표계나 3D 좌표계를 사용한다.

 

 


2D 좌표계를 사용하는 게임으로는 슈퍼 마리오 브라더스를 예로 들 수 있고, 3D 좌표계를 사용하는 게임으로는 하프라이프를 예로 들 수 있다. 2D 좌표계를 사용하는 게임은 움직임이 상하좌우 또는 전후좌우로 움직임이 제한되지만 3D 좌표계를 사용하는 게임은 전후좌우 뿐만 아니라 상하의 움직임까지 가능하다.

 

 

왼손 좌표계와 오른손 좌표계

 

좌표의 축을 정하는 방법은 여러 가지가 있는데 그 중 대표적인게 바로 왼손 좌표계와 오른손 좌표계이다.

 

 

우선 오른손 좌표계는 엄지 손가락이 X축, 검지 손가락이 Y축, 중지 손가락이 Z축이라고 가정하고 엄지를 종이 위에 수직선을 그었을 때 양수의 방향, 즉 오른쪽을 향하게 하고 검지를 X축과 직교하는 위 방향으로 향하게 했을 때, 중지가 나를 바라보는 방향이 되게 XYZ축을 정의하는 방식이다. 일반적인 수학에서는 이 오른손 좌표계를 표준으로 사용한다.

 

 

그 다음 왼손 좌표계는 엄지와 검지의 방향을 오른손 좌표계와 같이 맞췄을 때 중지는 내가 바라보는 방향을 가리키게 되도록 XYZ축을 정의한다. 유니티에서는 이 왼손 좌표계를 기준으로 사용한다.

 

한마디로 왼손 좌표계와 오른손 좌표계의 차이는 Z축이 가리키는 방향이 달라진다는 것이다. 오른손 좌표계에서는 화면에서 바라보는 사람에게로 다가오는 방식으로 Z축이 가리키게 되지만, 왼손 좌표계는 화면을 바라보는 사람에게서 화면 방향으로 Z축이 가리키게 된다.

 

 

 

 

 

Y-Up과 Z-Up

 

좌표계를 정의할 때, X축은 기본적으로 첫 번째 수평 방향의 수직선을 기준으로 하기 때문에 대부분 같은 방향으로 고정되어 있다. 여기서 발생하는 문제는 두 번째 축인 Y축의 방향을 어떻게 정의하느냐이다.

 

 

여기에는 두 가지 관점이 있는데 위에서 내려다보는 시점으로 Y축을 앞으로 나가는 방향으로 정의하는 방식이 하나로, 이렇게하면 새로 추가되는 세 번째 축인 Z축이 높이 축이 되는 Z-Up 방식이 된다. 언리얼 엔진과 3D 모델링 툴인 3ds Max가 이 방식을 채택한다.

 

 

다른 방식으로는 옆에서 바라보는 시점에서 Y축을 위로 향하는 방향으로 정의하는 것이다. Y축이 높이 축이 되기 때문에 Y-Up이라고 하고 유니티 엔진은 이 방식을 채택한다.

 

 

월드 좌표와 로컬 좌표

 

바로 전 파트까지 좌표계란 무엇인지와 유니티 엔진에서는 어떤 방식의 좌표계를 채택했는지를 설명했다. 이번 파트에서 이야기할 내용은 월드 좌표와 로컬 좌표에 대한 이야기이다.

 

월드 좌표란 세상을 중심으로 어느 위치에 있느냐를 의미하는 것이고, 로컬 좌표는 나 혹은 어느 한 오브젝트를 중심으로 어느 위치에 있느냐 하는 것이다.

 

사실 실제 세상에서 세상을 중심으로 어떠한 객체가 어느 위치에 있느냐 하는 것은 그 세상의 중심이 어디인지는 사람마다 생각이 다르고 절대적이라고 할 수 있는 중심이 없기 때문에 세상의 중심을 기준으로 한 위치라는 것은 구할 수 없겠지만, 게임이나 유니티 엔진에서는 가능하다.

 

 

바로 씬 안의 의 위치가 바로 게임 안에서의 세상의 중심이 된다.

 

 

 

 

월드 좌표를 대상으로 봤을 때, 선택된 큐브는 {-6, 0, -4}의 위치에 존재한다.

 

그렇다면 로컬 좌표란 무엇인가? 왜 월드의 중심이 아닌 어느 한 오브젝트를 중심으로 위치를 측정해야하는 걸까?

 

 

 

위의 이미지를 보자. 스피어 오브젝트 하나가 월드 좌표를 설명할 때 사용했던 큐브 오브젝트보다 XZ좌표가 각각 1씩 월드의 중심에 가깝게 존재하고 있다. 큐브 오브젝트의 위치가 {-6, 0, -4}였으니, 스피어 오브젝트는 {-5, 0, -3}의 위치에 있다. 만약에 추가된 이 스피어 오브젝트를 큐브 스피어를 중심으로 공전하게 만들고 싶다면 어떻게 해야할까?

 

 

만약 월드 좌표만으로 처리하려고 한다면 위의 이미지와 같이 좌표가 복잡하게 바뀌는 것을 알 수 있다.

 

 

하지만 스피어 오브젝트를 큐브 오브젝트의 자식 오브젝트로 만들면 포지션이 월드의 중심 좌표를 기준으로한 월드 좌표인 {-5, 0, -3}이 아니라 큐브 오브젝트를 중심으로한 로컬 좌표 로 표시되는 것을 확인할 수 있다.

 

 

이렇게 하고 나면 간단하게 큐브 오브젝트를 회전시키는 것만으로도 궤도를 따라서 스피어 오브젝트가 간단하게 공전하는 것을 볼 수 있다. 물론 큐브도 함께 자전한다는 문제가 있기는 하지만 이런 문제는 간단하게 해결하고 스피어 오브젝트만 궤도를 따라서 공전하게도 만들 수 있다.

 

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

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

 

에셋스토어

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

 

반응형

Tutorial (4)

 

오브젝트 다루기 기초

 

작성 기준 버전 :: 2018.3.1f1

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

 

이번 섹션에서는 유니티 에디터에서 오브젝트를 씬에 배치하고 이동하는 등의 오브젝트를 다루는 방법에 대해서 배워보자.

 

 

게임 오브젝트(Game Object)

 

 

게임 오브젝트는 씬에 배치되는 가장 기본 단위인 오브젝트로써 기본적으로 위치, 회전, 크기를 나타내는 트랜스폼 컴포넌트만을 가진 빈 오브젝트이다. 여기에 어떤 컴포넌트가 추가되느냐에 따라 천차만별로 달라질 수 있는 백지와 같은 오브젝트이다.

 

 

빈 게임 오브젝트에 Cube 메시를 가진 메시 필터 컴포넌트와, 메시를 그리기 위한 메시 렌더러 컴포넌트, 물리적인 실체를 가지기 위한 박스 콜라이더 컴포넌트를 추가함으로써 눈에 보이는 큐브 모양을 가진 게임 오브젝트가 되었다. 이처럼 게임 오브젝트에 어떤 컴포넌트를 붙이냐에 따라서, 그 컴포넌트는 플레이어가 조종하는 캐릭터가 될 수도 있고, 플레이어의 길을 막는 장애물이 되거나 플레이어와 싸우는 몬스터가 될 수도 있는 것이다.

 

 

오브젝트 생성하기

 

첫 번째로 알아볼 것은 오브젝트를 씬에 생성하는 방법이다.

 

 

하이어라키 탭 바로 아래에 있는 Create 드롭다운 메뉴를 누르면 생성할 수 있는 오브젝트들의 종류를 볼 수 있다. Create Empty 항목을 선택하면 앞에서 본 빈 게임 오브젝트를 생성한다.

 

 

이번에는 다음 과정들을 수행하기 위해서 Create>3D Object>Cube를 선택해서 눈에 보이는 상자 오브젝트를 추가하자.

 

 

Cube를 선택하면 씬 안에 새로운 큐브가 생겨난 것을 볼 수 있다. 이 외에도 몇 가지의 3D 오브젝트들을 생성해보자.

 

 

 

 

오브젝트 선택하기

 

이번에는 오브젝트를 선택해보자.

 

단일 오브젝트 선택하기

 

씬 뷰에서 오브젝트를 클릭하면 해당 오브젝트를 선택할 수 있고 선택된 오브젝튼느 테두리에 주황색 하이라이트가 표시된다.

 

 

오브젝트를 선택할 때 주의할 점이 있는데 씬 뷰에서 오브젝트를 선택하려고 할 때, 핸드툴 모드가 활성화된 상태에서는 오브젝트를 선택할 수 없다. W E R 등을 눌러서 다른 모드로 전환한 다음에 선택하도록 한다.

 

 

단일 오브젝트를 선택하는 또 다른 방법으로는 하이어라키 창에서 선택하고자 하는 오브젝트를 클릭하는 것이다.

 

 

하이어라키 창에서 오브젝트를 선택하면 다른 오브젝트를 잘못 선택할 확률이 줄어든다는 장점이 있다. 선택하고자 하는 오브젝트를 빨리 찾고 싶다면 오브젝트의 이름을 적절하게 정해서 보기 쉽게 정리하는게 좋다.

 

 

여러 오브젝트 선택하기

 

오브젝트 하나를 선택하는 방법 이외에도 여러 오브젝트를 한꺼번에 선택하는 방법도 있다.

 

전략 시뮬레이션 게임에서 여러 유닛을 한꺼번에 선택하듯이 선택하고자하는 오브젝트 근처에서 클릭하고 드래그하면 반투명한 사각형이 표시되는데 이 사각형 영역에 선택하고자 하는 오브젝트가 들어오게 만들면 여러 오브젝트가 동시에 선택된다. 이 방법은 손쉽게 여러 오브젝트를 선택할 수 있다는 장점이 있지만, 선택하고자하는 오브젝트 가까이에 선택하지 않으려고하는 다른 오브젝트가 함께 있다면 이 역시도 같이 선택될 수 있다는 단점이 있다.

 

 

여러 오브젝트를 선택하는 다른 방법으로는 역시 하이어라키 창에서 하는 방법이 있다. Shift 키를 사용하면 첫 번째 선택한 오브젝트와 두 번째 선택한 오브젝트 사이에 있는 모든 오브젝트가 선택되고, Ctrl 키를 사용하면 떨어져 있는 오브젝트를 하나씩 선택된 오브젝트에 추가시키며 선택할 수 있다.

 

 

 

 

 

 

 

오브젝트 이동시키기

 

배치한 오브젝트를 원하는 위치에 배치하기 위해서 오브젝트를 이동시키는 방법을 알아보자.

 

기즈모로 이동시키기

 

 

상단 버튼 중에 이동 툴 버튼 선택하거나 단축키 W키를 누르면 씬 뷰에서 오브젝트를 이동시킬 수 있게 된다.

 

이동 툴을 활성화시킨 채로 오브젝트를 선택하면 세 방향으로 뻗어나가는 화살표 모양의 기즈모가 오브젝트의 중앙에 생기는 것을 볼 수 있다.

 

 

세 화살표는 각 축 방향을 의미하며, 이 화살표를 클릭하고 드래그하면 그 축의 방향으로 오브젝트를 움직이게 된다. 빨간 화살표는 X축 방향, 초록 화살표는 Y축 방향, 파란 화살표는 Z축 방향이다.

 

 

각 축 화살표 사이를 보면 작은 면들이 보인다. 이것을 클릭하고 드래그하면 해당 색상의 축만 고정하고 나머지 축 방향으로 이동한다는 뜻이다.

 

 

즉 파란 면을 잡고 움직이면 오브젝트는 Z축은 고정한 채로 X축과 Y축에서만 움직이게 된다.

 

 

트랜스폼 컴포넌트로 이동시키기

 

오브젝트를 무브 툴과 기즈모가 아닌 인스펙터 창의 트랜스폼 컴포넌트를 이용해서 이동시킬 수도 있다. 기즈모를 통해 대강의 위치로 이동시키는 것이 아니라 정확히 원하는 좌표에 오브젝트를 가져다 놓고 싶다면 인스펙터 창의 트랜스폼 컴포넌트를 이용해서 오브젝트를 이동시키는게 더 좋다.

 

 

인스펙터 창에서 오브젝트를 이동시키는 다른 방법도 있는데 포지션의 각 축 이름에 마우스 커서를 가져다 대면 커서 앞에 양방향 화살표가 뜨는데 이 때 좌클릭 드래그를 하면 그 축의 값을 조절할 수도 있다.

 

 

 

오브젝트 회전시키기

 

오브젝트를 잘 배치하기 위해서는 위치뿐만 아니라 적절하게 회전시키는 것 역시 중요하다.

 

기즈모로 회전시키기

 

 

상단 버튼 중에 회전 툴 버튼을 클릭하거나 단축키 E를 누르면 씬 뷰에서 오브젝트를 회전시킬 수 있게 된다.

 

회전 툴을 활성화시킨 채로 오브젝트를 선택하면 구형의 회전 기즈모가 생긴다.

 

 

회전 기즈모에는 흰 원 안쪽으로 빨간 원, 초록 원, 파란 원이 보이는데 이것은 그 색상의 원 평면과 직교하는 축을 기준으로 회전한다는 의미이다.

 

 

기즈모의 축을 잡고 마우스를 움직이면 그 축을 기준으로만 회전한다. 아래의 이미지를 보면 순서대로 각각 X축, Y축, Z축을 잡고 오브젝트를 회전시키고 있다.

 

 

기즈모에서 축이 아닌 영역을 잡고 움직이면 오브젝트를 자유롭게 회전시킬 수 있다.

 

 

 

트랜스폼 컴포넌트로 회전시키기

 

오브젝트 이동과 마찬가지로 오브젝트 회전 역시 트랜스폼 컴포넌트를 통해서 할 수 있다. 인스펙터 창에서 트랜스폼 컴포넌트 값 중에 Rotation 값을 원하는 만큼 수정해주면 원하는 각도만큼 정확하게 오브젝트를 회전시킬 수 있다.

 

 

Rotation 값 중에 원하는 값의 이름에 마우스 커서를 대고 좌클릭 드래그하는 것으로도 오브젝트를 회전시킬 수 있다.

 

 

 

 

 

 

오브젝트 크기 조절하기

 

오브젝트의 적절한 크기 역시 자연스러운 오브젝트 배치에 있어서 중요한 요소 중에 하나이다. 의도되지 않을 잘못된 크기의 물체는 플레이어에게 큰 위화감을 주고 몰입을 방해하기 때문이다.

 

기즈모로 크기 조절하기

 

 

상단 버튼 중에 스케일 툴을 선택하거나 단축키 R키를 누르면 오브젝트의 크기를 조절할 수 있다.

 

스케일 툴을 활성화한 채로 오브젝트를 선택하면 이동 기즈모와 같은 형태지만 끝이 직육면체 모양인 기즈모가 생성된다.

 

 

빨간 색 막대는 오브젝트를 X축 방향으로 확대/축소하며, 초록 색 막대는 Y축 방향으로, 파란색 막대는 Z축 방향으로 오브젝트를 확대/축소한다.

 

 

가운데 있는 흰 색 정육면체를 잡고 마우스를 움직이면 모든 축 방향으로 오브젝트가 커졌다가 작아진다.

 

 

트랜스폼 컴포넌트로 크기 조절하기

 

인스펙터 창에서 트랜스폼 컴포넌트의 Scale 값을 통해서 오브젝트의 크기를 정확히 원하는 크기로 조절할 수 있다.

 

 

Scale 값 중에 원하는 값의 이름에 마우스 커서를 대고 좌클릭 드래그하는 것으로도 오브젝트의 크기를 조절할 수 있다.

 

 

 

오브젝트를 다른 오브젝트의 하위 오브젝트로 만들기

 

유니티에서는 단순히 하나의 오브젝트만을 사용하는 것이 아니라 오브젝트를 다른 오브젝트의 하위 오브젝트로 만들어서 사용하는 경우가 많다. 예를 들자면 캐릭터 오브젝트의 손에 무기 오브젝트를 붙여서 캐릭터가 무기를 들게 하거나, 차량 오브젝트를 만들 때, 차량의 몸체 오브젝트와 바퀴 오브젝트를 따로 만들어서 차량 몸체 오브젝트 하위에 바퀴 오브젝트를 붙이는 등의 방식을 사용한다.

 

이렇게 하면 어떤 장점이 있냐면 만약 캐릭터가 손에 들고 있는 무기를 다른 무기로 바꾸면 손에 무기 오브젝트를 다른 무기 오브젝트로 바꾸거나, 차량이 데미지를 입거나 폭발할 때, 차량의 바퀴가 차량에서 떨어져나가는 연출 등을 사용할 수 있게 된다. 만약 무기와 캐릭터가 통짜로 된 하나의 오브젝트라면 무기를 교체할 때마다, 캐릭터의 모델링마저 교체해야 될 것이고, 차량 몸체와 바퀴가 통짜로 된 하나의 오브젝트라면 차량에서 바퀴가 떨어져나가게 하기 위해서 바퀴가 떨어져 나가는 애니메이션을 만들어야 할 것이다.

 

이렇게 오브젝트를 다른 오브젝트의 하위 오브젝트로 만드는 것은 여러 곳에서 사용될 수 있는 좋은 기법으로 어떤 곳에 사용될 수 있는지 생각해보고 많이 활용해보는 것이 좋다.

 

한 오브젝트를 다른 오브젝트의 하위 오브젝트로 만드는 방법은 아주 간단하다. 하이어라키 뷰에서 다른 오브젝트의 하위 오브젝트로 만들고자 하는 오브젝트를 드래그해서 상위 오브젝트가 되고자 하는 오브젝트에 끌어다 놓으면 된다.

 

 

이렇게 해서 상위 오브젝트가 된 오브젝트를 부모 오브젝트라고 하며, 하위 오브젝트가 된 오브젝트를 자식 오브젝트라고 한다.

 

이렇게 다른 오브젝트의 하위 오브젝트가 된 자식 오브젝트는 부모 오브젝트의 이동, 회전, 스케일의 영향을 함께 받는다.

 

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

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

 

에셋스토어

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

 

반응형

Tutorial (3)

 

씬(Scene) 뷰 조작법

 

 

이번 섹션에서는 유니티 에디터의 씬 뷰에 대한 기초적인 조작법을 알아본다.

 
씬 기즈모
 

 

씬 기즈모는 씬 뷰에 우측 상단에 표시되며, 씬 뷰 카메라가 현재 바라보는 방향을 나타낸다. 각 축을 클릭해서 씬 뷰의 카메라가 해당 축을 기준으로 씬을 바라보게 할 수 있다.

 

 

그리고 씬 기즈모 아래에 Persp이라는 글자가 적혀있는데 이것은 현재 씬 뷰의 카메라가 원근 모드(Perspective Mode)라는 뜻으로 이것을 클릭하면 씬 뷰의 카메라를 직교 모드(Orthographic Mode)로 전환해서 원근감으로 왜곡되어 보이지 않게 만들 수 있다.

 

 

 
 

화살표 이동

 

씬 뷰에서는 키보드 화살표를 이용해서 카메라 정면을 기준으로 전후좌우로 움직일 수 있다.

 

 

Shift 키를 누르면 더 빠르게 움직일 수 있다.

 

 

핸드 툴

 

단축키 Q를 누르거나 핸드툴을 선택하면 아래와 같은 마우스 컨트롤을 할 수 있다.

 

Move : 좌클릭 드래그 하면 카메라를 기준으로 화면을 상하좌우로 움직일 수 있다.

 

 

 

Orbit : Alt 키를 누르고 좌클릭 드래그를 하면 카메라가 바라보는 피벗을 기준으로 궤도를 따라서 회전한다.

 

 

 

Zoom : Alt 키를 누르고 우클릭 드래그를 하면 씬 뷰를 확대/축소할 수 있다. Mac은 Control 키를 누르고 좌클릭 드래그해서 확대/축소할 수 있다. 마우스 스크롤을 회전시켜도 줌 조절이 가능하다.

 

 

 

 

Shift 키를 누르고 조작하면 이동, 회전, 줌 속도가 빨라진다.

 

 

플라이스루(Flythrough) 모드

 

플라이스루 모드를 통해서 1인칭 게임과 같은 동작으로 씬을 날아다니면서 탐색할 수 있다.

 

 

 

씬 뷰에 마우스를 우클릭한 채로 FPS 게임처럼 WASD를 통해서 전후좌우로 움직일 수 있다. QE 키로는 상하로 움직일 수 있다.

 

플라이스루 모드는 Perspective Mode에서만 동작하며, Orthgraphic Mode에서는 우클릭 드래그를 하면 피벗을 중심으로 회전하는 동작만 보인다. 더불어 2D모드에서도 플라이스루 모드가 동작하지 않고, 우클릭 드래그하면 화면을 이동시키는 동작만 보인다.

 

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

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

 

에셋스토어

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