Unreal Engine 들어가기전에 C++로 Text RPG를 만든다.
최대한 Unreal Engine이 사용하는 구조, 문법을 따르게 구조를 잡았다.
https://github.com/leeseungjae97/TEXT_RPG_5

실행 구조

현재 실행 루프는 GameInstance가 돌리고 각 Manager의 BeginPlay, Tick을 호출한다.

void GameInstance::BeginPlay()
{
    InputManager::GetInstance()->BeginPlay();
    RenderManager::GetInstance()->BeginPlay();
    TimeManager::GetInstance()->BeginPlay();

    SceneManager::GetInstance()->BeginPlay();
    MapManager::GetInstance()->BeginPlay();
    BattleManager::GetInstance()->BeginPlay();
}

void GameInstance::Tick()
{
    RenderManager::GetInstance()->ClearScreen();

    TimeManager::GetInstance()->Tick();
    double DeltaTime = TimeManager::GetInstance()->GetDeltaTime();
    InputManager::GetInstance()->Tick(DeltaTime);

    BattleManager::GetInstance()->Tick(DeltaTime);
    SceneManager::GetInstance()->Tick(DeltaTime);
    MapManager::GetInstance()->Tick(DeltaTime);

    RenderManager::GetInstance()->Tick(DeltaTime);
}

오브젝트와 컴포넌트

AObject가 직접 모든 기능을 들고 가는 대신, 필요한 기능을 CreateDefaultComponent()로 붙이는 방식으로 바뀌었다.

template
T* CreateDefaultComponent()
{
    if (Components.empty())
        Components.resize((int)ComponentType::MAX);

    const int Index = static_cast(T::Type);
    if (Components[Index])
        return static_cast(Components[Index].get());

    unique_ptr NewComponent(new T(this));
    T* RawPtr = NewComponent.get();
    Components[Index] = move(NewComponent);
    return RawPtr;
}

그리고 AObject::Tick()은 자기 자신이 직접 모든 걸 처리하지 않고,
붙어 있는 컴포넌트를 순회하면서 틱을 넘긴다.

void AObject::Tick(float DeltaTime)
{
    if (Components.empty())
        return;

    for (int i = 0; i < Components.size(); ++i)
    {
        if (Components[i])
            Components[i]->Tick(DeltaTime);
    }
}

맵과 렌더

맵 쪽은 MapManager, 렌더는 RenderManager가 맡는다.
현재 맵은 2차원 타일 배열을 유지하고, SceneManager가 가진 오브젝트 좌표를 다시 반영하는 방식이다.

void MapManager::UpdateMap()
{
    vector Objects = SceneManager::GetInstance()->GetObjects();

    for (AObject* Obj : Objects)
    {
        Vector PrevPos = Obj->GetPrevPosition();
        Map[PrevPos.Y][PrevPos.X] = 1;

        Vector Pos = Obj->GetPosition();
        if (Pos.Y < 0 || Pos.Y > MAP_MAX_Y - 1 || Pos.X < 0 || Pos.X > MAP_MAX_X - 1)
        {
            Obj->SetPosition(PrevPos);
            continue;
        }

        if (Monster* monster = dynamic_cast(Obj))
            Map[Pos.Y][Pos.X] = 4;
        else
            Map[Pos.Y][Pos.X] = 3;
    }
}