카테고리 없음

유니티 게임 미션 성공 이벤트

akama 2024. 9. 20. 19:40

게임에서 플레이어는 게임 속 아바타 역할을 하는 캐릭터를 조정하면서 게임 세계를 모험하면서 가상 세계에서 느끼는 색다른 느낌을 경험합니다. 그러나 게임이 단순히 탐험하거나 모험만 하는 것으로 끝난다면 그것은 게임보다는 가상 현실을 그냥 보는 것과 가깝다고 해야 할 것입니다. 그래서 게임은 플레이어에게 어떤 목표를 제시하고 그것을 완수 했을 때에 따른 보상을 주고 그 보상을 통해 캐릭터를 더 강하게 만들면서 플레이어에게 성장의 묘미를 느끼게 만듭니다. 미션 완수, 보상, 성장의 사이클이 되기 위해서는 미션의 목표가 있어야 합니다. 게임 시스템이 복잡한 RPG 장르의 경우에는 퀘스트에 따라서 목표가 다양하지만 단순한 캐주얼 또는 하이퍼 캐주얼 게임 장르는 목표가 단순합니다. 따라서 게임 제작 경험이 부족하거나 제작 스킬이 거의 없는 경우에는 하이퍼 캐주얼 장르로 목표를 잡는 것이 좋습니다. 제 경우도 하이퍼 캐주얼 장르로 제작하고 있습니다. 간단하게 플레이 하는 게임을 목표로 만들기 때문에 보상이나 성장은 배제하고 미션의 성공과 실패에 대해서 알아보겠습니다. 이런 부분은 개발 초기 단계에서 어느 정도 기획이 되어 있어야 하며 준비물로 성공 & 실패를 알려주는 UI와 목표물에 사용될 이미지가 필요합니다. 관련 UI는 유니티 에셋스토어에 수준 높은 에셋들이 많습니다. 다만 나의 게임과 컨셉이 비슷한 UI 에셋을 사용하는 것이 좋습니다. 목표물이 될 이미지는 쓰레기이기 때문에 그림툴에서 대충 구겨진 종이조각으로 그려서 유니티에 넣었습니다.

 

미션 성공 이벤트를 위해 할 일

이 게임의 캐릭터는 로봇으로 설정했습니다. 이 캐릭터는 인공지능(AI)을 지닌 청소 로봇이기 때문에 캐릭터 이미지가 청소 로봇과는 약간 거리가 있지만 대충 간단하게 직접 그리다 보니 흰색 네모의 캐릭터가 되었습니다. 이 로봇 청소기는 더러워진 장소를 청소하는 임무를 수행합니다. 따라서 미션의 목표는 스테이지 내 흩어져 있는 쓰레기들을 흡입해서 치워버리는 것입니다. 즉 쓰레기를 모두 제거하면 미션 성공 조건으로 만들고, 관련 UI를 플레이어에게 띄우는 것입니다. 

 

첫번째 할 일

남아 있는 쓰레기가 몇 개인지 알려주기!

화면 좌측 상단에 Text UI를 만들어서 남은 쓰레기 오브젝트가 몇 개인지 카운트를 해서 표시 해주는 스크립트를 작성해 보겠습니다. 스크립트 작성에 앞서 hierarchy뷰 canvas를 클릭한 상태에서 마우스 우클릭 UI - Text TextmeshPro 또는 Legacy - Text를 선택해서 UI를 만들어 주고(저는 Legacy-Text를 선택), 이름을 leftObjectsUI로 바꿔줍니다. 이렇게 하는 이유는 나중에 UI가 많아지는데 헷갈리지 않도록 변수이름과 똑같은 상태에서 UI만 붙여서 구분지어 주고자 하는 것입니다. 이제 게임의 목표물인 쓰레기 오브젝트를 만들어 줍니다. Hierarchy뷰에서 마우스 우클릭 2D Object - Sprite - Square 클릭 후 Sprite를 구겨진 종이조각 이미지로 바꾸어 주고 태그를 Trash로 설정해 줍니다. 태그에 Trash가 없을 경우 새로 만들어서 사용하면 됩니다. 그리고 rigidBody2D 컴포넌트와 BoxCollider2D 컴포넌트를 추가해 줍니다. rigidBody2D 컴포넌트에서 Gravity scale을 0으로 변경해 줍니다. rigidBody2D는 2D에서 강체로써 차량으로 치면 차량외관과 비유할 수 있으며 무게가 있기 때문에 유니티 엔진에서 물리값 적용을 받습니다. 여기서 Gravity scale을 0으로 하면 무게가 있더라도 중력은 0이 되기 때문에 해당 오브젝트가 낙하하지 않게 됩니다. 만약 중력값을 두고 실행하면 낙하하는 오브젝트 때문에 게임 화면에서 아무것도 볼 수 없게 되니 2D 탑뷰 게임이면 Gravity scale을 0으로 둡니다. Collider는 껍질 같은 개념인데 센서가 있는 껍질이라고 보면 됩니다. 물리적 충돌은 이 콜라이더에서 처리하기 때문에 충돌이나 접촉으로 인한 이벤트 처리에 관여하는 컴포넌트입니다.

다음은 Project뷰에서 마우스 우클릭 후 Create - c# script 클릭해서 스크립트를 만들고 GameConroller라고 지정합니다. 흔히 GameManager라고 하는 스크립트로 게임 목표의 성공, 실패 등 스테이지 상황을 관리하는 스크립트를 만드는 것입니다.

이 스크립트를 hierarchy뷰에서 만든 empty object(이름을 GameManager로 변경)에 추가시킵니다.

using UnityEngine.UI;

public class GameController : MonoBehaviour
{
     public Text leftObjects;
     int leftCount;

     void Start ()
     {
          leftCount = GameObject.FindGameObjectsWithTag("Trash").Length;
          leftObjects.text = "Left : "+ leftCount.ToString();
     }
}

 

GameManager 오브젝트에는 이제 GameController라는 스크립트 컴포넌트가 추가 된 것입니다. 이 오브젝트를 클릭해 보면 변수 leftObjects를 public으로 선언했기 때문에 Text에 Text UI를 지정할 수 있습니다. 아까 Hierarchy뷰 canvas 자식 오브젝트로 만든 leftObjectsUI를 마우스 드래그로 끌어와서 GameController 컴포넌트의 관련 public 변수에 드롭합니다. 이 부분을 지정하지 않고 플레이를 실행하면 unassign 관련 에러가 콘솔창에 뜨게 되니 변수를 public으로 선언했다면 플레이 실행 전에 반드시 지정해 줘야 합니다. 플레이 전에 쓰레기 오브젝트를 5개 정도 복사(Ctrl + D)로 만들어 주고 실행합니다. 게임 좌측 상단에 Left : 5가 나온 것을 확인할 수 있습니다. 이제 남은 개수가 0이 되면 미션 성공이고, 오브젝트를 회수하다가 컨트롤 실수로 인해 벽에 부딪히면 미션 실패가 되는 것을 만들어 보겠습니다.

 

두번째 할 일

미션 성공을 알리는 팝업창 나타나는 것을 구현해야 합니다. 준비물로 성공 팝업창과 실패 팝업창 UI를 준비해야 합니다. 에셋 스토어에 많은 자료들이 있지만 이번에는 그림툴을 이용해서 대충 한 번 그려봤습니다.

PSD import 한 UI atlas

연습용이 아니라면 UI 전문가에게 의뢰하거나 유니티 에셋 스토어 유료 버전을 추천합니다. Hierarchy뷰 Canvas를 클릭한 상태로 우클릭 UI - image를 클릭해서 image 컴포넌트가 있는 오브젝트를 생성합니다. 해당 오브젝트 이름을 SuccessBoard라고 변경하고 image 컴포넌트에 있는 source image에 관련 이미지를 드래그 앤 드롭합니다. 여기서는 흰색 보드를 기본 배경으로 정해서 적용했습니다. 잠깐 게임 팝업창에 적용하는 보드에 대해 설명하겠습니다. 메뉴판, 게임창에 사용하는 보드판이나 버튼은 개발자가 원하는 크기로 늘리거나 줄일 수 있어야 합니다. 원본 이미지가 게임 화면에 알맞으면 좋지만 조정이 필요한 상황도 발생합니다. 이럴 때 UI 오브젝트의 Rect Transform 컴포넌트에 있는 Width나 Height을 조정해서 보드판이나 버튼의 크기를 조절할 수 있습니다. 그러나 이럴 경우 문제점이 있습니다. 커지거나 줄어드는 만큼 이미지가 늘어지고 줄어드는 문제점이 발생합니다. 이를 해결하기 위해서는 유니티에서 UI astas를 클릭하고 Open sprite editor로 들어가서 보드판 이미지의 border 값을 통해 이미지를 구분지어 주어야 합니다.

sprite editor 하단 Border

border 부분이 아닌 영역만 늘이거나 줄여지고, 모서리 부분은 크기에 영향을 받지 않게 됩니다. 설정을 위해서는 sprite editor에서 원하는 이미지를 클릭하고 하단에 있는 border 값을 조정해 주고 apply하면 됩니다. 이제 SuccessBoard image컴포넌트에 있는 image Type을 Slice로 변경해 주면 됩니다. 참고로 border가 없을 경우에는 image Type의 Slice가 작동하지 않으니 slice로 사용하기 위해서는 이미지의  border를 설정해야 합니다.

(좌) Simple type일 때 가로 1200으로 늘림 (우) Slice type일 때 가로 1200으로 늘림

Border를 설정했다면 SuccessBoard를 클릭한 상태에서 다시 UI 앞면 보드를 만들어 주고 type을 slice로 변경합니다. 앞면 보드에는 하늘색 보드를 적용하고 똑같이 진행합니다. 차례로 타이틀 보드(image), 타이틀 이미지(Mission Success)를 생성해 줍니다. 그러면 Hierarchy뷰에서 Canvas-SuccessBoard 밑에 여러개의 자식 오브젝트(UI)들이 있게 되는 것입니다. 점수를 보여 줘야 하는데 점수는 쓰레기를 치워서 얻는 점수이니 청소 점수(Clean Score)로 하겠습니다. SuccessBoard를 클릭한 상태에서 UI - Text TextMeshPro를 만들고 이름을 CleanScoreText로 변경합니다. 같은 방법으로 하나 더 만들어서 CleanScoreData로 변경합니다. 마지막으로 UI button을 만들고 이름을 NextButton으로 변경하고, 주황색 버튼 이미지를 image source에 적용시킵니다.

여기서 왜 순서대로 진행하는지를 알려드리겠습니다. 유니티의 UI Canvas는 자식 오브젝트를 보여 줄 때 가장 하단에 있는 자식 오브젝트부터 플레이어에게 보여줍니다. 즉 가장 하단에 있는 오브젝트인 UI가 플레이어가 바라보는 가장 상단이 되는 것입니다. 

UI 는 단계적으로 보여집니다.

그림에서 볼 수 있듯이 가장 하단에 있는 NextButton이 플레이어에게 가장 상단에 보이고 가장 하단에는 SuccessBoard에 image가 가장 아래에 보이도록 한 것입니다. 자식 오브젝트 간의 순서는 드래그를 통해서 언제든지 바꿀 수 있기 때문에 UI가 보여지는 부분을 게임 화면을 통해 확인하면서 조정을 해야 합니다.

board와 titleBoard 순서 변경 시 화면

위 그림은 자식 오브젝트 간의 순서를 변경했을 때의 결과를 보여주고 있습니다. board(하늘색 사각형 보드)가 위로 올라오면서 titleBoard 이미지(회색)를 덮어버렸습니다. UI 화면 의도와 다른 결과가 나오기 때문에 확인하면서 순서를 조정해야 합니다. 좀 더 예시를 들어 보면 Hierarchy뷰에서 SuccessBoard 오브젝트가 ButtonRight 위에 있다고 가정하면 아마도 성공 팝창 위에 오른쪽 화살표 조정 버튼이 튀어 나와 보일 것입니다. UI는 매번 새로운 오브젝트 생성 시 순서가 중요하기 때문에 오브젝트 순서 배열에 신경을 써야 합니다. 치명적 오류가 아닐지라도 게임의 완성도를 떨어 뜨리는 요인이 됩니다.

 

세번째 할 일

캐릭터가 목표 오브젝트에 접촉하면 해당 오브젝트를 회수 및 제거하는 스크립트와 목표 오브젝트의 목표량 달성 시 미션 성공 팝업창을 발생시키는 스크립트를 만들어 보겠습니다.

먼저 팝업창 발생을 위해서 기존 컨트롤러 스크립트에 몇 가지 명령들을 추가합니다.

using UnityEngine.UI;

public class GameController : MonoBehaviour
{
    public Text leftObjects;
    int leftCount;
    public GameObject SuccessBoard;

    void Update()
    {
        UpdateScore();
    }

    void UpdateScore()
    {
        if(!isCleared)
        {
            if (leftCount > 0)
            {
                leftCount = GameObject.FindGameObjectsWithTag("Trash").Count();
                leftObjects.text = "Left : " + leftCount.ToString();
            }

            if ( leftCount <= 0)
            {
                leftCount = GameObject.FindGameObjectsWithTag("Trash").Count();
                leftCount = 0;
                leftObjects.text = "Left : " + leftCount.ToString();
                winBoard();
                isCleared = true;
            }
        }
    }

    void winBoard()
    {
        SuccessBoard.SetActive(true);
    }
}

SuccessBoard는 오브젝트 타입입니다. SetActive를 통해 오브젝트를 켜고 끌 수 있습니다. 따라서 준비된 SuccesssBoard는 해당 오브젝트를 클릭한 상태에서 inspector창 상단에 체크 박스를 해제해서 꺼줍니다. 이 상태가 SetActive(false) 상태로 게임 시작 시에는 UI 창이 보이지 않습니다.

게임을 하면서 쓰레기 오브젝트를 흡수하면 Update함수를 통해서 몇 개의 오브젝트(withTag"Trash")가 남았는지를 체크하면서 text로 좌측 화면에 보여주게 됩니다. 만약 남은 카운트가 0이 되면 winBoard 함수를 호출하면서 미션 성공창이 나타나게 됩니다.

캐릭터가 오브젝트를 회수하는 스크립트를 만들어 보겠습니다. 사실 이 부분은 캐릭터 컨트롤러(game manager)에서 처리해도 되며 반대로 목표 오브젝트에서 처리해도 됩니다. 스크립트를 생성하고 이름을 TrashObject로 변경하고 이 스크립트를 목표 오브젝트에 추가해 줍니다.

public class TrashObject : MonoBehaviour
{
    private void OnTriggerEnter2D(Collider2D collision)
    {
          if (collision.CompareTag("Player"))
          {
                gameObject.SetActive(false);
          }
    }
}

위 스크립트에서 OnTriggerEnter2D 함수는 MonoBehaviour에서 제공되는 것으로써 Collider의 충돌 및 접촉 시 이벤트를 만드는 용도로 사용합니다. 이 스크립트에서는 Player라는 태그를 가진 오브젝트의 콜라이더와 충돌했을 때 해당 오브젝트(trash Object)를 비활성화시킨다는 것입니다. 이를 통해 Trash라는 태그가 있는 오브젝트들이 비활성화가 된다면 어떤 이벤트가 발생할까요? 아마 게임 컨트롤러의 UpdateScore함수에서 매초 남은 Trash 태그 오브젝트 개수를 카운트하고 있기 때문에 목표 오브젝트 개수의 변화를 UI를 통해 확인할 수 있습니다.

 

이제 목표 오브젝트인 쓰레기 오브젝트를 원하는 만큼 복사해서 게임월드 내에 배치를 하고 플레이를 해보면 복사된 개수가 목표 개수로 좌측상단에 나오게 되며, 플레이어 캐릭터가 해당 오브젝트를 회수할 때마다 한 개씩 없어지면서 카운트 되는 것을 볼 수 있습니다. 0까지 만들어서 최종적으로 미션 성공 팝업창이 나오는지 확인합니다. 다음에는 성공 팝업창에서 점수가 발생하는 기능과 다음 스테이지로 전환되는 부분을 포스팅하겠습니다.