Programming 

코루틴(Coroutine) 다루기 2(코루틴 중단하기 + 코루틴 매개변수 + yield break)

 

작성 기준 버전 ::2019.2

 

[이 포스트는 유튜브 영상으로도 시청하실 수 있습니다]

 

이번 포스트에서는 지난 코루틴 포스트에서 다루지 못했던 코루틴 중단하기와 코루틴 함수에 매개변수 전달하기 그리고 yield break에 대해서 다뤄보도록 하자.

 

코루딘 중단하기

 

public class CoroutineTest : MonoBehaviour
{
    IEnumerator enumerator;

    void Start()
    {
        // 코루틴 함수를 직접 호출해서 중단시키려면 IEnumerator를 저장해서 사용
        enumerator = TestCoroutine();
        StartCoroutine(enumerator);
    }

    void Update()
    {
        if(Input.GetKeyDown(KeyCode.Space))
        {
            // 코루틴 함수를 직접 호출해서 중단시키려면 IEnumerator를 저장해서 사용
            StopCoroutine(enumerator);
        }
    }

    IEnumerator TestCoroutine()
    {
        int i = 0;
        while(true)
        {
            yield return null;
            Debug.Log("Coroutine " + i);
            i++;
        }
    }
}

 

코루틴을 정지시키는 기본적인 방법의 위의 코드와 같다.

 

위 코드는 게임이 시작되면 Start 함수에 StartCoroutine으로 TestCoroutine 함수를 실행시켜 준다. 그러면 코루틴 함수 내용에 따라서 "Coroutine"이라는 로그와 함께 반복된 횟수를 출력한다. 그리고 플레이 도중에 스페이스 키를 누르면 코루틴이 멈추면서 로그 출력이 중단된다.

 

StartCoroutine(TestCoroutine());

 

보통 때는 바로 위 코드처럼 코루틴 함수를 호출했을 텐데 IEnumerator에 저장해서 실행한 이유는 TestCoroutine 함수에서 받아온 IEnumerator를 StopCoroutine에 넣어주기 위해서 이다.

 

StartCoroutine과 StopCoroutine은 이 IEnumerator를 통해서 어떤 코루틴을 실행하고 중단시킬지 확인할 수 있다.

 

코루틴 함수 이름을 문자열로 이용하기

 

StartCoroutine("TestCoroutine");

StopCoroutine("TestCoroutine");

 

이전 코루틴 포스트에서는 알려주지 않은 방법이 있는데, 코루틴을 실행시킬 때 그 코루틴 함수의 이름만으로도 실행시킬 수 있다는 것이다. 그리고 코루틴의 이름으로도 코루틴을 정지시킬 수 있.

 

어떤 면에서 보면 코루틴의 이름으로 코루틴을 시작하고 멈추는 방법이 더 낫다고 여길 수도 있다. 하지만 한 오브젝트에서 같은 이름의 코루틴이 2개 이상 실행되고 있는 상태에서 코루틴의 함수 이름 문자열로 StopCoroutine을 호출하면 이름으로 실행된 모든 코루틴이 동시에 멈춰버린다.

 

StartCoroutine(TestCoroutine());   // 코루틴 함수를 호출해서 실행한 코루틴은

StopCoroutine("TestCoroutine");    // 코루틴 이름 문자열로 중단시킬 수 없음

 

그와 더불어 위 코드처럼 코루틴 함수를 호출해서 실행한 코루틴은 코루틴 이름 문자열로 중단시킬 수 없다.

 

문자열로 코루틴을 실행하는 방법은 편리하지만 사용을 권장하지는 않는다. 그 이유는 문자열이기 때문에 오타가 발생해도 에러 표시가 되지 않아서 문제가 발생할 소지가 높기 때문이다. 그리고 이렇게 상수 문자열은 게임을 출시하는 과정에서 앱 보안을 위한 암호화 과정을 거칠 때, 코루틴 이름과 다른 문자열로 변경되어서 코루틴 함수 호출에 실패하게 되는 경우가 발생하기도 한다.

 

모든 코루틴 동시에 중단시키기

 

StopAllCoroutines();

 

코루틴을 중단시키는 마지막 방법으로는 StopAllCoroutines가 있다. StopAllCoroutines 함수를 사용하면 이 컴포넌트가 실행하고있는 모든 코루틴을 중단시킨다.

 

코루틴 함수에 매개변수 전달하기

 

IEnumerator TestCoroutine(int count)
{
    int i = 0;
    while(i < count)
    {
        yield return null;
        Debug.Log("Coroutine " + i);
        i++;
    }
}

 

그럼 이번에는 코루틴 함수에 매개변수를 전달하는 방법을 알아보자.

 

TestCoroutine 코루틴 함수를 위와 같이 매개변수를 받을 수 있게 약간 변경한다. int 타입의 count 매개변수를 받아서 count 횟수만큼 반복하게 되었다.

 

StartCoroutine(TestCoroutine(10));

 

코루틴 함수 호출 방식에서는 TestCoroutine의 매개변수에 바로 값을 넣어주면된다.

 

StartCoroutine("TestCoroutine", 10);

 

코루틴 이름 문자열로 코루틴을 실행시킬 때는 문자열의 이름 뒤에 콤마를 찍고 매개변수를 넣어주면 된다.

 

Coroutine StartCoroutine(string methodName, object value);

 

코루틴 이름 문자열로 코루틴을 실행시키면서 매개변수를 받는 StartCoroutine의 오버로드 형식은 string으로 코루틴 함수의 이름을 받고 object 타입으로 매개변수를 받는다.

 

참고로 object 타입은 C# 프로그래밍에서 모든 변수 타입의 최상위 타입이다. 그래서 이렇게 int 타입의 숫자를 넣어주면 object 타입으로 바꿔서 받는다. 그리고 TestCoroutine 함수를 호출하면서 내부적으로 int 타입으로 바꿔서 전달해준다다.

 

StartCoroutine(TestCoroutine(10));

StartCoroutine("TestCoroutine", 10);

 

그렇기 때문에 이 두 가지 호출 방식은 똑같이 동작한다.

 

코루틴 함수 이름 문자열 실행 방식의 매개변수 전달 방식의 약점

 

박싱/언박싱의 오버헤드

 

// 박싱(Boxing)
int i = 10;
object obj = i;

// 언박싱(Unboxing)
int j = (int)obj;

 

하지만 이렇게 object로 매개변수를 전달하는 방법에는 약점이 몇 가지 있다. 먼저 프로그래밍에서 다른 타입의 변수를 object 타입으로 만드는 과정을 박싱(Boxing)이라고 부르고 object 타입의 변수를 원래 타입의 변수로 되돌리는 과정을 언박싱(Unboxing)이라고 부른다.

 

이 박싱/언박싱 과정은 미세하지만 분명히 성능적인 오버헤드를 일으킨다. 게임 최적화를 위해서는 남발하지 않는게 좋다.

 

전달 가능한 매개변수의 갯수

 

IEnumerator TestCoroutine(int count, float time)
{
    yield return new WaitForSeconds(time);

    int i = 0;
    while(i < count)
    {
        yield return null;
        Debug.Log("Coroutine " + i);
        i++;
    }
}

 

그리고 두 번째 약점을 설명하기 위해서 TestCoroutine의 기능을 조금 변경하기로 했다고 가정해보자.

 

매개변수에 float 타입으로 time 변수를 추가로 받아서 그 시간만큼 기다렸다가 count 횟수만큼 반복하도록 변경했다. 이렇게 되면 매개변수가 2개로 바뀌게 된다. 

 

StartCoroutine(TestCoroutine(10, 3f));

 

그러면 코루틴 함수 자체로 호출하는 방식에서는 새로운 매개변수를 넣어달라고 에러가 표시되기 때문에 추가된 기능에 맞게 매개변수를 넣어주기만 하면 된다.

 

하지만 코루틴 함수의 이름으로 호출하는 방식에서는 매개변수를 전달할 object가 하나 뿐이라 두 번째 매개변수를 전달할 방법이 없다. 그러니까 이름으로만 호출할 때는 매개변수를 하나 밖에 쓸 수 없는 것이다.

 

물론 클래스나 구조체로 묶어서 보내는 방법도 있겠지만 굳이 그렇게 번거로운 방법쓰는 것 보다는 코루틴 함수에 바로 매개변수 여러 개를 사용하는 것이 편할 것이다.

 

yield break

 

IEnumerator ReturnCoroutine()
{
    Debug.Log("Return 1");
    yield return null;  // 코드의 제어권을 잠시 양보했다가 돌려받아서 아래 코드를 계속 진행
    Debug.Log("Return 2");
}

IEnumerator BreakCoroutine()
{
    Debug.Log("Break 1");
    yield break;    // 코루틴 함수를 이 시점에 종료
    Debug.Log("Break 2");
}

 

마지막으로는 yield break 문에 대해서 알아보도록 하자.

 

yield return을 사용할 ReturnCoroutine과 yield break를 사용할 BreakCoroutine을 만든다.

 

이 두 코루틴을 실행하면 ReturnCoroutine은 Return 1과 Return 2가 모두 출력되지만 BreakCoroutine에서는 Break 1만 출력되고 Break 2는 출력되지 않는 것을 볼 수 있다.

 

yield break가 호출된 순간에 코루틴 함수가 완전히 멈춰버린 것이다.

 

yield return은 코드의 제어권을 유니티 엔진에 잠시 넘겼다가 특정 시점이 되면 다시 받아서 코드를 진행하지만 yield break는 그 시점에 코루틴을 완전히 멈춰버린다.

 

코루틴 내부에서 특정 조건을 만족하면 yield 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

 

반응형

Programming 

씬 불러오기

 

작성 기준 버전 :: 2019.2

 

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

 

유니티 엔진의 씬은 게임의 맵이나 레벨 개념에 해당한다. 그리고 대부분의 게임은 하나보다 많은 맵이나 레벨, 즉 씬으로 구성된다. 그래서 게임에서 다른 씬으로 넘어가기 위해서는 씬과 씬 사이를 이동하기 위해서 다른 씬을 불러오는 방법을 알아야 한다.

 

씬 세팅하기

 

 

먼저 프로젝트를 제일 처음 생성했을 때 있는 씬에 큐브 오브젝트를 생성하고 카메라 앞 왼쪽 화면에 보이게 적당히 배치한 다음 씬을 저장한다. [Ctrl + S] 단축키를 누르면 간단하게 씬의 변경 내용을 저장할 수 있다.

 

그리고 프로젝트 뷰의 Scenes 폴더 아래에 지금 로드되어 있는 씬인 Sample Scene의 이름을 Scene1로 변경한다.

 

그 다음에는 이 Scene1에서 이동하게 될 새로운 씬을 생성하자.

 

 

프로젝트 뷰에 우클릭하고 [Create > Scene] 항목을 선택하면 새로운 씬을 생성할 수 있다. 새로 생성한 씬의 이름은 Scene2로 한다.

 

 

이렇게 생성한 씬 애셋을 더블 클릭하면 하이어라키에 열려있던 씬이 Scene1에서 Scene2로 바뀐다.

 

 

그리고 Scene2에는 Shpere 게임 오브젝트를 생성해서 카메라 앞 오른쪽 화면에 보이게 배치한다. 이렇게 하면 Scene1에 있다가 Scene2를 불러오면 화면 앞에 있는 게임 오브젝트의 모양이 바뀌면서 씬이 바뀌었다는 것을 명확하게 알 수 있을 것이다.

 

Scene2를 저장하고 다시 Scene1로 돌아간다.

 

SceneMover 스크립트 작성하기

 

그 다음에는 SceneMover라는 이름으로 C# 스크립트를 생성하자.

 

using UnityEngine.SceneManagement;

 

제일 먼저 스크립트의 상단에 using UnityEngine.SceneManagement; 라는 코드를 작성한다. 이 코드는 개발자가 스크립트 에디터에게 "SceneMover.cs 파일에서 UnityEngine.SceneManagement 네임스페이스에 들어있는 씬과 관련된 기능을 사용하겠다"라고 알려주는 역할을 한다.

 

참고로 네임스페이스라는 것은 C#에서 특정한 기능을 묶어두는 데 주로 사용된다.

 

여기 UnityEngine.SceneManagement 네임스페이스에서는 유니티 엔진의 씬과 관련된 기능들이 담겨있다.

 

public class SceneMover : MonoBehaviour

{

    void Update()

    {

        if(Input.GetKeyDown(KeyCode.Space))

        {

            SceneManager.LoadScene("Scene2");

        }

    }

}

 

네임스페이스 선언이 끝나면 Update 함수에서 스페이스 키를 입력을 받았을 때, SceneManager.LoadScene 함수를 호출하게 만든다. 이 함수로 다른 씬을 불러올 수 있다. 

 

매개변수에 불러오고자하는 씬의 이름이나 번호를 넣으면 된다. 우리는 Scene1에서 Scene2로 이동할 생각이기 때문에 Scene2라는 이름을 넣어주면 된다.

 

코드를 저장하고 에디터로 돌아가서 게임 오브젝트를 하나 만들고 거기에 SceneMover 컴포넌트를 추가한다.

 

 

그리고 플레이 버튼을 누르고 스페이스 키를 눌러보면 Scene2라는 씬이 빌드 세팅에 추가되지 않았거나 애셋 번들로부터 불러올 수 없어서 로드할 수 없다는 로그가 나온다.

 

유니티에서 씬을 불러오기 위해서는 씬이 저장된 애셋 번들이 있든지 아니면 씬이 빌드 세팅에 추가되어 있어야 한다.

 

빌드 세팅에 씬 추가하기

 

씬을 빌드 세팅에 추가 시켜보자.

 

 

우선 상단 메뉴바에서 [File > Build Settings...] 항목을 선택하면 빌드 세팅 창이 열린다.

 

 

창 위쪽에 비어있는 Scenes In Build 칸을 볼 수 있는데 여기에 추가된 씬들은 게임에 포함되어 빌드된다.

 

만들어둔 Scene1과 Scene2를 Scenes In Build 칸에 끌어다 놓으면 Scenes In Build 칸에 추가한 씬이 표시될 것이다.

 

빌드 세팅 창을 끄고 다시 플레이를 시킨 뒤, 스페이스 버튼을 눌러보면 Scene1에서 Scene2로 이동하면서 Cube 오브젝트가 사라지고 Sphere 오브젝트가 나타난다.

 

이게 바로 제일 기본적인 씬 이동 방법이다.

 

Additive로 씬 불러오기

 

첫 번째 방법은 씬을 완전히 이동하는 방법이었다면 이번에는 기존의 씬을 남겨둔 상태로 새로운 씬을 겹쳐서 불러오는 방법을 알아보자.

 

public class SceneMover : MonoBehaviour

{

    void Update()

    {

        if(Input.GetKeyDown(KeyCode.Space))

        {

            SceneManager.LoadScene("Scene2", LoadSceneMode.Additive);

        }

    }

}

 

매개변수 "Scene2"뒤에 콤마(,)를 찍으면 두 번째 매개변수로 넣을 수 있는 옵션이 나타나는데, 형식이 LoadSceneMode 열거형인 것을 알 수 있다.

 

LoadSceneMode 모드에는 Single과 Addtive, 두 가지가 있는데 우선 Single은 기본 옵션으로 앞에서 구현한 것처럼 씬을 불러오면 이전 씬은 완전히 사라지고 새로운 씬으로 바뀌는 옵션이다. 그리고 Additive는 앞의 씬을 남겨두고 거기에 얹어서 새로운 씬을 불러오는 것이다.

 

두 번째 매개변수에 LoadSceneMode.Additive를 넣어준 뒤 코드를 저장하고 에디터로 돌아가서 게임을 플레이 시킨 다음에 스페이스 키를 누르면 Scene1이 남아있는 상태로 Scene2가 불러와지는 것을 볼 수 있다.

 

다만 콘솔 창을 보면 로그가 굉장히 많이 발생하고 씬을 불러오기 전보다 매우 밝은 것을 알 수 있는데, 이것은 씬 안에 소리를 감지하는 Audio Listener라는 컴포넌트와 Directional Light가 두 개가 있어서 발생하는 문제이다. 그리고 메인 카메라도 중복으로 두 개가 있는 상태이다.

 

이런 문제들 때문에 Addtive로 불러올 씬에는 불필요한 카메라나 Directional Light를 만들지 않는 것이 좋다.

 

비동기 방식 씬 불러오기

 

지금까지 사용한 LoadScene 함수는 동기 방식 함수이다. 이게 무슨 의미인가하면 LoadScene 함수로 씬을 호출하면 씬을 불러오는 과정이 끝날 때까지 다른 일을 아무 것도 하지 못한다는 뜻이다.

 

이 예시처럼 씬에 고작 오브젝트가 몇 개만 있는 게임이라면 LoadScene 함수만으로 씬을 불러와도 되겠지만 최신 게임들처럼 씬 하나에 엄청나게 많은 오브젝트들이 들어있는 무거운 게임이라면 씬을 불러오는 긴 시간동안 아무 것도 하지 못하게 될 것이다.

 

다른 씬을 불러오는 도중에 팁을 보여준다든지 플레이어에게 미니 게임을 할 수 있게 한다든지 해서 씬이 불러와지는 시간에 플레이어가 지루함을 느끼지 않도록 하려면 다른 작업을 처리할 수 있어야 한다. 거기에 필요한게 비동기 방식 씬 불러오기이다.

 

public class SceneMover : MonoBehaviour

{

    void Update()

    {

        if(Input.GetKeyDown(KeyCode.Space))

        {

            StartCoroutine(LoadSceneCoroutine());

        }

    }

 

    IEnumerator LoadSceneCoroutine()

    {

        yield return SceneManager.LoadSceneAsync("Scene2");

    }

}

 

LoadSceneAsync 함수를 사용하면 함수가 씬을 불러오는 도중에 다른 작업을 처리할 수 있게 된다.

 

단, 이 함수를 제대로 사용하려면 코루틴과 함께 사용해야 한다.

 

코드를 저장하고 에디터로 돌아가서 게임을 플레이 시킨 다음 스페이스 키를 누르면 사실 눈에 띄는 변화는 없다.

 

비동기 씬 로딩이 효과가 있으려면 그만큼 불러올 씬이 무겁고 불러올게 많아야 의미가 있다.

 

비동기 씬 불러오기 방식을 응용하는 방법은 아래의 포스트에서 확인할 수 있다.

 

[로딩 화면 구현하기(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

 

반응형

Programming 

코루틴(Coroutine) 다루기

 

작성 기준 버전 :: 2019.2

 

[이 포스트는 유튜브 영상을 통해서도 시청하실 수 있습니다]

 

Update 함수는 게임 오브젝트가 활성화된 상태에서 매 프레임 호출되어 수행된다. 그래서 유니티 엔진으로 게임을 만들 때는 대부분의 게임 동작을 Update 함수에서 작동하도록 구현한다.

 

그런데 Update 함수는 멈추지 않고 계속해서 동작하는 함수이기 때문에 여기서 일시적으로 돌아가는 서브 동작을 구현하는 것과 어떤 다른 동작이 처리되는 것을 기다리는 기능을 구현하기는 매우 까다롭다.

 

그리고 Update 함수에서 해당 기능을 구현하기 어렵지 않다고 하더라도, 잠시 돌아가는 기능을 Update 함수에 모두 구현하는 것은 비대한 몸집의 Update 함수를 만들어 내서 나중에 게임을 유지보수하는 것이 매우 어려워지는 결과를 낳게 된다.

 

이렇게 한 컴포넌트 내에서 Update 함수와 따로 일시적으로 돌아가는 서브 동작을 구현하거나, 어떤 다른 작업이 처리되는 것을 기다리는 기능을 구현하는데 쓰이는 것이 바로 코루틴이다.

 

Update로 구현한 공격 딜레이

 

코루틴이 필요할 법한 간단한 예시를 들기 위해서 스페이스 키를 누르면 캐릭터가 공격했다고 가정하고 딜레이를 줘서 그 시간 동안에는 다시 공격을 하지 못하게 만드는 기능을 만들어 보자.

 

public class Attacker : MonoBehaviour

{

    public bool isDelay;

    public float delayTime = 2f;

 

    float timer = 0f;

    void Update()

    {

        if (Input.GetKeyDown(KeyCode.Space))

        {

            if (!isDelay)

            {

                isDelay = true;

                Debug.Log("Attack");

            }

            else

            {

                Debug.Log("Delay");

            }

        }

 

        // 업데이트로 구현한 공격 딜레이

        if (isDelay)

        {

            timer += Time.deltaTime;

            if (timer >= delayTime)

            {

                timer = 0f;

                isDelay = false;

            }

        }

    }

}

 

위와 같이 코드를 작성하고 플레이 해보면, 처음 스페이스 키를 누르면 "Attack" 로그가 나오지만, 딜레이가 지나기 전에 다시 누르면 "Delay" 로그가 표시된다.

 

그리고 로그가 지난 이후에 스페이스 키를 눌러야 "Attack" 로그가 출력된다.

 

간단한 기능이라 구현이 그리 어렵지는 않았지만, 앞에서 이야기한 것과 같이 게임 기능이 계속해서 추가될 때마다 "공격 딜레이가 발생했을 때만" 동작하는 코드 같은 일시적 동작 코드가 Update 함수에 계속해서 늘어나면, Update 함수가 비대화되고 유지보수가 어려워진다.

 

코루틴으로 구현한 공격 딜레이

 

똑같은 기능을 이번에는 코루틴으로 구현해보자.

 

// 코루틴으로 구현한 공격 딜레이

IEnumerator CountAttackDelay()

{

    yield return new WaitForSeconds(delayTime);

    isDelay = false;

}

 

먼저 코루틴을 사용하기 위해서는 코루틴 함수를 만들어야 한다.

 

코루틴 함수를 만드는 방법은 간단하게 반환형만 IEnumerator로 만들어주면 된다.

 

그리고 yield return이란 코드를 작성해주면 된다. 이것은 코루틴에서 동작하는 제어권을 유니티에 다시 돌려준다는 뜻이다. 이 yield return 지점에 도착하면 코루틴은 반환 타입으로 정의한 만큼 코드 동작을 중지하고 제어권을 유니티에 돌려준다. 그리고 반환 타입의 조건이 충족되면 이 다음 줄부터 다시 코루틴이 동작한다.

 

코루틴이 제어권을 얼마나 양보할 지 정하는 반환 타입에는 여러 가지가 있다.

 

// 한 프레임 기다림

yield return null;

// 게임 시간으로 1초 기다림(time scale에 영향받음)

yield return new WaitForSeconds(1f);

// 실제 시간으로 1초 기다림(time scale에 영향받지 않음)

yield return new WaitForSecondsRealtime(1f);

// 다음 FixedUpdate 끝날 때까지 기다림

yield return new WaitForFixedUpdate();

// 다음 프레임의 Update와 모든 렌더링이 끝날 때까지 기다림

yield return new WaitForEndOfFrame();

 

코루틴을 모두 작성하고 나면 Update 함수를 아래와 같이 수정하면 된다.

 

void Update()

{

    if (Input.GetKeyDown(KeyCode.Space))

    {

        if (!isDelay)

        {

            isDelay = true;

            Debug.Log("Attack");

            StartCoroutine(CountAttackDelay());

        }

        else

        {

            Debug.Log("Delay");

        }

    }

 

    // 업데이트로 구현한 공격 딜레이

    //if (isDelay)

    //{

    //    timer += Time.deltaTime;

    //    if (timer >= delayTime)

    //    {

    //        timer = 0f;

    //        isDelay = false;

    //    }

    //}

}

 

코루틴 함수는 실행할 때 일반 함수처럼 호출하는 것이 아니라, StartCoroutine 함수를 이용해서 호출해야 한다.

 

코루틴으로 공격 딜레이를 구현하면 Update 함수에서 전부 구현하는 것보다 훨씬 쉽고 간단하게 똑같은 기능을 구현할 수 있다.

 

코루틴과 관련된 포스트

 

코루틴과 관련하여 추가로 확인할 만한 포스트는 아래와 같다.

 

[코루틴 내부에서 무한 루프를 사용할 때 주의점]

 

[코루틴의 호출 시점에 대한 주의점]

 

[커스텀으로 yield return 조건 만들기]

 

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

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

 

에셋스토어

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

 

반응형

Programming 

스크립트로 게임 오브젝트 생성하고 파괴하기

 

작성 기준 버전 :: 2019.2

 

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

 

 

씬에 새로운 게임 오브젝트를 생성하는 제일 기본적인 방법은 하이어라키 뷰에 우클릭해서 생성하고자 하는 게임 오브젝트를 선택하는 것이었다. 하지만 이렇게 에디터의 하이어라키 뷰에서 생성한 게임 오브젝트는 다른 컴포넌트나 스크립트의 개입 없이는 파괴되지 않기 때문에, 정적인 오브젝트이다.

 

반대로 스크립트의 개입으로 게임 플레이 도중에 생성되고 파괴되는 것을 동적으로 오브젝트를 생성하거나 파괴한다고 한다.

 

그럼 이제부터 이렇게 스크립트를 이용해서 게임 플레이 도중에 게임 오브젝트를 생성하고 파괴하는 방법을 알아보도록 하자.

 

게임 오브젝트 생성하기

 

public class GameObjectCreator : MonoBehaviour

{

    void Start()

    {

        new GameObject();

    }

}

 

게임 오브젝트를 생성하기 위해서는 위의 예시 코드와 같이 new GameObject()라는 코드를 작성하면 된다.

 

이 코드는 C#에서는 GameObject라는 클래스의 객체를 메모리에 생성하는 코드이지만, 유니티 엔진에서는 GameObject 클래스의 객체를 메모리에 생성하면서 씬에 실제 게임 오브젝트 역시 생성하는 기능을 한다.

 

 

이대로 코드를 저장하고 에디터로 돌아가서 하이어라키 뷰에서 게임 오브젝트를 하나 생성하고, 방금 만든 GameObjectCreator 컴포넌트를 부착해준 뒤, 플레이 버튼을 보면 "New Game Object"라는 이름으로 게임이 시작되는 시점에는 씬에 없던 새로운 게임 오브젝트가 동적으로 생성된 것을 볼 수 있다.

 

하지만 이렇게 생성된 게임 오브젝트는 말 그대로 '빈' 게임 오브젝트이다. 게임에서 아무것도 없는 빈 게임 오브젝트가 할 수 있는 역할은 아마 없을 것이다. 빈 게임 오브젝트에 새로운 역할을 부여해주려면 새로운 컴포넌트를 부착해야 한다.

 

그러니 앞 단계에 이어서 게임 오브젝트에 스크립트로 컴포넌트를 붙이는 방법을 알아보자.

 

프로젝트 뷰에 우클릭해서 TestComponent라는 이름으로 C# 스크립트를 생성한다. 그리고 GameObjectCreator 스크립트 파일에서 아래와 같이:

 

public class GameObjectCreator : MonoBehaviour

{

    void Start()

    {

        var newObj = new GameObject().AddComponent<TestComponent>();        newObj.name = "Test Component Game Object";

    }

}

 

.AddComponent 함수를 사용하면 비어있는 게임 오브젝트에 스크립트로 방금 만든 TestComponent 컴포넌트를 부착할 수 있다. 

 

그리고 .name 프로퍼티를 이용하면 게임 오브젝트의 이름을 바꿀 수 있다. 이렇게 생성한 게임 오브젝트의 이름을 적절하게 바꿔주면 나중에 에디터에서 작업할 때 찾고자 하는 게임 오브젝트를 빠르게 찾을 수 있게 된다.

 

 

코드를 저장하고 에디터로 돌아가서 실행해보면 게임이 플레이되면 아까처럼 새로운 게임 오브젝트가 생성되는데 새 게임 오브젝트의 이름이 "New Game Object"가 아닌 "Test Component Game Object"인 것을 볼 수 있다. 그리고 이 "Test Component Game Object"를 하이어라키 뷰에서 클릭해보면 이렇게 TestComponent가 부착되어 있는 것을 볼 수 있다.

 

게임 오브젝트 파괴하기

 

게임 오브젝트를 생성하기만 하고 생성된 게임 오브젝트를 적절하게 제거하지 않으면 게임이 사용하는 메모리가 점점 늘어나면서 게임이 점점 느려지고 마지막엔 결국 게임이 멈춰버릴 수도 있다.

 

그럼 이제 생성된 게임 오브젝트를 스크립트로 파괴하는 방법을 알아보자.

 

이번에는 TestComponent 스크립트 파일을 연다.

 

public class TestComponent : MonoBehaviour

{

    void Start()

    {

        Destroy(gameObject);

    }

 

    private void OnDestroy()    {        Debug.Log(name + "가 파괴됨!");

 

    }

}

 

Start 함수에서 Destory 함수를 호출하고 매개변수로 파괴하고자 하는 게임 오브젝트를 넣어주면 된다. 우리는 TestComponent가 부착된 게임 오브젝트가 파괴되기를 원하기 때문에 gameObject 프로퍼티를 넣어준다.

 

그리고 OnDestroy 함수를 만들고 게임 오브젝트가 파괴될 때, 파괴되는 게임 오브젝트의 이름과 함께 파괴되었다는 로그를 남겨주는 코드를 작성한다.

 

 

코드를 저장하고 에디터로 돌아가서 플레이해보면 게임이 시작되자마자 Test Component Game Object가 파괴되었다는 로그만 남기고 사라져버린다.

 

Destroy(gameObject, 5f);

 

게임 오브젝트가 생성되자마자 바로 파괴되는 것이 아니라 일정한 시간이 지난 뒤에 파괴되는 것을 원한다면, Destroy 함수의 두 번째 매개변수에 실수형 매개변수를 넣어주면 된다.

 

유니티 스크립트 작업의 기반이 되는 C# 프로그래밍에는 함수 오버로딩이라고 하는 기능을 제공하는데 이것은 똑같은 이름의 함수라도 입력하는 매개변수에 따라서 비슷하지만 다른 동작을 하도록 구현할 수 있도록 만들어준다.

 

gameObject만 매개변수로 넣었을 때의 Destory 함수는 입력받은 게임 오브젝트를 바로 파괴하지만, 두 번째 매개변수로 실수형 숫자를 같이 넣어주면, 넣어준 숫자만큼의 시간이 지난 후에 입력받은 gameObject를 파괴하는 식으로 조금 다르게 동작한다.

 

참고로 두 번째 매개변수의 시간은 초 단위로 동작한다. 그러니까 이 TestComponent가 부착된 게임 오브젝트는 생성되고나서 5초가 지나면 파괴된다.

 

코드를 저장하고 에디터로 돌아가서 플레이 버튼을 눌러보면 게임 오브젝트가 생성되고 약 5초가 지난 뒤 파괴되는 것을 확인할 수 있다.

 

이런 식으로 스크립트로 게임 오브젝트를 생성하고 원하는 시점에 파괴되게 만들 수 있다.

 

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

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

 

에셋스토어

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

 

반응형

Resources 

프로젝트 뷰에서 리소스 바로 가져오기

 

작성 기준 버전 :: 2019.2

 

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

 

 

유니티 엔진에 아직 숙달되지 않은 개발자들은 초반에 작업할 때, 위의 이미지와 같이 프로젝트 뷰에서 직접 인스펙터 뷰의 프로퍼티에 드래그&드롭으로 리소스를 할당해주는 방식을 사용할 것이다.

 

이렇게 드래그&드롭으로 리소스를 할당하는 방법은 매우 직관적이고 간편한 방법이지만, 지금처럼 하나의 오브젝트가 아닌 10개의 오브젝트를 세팅해야한다면 어떨까? 

 

이렇게 네 종류의 리소스를 하나의 게임 오브젝트에 끼워넣는 작업에 네 번의 드래그&드롭 작업을 했으니 10개의 게임 오브젝트에 세팅 작업을 하려면 무려 40번의 드래그&드롭 작업을 해야한다.

 

거기에 만약 작업할 게임 오브젝트의 수가 리그 오브 레전드의 챔피언 숫자처럼 100개가 넘어간다면 400번이 넘는 반복 작업을 해야할 것이다.

 

생각만 해도 아득해지고 손목이 아파오는 작업이다.

 

그리고 이렇게 프로젝트 뷰에서 인스펙터 뷰의 프로퍼티로 끌어다가 할당하는 작업은 일종의 정적인 할당에 가깝기 때문에 처음부터 끝까지 수동으로 사람의 손을 거쳐야만 해서 생각보다 많은 작업 시간을 소모하게 만든다.

 

그래서 이런 반복 작업에 대해서 프로그래머들이 말하는게 있다.

 

"10번만 반복해야해도 자동화시켜라."

 

하지만 지금처럼 손으로 끌어다 놓는 방법은 어떻게 해도 자동화가 불가능할 것이다.

 

그럼 이 문제를 해결하기 위해서 프로젝트에 포함된 애셋을 프로젝트 뷰에서 가져오는 방법을 알아야 한다.

 

 

프로젝트 뷰에 Resources라는 이름의 폴더를 만들고 아까 게임 오브젝트에 할당해줬던 애셋들을 생성한 폴더 안으로 이동시킨다.

 

void Start()

{

    prefab = Resources.Load<GameObject>("prefab");

    material = Resources.Load<Material>("material");

    texture2D = Resources.Load<Texture2D>("texture");

    textAsset = Resources.Load<TextAsset>("text");

}

 

Resources.Load 함수를 사용하면 Resources 폴더 안에 들어있는 애셋들을 스크립트에서 불러와서 사용할 수 있게 된다. 그리고 <> 괄호 안에는 게임 오브젝트는 GameObject, 머티리얼은 Material, 텍스쳐는 Texture2D, 텍스트 파일은 TextAsset과 같이 해당 애셋의 타입에 맞는 클래스 이름을 넣어주면 된다.

 

그리고 매개변수로는 Resources 폴더 아래에 있는 애셋의 경로를 넣어주면 되는데, 지금의 예시에서는 애셋들이 폴더 바로 아래에 있어서 애셋 파일의 이름만 넣어주면 된다. 예를 들어서 만약 애셋 종류별로 폴더를 나눠서 정리하기로 해서 texture 애셋을 Texture 폴더 안에 넣기로 했다면, "Texture/texture"로 경로를 적어주면 된다.

 

 

코드를 모두 작성하면 자동으로 할당되는지 테스트 해보기 위해서 이미 모든 프로퍼티가 할당되어 있는 컴포넌트를 삭제하고 새로 컴포넌트를 붙여서 프로퍼티를 비워준다.

 

그 다음 플레이 버튼을 눌러서 게임을 시작해보면 플레이가 시작되는 순간에 비어있던 프로퍼티들이 Resources 폴더 안에서 불러온 애셋들로 채워지는 것을 볼 수 있다.

 

사실 이 Resources 폴더는 유니티 엔진의 예약 폴더라는 것으로 Resources 클래스를 이용하면 유니티 프로젝트에 포함된 Resources라는 이름의 폴더에 접근할 수 있게 해주는 기능이다.

 

 

이 Resources 폴더가 어디에 있든 이렇게 A 폴더 아래에 있든 B 폴더 아래에 있든 Resources.Load 함수를 사용하면 언제든지 그 아래에 있는 애셋을 가지고 올 수 있다. 게다가 A 폴더와 B 폴더, 둘 다 Resources 폴더를 가지고 있어도 무리없이 하나의 Resources 폴더처럼 동작한다.

 
이렇게 편리하게 프로젝트 안에 있는 애셋을 불러올 수 있는 기능이지만, 한 가지 명심해야할 점이 있다. Resources 폴더에 들어있는 애셋들은 게임이 실행되면 무조건 메모리에 적재된다.
 
그렇기 때문에 게임이 과도한 메모리를 소모하는 문제를 발생시키고 싶지 않다면, 비교적으로 메모리를 적게 소모하면서 빠르고 손쉽게 가져와야하는 애셋만을 Resources 폴더에 넣어둘 것을 권장한다.

 

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

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

 

에셋스토어

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

 

반응형

Programming 

스크린 공간과 월드 공간

 

작성 기준 버전 :: 2019.2

 

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

 

이번 섹션에서는 스크린 공간와 월드 공간에 대해서 알아보자.

 

유니티의 좌표계 글에서 게임에서 사용되는 월드 좌표계와 로컬 좌표계에 대해서 알아보았다.

 

스크린 공간(Screen space)

 

유니티 엔진에서는 게임 속에서 사용되는 월드 좌표계와 로컬 좌표계로 대표되는 월드 공간 외에도 스크린 공간이 있다.

 

스크린 공간은 단어 그대로, 게임 화면 상에서의 좌표 공간을 의미한다. 그리고 일반적인 UI의 위치와 마우스 위치는 이 스크린 공간의 좌표를 기준으로 동작한다.

 

 

 

에디터에서는 게임 뷰 화면이 스크린 공간이고, 만약 전체화면인 게임이라면 모니터 전체가 스크린 공간이 된다.

 

 

우선 스크린 공간의 좌표를 테스트 하기 전에 게임 뷰의 Free Aspect라고 적힌 드롭다운 메뉴를 클릭해서 드롭다운 메뉴를 열어보자. 이것은 게임 뷰에서 보일 게임 화면의 해상도를 설정할 수 있는 곳이다. + 버튼을 누르면 해상도를 설정할 수 있는 대화상자가 표시된다.

 

 

1920 x 1080 해상도로 설정해보자.

 

 

설정하고나면 16:9 비율로 보이게 된다.

 

public class ScreenSpaceTest : MonoBehaviour

    void Update()

    {

        Debug.Log(Input.mousePosition);

    }

}

 

그럼 이제 C# 스크립트를 하나 생성해서, 마우스 좌표를 출력하는 코드를 작성한다.

 

 
그리고 플레이 버튼을 누른 뒤 게임 뷰의 화면 위에서 마우스를 움직여 보면 왼쪽 아래로 갈수록 출력되는 좌표가 (0, 0)에 가까워지고 오른쪽 위로 갈수록 아까 설정한 화면 해상도에 가까운 (1920, 1080)에 가까워지는 것을 알 수 있다. 지금 볼 수 있는 화면 해상도로 표현되는 공간이 바로 스크린 공간이다.

 

처음에 언급했듯이 이것을 통해서 UI의 위치를 컨트롤 하거나 마우스의 위치를 알아낼 수 있다.

 

공간 전환

 

게임의 기능을 구현할 때는 이 스크린 공간의 좌표를 월드 공간의 좌표로 변환하거나 그 반대로 변환하는 방법 역시 자주 사용하게 된다.

 

스크린 공간 -> 월드 공간

 

먼저 스크린 공간의 좌표를 월드 공간의 좌표로 변경하는 법을 알아보자. 스크린 공간의 좌표를 월드 공간으로 전환하는 예시는 주로 마우스를 클릭한 위치로 캐릭터를 이동시킨다던가 하는 종류의 처리를 하는데 사용된다.

 

스크린 공간의 좌표를 월드 공간의 좌표로 전환하는 방법을 배우기 전에 씬을 세팅해보자.

 

 

씬에 평면 하나를 만들고 그 평면을 내려다 보도록 카메라를 배치한다.

 

 

그리고 하이어라키 뷰에 우클릭하고 스피어 게임 오브젝트를 하나 만든다.

 

 

그 다음 생성된 스피어 게임 오브젝트를 프로젝트 뷰에 드래그해서 프리팹으로 만든다. 그 다음엔 씬에 배치되어 있는 스피어 게임 오브젝트는 삭제한다.

 

public class ScreenToWorld : MonoBehaviour

{

    public GameObject prefab;

    void Update()

    {

        if(Input.GetMouseButtonDown(0))

        {

            var ray = Camera.main.ScreenPointToRay(Input.mousePosition);

            RaycastHit hit;

            if(Physics.Raycast(ray, out hit))

            {

                Instantiate(prefab, hit.point, Quaternion.identity);

            }

        }

    }

}

 

그리고 위와 같은 코드를 작성한다. 위 코드는 다음과 같은 과정으로 처리된다.

 

1. 카메라에 내장된 함수인 ScreenPointToRay() 함수로 클릭한 순간의 마우스 위치를 화면 공간의 좌표에서 카메라에서 쏘아지는 광선으로 변환

2. Physics.Raycast() 함수로 광선이 부딪힌 지점을 검출

3. 광선이 부딪힌 위치에 Instantiate() 함수로 게임 오브젝트를 생성

 

 

코드를 모두 작성하면 저장하고 에디터로 돌아가서 게임 오브젝트를 하나 배치한다. 그리고 그 게임 오브젝트에 작성한 ScreenToWorld 컴포넌트를 부착합니다. Prefab 프로퍼티에는 아까 만든 Sphere 프리팹을 할당한다.

 

 

 

플레이를 하고 게임 뷰에서 평면을 클릭해보면 마우스의 위치 좌표가 월드 공간의 좌표로 바뀌면서 클릭한 위치에 게임 오브젝트가 생성되는 것을 볼 수 있다.

 

월드 공간 -> 스크린 공간

 

스크린 공간에서 월드 공간으로 변환하는 것과 반대로 월드 공간에서 스크린 공간으로 변환하는 것 역시 게임에서 자주 사용된다. 대표적인 예로는 캐릭터의 머리 위에 HP UI를 띄우는 것이다.

 

유니티의 UI는 일반적으로 스크린 공간에 그려지는데, 월드 공간에서 움직이는 캐릭터를 따라가려면, 월드 공간의 캐릭터 좌표를 스크린 공간으로 전환해서 HP UI에게 알려줘야 한다.

 

테스트 하기 위한 씬을 세팅해보자.

 

 

하이어라키 뷰에 우클릭해서 스피어 하나와 [UI > Image]를 선택해서 이미지 UI를 하나 만들자.

 

 

그리고 이 이미지의 Width와 Height를 50으로 변경하고 색깔도 빨간색으로 변경한다.

 

public class WorldToScreen : MonoBehaviour

{

    public GameObject worldObject;

    void Update()

    {

        transform.position = Camera.main.WorldToScreenPoint(worldObject.transform.position);

    }

}

 

그 다음 WorldToScreen C# 스크립트 파일을 생성하고 코드를 작성한다.

 

 

코드를 모두 작성하면 에디터로 돌아가 이미지 게임 오브젝트에 WorldToScreen 컴포넌트를 추가하고 wolrdObject 프로퍼티에 씬에 배치한 게임 오브젝트를 할당한다.

 

 

모든 세팅을 마치고 플레이 버튼을 누른 뒤, 씬 뷰에서 스피어 게임 오브젝트를 선택하고 움직여보면 스크린 공간에 있는 이미지가 월드 공간의 게임 오브젝트를 정상적으로 따라다니는 모습을 볼 수 있다.

 

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

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

 

에셋스토어

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

 

반응형

Input 

-

키보드 입력과 캐릭터 이동 구현하기

 

작성 기준 버전 :: 2019.2

 

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

 

이번 섹션에서는 키보드 입력과 캐릭터 이동 기능을 구현해보자.

 

게임을 컨트롤하기 위한 입력 장치는 키보드, 마우스가 될 수도 있고, 게임 패드가 될 수도 있다. 혹은 마이크가 게임의 입력 장치가 될 수도 있다.

 

PC 게임에서 가장 기본적인 입력 장치는 키보드로, 키보드 만으로 컨트롤하는 게임은 많아도, 오히려 마우스만으로 컨트롤하는 게임은 키보드만으로 컨트롤 하는 게임보다 그렇게 많지 않다.

 

사실 키보드 입력을 구현하는 것 자체는 아주 간단하고 쉽다.

 

키보드 입력의 기본

 

bool Input.GetKeyDown(KeyCode key);

bool Input.GetKey(KeyCode key);

bool Input.GetKeyUp(KeyCode key);

 

키보드의 입력은 마우스와 같이 Down, Hold, Up  이 세 가지 과정으로 나누어져서 처리된다. Down은 키보드를 누르는 순간을 의미하고, Hold는 누른 상태를 유지하는 것, Up은 키보드에서 손을 떼는 것을 의미한다.

 

void Update()

{

    if (Input.GetKeyDown(KeyCode.Space))

    {

        Debug.Log("스페이스 키 누름");

    }

 

    if (Input.GetKey(KeyCode.Space))

    {

        Debug.Log("스페이스 키 누르는 중");

    }

 

    if (Input.GetKeyUp(KeyCode.Space))

    {

        Debug.Log("스페이스 키 손뗌");

    }

}

 

[그림 1]

 

위와 같이 업데이트 함수를 구현하고 플레이 시킨 뒤 스페이스 키를 눌렀다 떼보면, 스페이스 키를 누르는 순간에는 GetKeyDown이, 누르고 있는 중에는 GetKey가, 손을 떼는 순간에는 GetKeyUp이 한 번 호출되는 것을 볼 수 있다.

 

캐릭터 이동 구현하기

 

캐릭터 컨트롤러 클래스 구현

 

그러면 이 키보드 입력을 응용해서 캐릭터의 이동을 구현해보자.

 

public class CharacterController : MonoBehaviour

{

}

 

우선 캐릭터의 이동을 컨트롤할 캐릭터 컨트롤러 클래스를 하나 생성한다.

 

void Update()

{

    float moveZ = 0f;

    float moveX = 0f;

    if (Input.GetKey(KeyCode.W))

    {

        moveZ += 1f;

    }

 

    if (Input.GetKey(KeyCode.S))

    {

        moveZ -= 1f;

    }

 

    if (Input.GetKey(KeyCode.A))

    {

        moveX -= 1f;

    }

 

    if (Input.GetKey(KeyCode.D))

    {

        moveX += 1f;

    }

 

    transform.Translate(new Vector3(moveX, 0f, moveZ) * 0.1f);

}

 

그리고 업데이트 함수에서 위와 같이 코드를 작성한다. 이 코드는 W/S 키를 누르면 게임 오브젝트의 Z축을 앞뒤로 움직이고, A/D 키를 누르면 좌우로 움직이게 만드는 코드이다.

 

맵 만들기

 

[그림 2]

 

그리고 코드를 모두 작성하면 빈 공간에서는 캐릭터 역할을 할 게임 오브젝트가 아무리 움직여도 티가 안나기 때문에 캐릭터가 움직이는 느낌을 주기 위해서 에디터로 가서 씬 뷰에 바닥을 만들고 적당히 큐브를 깔아 장애물을 만들어 준다. 

 

번외. 게임 오브젝트 색 변경하기

 

 

번외로 처음 오브젝트를 만들면 모두 같은 색이라 색상이 구별이 안될 수도 있는데, 게임 오브젝트에 색을 넣어주기 위해서는 프로젝트 뷰에 우클릭한 뒤 [Create > Material] 항목을 선택해서 새 머티리얼을 만들고,

 

 

생성한 머티리얼을 선택하고 알베도(Albedo) 값을 원하는 색으로 바꾼 뒤, 씬 뷰에 있는 게임 오브젝트에 넣어주면 된다.

 

캐릭터 만들기

 

 

 

다시 본론으로 돌아가서 다른 게임 오브젝트와 구별되는 Character 게임 오브젝트를 하나 만든 뒤, Character Controller 컴포넌트를 부착해준다. 그리고 장애물과 캐릭터가 충돌하게 만들기 위해서 Rigidbody 컴포넌트도 추가해준다.

 

 

세팅이 끝나고 플레이 버튼을 눌러 게임을 실행한 뒤, WASD 키를 눌러보면 구형 캐릭터가 이리저리 움직이면서 장애물과 충돌하는 것을 볼 수 있다. 

 

문제점 보완하기

 

카메라가 캐릭터 따라가게 만들기

 

하지만 아직 만족스러운 상태는 아닌데, 캐릭터를 한 방향으로 계속 움직이면 카메라가 캐릭터를 따라가지 않고, 캐릭터가 화면 밖을 벗어나 버리는 것을 볼 수 있다.

 

 

이 문제를 해결하기 위해서 카메라를 캐릭터의 자식 컴포넌트로 만들고 카메라가 위에서 아래로 캐릭터를 내려다보게 만들어보자.

 

 

대각선 이동 속도 문제 해결하기

 

카메라 문제 이외에도 아직 문제는 남아있다. 

 

 

그 문제는 바로 대각선 이동을 위해서 두 키를 동시에 누르면 발생하는 문제다. 한 번에 한 방향의 이동키만 누르면 속도가 일정하지만, 대각선 방향의 두 개의 키를 동시에 누르면 두 개의 벡터가 합쳐져서 한 방향 키만 눌렀을 때의 이동 벡터보다 길이가 약간 길어져버린다. 이 때문에 정방향 이동보다 대각선 이동의 속도가 더 빨라져버리는 문제가 발생한다.

 

 

그렇기 때문에 두 개의 키를 동시에 눌렀을 때 발생하는 이동 벡터의 길이를 한 방향 키만 눌렀을 때 발생하는 벡터의 길이와 같게 만들어 주어야 한다.

 

transform.Translate(new Vector3(moveX, 0f, moveZ).normalized * 0.1f);

 

위의 코드와 같이 이동 벡터를 정규화해서 단위 벡터로 만들어주면 정방향 이동이나 대각선 이동이나 같은 속도로 이동할 수 있게 된다.

 

GetAxis()로 구현하기

 

캐릭터의 이동을 위한 입력을 받는 다른 방법으로는 Input.GetAxis() 함수를 사용하는 방법도 있다.

 

void Update()

{

    float moveZ = Input.GetAxis("Vertical");

    float moveX = Input.GetAxis("Horizontal");

    transform.Translate(new Vector3(moveX, 0f, moveZ).normalized * 0.1f);

}

 

GetAxis() 함수를 이용한 이동 방식 구현은 위의 코드와 같이 구현된다. GetAxis() 함수는 유니티 엔진의 프로젝트 세팅에 정의된 축의 값을 가져오는 역할을 하는데, 그 정의된 축에는 "Vertical"과 "Horizontal"이 있다. 이것은 수직과 수평을 의미한다.

 

앞에서 구현한 WASD의 입력을 일일이 구현하는 방식에 비해서, 이렇게 GetAxis() 함수를 이용해서 축을 가져오는 방식은 여러 가지 장점이 있다. 우선은 간단히 코드만 살펴봐도 앞의 코드에 비해서 구현해야할 코드의 길이가 월등히 짧다. 그리고 WASD의 입력을 일일이 구현하는 방법은 WASD 키를 누를 때만 작동하며 게임 패드의 입력을 구현하려면 따로 구현해야 하지만 GetAxis() 함수를 사용하면 별도의 구현없이 게임 패드의 이동 입력까지 자동으로 지원한다.

 

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

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

 

에셋스토어

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

 

반응형

Transform 

게임 오브젝트의 공간 정보

 

작성 기준 버전 :: 2019.2

 

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

 

이번 섹션에서는 게임 오브젝트의 공간 정보를 관리하는 트랜스폼 컴포넌트에 대해서 알아보자.

 

본 내용에 앞서 벡터좌표계, 게임 오브젝트와 컴포넌트에 관한 지식이 필요하다면 링크된 글들을 읽어보면 도움이 된다.

 

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

 

 

[그림 1]

 

우선 트랜스폼 컴포넌트는 게임 오브젝트에 필수로 부착되는 컴포넌트로, 인스펙터 뷰에서 보면 [그림 1]과 같이 Vector3 형식의 포지션(Position), 로테이션(Rotation), 스케일(Scale) 프로퍼티를 사용자에게 공개하고 있다.

 

 

프로퍼티의 이름에 맞게 포지션 프로퍼티는 게임 오브젝트의 위치 정보를 수정할 수 있다.

 

 

로테이션 프로퍼티는 회전 정보를 가지고 이를 수정할 수 있다.

 

 

스케일 프로퍼티는 크기 정보에 관여한다.

 

이렇게 인스펙터 뷰에서 보이는 트랜스폼 컴포넌트로 씬 안에 있는 게임 오브젝트의 위치를 옮기거나, 회전시키고, 그 크기를 바꿀 수 있다. 하지만 인스펙터 뷰에서 트랜스폼 컴포넌트의 내용을 변경하는 것은 게임 중에는 불가능한 일로 고정된 건물이나 물건같은 오브젝트에나 사용할 수 있는 방법이다.

 

플레이어, 몬스터와 같은 캐릭터, 총알, 화살 같은 투사체, 말, 자동차 같은 탈 것처럼 게임 안에서 플레이어의 조작이나 AI의 조작을 따라서 움직일 게임 오브젝트들은 스크립트를 이용해서 이동시켜야 한다.

 

 

스크립트로 트랜스폼 컴포넌트 다루기

 

트랜스폼 컴포넌트 접근하기

 

public class TransformController : MonoBehaviour

{

    void Start()

    {

        Transform myTransformComponent = transform;

    }

}

 

커스텀 컴포넌트가 부착된 게임 오브젝트의 트랜스폼 컴포넌트를 가져오기 위해서는 모노비헤이비어(MonoBehaviour) 클래스를 통해서 상속받은 transform 프로퍼티를 호출하면 된다.

 

 

transform 프로퍼티를 어디서 상속받는지 궁금할 수도 있다. 그럴 때는 트랜스폼 컨트롤러 클래스가 상속받는 모노비헤이비어 클래스를 클릭하고 F12키를 눌러서 모노비헤이비어 클래스 파일로 이동한 다음, 같은 과정을 컴포넌트(Component) 클래스가 나올 때까지 반복하면 된다. 그러면 컴포넌트 클래스에 정의된 transform 프로퍼티를 확인할 수 있다.

 

위치 이동시키기

 

position으로 직접 이동시키기

 

그럼 제일 먼저 트랜스폼 컴포넌트를 이용해서 게임 오브젝트를 이동시켜보자.

 

public void MovePosition(Vector3 newPosition)

{

    transform.position = newPosition;

}

 

게임 오브젝트의 위치 정보를 다루는 포지션 프로퍼티에 접근하기 위해서는 위의 예시 코드와 같이 transform.position을 이용하면 된다.

 

float timer = 0f;

void Update()

{

    timer += Time.deltaTime;

    MovePosition(new Vector3(0f, Mathf.Cos(timer), 0f));

}

 

방금 만든 Update() 함수에서 MovePosition() 함수를 호출한다. 게임 오브젝트의 위치를 코사인 그래프에 따라서 위 아래로 움직이도록 만들어진 코드이다.

 

 

이 코드를 게임 오브젝트에 부착하고 에디터에서 플레이 시켜보면 코사인 그래프의 높이에 따라 게임 오브젝트가 위 아래로 천천히 움직이는 것을 볼 수 있다.

 

Translate() 함수로 이동시키기

 

위에서 position으로 이동시키기는 말그대로 트랜스폼 컴포넌트의 position 프로퍼티에 직접 위치를 넣어서 이동시키는 방법이다.

 

float timer = 0f;

void Update()

{

    timer += Time.deltaTime;

    MovePositionUseTranslate(new Vector3(0f, Mathf.Cos(timer), 0f));

}

 

public void MovePositionUseTranslate(Vector3 moveDirection)

{

    transform.Translate(moveDirection);

}

 

Translate() 함수는 position 프로퍼티에 직접 위치를 집어넣어서 이동시키는 것과는 달리 게임 오브젝트가 이동하고자 하는 방향과 속력인 벡터를 매개변수로 받아 그 벡터의 방향과 길이만큼 게임 오브젝트를 이동시키는 함수이다.

 

 

위 코드를 저장하고 플레이해보면 position을 이용한 오브젝트 이동에서는 1 ~ -1 사이에서만 움직이던 것과는 달리 Translate() 함수를 이용한 이동에서는 훨씬 큰 폭으로 움직이는 것을 볼 수 있다. 이것은 이동 방향 벡터가 코사인 그래프를 따라서 바뀌는 동안에 0보다 값이 커지면 위로, 0보다 작아지면 아래로 움직이기 때문이다.

 

position 이동과 Translate() 이동의 비교

 

public class TranslateMover : TransformController

{

    void Update()

    {

        MovePositionUseTranslate(new Vector3(0f, 0.1f, 0f));

    }

}

 

public class PositionMover :

 TransformController

{

    void Update()

    {

              MovePosition(new Vector3(0f, 0.1f, 0f));

    }

}

 

두 이동 방식을 비교하기 위해서 TransformController를 상속받는 두 클래스를 만들어보았다. PositionMover 클래스는 매 프레임 MovePosition() 함수를 호출해서 (0, 0.1, 0) 벡터를 넣어주고, TranslateMover 클래스는 매 프레임 MovePositionUseTranslate() 함수를 호출해서 역시 같은 벡터를 넣어주고 있다.

 

 

에디터로 돌아가서 게임 오브젝트 두 개를 만들고 이 두 컴포넌트를 각각 붙여주고 플레이하면 TranslateMover 컴포넌트를 붙인 게임 오브젝트만 저 멀리 올라가버리는 것을 볼 수 있다. 하지만 PositionMover 컴포넌트를 붙인 게임 오브젝트는 시작되는 순간에 (0, 0.1, 0) 좌표로만 이동한 다음에 그대로 움직이지 않는 것을 보면, 두 방법의 차이를 이해할 수 있다.

 

 

 

 

 

회전시키기

 

rotation으로 회전시키기

 

void Start()

{

    transform.rotation = new Quaternion();

}

 

게임 오브젝트를 회전시키기 위해서는 transform.rotation 프로퍼티를 사용하면 된다. 다만, 인스펙터 뷰에서 공개된 Rotation 프로퍼티가 Vector3 형식인 것과 달리 스크립트에서는 쿼터니언(Quaternion) 구조체를 사용한다.

 

Quaternion rotation = new Quaternion();

 

rotation.w

rotation.x

rotation.y

rotation.z

 

쿼터니언 구조체는 벡터와는 다른 사원수라는 체계를 사용해서 오브젝트의 회전을 표현한다. 이 사원수라는 체계는 상당히 난해한 체계이기 때문에 유니티의 공식 문서에서는 사원수에 대한 지식을 충분히 가지고 있지 않다면 쿼터니언을 직접 수정하지 않도록 권장하고 있다.

 

public void RotateRotation(Vector3 newRotation)

{

    transform.rotation = Quaternion.Euler(newRotation);

}

 

public void RotateRotation(Vector3 newRotation)

{

    transform.Rotate(newRotation);

}

 

그럼 사원수를 제대로 알지 못하면 게임 오브젝트를 회전시키지 못하게 되는가? 그렇지는 않다. 인스펙터 뷰에서처럼 3차원 벡터를 이용해서 회전을 다루는 방법을 오일러 각 체계(Euler angle system)라고 부른다. 오일러 각 체계 이용하면 xyz 각 축을 기준으로 오브젝트가 얼마나 회전한 상태인지 직관적으로 알 수 있다. 그래서 쿼터니언 구조체에는 이 오일러 각 체계의 회전을 사원수 체계의 회전으로 전환해주는 Euler() 함수가 포함되어 있다. 이 함수를 이용하면 Vector3로 표현된 각을 Quaternion으로 변환할 수 있다.

 

그리고 회전 역시 이동과 마찬가지로 rotation 프로퍼티를 직접 수정하는 방법과 Rotate() 함수를 사용하는 방법 두 가지가 있다. 그리고 그 차이점 역시 이동시키기에서의 position 직접 이동과 Translate() 함수를 이용한 이동과 비슷하다.

 

float timer = 0f;

void Update()

{

    timer += Time.deltaTime;

    RotateRotation(new Vector3(0f, ((Mathf.Cos(timer) + 1f) * 0.5f) * 360f, 0f));

}

 

 

RotateRotation() 함수를 업데이트에서 호출하도록 코드를 작성하고 플레이시켜보면 게임 오브젝트가 회전하는 것을 볼 수 있다.

 

transform.forward로 바라보는 방향 정하기

 

float timer = 0f;

void Update()

{

    timer += Time.deltaTime;

    ForwardControl(new Vector3(Mathf.Cos(timer), 0f, Mathf.Sin(timer)));

}

 

public void ForwardControl(Vector3 newForward)

{

    transform.forward = newForward;

}

 

게임 오브젝트를 회전시키는 다른 방법으로는 transform.forward 프로퍼티를 이용하면 게임 오브젝트의 forward, 즉 정면을 설정해서 특정한 방향을 바라보게 할 수 있다. 프로퍼티로 가져올 수 있는 방향으로는 forward, up, right가 있다. 

 

LookAt() 함수로 원하는 위치를 바라보게 하기

 

float timer = 0f;

void Update()

{

    timer += Time.deltaTime;

    ForwardControl(new Vector3(Mathf.Cos(timer), 0f, Mathf.Sin(timer)));

}

 

public void LookObject(Vector3 pos)

{

    transform.LookAt(pos);

}

 

transform 컴포넌트에 있는 LookAt() 함수를 사용하면 원하는 지점을 바라보게 할 수 있다. LookAt() 함수의 매개변수로 Vector3 뿐만 아니라 다른 게임 오브젝트의 트랜스폼 컴포넌트를 넣어서 다른 게임 오브젝트를 따라가며 바라보게 할 수도 있다.

 

 

크기 조절하기

 

float timer = 0f;

void Update()

{

    timer += Time.deltaTime;

    float scale = Mathf.Cos(timer) + 2f;

    Scaling(new Vector3(scale, scale, scale));

}

 

public void Scaling(Vector3 scale)

{

    transform.localScale = scale;

}

 

게임 오브젝트의 크기 조절은 transform.localScale 프로퍼티를 통해서 할 수 있다.

 

 

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

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

 

에셋스토어

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