유니티 엔진에서 차나 탈 것에 타고 내리는 기능을 만들어 봅시다!

 

차 모델 및 애니메이션 : https://drive.google.com/file/d/1Y-Ru93kIVXqjzD9xw4xPUs7VYotTFVny/view?usp=sharing 박스맨 모델 및 애니메이션 : https://drive.google.com/file/d/1EUdtP7LeL5yfyRPmKG6WGiLCXBMVkmeC/view?usp=sharing

 

타임라인

0:22 리소스 임포트

0:33 비히클 시스템 설계

5:22 설계대로 클래스 생성

7:17 캐릭터 조작 기능 구현

9:36 차량 조작 기능 구현

12:00 차에 타기 기능 구현

14:56 차에서 내리기 기능 구현

 

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

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

 

에셋스토어

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

 

반응형

Explorer 2D Game Kit 분석 (4) 

게임플레이 요소 (1)

 

작성 기준 버전 :: 2019.1.4f1

 

그림 1

 

본격적으로 게임플레이와 관련된 요소들에 대해서 분석해볼 차례이다.

 

그림 2

 

기본적인 하이어라키 뷰(Hierarchy View) 상태는 스타트 씬과 비슷하지만 게임이 진행되는 씬이기 때문에 플레이어와 연관된 에셋인 Player Assets 파트와 레벨을 구성하고 있는 Level Assets 파트가 추가되어 있는 것을 알 수 있다.

 

시스템(System)

 

시스템으로 분류된 게임 오브젝트는 씬 컨트롤러(Scene Controller), 트랜지션 스타트(Transition Start), 트랜지션 데스티네이션(Transition Destination), VFX 컨트롤러(VFX Controller), 백그라운드 뮤직 플레이어(Background Music Player), 피직스 헬퍼(Physics Helper)가 있다.

 

이 중에서 씬 컨트롤러와 그리고 백그라운드 뮤직 플레이어는 지난 섹션에서 다루었으니 넘어가도록 하고, 지난 섹션에서 다루기는 했으나 트랜지션 포인트(Transition Point) 컴포넌트를 부착하고 있는 트랜지션 스타트 게임 오브젝트는 약간의 차이가 있으나 가볍게 다루고 넘어가도록 한다.

 

트랜지션 스타트 게임 오브젝트(Transition Start Game Object)

 

그림 3

 

지난 섹션에서 다루었다시피 트랜지션 스타트 게임 오브젝트는 트랜지션 포인트 컴포넌트가 부착되어 있으며 플레이어의 캐릭터가 콜라이더에 닿으면 플레이어를 다른 씬으로 보내는 역할을 한다.

 

다만, 스타트 씬에서의 트랜지션 포인트와 다른 점은, 스타트 씬에서는 콜라이더에 닿은 캐릭터가 존재하지 않기 때문에 외부에서 트랜지션 포인트를 호출해서 씬을 전환하는 방식을 사용했다면, 이제부터는 본래의 방식에 맞게 캐릭터가 콜라이더에 닿으면 다른 씬을 보내도록 구성되어 있다는 점이다.

 

그림 4

 

실제로 씬에서 트랜지션 스타트 게임 오브젝트를 선택해서 보면 발판에서 캐릭터가 뛰어내리면 콜라이더에 닿을 수 있게 배치되어 있는 것을 확인할 수 있다.

 

그리고 원래의 스타트 씬에서는 Trasitioning Game Object 프로퍼티가 비어있었는데 지금은 Ellen이라는 게임 오브젝트가 할당되어 있는 것을 알 수 있다. 이 게임 오브젝트는 씬에 배치 되어있는 플레이어의 캐릭터로, 다른 물체나 몬스터 등의 다른 캐릭터가 아닌 플레이어의 캐릭터만 닿았을 때, 씬을 이동시키기 위해서 할당해둔 것이다.

 

void OnTriggerEnter2D (Collider2D other)

{

    if (other.gameObject == transitioningGameObject)

    {

        m_TransitioningGameObjectPresent = true;

 

        if (ScreenFader.IsFading || SceneController.Transitioning)

            return;

 

        if (transitionWhen == TransitionWhen.OnTriggerEnter)

            TransitionInternal ();

    }

}

 

트리거 설정된 콜라이더에 충돌이 발생했을 때 호출되는 OnTriggerEnter2D 콜백 함수를 보면 확실히 알 수 있다. 매개변수로 넘어온 충돌체의 게임 오브젝트와 미리 할당해둔 transitioningGameObject와 비교하여 같을 경우에만, 다른 씬으로 이동시키는 구조이다.

이렇게 하지 않으면 트랜지션 포인트의 콜라이더에 플레이어의 캐릭터가 아닌 총알이나 몬스터가 닿기만 해도 씬이 이동되는 상황을 보게 될 것이다.

 

유니티 콘텐츠 팀에서 선택한 방법에도 약간의 단점이 있다. 그것은 트랜지션 스타트에 미리 캐릭터를 할당해두는 방식이기 때문에 나중에 캐릭터를 선택할 수 있는 기능을 넣게 된다면, 다른 캐릭터로 시작하면 그 캐릭터는 이 콜라이더에 닿아도 다른 씬으로 이동하지 못할 수도 있다.

 

이것은 간단하게 만들어진 예시이기 때문에 발생한 문제로, 플레이어 캐릭터에 태그나 레이어를 설정하고, 태그나 레이어로 비교해서 통과시키는 방법으로 해결할 수 있다.

 

 

트랜지션 데스티네이션 게임 오브젝트(Transition Destination Game Object)

 

그림 5

 

트랜지션 데스티네이션 게임 오브젝트는 씬 트랜지션 데스티네이션 컴포넌트(Scene Transition Destination Component)와 캐릭터 스테이트 세터 컴포넌트(Character State Setter Component)로 구성되어 있다. 이 게임 오브젝트는 플레이어의 캐릭터가 씬 이동을 할 때 목적지 역할을 한다. 첫 번째 게임 플레이 씬인 Zone1에 두 개가 배치되어 있는데, 처음 게임이 시작되었을 때 배치되는 위치인 트랜지션 데스티네이션 스타트(Transition Destination Start)와 두 번째 게임 씬인 Zone2로부터 넘어왔을 때의 도착 지점인 트랜지션 데스티네이션 프롬 Zone2(Transition Destination From Zone2)가 그것이다.

 

씬 트랜지션 데스티네이션 컴포넌트(Scene Transition Destination Component)

 

public class SceneTransitionDestination : MonoBehaviour

{

    public enum DestinationTag

    {

        A, B, C, D, E, F, G,

    }

 

    public DestinationTag destinationTag;

    [Tooltip("This is the gameobject that has transitioned.  For example, the player.")]

    public GameObject transitioningGameObject;

    public UnityEvent OnReachDestination;

}

 

씬 트랜지션 데스티네이션 컴포넌트에는 사실상 큰 기능 자체는 존재하지 않고, 씬 컨트롤러(Scene Controller)에서 씬이 전환된 직후에 씬 안에 존재하는 씬 트랜지션 데스티네이션 컴포넌트가 부착된 모든 게임 오브젝트를 가지고 와서 데스티네이션 태그(Destination Tag)를 비교해 일치하는 트랜지션 데스티네이션 게임 오브젝트의 위치에 플레이어의 캐릭터를 이동시키기 위한 표지판 역할을 한다.

캐릭터 스테이트 세터 컴포넌트(Character State Setter Component)

 

캐릭터 스테이트 세터 컴포넌트는 씬 트랜지션 테스티네이션 컴포넌트와 함께 트랜지션 데스티네이션 게임 오브젝트에 부착된 컴포넌트로, 씬 트랜지션 데스티네이션 컴포넌트가 도착 위치를 지정하는 역할을 한다면 캐릭터 스테이트 세터 컴포넌트는 씬에 도착한 직후의 캐릭터의 상태를 설정하는 역할을 한다.

 

기본적으로 공개되어 있는 프로퍼티는 Set Character Velocity, Set Character Facing Contents, Set State, Set Parameter가 있으며 Set Character Velocity는 씬에 진입했을 때의 캐릭터의 속도를 설정할 수 있고, Set Character Facing Contents는 캐릭터가 바라볼 방향, Set State는 캐릭터의 애니메이션, Set Parameter는 캐릭터 애니메이터의 매개변수 값을 설정하는 옵션이다.

 

그림 6

 

캐릭터 스테이트 세터 컴포넌트에서 눈여겨 볼 점은 [그림 5]에서와 같이 프로퍼티가 선택되지 않았을 때는 해당 프로퍼티에 연관된 옵션이 보이지 않다가 프로퍼티 값이 true로 설정되면 [그림 6]과 같이 프로퍼티와 연관된 옵션이 보이도록 에디터가 커스터마이징되어 있다는 점이다. 프로젝트 뷰에서 CharacterStateSetterEditor를 검색해서 CharacterStateSetterEditor.cs 파일을 확인해보면 어떤 식으로 프로퍼티 값에 따라서 보여줄 옵션을 설정할 수 있는지 배울 수 있다.

 

이렇게 유니티 에디터에서 필요하거나 사용되는 옵션만 보여주는 것 만으로도 에디터에서 작업하는 디자이너 개발자의 작업 효율을 크게 상승시킬 수 있다.

 

[Header("Character Velocity")]

public bool setCharacterVelocity;

public Vector2 characterVelocity;

 

[Header("Character Facing")]

public bool setCharacterFacing;

public bool faceLeft;

 

public Animator animator;

 

[Header("Character Animation State")]

public bool setState;

public string animatorStateName;

 

[Header("Character Animation Parameter")]

public bool setParameters;

public ParameterSetter[] parameterSetters;

 

그림 7

 

여기에 더불어 Header 어트리뷰트를 사용하면 [그림 7]과 같이 프로퍼티의 분류를 훨씬 명확하게 인지하도록 만들 수 있다.

 

 

VFX 컨트롤러 게임 오브젝트(VFX Controller Game Object)

 

그림 8

 

VFX 컨트롤러 게임 오브젝트는 VFX 컨트롤러 컴포넌트(VFX Controller Component)가 부착되어 있다. 이 게임 오브젝트의 목적은 캐릭터가 점프하거나 달릴 때 먼지가 일너나는 들의 이팩트를 관리하는 것이다.

 

VFX 컨트롤러 컴포넌트(VFX Controller Component)

 

VFX 컨트롤러 컴포넌트는 이펙트를 관리하는 역할의 컴포넌트이다. 이것은 다른 시스템 컴포넌트와 마찬가지로 싱글톤 패턴으로 만들어졌으며, 같은 이펙트 게임 오브젝트들이 계속해서 생성되고 파괴됨으로써 발생하는 성능 저하를 막기 위해서 오브젝트 풀링 기법을 사용하고 있다. 오브젝트 풀링 기법은 기초적인 최적화 기법으로 오브젝트가 생성/파괴될 때 발생하는 오버헤드를 막기 위해서 오브젝트를 재사용하는 기법을 말한다.

 

그림 9

 

VFX 컨트롤러 컴포넌트에서는 게임에서 사용될 VFX 프리팹들을 리스트에 담아서 관리하는데, 만약 같은 방식으로 생성되어야 하는 VFX지만 상황에 따라서 다른 VFX를 생성해야 경우, 예를 들어 캐릭터가 달릴 때 풀 바닥에서는 풀이 날리는 VFX가 발생해야 하지만, 흙 바닥에서 달릴 때는 흙 먼지가 날리는 VFX가 생성되도록 하기 위해서, Vfx Override로 특별한 경우를 정의하고 있다.

 

 

피직스 헬퍼 게임 오브젝트(Physics Helper Game Object)

 

 

피직스 헬퍼 게임 오브젝트는 피직스 헬퍼 컴포넌트(Physics Helper Component)를 가진 게임 오브젝트로서 게임 내에서 불리적인 처리에 도움을 주는 역할을 한다.

 

피직스 헬퍼 컴포넌트(Physics Helper Component)

 

피직스 헬퍼 컴포넌트는 게임이 플레이되는 상황에서 어디서든지 호출될 수 있기 때문에 싱글톤 패턴으로 작성되어 있으며, 캐릭터가 밟고 서는 플랫폼이나 타일맵에 관련된 물리적인 처리를 담당하고 있다.

 

사실 피직스 헬퍼 같은 컴포넌트는 개발자가 편의에 따라서 작성하기 나름인 스크립트이다. 게임마다 필요한 물리 연산이나 처리는 모두 다르기 마련이라, 필요한 물리 작업에 따라서 피직스 헬퍼의 내용은 천차만별로 달라질 수 있다. 다만, 이렇게 자주 사용되는 기능을 분류 별로 묶어서 어디서든지 호출할 수 있게 헬퍼 클래스로 만드는 작업은 관련된 기능을 이리저리 흩뜨려 놓는 것보다 확실히 더 나은 효율적인 작업을 보장한다.

 


 

Explorer 2D Game Kit 분석 (1) - 개요

Explorer 2D Game Kit 분석 (2) - Start 씬 해부하기 (1)

Explorer 2D Game Kit 분석 (3) - Start 씬 해부하기 (2)

Explorer 2D Game Kit 분석 (4) - 게임플레이 요소 (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

 

반응형

Programming 

오브젝트 풀링 기법

 

작성 기준 버전 :: 2019.2

 

프로그래밍에서 오브젝트를 생성하거나 파괴하는 작업은 꽤나 무거운 작업으로 분류된다. 오브젝트 생성은 메모리를 새로 할당하고 리소스를 로드하는 등의 초기화하는 과정으로, 오브젝트 파괴는 파괴 이후에 발생하는 가비지 컬렉팅으로 인한 프레임 드랍이 발생할 수 있다.

 

이러한 문제를 해결하기 위해서 사용되는 기법이 바로 오브젝트 풀링(Object pooling)이다.

 

오브젝트 풀링의 개념

 

오브젝트 풀링의 개념은 아주 간단하다. 

 

 

먼저 풀링을 할 오브젝트를 담을 오브젝트 풀을 구성한다.

 

 

그 다음 풀링할 오브젝트를 생성해서 오브젝트 풀에서 그 오브젝트들을 관리하게 만든다.

 

 

외부에서 해당 오브젝트가 필요하면 오브젝트 풀에서 꺼내간다.

 

 

오브젝트 풀에서 꺼낸 오브젝트의 사용이 끝나면 오브젝트를 풀에 돌려준다.

 

 

오브젝트 풀에서 오브젝트를 가져오려고 할 때, 모든 오브젝트가 이미 사용중이라면 새로운 오브젝트를 생성해서 꺼내준다.

 

이것이 바로 오브젝트 풀의 개념이다. 게임에 필요한 오브젝트를 미리 생성해서 필요할 때마다 꺼내쓰고 사용이 끝나면 오브젝트 풀에 돌려주는 것이다. 이렇게 함으로써, 오브젝트가 필요할 때마다 생성하고 다 쓰면 파괴하는 것이 아니라, 게임이 시작될 때, 필요한 만큼의 오브젝트만 생성하고, 모자라면 추가로 생성하고, 게임이 끝나면 파괴하는 방식으로 오브젝트의 생성/파괴 횟수를 줄일 수 있게 된다.

 

오브젝트 풀링 기법은 특히 메모리가 부족하고 CPU의 성능이 낮은 디바이스의 사양이 낮았던 예전에 주로 사용되었던 기법이지만, 현재에 와서도 게임의 최적화를 위해서 많이 사용되며, 모바일 게임이 많이 제작되면서 PC에 비해서 모자란 디바이스 성능으로 인해서 재조명 받는 기법이다.

 

보통 자주 생성되었다가 파괴되어야 하는 총알이나, 캐릭터가 뛸 때 발생하는 먼지 이펙트 같은 곳에 많이 사용되는 기법이다.

 

 

 

유니티 엔진에서의 구현

 

기본 세팅

 

기본적인 세팅은 다음과 같다.

 

 

씬 한 가운데 캐릭터의 역할을 할 초록색 박스를 배치했다.

 

public class Shooter : MonoBehaviour
{
    [SerializeField]
    private GameObject bulletPrefab;

    private Camera mainCam;

    void Start()
    {
        mainCam = Camera.main;
    }

    void Update()
    {
        if(Input.GetMouseButton(0))
        {
            RaycastHit hitResult;
            if(Physics.Raycast(mainCam.ScreenPointToRay(Input.mousePosition), out hitResult))
            {
                var bullet = Instantiate(bulletPrefab).GetComponent<Bullet>();
                var direction = new Vector3(hitResult.point.x, transform.position.y, hitResult.point.z) - transform.position;
                bullet.transform.position = direction.normalized;
                bullet.Shoot(direction.normalized);
            }
        }
    }
}

 

그리고 박스 게임 오브젝트에는 마우스 클릭 방향으로 총알 게임 오브젝트를 생성해서 발사하는 Shooter 컴포넌트가 부착되어 있다.

 

 

public class Bullet : MonoBehaviour
{
    private Vector3 direction;
    public void Shoot(Vector3 direction)
    {
        this.direction = direction;
        Destroy(gameObject, 5f);
    }

    void Update()
    {
        transform.Translate(direction);   
    }
}

 

그리고 지정된 방향으로 5초 간 날아가다 자동으로 소멸하는 총알 프리팹 역시 만들어두었다.

 

 

이것을 테스트 해보면 마우스를 클릭하면 엄청난 양의 총알 오브젝트가 새로 생성되고 시간이 지나면 모두 파괴되는 것을 볼 수 있다.

 

 

이 상황을 프로파일러를 통해서 확인해보면 총알을 생성하는 Instantiate와 파괴하는 Destroy가 어느 정도 성능을 잡아먹고 있는 것을 확인할 수 있다.

 

오브젝트 풀 구현

 

public class ObjectPool : MonoBehaviour
{
    public static ObjectPool Instance;

    [SerializeField]
    private GameObject poolingObjectPrefab;

    Queue<Bullet> poolingObjectQueue = new Queue<Bullet>();

    private void Awake()
    {
        Instance = this;

        Initialize(10);
    }

    private void Initialize(int initCount)
    {
        for(int i = 0; i < initCount; i++)
        {
            poolingObjectQueue.Enqueue(CreateNewObject());
        }
    }

    private Bullet CreateNewObject()
    {
        var newObj = Instantiate(poolingObjectPrefab).GetComponent<Bullet>();
        newObj.gameObject.SetActive(false);
        newObj.transform.SetParent(transform);
        return newObj;
    }

    public static Bullet GetObject()
    {
        if(Instance.poolingObjectQueue.Count > 0)
        {
            var obj = Instance.poolingObjectQueue.Dequeue();
            obj.transform.SetParent(null);
            obj.gameObject.SetActive(true);
            return obj;
        }
        else
        {
            var newObj = Instance.CreateNewObject();
            newObj.gameObject.SetActive(true);
            newObj.transform.SetParent(null);
            return newObj;
        }
    }

    public static void ReturnObject(Bullet obj)
    {
        obj.gameObject.SetActive(false);
        obj.transform.SetParent(Instance.transform);
        Instance.poolingObjectQueue.Enqueue(obj);
    }
}

 

오브젝트 풀링 기법을 구현하는 기초적인 코드의 전체는 위의 코드와 같다. 

 

public static ObjectPool Instance;

private void Awake()
{
    Instance = this;

    Initialize(10);
}

 

이 코드를 하나씩 살펴보자. 우선 오브젝트 풀의 경우 오브젝트를 생성하는 어떤 코드에서든 접근이 가능해야하는 경우가 많기 때문에 싱글톤 패턴으로 구현되는 경우가 많다. 

 

[SerializeField]
private GameObject poolingObjectPrefab;

 

그리고 생성해야할 오브젝트의 프리팹을 가지고 있다. 프리팹을 가지고 있어야 하는 이유는 처음 생성해둔 오브젝트를 모두 꺼내주고 나서, 꺼내준 오브젝트를 미처 돌려받기 전에, 오브젝트를 요청받았을 때, 새로운 오브젝트를 생성해서 꺼내주기 위한 것이다.

 

Queue<Bullet> poolingObjectQueue = new Queue<Bullet>();

 

큐(Queue)를 이용해서 생성된 총알 오브젝트를 순서대로 관리한다. 만약 오브젝트 풀에서 오브젝트를 꺼내주는 순서와 돌려받는 순서를 제대로 관리하지 못하면, 하나의 오브젝트를 여러 곳에 빌려주는 등의 문제가 발생할 수 있다.

 

private void Awake()
{
    Instance = this;

    Initialize(10);
}

 

게임 오브젝트가 생성되면 제일 먼저 실행되는 Awake() 함수에서는 싱글톤 패턴에서 사용되는 오브젝트 풀 인스턴스에 자신을 할당하는 일과 오브젝트 풀을 초기화하는 Initialize() 함수를 호출한다. 

 

private void Initialize(int initCount)
{
    for(int i = 0; i < initCount; i++)
    {
        poolingObjectQueue.Enqueue(CreateNewObject());
    }
}

 

Initialze() 함수에서는 매개변수로 받은 초기화 갯수 값에 따라서 CreateNewObject() 함수에서 만들어진 새로운 총알 오브젝트를 pooling Object Queue에 넣어준다. 이렇게 게임이 시작하기 전에 사용될 게임 오브젝트를 미리 적절한 갯수를 만들어 줌으로써 게임 플레이 도중에 발생할 과부하를 게임 준비 과정인 로딩으로 돌릴 수 있다. 모두 잘 알겠지만, 플레이어는 아주 조금 더 긴 로딩은 당장 용납하지만, 플레이 도중에 발생하는 렉은 용납하지 못한다.

 

private Bullet CreateNewObject()
{
    var newObj = Instantiate(poolingObjectPrefab).GetComponent<Bullet>();
    newObj.gameObject.SetActive(false);
    newObj.transform.SetParent(transform);
    return newObj;
}

 

CreateNewObject() 함수는 poolingObjectPrefab으로부터 새 게임 오브젝트를 만든 뒤 비활성화해서 반환하는 역할을 한다. 이렇게 곧바로 비활성화하는 이유는 아직 오브젝트 풀이 꺼내주지 않은 오브젝트는 비활성화된 것으로써 플레이어의 눈에 띄지 않아야하기 때문이다.

 

public static Bullet GetObject()
{
    if(Instance.poolingObjectQueue.Count > 0)
    {
        var obj = Instance.poolingObjectQueue.Dequeue();
        obj.transform.SetParent(null);
        obj.gameObject.SetActive(true);
        return obj;
    }
    else
    {
        var newObj = Instance.CreateNewObject();
        newObj.gameObject.SetActive(true);
        newObj.transform.SetParent(null);
        return newObj;
    }
}

 

GetObject() 함수는 오브젝트 풀이 가지고 있는 게임 오브젝트를 요청한 자에게 꺼내주는 역할을 한다. 단, 모든 오브젝트를 꺼내서 빌려줘서 poolingObjectQueue에 빌려줄 오브젝트가 없는 상태라면 CreateNewObject() 함수를 호출해서 새로운 오브젝트를 생성해서 빌려준다.

 

public static void ReturnObject(Bullet obj)
{
    obj.gameObject.SetActive(false);
    obj.transform.SetParent(Instance.transform);
    Instance.poolingObjectQueue.Enqueue(obj);
}

 

ReturnObject() 함수는 빌려준 오브젝트를 돌려받는 함수이다. 돌려받은 오브젝트를 비활성화한 뒤 정리하는 일을 처리한다.

 

무엇이든지 빌려줬으면 돌려받아야 한다. 이 원칙은 프로그래밍에서도 매우 중요한 원칙으로 사용한 오브젝트를 제대로 돌려받지 않으면 GetObject() 함수를 호출할 때, 계속해서 새로운 오브젝트를 생성하고 사라지지 않은 오브젝트들이 씬에 쌓이게 된다. 이것이 바로 메모리 누수이다.

 

 

코드 작성이 끝나면 씬에 새 게임 오브젝트를 배치하고 오브젝트 풀 컴포넌트를 부착하고 pooling Object Prefab 프로퍼티에 풀링해야할 게임 오브젝트의 프리팹을 할당해주면 된다.

 

 

 

그 다음에는 총알 오브젝트를 생성하는 부분과 파괴하는 부분을 수정해야 한다.

 

public class Shooter : MonoBehaviour
{
    void Update()
    {
        if(Input.GetMouseButton(0))
        {
            RaycastHit hitResult;
            if(Physics.Raycast(mainCam.ScreenPointToRay(Input.mousePosition), out hitResult))
            {
                var bullet = ObjectPool.GetObject(); // 수정
                var direction = new Vector3(hitResult.point.x, transform.position.y, hitResult.point.z) - transform.position;
                bullet.transform.position = direction.normalized;
                bullet.Shoot(direction.normalized);
            }
        }
    }
}

 

우선 생성하는 부분은 Shooter 클래스의 Update() 함수에 Instanitate()로 총알을 생성하던 부분을 ObjectPool.GetObject()로 대체하면 된다.

 

public class Bullet : MonoBehaviour
{
    private Vector3 direction;
    public void Shoot(Vector3 direction)
    {
        this.direction = direction;
        Invoke("DestroyBullet", 5f);
    }

    public void DestroyBullet()
    {
        ObjectPool.ReturnObject(this);
    }

    void Update()
    {
        transform.Translate(direction);   
    }
}

 

그 다음 파괴하는 부분은 Bullet 클래스에서 DestoryBullet() 함수에서 ObjectPool.ReturnObject()를 호출해서 오브젝트 풀에 돌려주도록 구현하고 Shoot() 함수에서 Destory() 함수를 호출하던 부분을 Invoke()로 5초 뒤에 DestoryBullet() 함수를 호출하도록 수정한다.

 

 

 

플레이를 해서 테스트 해보면 총알이 일정 갯수만큼 생성되고 나면 더 이상 생성되지 않으며, 시간이 지나면 소멸되어야 하는 총알 오브젝트들이 비활성화되서 오브젝트 풀로 반환되는 것을 확인할 수 있다.

 

 

프로파일러를 통해서 확인해보면 최대 생성량에 도달한 이후에는 Instatiate와 Destroy로 인한 작업이 발생하지 않는 것을 볼 수 있다.

 

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

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

 

에셋스토어

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

 

반응형

Physics 

컴포지트 콜라이더 2D(Composite Collider 2D)

 

작성 기준 버전 :: 2019.1.4f1

 

컴포지트 콜라이더 2D 컴포넌트는 다수의 2D 콜라이더를 하나의 콜라이더로 합쳐주는 역할을 하는 컴포넌트이다. 이 컴포넌트는 주로 여러 개의 콜라이더가 하나의 콜라이더로 취급하기 위해서 사용된다.

 

두 콜라이더의 충돌 감지

 

 

그림 1

 

[그림 1]을 보면 원형의 폴리곤 콜라이더를 가진 오브젝트와 사각 콜라이더를 가진 오브젝트가 함께 겹쳐져 있으며 두 오브젝트가 같은 부모 게임 오브젝트 밑에 있는 것을 확인할 수 있다. 위와 같은 상태에서는 두 개의 각 콜라이더가 충돌을 각각 감지한다.

 

public class CollisionTest : MonoBehaviour

{

    private void OnCollisionEnter2D(Collision2D collision)

    {

        Debug.Log(string.Format("OnCollisionEnter2D :: / ", gameObject.name, collision.gameObject.name));

    }

 

    private void OnCollisionStay2D(Collision2D collision)

    {

        Debug.Log(string.Format("OnCollisionStay2D :: / ", gameObject.name, collision.gameObject.name));

    }

 

    private void OnCollisionExit2D(Collision2D collision)

    {

        Debug.Log(string.Format("OnCollisionExit2D :: / ", gameObject.name, collision.gameObject.name));

    }

}

 

위와 같이 충돌을 감지하는 코드를 작성하고,

 

그림 2
그림 3

 

콜라이더가 있는 각 오브젝트에 부착한 뒤,

 

그림 4

 

[그림 4]와 같이 아래로 떨어지는 게임 오브젝트를 만들어서 충돌 테스트를 해보면,

 

그림 5

 

[그림 5]의 로그와 같이 각각의 오브젝트가 따로 충돌을 감지하는 것을 확인할 수 있다.

 

 

컴포지트 콜라이더 2D(Composite Collider 2D)

 

이런 식으로 각각의 콜라이더가 따로 충돌을 감지하는 것이 아니라 두 콜라이더가 하나로 취급되어 충돌을 감지하고자 할 때, 컴포지트 콜라이더 2D를 사용하게 된다.

 

그림 6

 

컴포지트 콜라이더 2D 컴포넌트를 사용하는 방법은 위와 같이 통합하고자하는 콜라이더들의 상위에 컴포지트 콜라이더를 추가해주고,

 

그림 7

 

[그림 7]과 같이 하위에 속하는 콜라이더 컴포넌트들의 Used By Composite 프로퍼티를 true로 설정해주면 된다.

 

그림 8

 

그렇게 하면 상위 오브젝트를 선택했을때, [그림 8]과 같이 하위의 콜라이더들이 하나로 통합되어서 보이는 것을 볼 수 있다.

 

그림 9

 

로그에서도 상위 오브젝트에서의 충돌 체크만 발생하는 것을 알 수 있다.

 

리지드바디 2D(Rigidbody 2D)

 

그림 10

 

컴포지트 콜라이더 2D 컴포넌트를 게임 오브젝트에 부착하면 자동으로 리지드바디 2D 컴포넌트 역시 게임 오브젝트에 부착된다. 때문에 지형지물의 요소에 컴포지트 콜라이더 2D를 사용할 때는 이 리지드바디 2D 컴포넌트의 옵션에 신경써야 한다.

 

그림 11

 

옵션에 신경쓰지 않을 경우, [그림 11]처럼 캐릭터가 지형에 닿자마자 지형이 떨어지거나, 게임이 시작하자마자 지형이 추락해서 지형이 사라지는 모습을 보게 될 수도 있다.

 

그림 12

 

이렇게 컴포지트 콜라이더 2D 컴포넌트를 적용한 게임 오브젝트를 고정된 지형요소로 사용하고자 한다면 리지드바디2D의 바디타입을 Static으로 설정해야한다.

 

그림 13

 

 

 

생성 타입(Generation Type)

 

그림 14

 

컴포지트 콜라이더 2D 컴포넌트에는 생성 타입이라는 프로퍼티가 존재하는데 Synchronous와 Manual 옵션이 존재한다.

 

그림 15

 

Synchronous는 [그림 15]처럼 하위 콜라이더들이 변동되는 때마다 바로 통합된 콜라이더를 새로 만드는 옵션이고, 

 

그림 16

 

Manual은 컴포지트 콜라이더 2D 컴포넌트가 처음 부착되는 시점 호은 콜라이더 재생성(Regenerate Collider) 버튼을 누른 시점에만 콜라이더를 만드는 옵션이다.

 

그림 17

 

[그림 17]과 같이 아래에 있는 노란 원이 애니메이션에 의해서 위치가 바뀌어도 콜라이더는 따라서 움직이지 않기 때문에 빈 공간에 충돌하는 것을 알 수 있다.

 

위의 설명에서 알 수 있듯이 Synchronous 옵션은 통합된 콜라이더가 변경되는 애니메이션에 따라서 지속적으로 업데이트가 되어야하는 경우에 사용하고, 반대로 Manual은 통합된 콜라이더가 일반 지형과 같이 변경되는 경우가 없는 경우에 사용된다.

 

지오메트리 타입(Geometry Type)

 

그림 18

 

그 다음 중요한 옵션은 지오메트리 타입이다. 이 프로퍼티는 콜라이더를 생성할 때 어떤 형태로 생성할 것인가를 결정한다. 옵션 값은 Outlines와 Polygons가 있다. 

 

그림 19
그림 20

 

Outlines로 콜라이더를 생성하면 [그림 19]와 같이 내부에 아무선 선이 없이 생성되고, Polygons로 콜라이더를 생성하면 [그림 20]과 같이 내부에 선이 그어져서 나온다. 이 차이가 의미하는 것은 Outlines 옵션의 경우에는 콜라이더가 외부에 선만 그어져 있고 속은 비어있다는 뜻이고, Polygons 옵션은 내부가 꽉 차있다는 뜻이다.

 

콜라이더의 내부를 굳이 채울 필요가 있냐고 생각할 수도 있지만, 이것은 확실히 필요한 개념이다. 게임 내에서의 발생하는 충돌 감지나 레이캐스트는 주로 옆에서 날아오기 때문에 Outlines 옵션 만으로도 충분하지만 화면 밖에서 들어오는 터치나 사용자의 클릭 같은 이벤트는 개념적으로 위에서 들어오기 때문에 콜라이더의 내부가 차있는 것이 좋다.

 

public class Picker : MonoBehaviour

{

    private void Update()

    {

        if (Input.GetMouseButtonDown(0))

        {

            var hitResult2D = Physics2D.Raycast(Camera.main.ScreenToWorldPoint(Input.mousePosition), transform.up, 0.1f);

            Debug.Log("2D Raycast Result :: " + hitResult2D.collider.name);

        }

    }

}

 

위와 같이 마우스를 클릭한 지점에서 레이캐스트를 발사해서 콜라이더를 검출하는 코드를 작성해서 Outlines 옵션으로 만들어진 컴포지트 콜라이더와 Polygons 옵션으로 만들어진 컴포지트 콜라이더를 클릭하는 테스트를 진행해보면 그 차이를 명확하게 인지할 수 있다.

 

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

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

 

에셋스토어

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

 

반응형

Tilemap (3) 

타일맵에 콜라이더 추가하기

 

작성 기준 버전 :: 2019.1.4f1

 

이전 섹션들에서는 타일맵 생성 방법2D 엑스트라의 룰 타일을 이용한 타일 자동 연결 기능을 알아보았다. 이번 섹션에서는 타일맵에 콜라이더를 추가하는 방법을 배워볼 것이다.

 

이제까지 타일맵에 사용될 타일 이미지를 만들고 가져오는 방법, 타일맵을 만들고 사용하는 방법들을 배웠다. 이제 만들어낸 타일맵을 이용해서 맵을 그려내면 게임 레벨이 될 것이다. 하지만 여기에 아직 부족한 점이 있다.

 

지금까지 배운 것들로는 맵을 그리기만 할 수 있다. 무슨 말인가 하면, 2D RPG 류의 게임에서는 어떤 타일은 벽이 되서 캐릭터가 이동하는 것을 막는 장애물이 되어야 하고, 플랫폼 게임(Platform Game)에서는 타일이 캐릭터가 딪고 설 바닥이 되어주어야 한다. 즉, 타일에 콜라이더를 추가해서 물리적인 작용이 가능하게 만들어야 한다는 뜻이다.

 

그림 1

 

플랫폼 게임을 만드는데 노란 공이 떨어져서 바닥에 닿으면 튕기게 만들고 싶다고 가정해보자.

 

그림 2

 

노란 공에 물리효과를 주기 위해서 Circle Collider 2D 컴포넌트와 Rigidbody 2D 컴포넌트를 부착해주었다. 그리고 꽤 그럴듯하게 공처럼 튀기게 만들기 위해서 물리 머티리얼(Physics Material)까지 넣어주었다.

 

그림 3
그림 4

 

하지만 타일맵에 물리적인 컴포넌트가 아무것도 없는 상태이기 때문에 플레이를 시작하면 떨어지는 공은 타일맵을 그냥 통과해버린다. 

 

그림 5

 

타일맵 콜라이더 2D 컴포넌트(Tilemap Collider 2D Component)

 

이전 섹션을 진행해왔다면 하이어라키 뷰에 존재하는 타일맵은 게임 오브젝트하나로 존재하기 때문에 어떻게 콜라이더를 배치해야할지 난감할 수도 있다.

 

그림 6

 

타일맵을 위한 콜라이더를 유니티에서는 이미 제공하고 있다. 타일맵 콜라이더 2D 컴포넌트(Tilemap Collider 2D Component)가 바로 그것이다.

 

그림 7

 

타일맵 컴포넌트가 붙어있는 게임 오브젝트에 타일맵 콜라이더 2D 컴포넌트를 부착하면 씬에서 위의 이미지와 같이 각 타일마다 콜라이더가 생겼음을 알 수 있다.

 

그림 8

 

타일맵에 콜라이더 컴포넌트를 붙인 상태로 다시 게임을 플레이해보면 떨어진 공이 바닥에 맞고 튕기는 것을 볼 수 있다.

 

 

컴포지트 콜라이더 2D 컴포넌트(Composite Collider 2D Component)

 

[그림 8]을 보면 타일맵 컴포넌트 2D를 이용해서 생성된 콜라이더가 각 타일마다 따로 생성되어 있는 것을 볼 수 있다. 이렇게 분할된 콜라이더는 퍼포먼스 상의 문제와 가끔 이동하는 캐릭터가 콜라이더에 끼어서 움직이지 못하게 되는 등의 문제가 발생할 수 있다.

 

그런 문제를 해결하기 위해서 제공되는 것이 컴포지트 콜라이더 2D 컴포넌트이다. 이 컴포넌트는 해당 컴포넌트가 붙어있는 게임 오브젝트의 하위에 존재하는 콜라이더들을 하나로 묶어주는 역할을 한다.

 

그림 9

 

컴포지트 콜라이더 2D 컴포넌트를 사용하기 위해서는 타일맵 콜라이더 2D 컴포넌트를 부착한 컴포넌트에 컴포지트 콜라이더 2D 컴포넌트를 부착하고 타일맵 콜라이더 2D 컴포넌트의 Used By Composite 프로퍼티를 체크해주면 된다.

 

그림 10

 

그렇게 하고 나서 씬 뷰에서 타일맵 게임 오브젝트를 선택해보면 초록색 콜라이더 박스가 타일마다 나누어지지 않고 하나로 합쳐져 있는 것을 확인할 수 있다.

 

그림 11

 

하지만 아직 설정이 다 끝나지 않았다. 플레이를 눌러보면 타일맵이 공과 함께 떨어지는 어이없는 상황이 발생한다. [그림 9]를 보면 그 이유를 조금 짐작할 수 있는데 컴포지트 콜라이더 2D 컴포넌트를 추가할 때, 리지드바디 2D 컴포넌트(Rigidbody 2D 컴포넌트)가 자동으로 추가된 것을 알 수 있는데, 리지드바디 컴포넌트는 게임 오브젝트가 외부의 힘이나 토크를 받아 사실적인 물리적인 운동을 보이도록 도와주는 컴포넌트이다.

 

그림 12

 

자동으로 추가된 리지드바디 2D 컴포넌트를 보면 바디 타입(Body Type)이 다이나믹(Dynamic)으로 설정있는 것을 알 수 있다. 즉 타일맵의 리지드바디가 고정된 것이 아니기 때문에 공과 함께 떨어지는 것이다.

 

그림 13
그림 14

 

바디 타입을 고정(Static)으로 변경하고 실행해보면 [그림 14]와 같이 타일맵이 떨어지지 않고 정상적으로 동작하는 것을 확인할 수 있다.

 

다만 컴포지트 콜라이더 2D를 사용하는 경우에 주의할 점은 하위에 있는 모든 콜라이더를 하나로 통합하기 때문에, 플랫폼 게임을 만들 때 벽 타일의 콜라이더와 바닥 타일의 콜라이더가 플레이어와 충돌 시 다른 동작을 하게 만들고 싶다면 벽 타일의 타일맵과 바닥 타일의 타일맵을 분리하거나, 캐릭터가 충돌한 방향을 검출해서 벽인지 바닥인지를 검출하는 등의 추가 작업이 필요하다.  

 


 

Tilemap (1) - 2D 게임의 기본 타일맵!

Tilemap (2) - 룰 타일로 타일맵 자동 연결하기

Tilemap (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

 

반응형

+ Recent posts