Framework de jeu 2D en C ++ et SDL
Je crée pour la première fois un cadre / moteur 2D simple basé sur deux livres et avant que le projet ne devienne trop grand, j'aimerais savoir si je commande les choses de la bonne manière. Il existe peut-être de meilleures façons de faire cela. J'ai essayé d'appliquer des motifs pour pouvoir découpler autant que possible et garder les choses abstraites.
Par exemple, voici la Gameclasse:
#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);
}
Ou la classe Drawer (sur laquelle j'ai des doutes, la méthode DrawEntity est statique)
#include "Drawer.h"
void Drawer::DrawEntity(Entity& entity, TextureManager* texture_manager, SDL_Renderer * renderer){
texture_manager->Draw(entity.GetSprite(), entity.GetPosition(), renderer);
}
Il agit comme un médiateur entre TexureManager (qui dessine des textures) et Entity qui a toutes les informations comme la position, l'échelle, etc.
Enfin l'exemple de classe d'entrée
#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;
}
Je vais télécharger un diagramme de dépendances ainsi que le dépôt GitHub:
Github Repo avec tout le code
https://github.com/grazianobolla/monke-engine
Réponses
Mise à jour:
Eh bien, si quelqu'un veut savoir, j'ai fait quelques changements pour de bon, l'un des principaux problèmes que j'ai eu était que je ne pouvais pas vraiment "connecter" les GameStates au moteur principal lui-même sans passer un pointeur vers le moteur (qui est en désordre) ou des pointeurs individuels au InputHandler, Displayetc.
Le fait était que, par exemple, InputHandler gérait les événements Window et l'entrée au clavier, donc pour qu'un GameState y ait accès, je devais créer un pointeur vers Game :: InputHandler, donc j'ai fait quelques choses:
J'ai d'abord créé une KeyboardHandleclasse qui est totalement indépendante du moteur lui-même et sa seule fonction est d'obtenir une entrée au clavier, de cette façon je peux faire en sorte que chaque état du jeu ait un état indépendant KeyboardHandle, chaque état du jeu a également un pointeur vers la Displayclasse du moteur , cela signifie qu'à partir de l'état du jeu, vous pouvez accéder à des informations telles que la taille des fenêtres ou le SDL_Renderersans avoir beaucoup de problèmes, vous ajoutez des états à partir du Engineafin que le Displaypointeur soit défini à ce moment.
La GameStateclasse est la base de tous les jeux, maintenant elle ressemble à ceci:
#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;
};
Comme vous pouvez le voir, il est presque entièrement indépendant du Engine, il partage simplement l'affichage afin que nous puissions rendre les graphiques.
Voici un exemple d'état (dessin d'une image au centre de l'écran):
#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());
}
};
Et juste pour le plaisir, voici le diagramme de dépendance: