2D Game Framework ใน C ++ และ SDL

Oct 14 2020

ฉันกำลังสร้างเฟรมเวิร์ก / เอนจิ้น 2 มิติอย่างง่ายเป็นครั้งแรกโดยอิงจากหนังสือสองเล่มและก่อนที่โปรเจ็กต์จะใหญ่เกินไปฉันอยากรู้ว่าฉันสั่งของถูกทางหรือเปล่า อาจมีวิธีที่ดีกว่านี้ในการทำเช่นนี้ ฉันพยายามใช้รูปแบบเพื่อให้ฉันสามารถแยกส่วนให้มากที่สุดเท่าที่จะเป็นไปได้และทำให้สิ่งต่าง ๆ เป็นนามธรรม

ตัวอย่างเช่นนี่คือGameคลาส:

#include "Game.h"

void Game::Create(int width, int height, const char * title, int window_flag, int renderer_flag){
      display.CreateWindow(800,600,"Game", SDL_WINDOW_SHOWN, SDL_RENDERER_ACCELERATED);
}

void Game::Start(){
      Uint32 first_frame_ticks; float delta_time = 0;
      SDL_Renderer * renderer = display.GetRenderer();
      while(input_handler.GetLastEvent() != SDL_QUIT){
            first_frame_ticks = SDL_GetTicks();

            bool state_manager_empty = state_manager.isEmpty();
            //update
            input_handler.Update();
            if(!state_manager_empty) state_manager.GetGameState()->onUpdate(delta_time);

            //render
            SDL_RenderClear(renderer);
            if(!state_manager_empty) state_manager.GetGameState()->onRender();
            SDL_RenderPresent(renderer);

            delta_time = (SDL_GetTicks() - first_frame_ticks) / 1000.0f;
      }

      display.Clear();
}

void Game::AddState(GameState* state){
      state->Set(display.GetRenderer(), &input_handler);
      state_manager.Add(state);
}

หรือคลาส Drawer (ซึ่งฉันมีข้อสงสัยเกี่ยวกับเมธอด DrawEntity เป็นแบบคงที่)

#include "Drawer.h"

void Drawer::DrawEntity(Entity& entity, TextureManager* texture_manager, SDL_Renderer * renderer){
      texture_manager->Draw(entity.GetSprite(), entity.GetPosition(), renderer);
}

มันทำหน้าที่เหมือนคนกลางระหว่าง TexureManager (ซึ่งจะดึง texures) และ Entity ซึ่งมีข้อมูลทั้งหมดเช่นตำแหน่งมาตราส่วน ฯลฯ

ในที่สุดคลาสอินพุตตัวอย่าง

#include "InputHandler.h"
#include <cstdio>

InputHandler::InputHandler(){
      keyboard_state = SDL_GetKeyboardState(NULL);
      printf("Event handler initialized\n");
}

void InputHandler::Update(){
      SDL_Event ev;
      while(SDL_PollEvent(&ev)){
            if(ev.type == SDL_KEYUP || ev.type == SDL_KEYDOWN){
                  keyboard_state = SDL_GetKeyboardState(NULL);
            }else last_event = ev.type;
      }
}

bool InputHandler::isKeyPressed(SDL_Scancode key){
      if(keyboard_state[key]) return true;
      else return false;
}

Uint32 InputHandler::GetLastEvent(){
      return last_event;
}

ฉันจะอัปโหลดแผนภาพการอ้างอิงรวมถึง repo GitHub:

Github Repo พร้อมรหัสทั้งหมด

https://github.com/grazianobolla/monke-engine

คำตอบ

GrazianoBolla Oct 15 2020 at 06:01

อัปเดต:

ถ้ามีคนอยากรู้ฉันได้ทำการเปลี่ยนแปลงบางอย่างไปแล้วปัญหาหลักอย่างหนึ่งที่ฉันมีก็คือฉันไม่สามารถ "เชื่อมต่อ" GameStates กับ Engine หลักได้โดยไม่ต้องส่งตัวชี้ไปที่เอ็นจิ้น (ซึ่ง จะยุ่ง) หรือตัวชี้บุคคลที่InputHandler, Displayฯลฯ

สิ่งนั้นก็คือตัวอย่างเช่น InputHandler จัดการเหตุการณ์ของหน้าต่างและอินพุตแป้นพิมพ์ดังนั้นเพื่อให้ GameState เข้าถึงได้ฉันจะต้องสร้างตัวชี้ไปที่ Game :: InputHandler ดังนั้นฉันจึงทำบางสิ่ง:

ก่อนอื่นฉันสร้างKeyboardHandleคลาสที่เป็นอิสระจาก Engine เองโดยสิ้นเชิงและมีหน้าที่เดียวในการรับอินพุตคีย์บอร์ดด้วยวิธีนี้ฉันสามารถทำให้ทุกสถานะของเกมมีความเป็นอิสระKeyboardHandleและทุกสถานะของเกมจะมีตัวชี้ไปที่Displayคลาสเอนจิ้น จากสถานะเกมคุณสามารถเข้าถึงข้อมูลเช่นขนาดหน้าต่างหรือSDL_Rendererโดยไม่ต้องมีปัญหามากคุณจะเพิ่มสถานะจากEngineเพื่อให้Displayตัวชี้ถูกตั้งค่าในขณะนั้น

GameStateระดับเป็นฐานสำหรับทุกเกมที่ตอนนี้ก็มีลักษณะเช่นนี้

#pragma once
#include "KeyboardHandle.h"
#include "TextureManager.h"
#include "Display.h"

class GameState{
protected:
      KeyboardHandle keyboard;
      TextureManager texture_manager;
      Display * screen_display;
public:
      GameState(Display * display){ this->screen_display = display; }
      virtual void onStart() = 0;
      virtual void handleInput() = 0;
      virtual void onUpdate(float) = 0;
      virtual void onRender() = 0;
};

อย่างที่คุณเห็นเกือบทั้งหมดเป็นอิสระจากEngineมันเพียงแค่แชร์การแสดงผลเพื่อให้เราสามารถแสดงผลกราฟิกได้

นี่คือสถานะตัวอย่าง (การวาดภาพตรงกลางหน้าจอ):

#include "GameState.h"
#include "Entity.h"

class DefaultState : public GameState{
      Entity splash_logo;
public:
      DefaultState(Display * display) : GameState(display){ }

      void onStart(){
            texture_manager.Load("textures/splash_logo.png", "splash_logo", screen_display->GetRenderer());
            splash_logo = Entity("splash_logo", Vector2i(0, 0), Vector2i(336, 336), 1.0f);

            float center_x = (screen_display->GetWindowWidth() / 2) - splash_logo.GetCollisionBox().w / 2;
            float center_y = (screen_display->GetWindowHeight() / 2) - splash_logo.GetCollisionBox().h / 2;
            splash_logo.SetPosition({center_x, center_y});
      }

      void handleInput(){

      }

      void onUpdate(float delta_time){

      }

      void onRender(){
            texture_manager.Draw(splash_logo.GetSprite(), splash_logo.GetPosition(), screen_display->GetRenderer());
      }
};

และเพื่อความสนุกสนานนี่คือแผนภาพการพึ่งพา: