Advertisement

[C++ SDL] Is there anything wrong with my game loop?

Started by June 27, 2011 10:41 AM
0 comments, last by rip-off 13 years, 5 months ago
Okay so I've just tried making some menus with my game loop but I got the buttons to work just fine before but now, it doesn't show the image when I click the button, it seems to be loading quite fine. Is there anything wrong with the game loop, or is there any easier way to make menus in SDL?
My main.cpp
#include "miguelsengine.h"
int main(int argc, char *args[])
{
APP main_game;
//load the settings
main_game.load_file("settings/window_info.txt");
//system set up...
main_game.Setup();
return main_game.main_loop();
}

and the actual APP class, which is derived from a system class...

the .h for APP
#pragma once

#include "System.h"
#include "Timer.h"
class APP : public System{
Char pone;
Tile BG;
Timer fps;
Button bPlay;
Button bExit;
int menuState;
public:
APP();

//inits everything (loads)
bool Init();
//single frame for menu..
int menu();
//single frame for game..
int game();
//clears screen
bool clear_screen();
//exits the game (clears everything up, sets Done to true)
void exit();
//handles the input...
void handle_menu_input();
void handle_game_input();
//the main loop...
virtual int main_loop();
};


the .cpp for APP
#include "APP.h"
APP::APP(){
menuState = 0;
}
bool APP::Init(){
//player set up...
if(!pone.setup("images/sprites/AWESOME.png", 32, 48, false, true)){
fprintf(stderr,"ERROR: cannot load image for player\n");
return false;
}
//set poisition and collision for player...
pone.load_info("settings/pone_settings.txt");
//pone.set_collision(0,0,32,48);

// background setup...
if(!BG.setup("images/sprites/grassandsticks.png", 32, 32, false, true)){
fprintf(stderr,"ERROR: cannot load background image\n");
return false;
}
bPlay.load_info("settings/play_button_info.txt");
bExit.load_info("settings/exit_button_info.txt");
fps.start();
return true;
}

int APP::menu(){
if(!bPlay.Draw(screen)){
return 3;
}
if(!bExit.Draw(screen)){
return 4;
}
return 0;
}
int APP::game(){
pone.move();
if(!pone.Draw(screen, pone.get_frameno())){
return 3;
}
return 0;
}

bool APP::clear_screen(){
SDL_Rect temp;
temp.x = 0;
temp.y = 0;
temp.w = get_screen_w();
temp.h = get_screen_h();
if(SDL_FillRect(screen,&temp,SDL_MapRGB(screen->format,0,0,0)) == -1){
return false;
}
return true;
}
//exit function
void APP::exit(){
pone.destroy();
BG.destroy();
bPlay.destroy();
bExit.destroy();
}

//handle input functions
void APP::handle_game_input(){
pone.handle_input(&in_event);
}
void APP::handle_menu_input(){
if(in_event.type == SDL_MOUSEMOTION){
bPlay.forceUpdateButtonIMG(&in_event);
bExit.forceUpdateButtonIMG(&in_event);
}
}

//main loop
int APP::main_loop(){
if(!Init()){
//first error that could occur...
return 1;
}
int error = 0;
for(int frame = 0; GetDone() != true; frame++){
//poll event...
while(SDL_PollEvent(&in_event)){
handle_input(&in_event);
if(menuState == 0){
handle_menu_input();
}else if(menuState == 1){
handle_game_input();
}
}
if(menuState == 0){
if(error = menu() != 0){
break;
}
}else if(menuState == 1){
if(error = game() != 0){
break;
}
}else{
break;
}
RenderScreen();
//limit the frames per second its drawn...
if(fps.get_ticks() < 1000 / FRAMES_PER_SECOND){
SDL_Delay( (1000 / FRAMES_PER_SECOND) - fps.get_ticks() );
}
//reset the frame number...
if(frame >= FRAMES_PER_SECOND) frame = 0;
frame++;
}

exit();
return 0;
}


and also the .h for the system class:
#pragma once
#ifndef SYSTEM_H
#define SYSTEM_H

#include "sdlstuff.h"
#include "input.h"
#include "IMG.h"
#include "Crop.h"
#include "Tile.h"
#include "Physics.h"
#include "anim.h"
#include "Character.h"
#include "Button.h"
#include <fstream>
#include <string>

class System : public Input
{
protected:
int screen_w, screen_h, screen_bpp;
bool fullscreen, Done;
SDL_Event in_event;
SDL_Surface* screen;
std::string window_text;
public:
//default constructor
System(void);
System(const System &Obj);
//custom constructor from a file...
System(std::string filename);
//custom constructor
System(int w, int h, int bpp, bool fs = false);
//virtual default destructor
virtual ~System(void);

//quits the application by calling SDL_Quit() and other things
void quit();
//sets up the screen and etc...
bool Setup();
bool Setup(char* titlebar, bool fs = false);
//sets up the screen depending if its fullscreen or not...
bool changescreen();

//draws the screen to the window
bool RenderScreen();

//virtual functions...
//handles input
virtual void handle_input(SDL_Event *event);
virtual int main_loop(){
//pure virtual
return 0;
}
//getters
int get_screen_w();
int get_screen_h();
int get_screen_bpp();
bool GetDone();
std::string get_window_text();
//setters
void set_screen_w(int w);
void set_screen_h(int h);
void set_screen_bpp(int bpp);
void set_Done(bool B = true);
void set_fullscreen(bool f = true);
//window_text functions
void set_window_text(std::string text);
void update_window_title();
//load function
bool load_file(std::string filename);
};
#endif
The basics look all right.

Here are some "big" things you might want to look at:

  • Deriving APP from System from Input. You shouldn't need all this inheritance. Try think in terms of composition instead (APP contains a Game, a Menu, a Window and an Input...).

  • Use the Single Responsibility Principle to guide your design. Your classes do too much.

  • I would be pretty impressed if you managed to implement a sane copy constructor for the System class! I don't know how I would do it, how do you copy the screen? I think that your copy constructor is probably incorrect, and that your class should be noncopyable. Don't forget to obey the rule of three.

  • You have so many "initialisation" function.

    • There are constructors, Init(), Setup(), load_file(), load_info()...
    • I wouldn't know what order to call them in.
    • I might forget to call one

      Consider doing as much as possible in the constructor, because it is the hardest to forget about! Though it is also the most awkward, because any errors must be reported as exceptions (or stored in the object, but that can be nastier). At the very least provide only a single, consistent non-constructor initialisation function. Consider loading the configuration from a file before constructing the objects. Likewise consider using the destructor rather than an exit() function.

    • You shouldn't need to pass a Character's "frame number" into its own Draw() method. It has the frame number inside it, so it can manage it itself.

    • I am suspicious of the order of precendence in the expression "error = menu() != 0". Use parentheses to clarify your intent, or split the expression into a statement and the conditional after.

    • APP seems to contain two different sets of logic, one for the menu and one for the game. One solution to this is to create a class to represent the menu and the game, and have APP simply call whichever is "active".


      Minor stylistic issues.

      • APP conflicts with your established naming scheme. Consider "Application" or "Game" instead.

      • APP.h lacks include guards, but you're using them in System.h. Aim for consistency, use them everywhere or nowhere. The portable thing is to use them everywhere.

      • System::Setup takes a (non-const) character pointer. Consider using std::string to match the rest of your app?ication.

      • Consider passing std::string instances by reference.

      • The for loop setup, condition and progress statements are unrelated in APP::main_loop. Consider using a while loop instead. Particularly odd is resetting the value every so often - it makes sense for what you want to do but it makes your loop look really unusual.

      • Pass SDL_Events by value or const reference, not by non-const pointer.

      • You use integers for error conditions, but this only makes sense if you have a known mapping from error conditions to solutions. There doesn't appear to be a consistent one in your application. One option is to make a big error enum, another is to use exceptions with meaningful types and error strings. Finally, you could just rely on the logs and return boolean values.

      • I prefer to have only one place in my application where I convert to C's unusual "main returning 0 is success" rule. The rest of my application usually uses booleans or exceptions.

This topic is closed to new replies.

Advertisement