I'm making a simple 2D framework / engine for the first time based on two books and before the project gets too big I would like to know if I'm ordering things the right way. Maybe there are better ways of doing this. I tried to apply patterns so I could decouple as much as possible and keep things abstract.
For example this is the Game
class:
#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);
}
Or the Drawer class (wich I have some doubts about, the DrawEntity method is static)
#include "Drawer.h"
void Drawer::DrawEntity(Entity& entity, TextureManager* texture_manager, SDL_Renderer * renderer){
texture_manager->Draw(entity.GetSprite(), entity.GetPosition(), renderer);
}
It acts like a mediator between TexureManager(wich draws texures) and Entity wich has all the information like position, scale, etc.
Finally the example input class
#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;
}
I'm going to upload a dependency diagram as well as the GitHub repo: enter image description here
Github Repo with all the Code
-
4\$\begingroup\$ Long code isn't frowned upon on code review, you should post your full project here \$\endgroup\$user228914– user2289142020年10月13日 21:00:41 +00:00Commented Oct 13, 2020 at 21:00
1 Answer 1
Update:
Well, if someone wants to know, I've done some changes for good,
one of the main problems I had was that I couldn't really "connect" the GameStates to the main Engine itself without passing a pointer to the engine (wich is messy) or individual pointers to the InputHandler
, Display
, etc.
The thing was that for example the InputHandler handled the Window events and the keyboard input, so for a GameState to have access to it, I would have to create a pointer to Game::InputHandler, so I did a few things:
First I created a KeyboardHandle
class wich is totally independent from the Engine itself and its only function its to get keyboard input, that way I can make every game state to have an independent KeyboardHandle
, also every game state has a pointer to the engine Display
class, that means that from the game state you can get access to information like windows size or the SDL_Renderer
without having much trouble, you add states from the Engine
so the Display
pointer is set at that moment.
The GameState
class is the base for all games, now it looks like this:
#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;
};
As you can see is almost all independent from the Engine
, it just shares the display so we can render the graphics.
Here is an example state (drawing an image on the center of the screen):
#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());
}
};
And just for fun here is the dependency diagram: Dependency Diagram