2D Game Framework на C ++ и SDL
Я впервые делаю простой 2D-фреймворк / движок на основе двух книг, и, прежде чем проект станет слишком большим, я хотел бы знать, правильно ли я все упорядочиваю. Может быть, есть лучшие способы сделать это. Я пытался применять шаблоны, чтобы можно было максимально отделить друг от друга и сохранить абстрактность.
Например, это 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 (который рисует текстуры) и 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;
}
Я собираюсь загрузить диаграмму зависимостей, а также репозиторий GitHub:
Github Repo со всем кодом
https://github.com/grazianobolla/monke-engine
Ответы
Обновить:
Что ж, если кто-то хочет знать, я сделал некоторые изменения навсегда, одна из главных проблем, которые у меня были, заключалась в том, что я не мог действительно «подключить» GameStates к самому основному движку, не передав указатель на движок (который грязен) или отдельные указатели на InputHandler, Displayи т.д.
Дело в том, что, например, InputHandler обрабатывал события Window и ввод с клавиатуры, поэтому для того, чтобы GameState имел к нему доступ, мне пришлось бы создать указатель на Game :: InputHandler, поэтому я сделал несколько вещей:
Сначала я создал KeyboardHandleкласс, который полностью независим от самого движка, и его единственная функция - ввод с клавиатуры, таким образом я могу сделать каждое игровое состояние независимым 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());
}
};
И для развлечения вот диаграмма зависимостей: