[TIL] 43일 차 - Quest 만들기

ChangBeom·2025년 3월 27일

TIL

목록 보기
44/53
post-thumbnail

오늘은 팀프로젝트 시작 날이라 대부분 시간을 회의를 진행했다. 그렇게 Quest 파트를 맡게 되었고, 시간이 부족해서 Quest의 기본 틀만 잡아놨다.


[Quest Base]

기본적으로 Quest는 게임에 하나만 존재하는 것이 아니라 여러 종류의 Quest가 존재한다. 그래서 공통적으로 Quest라면 포함해야되는 정보나, 함수를 담은 추상 클래스를 만들었다.

using UnityEngine;

public enum QuestState
{
    BEFORE,
    ONGOING,
    CLEAR,
}

public abstract class QuestBase : MonoBehaviour
{
    [Header("공통 퀘스트 정보")]
    public string questName;
    public string questDescription;

    protected QuestState questState = QuestState.BEFORE;

    /// <summary>
    /// 퀘스트 시작 시 text를 UI에 출력
    /// </summary>
    private void QuestStart()
    {
        Debug.Log("퀘스트 시작");
    }

    /// <summary>
    /// 퀘스트의 목적, 무엇을 해야 완료되는 지
    /// </summary>
    public abstract void QuestGoal();

    /// <summary>
    /// 퀘스트 클리어, 퀘스트에 따라 보상을 다르게 줄 수 있으므로 virtual로 구현
    /// </summary>
    public virtual void QuestClear()
    {
        questState = QuestState.CLEAR;

        Debug.Log("퀘스트 완료");
        // 퀘스트 완료 관련 text를 UI에 출력
    }

    /// <summary>
    /// 해당 퀘스트의 범위 안에 들어왔을 때, 대상이 플레이어인 경우, 퀘스트 시작
    /// </summary>
    /// <param name="other"> 충돌 대상 </param>
    protected virtual void OnTriggerEnter(Collider other)
    {
        if(questState == QuestState.BEFORE)
        {
            if (other.CompareTag("Player"))
            {
                questState = QuestState.ONGOING;
                QuestStart();
            }
        }
    }
}

퀘스트는 기본적으로 퀘스트 이름과 퀘스트에 대한 설명을 갖고 있는다. (후에 갖고 있어야 되는 데이터가 많아지면 ScriptableObject로 저장할 예정)

기본적인 로직은 BEFORE, ONGOING, CLEAR 상태로 나뉘어서 상태에 따라 로직을 수행한다. BEFORE 상태에서는 퀘스트를 받을 수 있는 범위 내에 들어오면 ONGOING 상태로 변해서 퀘스트를 진행할 수 있도록한다. ONGOING 상태에서는 퀘스트를 진행할 수 있도록 하고 퀘스트를 완료하면 CLEAR 상태로 만든다. Clear 상태가 되면 이제 완료한 퀘스트로 처리되어 다시 퀘스트를 진행할 수 없게 된다.

[QuestMove]

이제 위에서 만든 QuestBase 클래스를 상속받아서 여러 퀘스트를 만들어보자. 이번 팀 프로젝트는 튜토리얼 퀘스트를 만들어야 하므로, 먼저 플레이어가 일정거리 이상 이동하면 클리어되는 퀘스트를 만들었다.

using UnityEngine;

public class QuestMove : QuestBase
{
    [Space(10)]
    [Header("이동 퀘스트 정보")]
    public float requiredDistance;

    private float _curMovedDistance;

    private Vector3 _startPlayerPosition;
    private Transform _curPlayerTransform;

    private void Start()
    {
        _curPlayerTransform = GameObject.Find("Player").transform;
    }

    private void Update()
    {
        UpdatePlayerMovement();

        if(questState == QuestState.ONGOING)
        {
            QuestGoal();
        }
    }

    /// <summary>
    /// 플레이어가 얼마나 이동했는 지 체크
    /// </summary>
    private void UpdatePlayerMovement()
    {
        if (questState == QuestState.ONGOING)
        {
            _curMovedDistance = Vector3.Distance(_startPlayerPosition, _curPlayerTransform.position);
            Debug.Log(_curMovedDistance);
        }
    }

    /// <summary>
    /// 플레이어의 위치가 퀘스트를 시작했을 때 부터 일정거리 이상 멀어지면 퀘스트 클리어
    /// </summary>
    public override void QuestGoal()
    {
        if(_curMovedDistance >= requiredDistance)
        {
            QuestClear();
        }
    }

    /// <summary>
    /// 퀘스트 시작 시 플레이어의 위치를 받아옴
    /// </summary>
    protected override void OnTriggerEnter(Collider other)
    {
        base.OnTriggerEnter(other);

        _startPlayerPosition = other.transform.position;
    }
}

간단하게 설명하면 퀘스트 시작 시 플레이어의 위치를 받아와서 그 위치에서 requiredDistance 이상 이동하면 클리어되는 로직이다. 주의해야할 점은 퀘스트의 목표를 처리하는 QuestGoal() 함수는 추상 함수이므로 무조건 구현하지 않으면 오류가 난다. 퀘스트인데 퀘스트의 완료조건이 없으면 안되므로 추상 함수로 만든 것이다.

이제 내일부터 이것을 토대로 더 많은 퀘스트들을 만들 예정이다.

0개의 댓글