XR플밍 - 9. UnityEngine2D 인터렉티브 프로그래밍 - 개인 프로젝트 5일차, 후기(6/4)

이형원·2025년 6월 4일
0

XR플밍

목록 보기
94/215

0. 들어가기에 앞서

게임 제작 마지막 일자, 마지막으로 나타나는 버그들을 고치고 빌드까지 해서 게임이 잘 작동하는지 살펴보자.

1. 5일차 작업 정리

최종적인 빌드 파일 게임을 완성했다.

<맵>

  • 최종적으로 다듬기 및 정리

<버그 수정>

  • 플레이어가 일시정지 버튼을 눌렀을 때 공격이 나가는 버그 수정
  • Pixel Perfect 적용 오류 수정

<추가한 기능>

  • 타임라인 기능을 사용하여 보스 방 입장 시 연출 추가

<리팩토링>

  • 매직넘버로 선언된 변수를 Scriptable Object 혹은 SerializeField로 변환하는 작업(일부만 진행)

2. 문제의 발생과 해결과정

2.1 (이슈) 픽셀 퍼펙트 적용

픽셀 퍼펙트에 대해서 적용하는 건에 대하여, 다시 공부해 보고 잘못 적용하고 있었다는 걸 알게 되었다.

가상 카메라에 픽셀 퍼펙트를 적용하고, 메인 카메라에도 해당 내용을 적용하여야 한다.
Asset Pixels Per Unit은 받은 에셋의 픽셀을 확인하여 해당 값을 입력해주고,
Reference Resolution은 화면 해상도를 넣어주면 된다.

2.2 (이슈) 플레이어가 일시정지 버튼을 누를 시 공격이 나가는 버그

해당 버그의 내용 또한 수정했다. 생각해낸 방법은 UI에 인터페이스를 적용하여 만든 컴포넌트를 적용시키는 것이었다. 다만 이렇게만 적용하면 일시정지 UI가 켜지는 문제 때문에, Resume에서도 해당 Bool 값을 수정했다.

2.3 리팩토링할 때의 주의점

이때까지 게임이 심각한 문제를 일으킨 경우가 없었지만, 막상 리팩토링을 하는 과정에서 한 번 프로젝트를 터뜨렸다.
중간에 테스트를 하면서 잘 작동하는지 해 보면서 리팩토링을 진행했어야 했는데, 놓쳤던 부분이다.

결국 40분 정도 작업했던 리팩토링 작업을 전부 날리고 이전 버전으로 돌아갈 수밖에 없었다.

꽤 뼈아픈 경험이 되었고, 리팩토링도 함부로 하지 말자는 교훈을 얻었다.

3. 전체적인 구현 과정 요약

3.1 플레이어

플레이어는 크게 아래와 같이 많은 상태가 있으므로, 상태 패턴을 이용해 구현하였다.

  • 대기
  • 걷기
  • 점프
  • 근접공격
  • 차지
  • 원거리 공격
  • 죽음

플레이어를 구성하는 컴포넌트는 따라서 아래와 같이 세 가지로 구성하였다.

  • PlayerController, PlayerState, PlayerMuzzle(스펠 발사용)

플레이어에서 최종적인 행동 반영을 하고, PlayerState에서 상태에 대한 조건을 설정하여 상태를 오가는 방식으로 만들었다.

  • 플레이어의 체력과 스펠 쿨타임은 플레이어가 아닌 게임매니저 쪽으로 옮겼고, 이를 통해 UI에 반영되는 방식을 채용했다.

  • Spell의 경우 발사 Muzzle을 따로 만들어 스펠이 나가는 방식을 설정했다. 또한 Spell의 경우 Object Pool 패턴으로 구현하여 Muzzle내에 스펠을 저장하여 최적화의 효과를 노렸다.

  • 또한 공격 판정에 대한 부분은 애니메이션 이벤트를 활용하여 공격이 들어가는 방식을 취했다.

  • 플레이어의 걸음, 점프, 근접 공격, 차징, 스펠 공격 등에 대한 효과음은, 플레이어가 가지고 있는 것이 좋겠다고 판단하여 플레이어가 가지게 만들었다. SFXController라는 컴포넌트를 만들어 각 상황마다 효과음이 전환되도록 설정하였다.

3.2 Monster

몬스터는 플레이어와 같이 상태 패턴을 채용함과 동시에, Scriptable Object와 Object Pool 패턴을 적용했다.
이에 따라 다른 스텟을 가진 몬스터를 양산함과 동시에 향후 게임에서 몬스터의 리젠 효과가 필요할 경우 최적화를 노릴 수 있는 방식으로 설계했다.

종류는 일반 몬스터와 보스몬스터 - Demon Slime 까지 구현했다.

<일반 몬스터>

  • 일반 몬스터는 각기 다른 공격력, 체력, 시야 및 처치시 스코어 등을 가지고 있으며, 플레이어에게 공격을 당할 시 소리를 내도록 구현했다.

  • 일반 몬스터의 행동 양상 자체는 똑같으나, 다른 스테이터스 및 애니메이션의 몬스터를 만들 수 있다.

  • 일반 몬스터의 행동 양상은 아래와 같이 되어 있다

    • 대기
    • 걷기(Detect)
    • 추격(Trace)
    • 공격
    • 죽음
  • 일반 몬스터는 Raycast 방식을 이용하여 넘어갈 수 없는 벽 혹은 떨어질 수 있는 계단/절벽을 인식하게 하였고, 해당 구간에서만 움직이도록 하였다.

  • 플레이어를 탐지해도 추격할 수가 없다면 플레이어를 바라보기만 할 뿐 벽/절벽을 넘어서 쫓아오지 않게끔 설정했다.

<보스 몬스터>

  • 보스 몬스터의 행동 양상의 경우 일반 몬스터와 거의 비슷하나, 약간 다르게 구성했다.

    • 대기
    • 추격(Trace)
    • 공격(스펠 공격)
    • 죽음
  • Boss Monster의 경우 일반 몬스터와 달리 스킬을 사용할 수 있고, 일반 몬스터와 달리 시야 설정 없이 바로 플레이어를 쫓아오도록 설정했다. 보스 방 자체를 폐쇄적으로 설정하여 플레이어와의 대치를 하게끔 설계한 구조이다.

  • 보스룸 입장 시에 타임라인을 사용하여 보스를 한 번 클로즈 업 하고, 위협적인 모습을 보이는 연출을 추가했다.

  • Boss가 스킬을 쓰는 방식은 애니메이션 이벤트 방식을 사용했으며, 스킬은 쿨타임이 있어 매번 나가는 것이 아니고 공격 타이밍에 맞춰 한 번 씩 불꽃이 나가는 방식으로 구현했다.

또한 보스는 공격 시의 타격음과 피격음이 들어가 있다.

3.3 아이템

게임의 기획 의도상 인벤토리가 존재하는 시스템은 아니고, 필드에 드랍한 포션을 취득하면 바로 회복하는 식으로 구현했다.

  • 포션을 구할 수 있는 방법은 크게 두 가지이며, 몬스터(보스 몬스터 포함)에게서 일정 확률로 떨어지거나, 부술 수 있는 오브젝트를 통해 확정적으로 얻을 수 있다.

  1. 몬스터가 비활성화되는 순간 확률적으로 포션이 뜨게 설정해 놓았다.

  2. 부술 수 있는 오브젝트(상자)를 플레이어가 공격하면, 확정적으로 포션을 얻을 수 있도록 설정하였다.

  • 아이템의 경우에도 생성되었을 때의 회복 수치가 랜덤으로 나오도록 설정되어 있다. 또한 플레이어의 체력 혹은 마나가 최대치일 경우 포션을 습득하지 못하게끔 설정되어 있다.

ex) 체력 포션

using UnityEngine;

/// <summary>
/// 체력 포션 생성기
/// </summary>
public class HealthPotionCreater : PotionCreater
{
    // 포션의 회복 수치
    private int m_status;

    private void Awake() => Init();

    // 체력 포션은 1에서 4 사이의 랜덤한 회복 수치로 생성됨(밸런싱)
    private void Init()
    {
        m_status = Random.Range(1, 5);
    }

    // 플레이어의 현재 체력이 최대치일 때에는 습득 불가
    // 플레이어의 체력을 회복시켜주고 비활성화됨
    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.gameObject.CompareTag("Player"))
        {
            if (GameManager.Instance.GetCurHP() == GameManager.Instance.GetMaxHP()) return;
            Recover();
            gameObject.SetActive(false);
        }
    }

    // 플레이어의 체력을 회복시킴
    public override void Recover()
    {
        GameManager.Instance.RecoverHp(m_status);
    }
}

3.4 스테이지

타이틀 맵에서는 게임 시작과 게임 종료가 가능하다.

플레이어에게 조작키를 알려주는 방식 처음에는 UI를 통해 구현하려 했으나, 방향을 변경하여 스테이지에 월드스페이스에 존재하는 UI를 띄우는 방식으로 구현했다.

따라서 1스테이지를 튜토리얼 및 조작키를 알려주는 맵으로 설정하고, 총 맵의 수를 타이틀 씬 포함 6개의 맵으로 구성했다.

2스테이지에 갈림길이 발생하는 방식을 이용하여, 3-1 맵과 3-2 맵 중 선택하여 이동할 수 있도록 구현했다.
난이도를 선택할 수 있는 방식으로 고득점을 노릴지, 안전하게 갈지 선택할 수 있다.

마지막 스테이지에 보스를 배치하여 대치하도록 하고, 폐쇄적인 구조로 만들어 보스 몬스터가 플레이어를 쫓게끔 구성되어 있다.
또한 보스를 잡는 것은 선택이며, 보스를 밟고 뛰어 넘어 잡지 않고 가도 클리어가 가능하도록 의도되어 있다.

3.5 UI

UI의 경우 이번 작업에서 조금 아쉬움이 남았던 부분이기도 했다.
사운드 조절 같은 기능을 넣어보고 싶었는데 시간 상으로 구현하지 못했다.

  • 인게임 UI는 크게 플레이어 체력, 마력, 그리고 점수 부분과 스펠 차징 시스템을 넣었다.
    스펠 차징 시스템은 마우스 우클릭으로 차징할 때 게이지가 차도록 하고, 스펠을 사용했을 때는 쿨타임이 보이도록 처리했다.

  • 팝업 UI는 일시정지, 게임오버, 게임 클리어 등으로 구현했다.
  1. 일시정지

  2. 게임 클리어

  3. 게임 오버

3.6 Audio

특히나 어려웠던 작업이 오디오 부분이었다. 오디오 매니저 자체는 구성해두긴 했지만, 사실 Bgm 재생 말고는 쓰지 못했다.
UI를 사용할 때 효과음이 출력되는 기능을 만들려고 했지만 시간상 부족해서 구현하지 못했다.

다만 SFXController를 통해 플레이어, 몬스터의 각각의 효과음은 구현하여 게임에 생동감을 넣을 수 있었다.

4. 후기

혼자서 게임을 만든다는 작업은 참 쉽지 않았다. 하지만, 게임의 규모가 작긴 해도 결국 빌드 단계에서까지 작동하는 게임 하나를 만들었다는 쾌거를 이뤘다.
특히나 매일 TIL을 쓰면서 도전 과제 같은 것을 추가하면서 기능을 더해 가고, 성공한 것도 있었고 결국 구현하지 못한 기능도 있었다.

주말과 연휴까지 풀타임은 아니어도 시간을 투자했다 보니, 사실상 5일만에 제작한 프로젝트는 아니긴 하지만 이 정도 퀄리티의 게임을 뽑는 것도 쉽지 않은 것을 새삼 깨달았다.
또한 버그 자체는 거의 다 해결하긴 했지만 아주 간혹 마우스 키씹힘 현상이나 점프 후 바닥 착지 판정이 안되는 경우가 있기도 했다.
이 부분에 대해 보완점을 생각해보고, 다음에는 더 좋은 퀄리티의 게임을 만들어보고 싶다.

profile
게임 만들러 코딩 공부중

0개의 댓글