본문 바로가기
게임 프로그래밍/게임개발 중급

게임개발 중급(25) - 3D 게임 만들기(5)

by jyppro 2023. 5. 15.

3D 게임 만들기(5)

저번에는 UI를 사용한 움직임과 다양한 방법으로 움직임을 구현하는 것에 대해 살펴보았습니다. 이번에는 UI를 사용한 움직임을, 코드수정을 통해 자연스럽게 움직이도록 바꿔주고, 상하좌우 4방향으로 만들어 보겠습니다. 그리고 게이지 UI를 만들어 아이템을 먹으면 게이지가 줄어들도록 하는 시스템을 추가하는 것을 해보도록 하겠습니다.

 

상, 하 방향 UI 만들기

이전에는 좌우방향 UI 컨트롤까지 만들고, 상하방향은 F,B키로 처리했습니다. 이제 상하방향도 UI로 만들겠습니다.

방법은 이전과 같습니다. 우선 상하 버튼은 다른 에셋을 사용해 만들어 보겠습니다.

사용한-에셋
에셋 사용

2D Simple UI Pack이란 에셋을 에셋스토어에서 다운받아서, 버튼에 적용시키겠습니다.

 

UI-화면구성
UI 화면구성

상하 버튼의 코드 작성은 이전에 작성했던 좌우버튼과 같은 방식입니다. 적용시키는 좌표만 X좌표에서 Z좌표로 바꿔주면 되는 것이니 넘어가겠습니다. 잘 모르시겠다면, 이전에 올린 3D 게임 만들기(4)를 보시는 것을 추천드립니다.

UI를 이용한 자연스러운 상하좌우 이동

우선 UI를 눌렀을 때, 자연스럽게 움직이려면 꾹 누르고 있을 때 계속 해당 방향으로 움직이도록 처리해주어야합니다.

그러기 위해선, 지금과는 다른 방식으로 접근해야 합니다.

현재 사용하고 있는 Button 컴포넌트의 OnClick()은 더이상 사용하지 않을 것이니 삭제하겠습니다.

그리고 다음과 같은 코드를 작성합니다.

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

public class PlayerController : MonoBehaviour
{
    private bool isMovingForward = false;
    private bool isMovingBackward = false;
    private bool isMovingLeft = false;
    private bool isMovingRight = false;
    Rigidbody rigid;
    GameObject item;
    GameObject RButton;
    GameObject LButton;
    GameObject UButton;
    GameObject DButton;

    float jumpForce = 5.0f;
    float walkForce = 5.0f;
    float forwardForce = 1.0f;
    int score = 0;
    int key = 0;

    void Start()
    {
        this.rigid = GetComponent<Rigidbody>();
        this.item = GameObject.Find("Score");
    }

    void Update()
    {
        //점프
        if(Input.GetKeyDown(KeyCode.Space) && this.rigid.velocity.y == 0f){
            rigid.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
        }

        //F키, B키로 움직이기
        if(Input.GetKey(KeyCode.F)) // 앞
        {
            key = 1;
            transform.Translate(0f,0f,forwardForce*key*Time.deltaTime);
        }
        else if(Input.GetKey(KeyCode.B)) // 뒤
        {
            key = -1;
            transform.Translate(0f,0f,forwardForce*key*Time.deltaTime);
        }

        //방향키로 움직이기
        if(Input.GetKey(KeyCode.RightArrow)){ // 오른쪽
            key = 1;
            this.rigid.AddForce(transform.right * key * this.walkForce);
        }
        if(Input.GetKey(KeyCode.LeftArrow)){ // 왼쪽
            key = -1;
            this.rigid.AddForce(transform.right * key * this.walkForce);
        }
        if(Input.GetKey(KeyCode.UpArrow)){ // 앞
            key = 1;
            this.rigid.AddForce(transform.forward * key * this.walkForce);
        }
        if(Input.GetKey(KeyCode.DownArrow)){ // 뒤
            key = -1;
            this.rigid.AddForce(transform.forward * key * this.walkForce);
        }

        //4방향 UI로 움직이기
        if (isMovingForward)
        {
            MoveForward();
        }
        else if (isMovingBackward)
        {
            MoveBackward();
        }
        else if (isMovingLeft)
        {
            MoveLeft();
        }
        else if (isMovingRight)
        {
            MoveRight();
        }
    }

    //아이템 먹으면 점수 올라가는 기능
    void OnTriggerEnter(Collider other)
    {
        score += 10;

        if(score >= 0){
            this.item.GetComponent<Text>().text = "Score: " + score.ToString();
        }

        Destroy(other.gameObject);
    }

    public void ForwardButtonDown()
    {
        isMovingForward = true;
    }

    public void ForwardButtonUp()
    {
        isMovingForward = false;
    }

    public void BackwardButtonDown()
    {
        isMovingBackward = true;
    }

    public void BackwardButtonUp()
    {
        isMovingBackward = false;
    }

    public void LeftButtonDown()
    {
        isMovingLeft = true;
    }

    public void LeftButtonUp()
    {
        isMovingLeft = false;
    }

    public void RightButtonDown()
    {
        isMovingRight = true;
    }

    public void RightButtonUp()
    {
        isMovingRight = false;
    }

    private void MoveForward()
    {
        key = 1;
        transform.Translate(0f,0f,walkForce*key*Time.deltaTime);
    }

    private void MoveBackward()
    {
        key = -1;
        transform.Translate(0f,0f,walkForce*key*Time.deltaTime);
    }

    private void MoveLeft()
    {
        key = -1;
        transform.Translate(walkForce*key*Time.deltaTime,0f,0f);
    }

    private void MoveRight()
    {
        key = 1;
        transform.Translate(walkForce*key*Time.deltaTime,0f,0f);
    }
}

코드를 변경한 PlayerController를 그대로 가져왔습니다. 천천히 설명을 드리겠습니다.

우선 OnClick()에 연결하여 사용하던 함수들은 전부 지워주었습니다.

그리고 상하좌우를 판단할 수 있는 bool 타입의 변수를 각 방향마다 한개씩 만들어줍니다.

이전의 사용했던 코드들은 그대로 둔 상태에서, Update()문에 각 방향키가 눌렸을 때 함수를 실행하는 if문을 넣어줍니다.

버튼이 클릭중이라는 처리를 해줄 "상하좌우"ButtonDown 과 Up 함수를 만들고 각 방향의 상태를 참/거짓으로 판단해 바꿔주는 문장을 넣습니다. Down일때 true, Up일때 false로 처리합니다.

상태에 따라 Update()문에서 실행될 수 있는 함수를 각 방향마다 만들어줍니다.

 

코드가 상당히 길어졌지만, 개념에 대해서만 설명드리기 위해 전부 작성하였습니다.

 

이전에 사용한 OnClick()은 단순히 눌리면 한번 작동하는 방식입니다.

저희는 꾹 눌린 상태와 버튼을 뗀 상태 두가지를 처리해주어야 합니다.

그러기 위해선 여러가지 방식이 있지만, 지금 제가 설명드리는 것은 EventTrigger를 사용하는 방식입니다.

 

EventTrigger

EventTrigger는 컴포넌트입니다. 현재 만들어진 4종류의 버튼에 EventTrigger를 우선 추가해보겠습니다.

추가하면, Add New Event Type이란 버튼이 있는데 이것을 클릭해주고, 저희가 사용할 PointDown과 PointUp을 각각 한개씩 추가해 주겠습니다.

이벤트-트리거
이벤트 트리거

현재 그림과 같은 상태입니다. 여기서 + 버튼을 눌러 각각 하나씩 생성해주고, 이전에 OnClick()을 연결했던 것처럼 저희가 사용할 PlayerController 코드가 들어있는 Player 오브젝트를 드래그 앤 드롭해서 연결시킨 뒤, 아까 만들어 두었던 (Right, Left, Forward, Backward) ButtonDown, Up을 하나씩 연결해 줍니다.

이벤트-트리거
Right 버튼 연결 모습

위 그림은 Right 방향키 UI에 추가한 EventTrigger의 모습입니다. Player의 PlayerController에 접근해 RighButtonDown과 RighButtonUp을 각각 PointerDown과 PointerUp에 연결해준 모습입니다.

 

이렇게 모든 버튼에 연결이 끝이 났다면, 이제 실행을 시켜보겠습니다. 이전에 사용했던 Time.deltaTime을 이용하여 자연스럽게 움직이도록 바꾸었으니 꾹 누르면 해당 방향으로 계속 움직이도록 만들어졌습니다.

전진버튼-꾹누르기
전진버튼 꾹누르기

앞으로 가는 버튼을 꾹 누르니 앞으로 계속 가는 모습입니다. 벽에 가로막혀 더 이상 앞으로 전진하지는 못합니다.

이런 UI로 플레이어를 움직여주는 방법은 모바일로 게임을 빌드할때 주로 사용됩니다. PC게임이라면 키보드와 마우스를 통해 움직임을 제어할 수 있지만, 스마트폰으로 게임을 할 때에는 터치로 대부분의 동작을 처리해야 하기 때문입니다.

꾹 누르는 처리를 해주는 방법은 이것 말고도 꽤 다양한 방법이 많습니다. 저는 어디까지나 예제로서 알려드린 것이니 필요하시다면 많이 찾아보시기 바랍니다.

 

<NEXT>

원래 오늘 방향UI와 함께 게이지 UI 시스템까지 하려고 했지만, 시간 관계상 오늘은 여기서 마무리 짓도록 하겠습니다. 다음에는 오늘 못한 게이지 UI 시스템과 더불어, 소리와 파티클을 적용시키는 방법에 대해서 알아보겠습니다. 감사합니다.