게임에서 업적퀘스트 시스템은 플레이어에게 목표를 부여하고, 게임의 몰입도를 높여주는 중요한 요소입니다. 이러한 시스템은 단순히 퀘스트 목표를 달성하는 것 이상의 의미를 지닙니다. 예를 들어, 퀘스트는 주간/월간 리셋, 로테이션 시스템, 다양한 상태 등 복잡한 요구사항을 충족해야 할 수 있습니다. 이와 같은 요구사항을 충족하기 위해서는 확장 가능하고 유연한 구조를 설계해야 합니다.

이번 글에서는 언리얼 엔진을 이용하여 주간/월간 리셋, 로테이션 시스템, 상태 관리 등 다양한 시스템을 구현한 업적 및 퀘스트 시스템을 어떻게 설계할 수 있는지에 대해 자세히 설명합니다. 실용적인 코드 예시를 통해 게임에서 실제로 사용할 수 있는 확장 가능한 시스템을 만들어보겠습니다.


1. 업적 및 퀘스트 시스템의 중요성

업적 시스템과 퀘스트 시스템은 게임의 목표 설정, 동기 부여, 그리고 플레이어의 만족도를 높이는 중요한 역할을 합니다. 퀘스트는 단기적인 목표를 제공하고, 업적은 장기적인 목표를 설정합니다. 이러한 시스템이 유기적으로 연결되어 있을 때, 플레이어는 지속적으로 게임을 진행하면서 다양한 보상과 경험을 얻을 수 있습니다.

게임이 발전함에 따라 퀘스트와 업적 시스템은 주간/월간 리셋, 로테이션 시스템, 데이터 동기화복잡한 관리가 필요해집니다. 이에 따라 확장성유지보수 용이성을 고려한 시스템 설계가 필수적입니다.


2. 퀘스트 및 업적 시스템 설계

퀘스트와 업적 시스템의 설계는 데이터 구조화, 상태 관리, 그리고 주기적인 리셋 및 로테이션을 고려해야 합니다. 이를 위해 언리얼 엔진의 클래스, 구조체(Struct), 인터페이스(Interface) 등을 적절히 활용하여 효율적으로 시스템을 구현할 수 있습니다.

2.1 퀘스트 구조화

각 퀘스트는 목표, 상태, 보상, 그리고 리셋 주기(주간, 월간 등)를 설정할 수 있어야 합니다. 이를 위해 퀘스트 구조체를 다음과 같이 확장합니다.

// FQuest 구조체 정의
USTRUCT(BlueprintType)
struct FQuest
{
    GENERATED_BODY()

    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    FString QuestName; // 퀘스트 이름

    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    FString Description; // 퀘스트 설명

    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    TArray<FString> Objectives; // 퀘스트 목표 목록

    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    int32 RewardPoints; // 보상 점수

    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    bool bIsCompleted; // 완료 여부

    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    FDateTime LastCompleted; // 마지막 완료 날짜

    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    EQuestResetType ResetType; // 리셋 유형 (주간/월간 등)

    FQuest() : RewardPoints(0), bIsCompleted(false), ResetType(EQuestResetType::None) {}
};

// 퀘스트 리셋 유형 열거형
UENUM(BlueprintType)
enum class EQuestResetType : uint8
{
    None UMETA(DisplayName = "None"),
    Weekly UMETA(DisplayName = "Weekly"),
    Monthly UMETA(DisplayName = "Monthly"),
};

위 구조체에서는 각 퀘스트에 대해 ResetType을 정의하여 주간 또는 월간 리셋 기능을 추가했습니다. LastCompleted는 퀘스트가 마지막으로 완료된 시간을 추적합니다. 이 정보를 통해 리셋 타이밍을 계산할 수 있습니다.

2.2 퀘스트 리셋 및 로테이션 관리

퀘스트는 주간 또는 월간 리셋이 필요할 수 있습니다. 이를 위해 주기적인 리셋과 함께 퀘스트 내용 로테이션을 처리하는 시스템을 구현합니다.

class FQuestManager
{
private:
    TArray<FQuest> ActiveQuests; // 진행 중인 퀘스트 목록
    TMap<FString, IQuestState*> QuestStates; // 퀘스트 상태 관리

public:
    void AddQuest(const FQuest& NewQuest)
    {
        ActiveQuests.Add(NewQuest);
        QuestStates.Add(NewQuest.QuestName, new FInProgressState()); // 퀘스트를 추가하고 기본 상태 설정
    }

    void CompleteQuest(const FString& QuestName)
    {
        for (FQuest& Quest : ActiveQuests)
        {
            if (Quest.QuestName == QuestName)
            {
                QuestStates[QuestName]->CompleteQuest();
                Quest.bIsCompleted = true;
                Quest.LastCompleted = FDateTime::Now(); // 퀘스트 완료 시점 기록
                RewardPlayer(Quest.RewardPoints);
            }
        }
    }

    void RewardPlayer(int32 Points)
    {
        // 보상 지급 로직 (플레이어에게 포인트 또는 아이템을 지급)
    }

    void DisplayQuestStatus()
    {
        for (const FQuest& Quest : ActiveQuests)
        {
            FString State = QuestStates[Quest.QuestName]->GetState();
            UE_LOG(LogTemp, Log, TEXT("Quest: %s, Status: %s"), *Quest.QuestName, *State);
        }
    }

    void ResetQuests()
    {
        // 리셋 로직: 주간 또는 월간 리셋을 진행
        FDateTime CurrentDate = FDateTime::Now();
        
        for (FQuest& Quest : ActiveQuests)
        {
            if (Quest.ResetType == EQuestResetType::Weekly)
            {
                if (Quest.LastCompleted.GetWeekOfYear() != CurrentDate.GetWeekOfYear())
                {
                    Quest.bIsCompleted = false; // 주간 리셋
                    Quest.LastCompleted = CurrentDate; // 리셋 날짜 갱신
                }
            }
            else if (Quest.ResetType == EQuestResetType::Monthly)
            {
                if (Quest.LastCompleted.GetMonth() != CurrentDate.GetMonth())
                {
                    Quest.bIsCompleted = false; // 월간 리셋
                    Quest.LastCompleted = CurrentDate; // 리셋 날짜 갱신
                }
            }
        }
    }

    void RotateQuests()
    {
        // 퀘스트 로테이션 로직: 일정 기간마다 퀘스트 내용 변경
        for (FQuest& Quest : ActiveQuests)
        {
            // 예시로 퀘스트 목표 변경 (추가 또는 수정)
            if (FDateTime::Now().GetDayOfYear() % 7 == 0) // 매주 변경 예시
            {
                Quest.Objectives.Add(TEXT("새로운 목표"));
            }
        }
    }
};

위 코드에서는 ResetQuests 함수에서 주간 리셋월간 리셋을 처리합니다. LastCompleted와 비교하여 리셋을 결정합니다. RotateQuests 함수는 주기적인 퀘스트 내용 변경을 관리하는 함수로, 예를 들어 매주 퀘스트 목표를 추가하는 방식으로 로테이션을 구현할 수 있습니다.

2.3 퀘스트 및 업적 상태 관리

퀘스트와 업적 시스템에서는 다양한 상태 관리가 필요합니다. 각 퀘스트와 업적의 진행 상태를 효과적으로 추적하려면 **상태 패턴(State Pattern)**을 활용할 수 있습니다.

class IQuestState
{
public:
    virtual void StartQuest() = 0;
    virtual void CompleteQuest() = 0;
    virtual FString GetState() = 0;
};

class FInProgressState : public IQuestState
{
public:
    void StartQuest() override
    {
        // 퀘스트 시작 로직
    }

    void CompleteQuest() override
    {
        // 퀘스트 완료 처리 로직
    }

    FString GetState() override
    {
        return "In Progress";
    }
};

class FCompletedState : public IQuestState
{
public:
    void StartQuest() override
    {
        // 이미 완료된 퀘스트는 시작할 수 없음
    }

    void CompleteQuest() override
    {
        // 이미 완료된 퀘스트
    }

    FString GetState() override
    {
        return "Completed";
    }
};

IQuestState 인터페이스를 통해 퀘스트의 진행 상태를 관리하며, FInProgressStateFCompletedState 클래스를 통해 각 상태에 맞는 처리 로직을 분리합니다. 이 방식은 퀘스트의 상태 관리로직 분리를 더욱 명확하게 할 수 있습니다.


3. 최종 결론

이번 글에서는 언리얼 엔진에서 주간/월간 리셋과 퀘스트 내용 로테이션을 지원하는 업적 및 퀘스트 시스템을 설계하고, 이를 구현하기 위한 구조체, 상태 패턴, 주기적 리셋 관리 등을 다뤘습니다. 이 시스템은 게임의 목표 관리, 플레이어 보상, 그리고 게임 콘텐츠 업데이트에 유용하게 사용될 수 있으며, 확장성유지보수 용이성을 고려하여 설계되었습니다.

게임에서 퀘스트와 업적 시스템의 복잡도를 확장하고 싶다면, 이 구조를 바탕으로 다양한 기능을 추가하고 발전시킬 수 있습니다.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다