Programming 

클릭한 위치로 캐릭터를 이동시키는 기능 구현하기

 

작성 기준 버전 :: 2019.2

 

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

 

게임의 조작 방식에는 굉장히 다양한 방법이 있다. 그 중에서 쿼터 뷰 형식의 RPG 게임이나 AOS 장르에서는 맵의 지형을 클릭하고 그 클릭한 위치로 캐릭터를 이동시키는 조작 방식을 사용한다.

 

쿼터 뷰 RPG나 AOS 장르처럼 클릭한 위치로 캐릭터를 이동시키는 기능을 만드는 방법을 알아보자.

 

클릭한 위치로 캐릭터 이동시키기

 

public class ClickMovement : MonoBehaviour
{
    private Camera camera;

    private bool isMove;
    private Vector3 destination;

    private void Awake()
    {
        camera = Camera.main;
    }
}

 

클릭한 위치로 캐릭터를 이동시키는 기능을 구현하기 위해서 먼저 위와 같은 변수들을 선언해준다.

 

Camera는 스크린 좌표계로 표시되는 마우스의 위치를 월드 좌표계로 표시하기 위해서 선언했다. 그리고 isMove는 캐릭터가 움직이고 있는 중인지 판정하기 위해서, destination은 캐릭터가 목표 위치를 기억하기 위해서 선언했다.

 

그리고 Awake 함수에서 씬에 존재하는 메인 카메라를 가져와서 camera 매개변수에 저장해준다.

 

private void SetDestination(Vector3 dest)
{
    destination = dest;
    isMove = true;
}

 

SetDestination 함수를 만들어서 그 안에 매개변수로 받아온 dest를 destination에 넣어주고 isMove를 true로 만들어 주는 코드를 작성한다. SetDestination 함수는 클릭으로 찾아낸 마우스의 위치를 저장해두고 이동을 시작하게 하는 역할을 한다.

 

private void Move()
{
    if (isMove)
    {
        if (Vector3.Distance(destination, transform.position) <= 0.1f)
        {
            isMove = false;
            return;
        }
        var dir = destination - transform.position;
        transform.position += dir.normalized * Time.deltaTime * 5f;
    }
}

 

Move 함수를 만든다. Move 함수에서는 isMove가 true일 때, 즉 SetDestination에서 목적지를 지정한 뒤에 목적지와 캐릭터의 거리가 0.1 유니티 미터보다 작으면 이동을 중지하고, 아직 도착하지 못한 상태라면 캐릭터의 위치에서 목적지로 향하는 방향을 구해서 캐릭터를 이동시키는 작업을 처리한다.

 

void Update()
{
    if (Input.GetMouseButton(1))
    {
        RaycastHit hit;
        if (Physics.Raycast(camera.ScreenPointToRay(Input.mousePosition), out hit))
        {
            SetDestination(hit.point);
        }
    }

    Move();
}

 

SetDestination 함수와 Move 함수를 모두 작성하면 Update 함수로 가서 마우스 오른쪽 버튼을 클릭했을 때 마우스 커서의 위치를 찾아내는 코드를 작성한다.

 

public class ClickMovement : MonoBehaviour
{
    private Camera camera;

    private bool isMove;
    private Vector3 destination;

    private void Awake()
    {
        camera = Camera.main;
    }

    void Update()
    {
        if (Input.GetMouseButton(1))
        {
            RaycastHit hit;
            if (Physics.Raycast(camera.ScreenPointToRay(Input.mousePosition), out hit))
            {
                SetDestination(hit.point);
            }
        }

        Move();
    }

    private void SetDestination(Vector3 dest)
    {
        destination = dest;
        isMove = true;
    }

    private void Move()
    {
        if (isMove)
        {
            if (Vector3.Distance(destination, transform.position) <= 0.1f)
            {
                isMove = false;
                return;
            }

            var dir = destination - transform.position;
            transform.position += dir.normalized * Time.deltaTime * 5f;
        }
    }
}

 

코드의 전문은 위와 같다.

 

 

이 컴포넌트를 캐릭터에 부착하고 콜라이더가 있는 맵에 우클릭하면 클릭한 위치로 캐릭터가 이동하는 것을 볼 수 있다.

 

기능 개선 1 : 카메라가 캐릭터 따라가게 하기

 

하지만 몇 가지 개선해야할 점이 보인다. 우선 카메라가 캐릭터를 따라가지 않아서 위쪽을 클릭하면 캐릭터가 화면 밖을 벗어나버리는 것을 볼 수 있다.

 

 

카메라가 캐릭터를 따라가게 하기 위해서 카메라를 캐릭터의 자식 오브젝트로 만들어보자.

 

 

다시 게임을 플레이시키고 우클릭으로 캐릭터를 이동시켜보면 카메라가 캐릭터를 따라서 움직이는 것을 확인할 수 있다.

 

기능 개선 2 : 캐릭터가 이동할 방향을 바라보게 하기

 

두 번째로 개선할 점은 캐릭터가 이동할 방향을 바라보게 하는 것이다. 지금은 캐릭터를 이동시켜도 게임을 시작할 때 바라보고 있던 방향만을 바라보면서 이상하게 이동하는 것을 볼 수 있다.

 

private void Move()
{
    if (isMove)
    {
        if (Vector3.Distance(destination, transform.position) <= 0.1f)
        {
            isMove = false;
            return;
        }
        var dir = destination - transform.position;
        transform.foward = dir;	// 추가
        transform.position += dir.normalized * Time.deltaTime * 5f;
    }
}

 

Move 함수를 보면 캐릭터가 이동할 방향을 구해둔 부분이 있다. 이 방향을 캐릭터의 transform.forward에 넣어주면 이동하는 방향을 바라보게 할 수 있다.

 

 

그리고 다시 게임을 플레이시키고 이동할 지점을 우클릭 해보면 우클릭한 지점을 캐릭터가 바라보면서 이동하기는 한다. 하지만 카메라가 캐릭터의 자식 오브젝트로 되어있어서 캐릭터의 방향이 바뀔 때 카메라도 함께 돌아가서 굉장히 어지럽다.

 

 

약간 구조를 바꿔야 될 것 같다. 우선 Character Root라는 이름으로 빈 게임 오브젝트를 만든다. 그리고 BoxMan을 그 아래로 옮기고 BoxMan 밑에 있는 카메라 역시 Charactor Root 아래로 옮겨준다.

 

이렇게 구조를 만들면 캐릭터를 이동시킬 때 카메라가 캐릭터가 따라서 움직이는 것을 유지하면서 BoxMan을 정상적으로 회전시킬 수 있다.

 

구조는 완성되었으니 본격적으로 기능을 수정해보자.

 

 

우선 BoxMan에 붙어있는 ClickMovement를 Character Root로 옮겨서 이동하는 주체를 Character Root로 바꿔준다.

 

[SerializeField]
private Transform character;	// 추가

private void Move()
{
    if (isMove)
    {
        if (Vector3.Distance(destination, transform.position) <= 0.1f)
        {
            isMove = false;
            return;
        }
        var dir = destination - transform.position;
        character.transform.foward = dir;	// 변경
        transform.position += dir.normalized * Time.deltaTime * 5f;
    }
}

 

캐릭터 몸체 만을 회전시키기 위해서 Transform 변수를 선언하고 회전 주체를 transform에서 character.transform으로 바꿔준다.

 

 

코드를 모두 작성하면 유니티 에디터로 돌아와서 비어있는 Character 프로퍼티에 회전해야 하는 캐릭터 게임 오브젝트를 넣어준다.

 

 

그 다음 플레이시켜보면 카메라는 회전하지 않고 BoxMan이 우클릭한 방향을 바라보며 이동하는 것을 볼 수 있다.

 

이제 기능이 완성된 것처럼 보인다.

 

 

하지만 여기에 장애물이 추가되면 어떨까?

 

 

Character Root에는 장애물과 충돌할 수 있게 Capsule Collider와 Rigidbody를 추가한다. 그리고 장애물과 충돌했을 때 캐릭터가 넘어지지 않도록 Constraints의 Freeze Rotation을 전부 체크해준다.

 

 

다시 게임을 플레이시키고 캐릭터를 벽 너머로 이동시키기 위해서 벽 너머를 우클릭하면 돌아서 이동하지 못하고 벽에 계속 부딪히기만 하는 것을 볼 수 있다. 게다가 벽 위를 클릭하면 벽 위에 올라서기 위해서 점프까지 하는 것을 볼 수 있다.

 

기능 개선 3 : 길찾기 기능 추가하기

 

꽤 재밌어보이는 기능이 완성됐지만 우리의 목적은 캐릭터가 장애물을 회피해서 이동하는 것이기 때문에 다시 개선하는 작업에 들어가보자.

 

일단 Character Root에 붙어있는 Capsule Collider의 Is Trigger를 체크해준다.

 

일반적인 RPG 게임이나 AOS 게임에서는 장애물 건너편으로 이동하기 위해서 조작을 하면 캐릭터가 자동으로 이동할 수 있는 길을 찾아서 이동하는 것을 볼 수 있다.

 

캐릭터가 장애물을 돌아서 이동하기 위해서는 길찾기 기능이 필요하다. 길찾기 알고리즘을 직접 구현할 수도 있지만, 유니티 엔진에서 제공하는 내비게이션 기능을 사용하면 훨씬 간단하게 구현할 수 있다.

 

 

우선 상단 메뉴 바에서 [Window > AI > Navigation]을 선택해서 내비게이션 뷰를 연다. 그리고 장애물과 바닥을 선택하고 내비게이션 뷰에서 Navigation Static을 체크하고 내비게이션 메시를 구워준다.

 

 

그러면 캐릭터가 이동할 수 있는 영역이 파란색으로 표시된다.

 

 

그 다음에는 캐릭터를 이 내비게이션 메시 위에서 길을 찾아 움직일 수 있는 에이전트로 만들어줘야 한다. Character Root에 NavMeshAgent 컴포넌트를 붙여준다. 

 

using UnityEngine.AI;  // 추가

public class ClickMovement : MonoBehaviour
{
    private Camera camera;
    private NavMeshAgent agent;  // 추가

    private bool isMove;
    private Vector3 destination;

    private void Awake()
    {
        camera = Camera.main;
        agent = GetComponent<NavMeshAgent>();  // 추가
        agent.updateRotation = false;  // 추가
    }

    void Update()
    {
        if (Input.GetMouseButton(1))
        {
            RaycastHit hit;
            if (Physics.Raycast(camera.ScreenPointToRay(Input.mousePosition), out hit))
            {
                SetDestination(hit.point);
            }
        }

        // Move();
        LookMoveDirection();  // 변경
    }

    private void SetDestination(Vector3 dest)
    {
        agent.SetDestination(dest);  //추가
        destination = dest;
        isMove = true;
    }

    // private void Move()
    private void LookMoveDirection()  // 변경
    {
        if (isMove)
        {
            // if (Vector3.Distance(destination, transform.position) <= 0.1f)
            if (agent.velocity.magnitude == 0.0f)  // 변경
            {
                isMove = false;
                return;
            }

            // var dir = destination - transform.position;
            var dir = new Vector3(agent.steeringTarget.x, transform.position.y, agent.steeringTarget.z) - transform.position;  // 변경
            character.transform.foward = dir;
        }
    }
}

 

그리고 스크립트 에디터로 돌아가서 AI 네임스페이스를 추가해주고 NavMeshAgent 변수를 만든다. 그 다음 Awake 함수에서 게임 오브젝트에 붙어있는 NavMeshAgent 컴포넌트를 가져와서 넣어준다.

 

컴포넌트를 가져온 다음에는 agent가 Character Root를 회전시키지 않도록 updateRotation을 false로 바꿔줍니다. 그리고 SetDestination 함수에서는 매개변수로 받아온 목표 위치를 agent.SetDestination 함수에 넣어준다.

 

이렇게 하면 NavMeshAgent가 자동으로 길을 찾아서 캐릭터를 이동하도록 만들어준다.

 

그러면 Move 함수의 원래 목적은 사라지는데, 대신 Move 함수는 BoxMan이 이동 방향을 쳐다보게 하는 기능을 해야한다.

 

변경되는 기능에 따라 함수의 이름은 LookMoveDirection으로 바꿔주면 좋다. 원래 코드는 BoxMan이 destination을 쳐다보게 되어있는데 이렇게 하면 캐릭터가 길을 돌아서 이동하는 와중에도 목적지만 쳐다봐서 부자연스럽게 움직이게 된다.

 

캐릭터는 목적지 대신에 자신이 이동하는 방향을 쳐다봐야 한다. direction을 구할 때 destination 대신 agent.steeringTarget을 사용해야 한다. 다만 아까 전에 캐릭터와 높이가 다른 목적지를 클릭 했을 때 캐릭터가 기울어졌던 것을 기억할 것이다. 캐릭터가 높이가 다른 곳을 쳐다보면서 기울어지는 것을 막기위해서 캐릭터가 agent의 steeringTarget 방향을 바라보되 높이는 캐릭터 자신의 높이를 바라보게 해야 한다. 그리고 도착 판정을 거리 대신에 agent의 속도로 변경하여 목적지에 도착한 경우면 함수를 종료하도록 만들어 준다.

 

이렇게 하면 캐릭터가 목적지에 도착한 다음 엉뚱한 방향을 바라보지 않게 될 것이다.

 

 

게임을 플레이시키고 우클릭으로 캐릭터를 이동시켜보면 잘 동작하는 것을 볼 수 있다. 벽 건너편을 클릭해도 제대로 돌아서 이동하고 벽 위를 클릭해도 캐릭터가 기울어지며 올라가려는 시도를 하지 않는 것을 알 수 있다.

 

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

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

 

에셋스토어

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

 

반응형

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

 

반응형

RPG :: 마우스 입력 이동 구현하기

 

영상 기준 버전 : 4.27

 

작성 기준 버전 :: 4.21

 

이전 섹션에서는 C++ 내려보기 템플릿을 참고해서 일반적인 RPG처럼 마우스 클릭을 통해 캐릭터를 이동시키는 방법을 구현해보고 코드를 분석해본다.

 

프로젝트 세팅

 

RpgProject 라는 이름으로 새 프로젝트를 하나 만든다.

 

 

 

RpgProject를 생성한 뒤에는 내려보기 템플릿으로도 새 프로젝트를 하나 만드는데, 이것은 캐릭터의 메시와 애니메이션을 가져오기 위함이다.

 

제일 먼저 할 일은 내려보기 프로젝트에서 캐릭터의 메시와 애니메이션을 가져올 것이다. 방금 만든 내려보기 프로젝트의 콘텐츠 패널의 콘텐츠 폴더 하위에 Mannequin 폴더가 보일 것이다. 이 안에 캐릭터의 메시와 애니메이션이 들어있다. 이 폴더의 내용물들을 RpgProject로 옮겨야 한다. 이렇게 프로젝트에 포함된 애셋들을 다른 프로젝트로 옮기는 작업을 이주(Migrate)라고 한다. 직접 파일을 옮기지 않아도 언리얼 엔진에서는 이것을 도와주는 기능을 제공한다.

 

 

Mannequin 폴더에 우클릭을 하고, 이주... 를 선택한다. 애셋 리포트 창이 뜨면 리스트를 체크하고 확인 버튼을 누른다.

 

 

그 다음 대상 콘텐츠 폴더 선택 대화상자가 열리면 RpgProject 프로젝트의 Content 폴더를 찾아서 폴더 선택을 한다.

 

 

이주 작업이 끝난 뒤에 RpgProject로 가서 콘텐츠 브라우저 패널을 확인하면 내려보기 프로젝트에 있던 Mannequin 폴더와 그 안의 애셋들이 RpgProject에 성공적으로 옮겨진 것을 확인할 수 있다.

 

 

캐릭터의 메시와 애니메이션을 모두 이주시켰으면 그 다음은, 비주얼 스튜티오를 열고 솔루션 탐색기에서 RpgProject.Build.cs를 찾아서 소스파일을 연다.

 

 

Build.cs에서는 게임을 개발하면서 사용할 모듈을 추가하거나 뺄 수 있는데, 여기서는 두 가지 모듈을 추가할 것이다. 아래의 예시 코드와 같이 PublicDependencyModuleNames에 "NavigationSystem"과 "AIModule"을 추가해주자. NavigationSystem 모듈은 네비게이션 메시와 관련된 기능에 도움을 주는 모듈이고 AIModule 은 이름 그대로 AI 기능에 관련된 모듈이다. 클릭된 위치로 캐릭터를 이동시킬 때, 처리하는 코드를 일일이 만드는 대신, 이 두 모듈의 기능의 도움으로 내비게이션된 경로를 따라서 움직이도록 만들 예정이다.

 

PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "NavigationSystem", "AIModule" });

 

 

맵 세팅하기

 

프로젝트에 대한 세팅이 끝났다면, 캐릭터가 움직일 맵을 구성해보자.

 

맵을 세팅하기 이전에 맵에 배치할 기둥이나 벽을 대신할 메시 파일을 다운받는다.

 

Box.zip
다운로드

 

그리고 콘텐츠 브라우저 패널에서 Props 폴더를 생성하고 그 폴더에 방금 다운받은 Box.fbx를 임포트(Import)한다.

 

 

그 다음 RpgProject의 레벨 에디터를 보면 빈 평면만 있는 것을 볼 수 있다. 여기에 방금 전에 받은 박스를 이용해서 유닛의 이동을 방해할 수 있게 적절하게 배치해주도록 하자. 아래의 예시와 같이 배치하여도 되고 원하는 대로 편하게 배치해도 된다.

 

 

맵에 장애물들을 모두 배치했다면, 모드 패널의 볼륨에서 내비 메시 바운드 볼륨을 선택해서 레벨의 정중앙에 배치한다. 이 내비 매시 바운드 볼륨은 볼륨의 영역 내에 있는 오브젝트들을 찾아서 계산한 뒤 이동 경로를 찾아줄 내비 메시를 만들어내는 역할을 한다.

 

 

내비 메시 바운드 볼륨을 맵 중앙에 배치한 뒤, 이것의 스케일을 맵을 충분히 덮을 만큼 키워준 다음, P[각주:1]를 눌러보면 캐릭터가 이동할 수 있는 범위가 초록색으로 표시되는 것을 볼 수 있다.

 

 

모든 맵 세팅을 마쳤다면 콘텐츠 브라우저에서 Maps 폴더를 만들고 Ctrl + S를 눌러서 Maps 폴더에 지금 만든 맵을 RpgTestMap 이라는 이름으로 저장한다.

 

 

맵을 저장한 뒤에는 프로젝트 세팅 창을 열고 맵 & 모드에서 Editor Startup Map을 방금 저장한 RpgTestMap으로 설정해준다. 이렇게 하면 다음에 프로젝트를 열었을 때, 지정한 멥이 제일 먼저 열릴 것이다.

 

 

 

Player Controller로 마우스 입력 받기

 

이 다음 작업은 플레이어 컨트롤러로 클릭 입력을 받아서 컨트롤러가 소유한 폰을 클릭한 위치로 이동시키는 코드를 작성한다.

 

코드 작성 이전에 프로젝트 세팅 창의 입력에서 다음과 같이 입력 매핑을 세팅해준다. 이번 섹션에서는 클릭 지점으로 캐릭터를 이동시키는 것만을 목표로 할 것이기 때문에, 입력 매핑은 InputClick을 왼쪽 마우스 버튼으로 하는 것으로 충분하다.

 

 

입력 환경설정을 마쳤다면, 콘텐츠 브라우저 패널에서 신규 추가 버튼을 눌러서 새 C++ 클래스를 추가한다. 부모 클래스로는 Player Controller를 선택하고 다음 버튼을 누른다.

 

 

클래스의 이름은 RpgPlayerController로 하고 클래스 생성 버튼을 누른다.

 

 

클래스가 생성되고 비주얼 스튜디오가 열리면, 우선 다음 코드를 추가해서 생성자를 선언한다.

 

public:
    ARpgPlayerController();

 

RpgPlayerController.cpp에 생성자를 구현한다.

 

ARpgPlayerController::ARpgPlayerController()
{
    bShowMouseCursor = true;
}

 

bShowMouseCursor 프로퍼티를 true로 설정하면 게임 내에서 마우스 커서가 보이도록 만들어준다. 우리는 마우스로 캐릭터를 이동시킬 계획이기 때문에 반드시 필요한 코드이다.

 

RpgPlayerController.h에 다음 변수를 추가한다.

 

protected:
    bool bClickMouse;

 

이 변수는 왼쪽 마우스 버튼을 누를 때 true가 되고, 뗄 때 false가 될 것이다.

 

그 아래에 다음 함수들을 선언을 추가한다.

 

void InputClickPressed();

void InputClickReleased();

 

이 함수들은 입력 매핑과 바인딩되어서 입력을 받으면 bClickMouse 변수의 값을 바꿔주는 역할을 한다. RpgPlayerController.cpp에 위 두 함수를 구현한다.

 

void ARpgPlayerController::InputClickPressed()
{
    bClickMouse = true;
}

void ARpgPlayerController::InputClickReleased()
{
    bClickMouse = false;
}

 

SetupInputComponent() 함수를 덮어씌워서 구현할 차례다. 헤더에 다음 선언을 추가한다.

 

virtual void SetupInputComponent() override;

 

다시 RpgPlayerController.cpp로 가서 SetupInputComponent() 함수를 구현한다.

 

void ARpgPlayerController::SetupInputComponent()
{
    Super::SetupInputComponent();

    InputComponent->BindAction("InputClick", IE_Pressed, this, &ARpgPlayerController::InputClickPressed);
    InputComponent->BindAction("InputClick", IE_Released, this, &ARpgPlayerController::InputClickReleased);
}

 

왼쪽 마우스 버튼을 누르면 InputClickPressed() 함수가 호출되서 bClickMouse 변수가 true가 되고, 왼쪽 마우스 버튼을 떼면 InputClickReleased() 함수가 호출되서 bClickMouse 변수가 false가 되도록 세팅되었다.

 

이 다음 작업은 마우스를 클릭하면 클릭한 위치로 캐릭터를 이동시키는 코드를 작성하는 작업이다.

 

먼저 RpgPlayerController.h에 다음 함수를 정의한다.

 

void SetNewDestination(const FVector DestLocation);

 

이 함수의 역할은 새로운 목표 위치를 받아서 컨트롤러가 소유한 폰을 그 위치로 이동시키는 역할을 할 것이다.

 

SetNewDestination() 함수에서 내비 메시 위에서 움직이기 위한 작업을 처리하기 위해 다음 전처리기를 추가한다.

 

#include "Blueprint/AIBlueprintHelperLibrary.h"

 

이제 RpgPlayerController.cpp에 SetNewDestination() 함수를 구현해보자.

 

void ARpgPlayerController::SetNewDestination(const FVector DestLocation)
{
    APawn* const MyPawn = GetPawn();
    if (MyPawn)
    {
        float const Distance = FVector::Dist(DestLocation, MyPawn->GetActorLocation());

        if (Distance > 120.0f)
        {
            UAIBlueprintHelperLibrary::SimpleMoveToLocation(this, DestLocation);
        }
    }
}

 

이 함수에서는 우선 컨트롤러가 소유하고 있는 폰을 가져와서 폰과 목적지 사이의 거리를 측정해서, 그 거리가 120 언리얼 유닛보다 크면 폰을 목적지로 이동시킨다. UAIBlueprintHelperLibrary클래스의 SimpleMoveToLocation() 함수는 프로그래머가 목적지로 폰을 이동시키기 위한 처리를 하는 모든 코드를 일일이 작성하는 대신에 간단한 함수 호출로 그 모든 일을 할 수 있도록 도와준다. 아까 전 프로젝트 세팅 단계에 모듈을 추가한 것은 이 기능을 사용하기 위해서 였다.

 

헤더 파일로 돌아가서 다음 함수를 정의한다.

 

void MoveToMouseCursor();

 

그리고 cpp파일에 MoveToMouseCursor() 함수를 구현한다.

 

void ARpgPlayerController::MoveToMouseCursor()
{
    FHitResult Hit;
    GetHitResultUnderCursor(ECC_Visibility, false, Hit);

    if (Hit.bBlockingHit)
    {
        SetNewDestination(Hit.ImpactPoint);
    }
}

 

MoveToMouseCursor() 함수는 GetHitResultUnderCursor() 함수를 통해 마우스 커서 아래에 레이 트레이스를 쏴서 그 위치를 SetNewDestination() 함수에 전달하는 역할을 한다.

 

PlayerTick() 함수를 덮어쓸 차례이다. 헤더에 다음 함수 선언을 추가한다.

 

virtual void PlayerTick(float DeltaTime) override;

 

Cpp 파일에 함수 구현 코드를 추가한다.

 

void ARpgPlayerController::PlayerTick(float DeltaTime)
{
    Super::PlayerTick(DeltaTime);

    if (bClickMouse)
    {
        MoveToMouseCursor();
    }
}

 

코드 추가가 끝났다면 솔루션 탐색기에서 RpgProject를 우클릭 해서 빌드하고 에디터로 돌아간다.

 

 

캐릭터 구현

 

캐릭터가 움직일 맵과 캐릭터를 컨트롤할 플레이어 컨트롤러를 모두 만들었으니 이제 맵 위에서 움직일 캐릭터를 만들 차례이다.

 

콘텐츠 브라우저 패널에서 신규 추가 버튼을 누르고 새 C++ 클래스를 추가한다. 부모 클래스로는 Character 클래스를 선택한다.

 

 

클래스의 이름은 RpgCharacter로 한다.

 

 

RpgCharacter 클래스가 생성되면 RpgCharacter.h로 가서 다음 변수들을 추가한다.

 

private:
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
        class UCameraComponent* RpgCameraComponent;

    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
        class USpringArmComponent* RpgCameraSpringArmComponent;

 

이 변수들은 카메라의 위치를 Rpg 게임에 알맞은 위치로 맞춰주는 역할을 할 것이다. 카메라 컴포너트와 스프링 암 컴포넌트를 초기화 시켜주기 전에, 필요한 컴포넌트들을 사용하기 위한 헤더들을 포함시키는 전처리기들을 RpgCharacter.cpp에 추가해주자.

 

#include "Engine/Classes/Components/CapsuleComponent.h"
#include "Engine/Classes/Camera/CameraComponent.h"

#include "Engine/Classes/GameFramework/CharacterMovementComponent.h"
#include "Engine/Classes/GameFramework/SpringArmComponent.h"

 

그리고 ARpgCharacter::ARpgCharacter() 생성자 함수로 가서 다음 코드들을 차례로 추가한다.

 

GetCapsuleComponent()->InitCapsuleSize(42.0f, 96.0f);

 

이 코드는 캐릭터 클래스가 기본적으로 가지고 있는 캡슐 콜라이더의 크기를 초기화한다.

 

bUseControllerRotationPitch = false;
bUseControllerRotationYaw = false;
bUseControllerRotationRoll = false;

 

이 코드는 캐릭터가 카메라의 회전을 따라서 회전하지 않도록 한다.

 

GetCharacterMovement()->bOrientRotationToMovement = true;
GetCharacterMovement()->RotationRate = FRotator(0.0f, 640.0f, 0.0f);
GetCharacterMovement()->bConstrainToPlane = true;
GetCharacterMovement()->bSnapToPlaneAtStart = true;

 

이 코드는 캐릭터의 무브먼트를 규정하는 코드로, 캐릭터를 이동시키기 전에 이동 방향과 현재 캐릭터의 방향이 다르면 캐릭터를 이동 방향으로 초당 640도의 회전 속도로 회전시킨다음 이동시킨다. 그리고 캐릭터의 이동을 평면으로 제한하고, 시작할 때 캐릭터의 위치가 평면을 벗어난 상태라면 가까운 평면으로 붙여서 시작되도록 한다. 여기서 평면이란 내비게이션 메시를 의미한다.

 

RpgCameraSpringArmComponent = CreateDefaultSubobject<USpringArmComponent>(TEXT("RpgCameraSpringArm"));
RpgCameraSpringArmComponent->SetupAttachment(RootComponent);
RpgCameraSpringArmComponent->SetUsingAbsoluteRotation(false);
RpgCameraSpringArmComponent->TargetArmLength = 800.0f;
RpgCameraSpringArmComponent->RelativeRotation = FRotator(-60.0f, 45.0f, 0.0f);
RpgCameraSpringArmComponent->bDoCollisionTest = false;

 

이 코드는 카메라를 캐릭터에게서 적절한 위지를 잡도록 도와주는 스프링 암 컴포넌트를 생성하고 설정한다.

 

bAbsoluteRotation은 스프링 암의 회전이 루트 컴포넌트와 상위 컴포넌트를 따르지 않고 월드 좌표계의 회전을 따르도록 한다.

 

TargetArmLength는 카메라와 캐릭터의 거리를 800으로 설정하고 ReleativeRotation은 스프링 암을 회전시켜 위에서 캐릭터를 내려다보도록 설정한다.

 

bDoCollisionTest는 카메라가 벽에 닿으면 충돌 계산을 통해 카메라와 캐릭터의 거리를 좁혀 카메라가 벽을 뚫지 않게 만들어주는 프로퍼티이지만, Rpg게임에서는 사용되지 않는 옵션이기 때문에 false로 설정한다.

 

RpgCameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("RpgCamera"));
RpgCameraComponent->SetupAttachment(RpgCameraSpringArmComponent, USpringArmComponent::SocketName);
RpgCameraComponent->bUsePawnControlRotation = false;

 

이 코드는 카메라 컴포넌트를 생성하고 스프링 암 컴포넌트에 붙이는 작업을 한다.

 

PrimaryActorTick.bCanEverTick = true;
PrimaryActorTick.bStartWithTickEnabled = true;

 

그리고 마지막으로 틱 함수가 동작하도록 설정한다.

 

모든 코드 작업이 끝났다면 솔루션 탐색기에서 RpgProject를 우클릭해서 프로젝트를 빌드하고 에디터로 돌아간다.

 

에디터로 돌아왔다면, 콘텐츠 브라우저 패널에서 Bluprints 폴더를 생성한 뒤, RpgCharacter 클래스를 찾아서 우클릭하고 RpgCharacter 기반 블루프린트 클래스 생성을 선택한다.

 

 

 

 

그리고 Blueprints 폴더에 BP_RpgCharacter라는 이름으로 블루프린트 클래스를 생성한다.

 

 

블루프린트가 생성되면, 생성된 블루프린트 클래스를 더블클릭해서 블루프린트 에디터를 열고 컴포넌트 패널에서 Mesh 컴포넌트를 선택한다.

 

 

그리고 디테일 패널에서 Mesh 카테고리를 찾아 Skeletal Mesh 프로퍼티를 내려보기 프로젝트에서 이주시킨 SK_Mannequin으로 설정한다.

 

 

애니메이션 역시 내려보기 프로젝트에서 가져온 ThirdPerson_AnimBP로 설정한다.

 

 

위 작업을 하고 나서 블루프린트 에디터의 뷰포트 패널을 보면 캐릭터의 메시가 캡슐 콜라이더를 벗어나고 방향 역시 다르게 되어 있을 것이다.

 

 

이를 일치시키기 위해서 메시 컴포넌트의 위치를 {0.0, 0.0, -90.0}으로 회전을 {0.0, 0.0, -90.0}으로 수정해주자.

 

 

세팅이 모두 끝났다면 블루프린트 클래스를 컴파일하고 저장한다.

 

 

게임 모드 설정

 

플레이어 컨트롤러와 캐릭터의 설정이 모두 끝났으니, 이제 게임이 우리가 만든 플레이어 컨트롤러와 캐릭터를 사용하도록 할 차례이다.

 

콘텐츠 브라우저 패널에서 RpgProjectGameModeBase 클래스를 찾아서 우클릭하여 RpgProjectGameModeBase 기반 블루프린트를 생성한다.

 

 

BP_RpgProjectGameModeBase라는 이름으로 Blueprints 폴더에 블루프린트 클래스를 생성한다.

 

 

게임 모드 블루프린트가 생성되면 더블클릭하여 블루프린트 에디터를 열고, 디테일 패널에서 Player Controller Class를 RpgPlayerController로, Default Pawn Class를 BP_RpgCharacter로 설정한다. 그리고 블루프린트를 컴파일하고 저장한 뒤 블루프린트 에디터를 닫는다.

 

 

레벨 에디터 상단 메뉴바에서 세팅>월드 세팅을 선택하면 월드 세팅 패널이 열린다.

 

 

월드 세팅 패널에서 Game Mode Override를 방금 만든 BP_RpgProjectGameMode로 설정한다.

 

 

모든 과정을 마친 뒤 레벨 에디터에서 플레이 버튼을 눌러서 PIE 모드로 들어가면 캐릭터가 마우스 클릭 지점으로 이동하고, 그 과정에서 적절하게 장애물을 회피하는 것을 볼 수 있다.

 

 

  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

 

반응형

잘못된 애니메이션 리소스로 인해서 발생하는 버그

 

게임 제작에서 일반적으로 버그라고 하면, 프로그래머의 전유물 되는 경우가 많다. 물론 대다수의 게임 버그는 프로그래머의 실수나 설계 미스로 인해서 발생하는 것이지만, 잘못된 리소스로 인해서 발생하는 경우도 꽤나 있다. 대표적인 리소스로 인한 버그를 예로 들자면 3D 모델을 3ds 맥스나 마야같은 3D 툴에서 작업할 때, 실수로 폴리곤을 뒤집어서 내보내는 것과 같은 것이 있다. 이렇게 폴리곤이 뒤집힌 모델은 게임 엔진에 올릴 경우, 해당 부위가 뻥 뚫려서 보이는 문제가 발생한다. 이렇게 눈에 바로 보이는 버그라면 다행이지만 눈에 보이지 않는 리소스로 인한 버그라면 찾아내기가 매우 어려울 것이다.

 

찾아내기 어려운 리소스 버그 중에 하나가 바로 잘못된 애니메이션 리소스로 인해서 발생하게 되는 버그이다. 단지 애니메이션이 이상하게 움직이는 버그라면 상관이 없지만, 애니메이션은 정상적으로 동작하지만 어느 순간부터인가 캐릭터의 로테이션이 돌아가있다던지 정상적인 위치를 조금씩 벗어나게 되는 문제가 발생한다면 어떨까? 프로그래머나 엔진 작업자는 우선 캐릭터의 로테이션이나 포지션에 관여하는 모든 코드를 검토해야할 것이다. 이상한 현상을 보일 수 있는 코드가 있는지 모든 부분을 확인해야 한다. 만약 규모가 있는 프로젝트라면 이 작업만으로도 엄청난 시간을 소모해야 하지만, 이 과정을 거친 이후에 코드에 문제가 없음을 확인했다면 어떨까? 만약 이러한 문제에 대한 경험이 없다면 큰 혼란에 빠질 것이다.

 

나는 최근에 이렇게 코드에 문제가 없음에도 불구하고 캐릭터가 회전하는 문제와 원래 위치를 조금씩 벗어나서 움직이는 버그를 모두 겪어본 적이 있다. 코드를 검토했음에도 불구하고 코드에는 문제가 없음을 확인했지만 여전히 캐릭터가 이상한 회전이나 이동을 하는 문제가 발생했는데, 그때가 되어서야 나는 코드가 아닌 리소스를 의심해보기로 했다. 우선은 어떤 상황에서 이런 문제가 발생하는지 명확하게 확인해야 했다. 여러 번 테스트해본 결과 캐릭터의 로테이션이 이상한 각도로 변하는 문제는 캐릭터가 사망하고 난 이후에, 오브젝트 풀링으로 재활용해서 가져왔을 때 발생했고, 캐릭터가 원래 위치를 벗어나는 문제는 캐릭터가 공격 모션을 취한 다음에 조금씩 이동하는 것을 확인할 수 있었다. 그렇게 해서 우선 문제가 발생하는 원인이 사망 애니메이션과 공격 애니메이션이라는 것을 알 수 있었고, 그 이후에는 유니티에서 캐릭터의 본들의 트랜스폼을 분석해서 특정한 본이 애니메이션 이후에 원래 상태로 돌아가지 않고 이상한 각도를 유지하거나 이상한 위치를 유지하는 것을 확인할 수 있었다. 이러한 문제를 애니메이터에게 알려주고 수정된 애니메이션 리소스를 받아서 적용한 이 후에는 이 버그는 해결되었다.

 

버그는 코드에만 있지 않다. 스크립트에 캐릭터의 회전이나 이동에 이산한 동작을 하는 코드가 없음에도 불구하고 이상한 회전을 보인다거나 원래 위치를 조금씩 벗어나서 이동하는 버그를 보게 된다면 애니메이션 리소스의 버그를 의심해보라.

 

 

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

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

 

에셋스토어

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