Make Unreal REAL.
article thumbnail
이득우의 게임 수학

 

게임 엔진이 제공하는 실행 흐름을 워크플로우(Workflow)라고 한다.

 

 

책의 예제인 CK소프트렌더러는 총 4단계의 워크플로우를 제시한다.

  1. 리소스 로딩
    메모리에 게임 진행에 필요한 메시, 텍스처 등의 리소스를 미리 올려둔다.
  2. 씬 구축
    씬에 속한 게임 오브젝트를 생성하고 트랜스폼 정보가 설정되며, 리소스 정보가 게임 오브젝트에 연결된다.

  3. 게임 로직
    게임 오브젝트의 트랜스폼을 변경하는 작업이다.
  4. 렌더링 로직
    게임 로직에서 완성된 씬 정보를 바탕으로 최종 화면을 그려내는 과정이다.
    게임 오브젝트의 트랜스폼 정보와 오브젝트에 연결된 메시, 텍스처 등의 리소스를 활용한다.
  5. 매 프레임마다  3 -> 4 과정을 반복한다.

 

1. 리소스 로딩 단계

  • 불러들인 리소스에 고유한 키를 설정한다.

 

// 책의 예제인 CK소프트렌더러에 구현된 리소스 로딩 단계

// 메시의 리소스 키
const size_t GameEngine::QuadMesh = hash<string>()("SM_Quad");

// 텍스처의 리소스 키
const size_t GameEngine::BaseTexture = hash<string>()("Base");
// 텍스처의 파일 경로
const string GameEngine::CharacterTexturePath("CKMan.png");

// 리소스 로딩
bool GameEngine::LoadResources()
{
    // GameEngine::QuadMesh를 리소스 키로 갖는 사각형 메시 생성
    Mesh& quadMesh = CreateMesh(GameEngine::QuadMesh);

    constexpr float squareHalfSize = 0.5f;
    constexpr int vertexCount = 4;
    constexpr int triangleCount = 2;
    constexpr int indexCount = triangleCount * 3;

    auto& v = quadMesh.GetVertices();
    auto& i = quadMesh.GetIndices();
    auto& uv = quadMesh.GetUVs();

    v = {
        Vector2(-squareHalfSize, -squareHalfSize),
        Vector2(-squareHalfSize, squareHalfSize),
        Vector2(squareHalfSize, squareHalfSize),
        Vector2(squareHalfSize, -squareHalfSize)
    };

    uv = {
        Vector2(0.125f, 0.75f),
        Vector2(0.125f, 0.875f),
        Vector2(0.25f, 0.875f),
        Vector2(0.25f, 0.75f)
    };

    i = {
        0, 2, 1,
        0, 3, 2
    };

    quadMesh.CalculateBounds();

    // GameEngine::BaseTexture를 리소스 키로 갖는 CharacterTexturePath 경로의 텍스처 생성
    Texture& baseTexture = CreateTexture(GameEngine::BaseTexture, GameEngine::CharacterTexturePath);
    if (!baseTexture.IsIntialized())
    {
        return false;
    }

    return true;
}

 

2. 씬 구축 단계

  • 이름을 통해 게임 오브젝트를 생성하고 메시 리소스 키, 트랜스폼 정보 등을 설정한다.

 

// 책의 예제인 CK소프트렌더러에 구현된 씬 구축 단계

// 게임 오브젝트 목록
static const string PlayerGo("Player");

// 씬 구축
void SoftRenderer::LoadScene2D()
{
    // 최초 씬 로딩에서 사용하는 모듈 내 주요 레퍼런스
    auto& g = Get2DGameEngine();

    // 플레이어의 생성과 설정
    // 플레이어 게임 오브젝트의 최초 크기
    constexpr float playerScale = 30.f;
    // PlayerGo 이름을 갖는 플레이어 게임 오브젝트 생성
    GameObject& goPlayer = g.CreateNewGameObject(PlayerGo);
    // GameEngine::QuadMesh를 메시의 리소스 키로 설정
    goPlayer.SetMesh(GameEngine::QuadMesh);
    // 최초 트랜스폼 설정
    goPlayer.GetTransform().SetScale(Vector2::One * playerScale);
    // 와이어프레임 모드에서의 색 설정
    goPlayer.SetColor(LinearColor::Red);

    // 100개의 배경 게임 오브젝트 생성과 설정
    char name[64];
    // 배경 게임 오브젝트의 최초 크기
    constexpr float squareScale = 20.f;
    mt19937 generator(0);
    uniform_real_distribution<float> dist(-1000.f, 1000.f);
    for (int i = 0; i < 100; ++i)
    {
        snprintf(name, sizeof(name), "GameObject%d", i);
        GameObject& newGo = g.CreateNewGameObject(name);
        newGo.GetTransform().SetPosition(Vector2(dist(generator), dist(generator)));
        newGo.GetTransform().SetScale(Vector2::One * squareScale);
        newGo.SetMesh(GameEngine::QuadMesh);
        newGo.SetColor(LinearColor::Blue);
    }
}

 

3. 게임 로직 단계

  • 실시간 사용자 입력을 통해 트랜스폼 정보를 변경한다.

 

// 책의 예제인 CK소프트렌더러에 구현된 게임 로직 단계

// 게임 로직
void SoftRenderer::Update2D(float InDeltaSeconds)
{
    // 게임 로직에서 사용하는 모듈 내 주요 레퍼런스
    auto& g = Get2DGameEngine();
    const InputManager& input = g.GetInputManager();

    // 게임 로직의 로컬 변수
    static float moveSpeed = 200.f;
    static float rotateSpeed = 180.f;
    static float scaleMin = 15.f;
    static float scaleMax = 30.f;
    static float scaleSpeed = 180.f;

    // 플레이어에 대한 주요 레퍼런스
    GameObject& goPlayer = g.GetGameObject(PlayerGo);
    TransformComponent& transform = goPlayer.GetTransform();

    // 입력에 따른 플레이어 위치와 크기의 변경
    float newScale = Math::Clamp(transform.GetScale().X + scaleSpeed * input.GetAxis(InputAxis::ZAxis) * InDeltaSeconds, scaleMin, scaleMax);
    transform.SetScale(Vector2::One * newScale);
    transform.AddRotation(input.GetAxis(InputAxis::XAxis) * rotateSpeed * InDeltaSeconds);
    transform.AddPosition(transform.GetLocalY() * input.GetAxis(InputAxis::YAxis) * moveSpeed * InDeltaSeconds);
}

 

4. 렌더링 로직 단계

  • 씬에 속한 모든 게임 오브젝트를 순회하면서, 각 오브젝트의 트랜스폼 정보로부터 생성된 모델링 행렬을 통해 화면에 오브젝트들을 그린다.

 

// 책의 예제인 CK소프트렌더러에 구현된 렌더링 로직 단계

// 렌더링 로직
void SoftRenderer::Render2D()
{
    // 렌더링 로직에서 사용하는 모듈 내 주요 레퍼런스
    auto& r = GetRenderer();
    const auto& g = Get2DGameEngine();

    // 배경에 격자 그리기
    DrawGizmo2D();

    // 씬에 속한 전체 게임 오브젝트의 개수
    size_t totalObjectCount = g.GetScene().size();

    // 씬을 구성하는 모든 게임 오브젝트 순회
    for (auto it = g.SceneBegin(); it != g.SceneEnd(); ++it)
    {
        // 게임 오브젝트의 레퍼런스를 얻기
        const GameObject& gameObject = *(*it);
        // 메시가 없거나, Visible 속성이 없으면 그리지 않는다.
        if (!gameObject.HasMesh() || !gameObject.IsVisible())
        {
            continue;
        }

        // 렌더링에 필요한 게임 오브젝트의 주요 레퍼런스를 얻기
        // 게임 오브젝트에 설정된 메시 리소스 키로 가져온 메시
        const Mesh& mesh = g.GetMesh(gameObject.GetMeshKey());
        // 게임 오브젝트의 트랜스폼
        const TransformComponent& transform = gameObject.GetTransform();
        // 게임 오브젝트의 모델링 행렬
        Matrix3x3 finalMatrix = transform.GetModelingMatrix();

        // 게임 오브젝트의 메시, 모델링 행렬, 색상 정보를 이용해 렌더링 수행
        // DrawMesh2D() 함수에서는 기본적으로 GameEngine::BaseTexture 리소스 키를 통해 텍스처를 가져와 그린다.
        DrawMesh2D(mesh, finalMatrix, gameObject.GetColor());
    }
}

 

리소스 로딩 -> 씬 구축 -> 게임 로직 -> 렌더링 로직 -> 게임 로직 -> 렌더링 로직 -> ... 단계를 거쳐, 1개의 메시 리소스와 1개의 텍스처 리소스를 공유하는 101개의 플레이어 게임 오브젝트를 그려냈다.

 

'게임 수학 > 이득우의 게임 수학' 카테고리의 다른 글

카메라 시스템  (0) 2023.05.03
렌더링 파이프라인  (0) 2023.05.02
리소스 저장소  (0) 2023.04.30
로컬 공간과 로컬 축  (0) 2023.04.29
모델링 행렬의 설계  (0) 2023.04.28
profile

Make Unreal REAL.

@diesuki4

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!

검색 태그