What I'm trying to do is sort of recreate the begginning of a pokemon game, but I don't seem to know how to load the different events. Does anyone have any idea ? They both load at the same time btw, and not just one at a time.
You're calling SDL_Flip(screen) in between of Polling events, which is bad, Whenever there is an event like moving mouse, It will flip the screen, You would get weird results.
Instead you could use an enumeration and traverse using that.
enum Gamestate
{
Gender,
Main,
Exit,
};
//Create a gamestate so that only the things you are viewing at the current state will only be shown
int main(int argc, char** argv )
{
Pokemon myPokemon;
Gender myGender;
//Initialize..
Gamestate current = Gender; //Now the game will start in the Gender section
while( current != Exit )
{
while(PollEvent)
{
if(event.type == SDL_QUIT)
{
current = Exit; //Exit out of the game;
}
switch(current) //Now for each Gamestate you can handle events seperately
{
case Gender:
//Handle quitting.
if(Gender)
{
current = Main;
}
break;
case Main:
Pokemon.HandleEvents();
break;
}
}
switch(current)
{
case Gender:
myGender.Draw();
break;
case Main:
myPokemon.Draw();
break;
}
SDL_Flip(screen);
}
}
The problem here is that when the gender selection completes, your code allows for the pokemon selection to run "immediately". What I mean here is that despite the delay call, the same event is consumed twice, once by the Gender instance and once by the PokemonChoice. To avoid this, you need to structure your code such that each event causes only one sequence of actions.
JustinDaniel's code is one way of doing this. Another is to have a boolean indicating which is active that persists between calls:
Note that in the above code it is impossible for the same event to be processed by both instances. JustinDaniel's approach is more general and will be easier to manage when more states are added.
Fundamentally your problem is unrelated to flipping the screen while processing events. That said, I think you need to make an architectural decision on what style of game you are going to write. If the game is event driven, then you should use a blocking event loop and ensure your game can handle SDL_VIDEOEXPOSE events or similar. Alternatively, write your game as a simulation loop and simply re-draw the screen every frame. The latter is usually easier to write, although wouldn't be a good idea if you were targeting battery powered devices, such as smart phones.
Either way, I would advise against using long SDL_Delays to implement timing based systems. Instead, your code should use SDL_Timers or calls to SDL_GetTicks() with periodic polling. This keeps your user interface nice and responsive. When you call SDL_Delay(), you lose the ability to respond to events in that time. When the user interface becomes unresponsive, users frequently start clicking wildly, and will eventually hit the "close" button. When you application wakes up, it tries to process all these events out of context, and usually ends up doing a bunch of stuff that the user didn't really intend you to do.
It is a state machine. The different states are represented by different enumeration values. The active state is stored by the value of "current", and transitions are signalled by changing the value of "current" to the desired next state.
Is there any specific part you are having trouble understanding? Perhaps you haven't encountered enumerations yet, or the switch statement? Or is it some broader concept?
switch(current) //Now for each Gamestate you can handle events seperately
{
case Gender:
//Handle quitting.
if(Gender)
{
current = Main;
}
break;
case Main:
Pokemon.HandleEvents();
break;
}
}
switch(current)
{
case Gender:
myGender.Draw();
break;
case Main:
myPokemon.Draw();
break;
}
I don't understand the switch statement part, I know how to use switch statements but this is confusing :S The second switch statement in specific
One problem is that the enumeration names are colliding with your class name. You can disambiguate using the "class" keyword, but instead you might consider changing the enumeration names - e.g. GenderState or GENDER.
Note that the if(Gender) test in your program was supposed to be a piece of psuedo code indicating that when the change from the Gender screen to the Pokemon screen is made.
Here is your first post rewritten as a state machine (with many other things too):
// It is conventional to include standard headers first (unless foo.cpp is including foo.h)
// Also note the space between the include and the <> or "". This makes it easier to read.
#include <string>
#include <iostream>
#include <algorithm>
// To be cross platform, you should omit the SDL/ directory here.
// To do this, you'll have to configure your compiler though.
#include "SDL/SDL.h"
#include "SDL/SDL_image.h"
using namespace std;
// Most coding conventions use UpperCase or ALL_CAPS for constants
// And camelCase for variables.
const int ScreenWidth = 640;
const int ScreenHeight = 480;
const int ScreenBPP = 32;
// Note: Global variables removed! (global constants are fine).
SDL_Surface* load_image( string filename )
{
// Delay declarations of variables until you can initialise them
SDL_Surface* image = IMG_Load( filename.c_str() );
// Sometimes it can be more convenient to "early out" rather than
// make a deeply nested set of NULL checks
if(!image)
{
return NULL;
}
// In your original code, failing to optimise the image would result in NULL
// being returned. This seems a little harsh given that the image loaded successfully!
// In this implementaiton, failing to optimise means that the unoptimised image is returned instead.
SDL_Surface* temp = SDL_DisplayFormat( image );
if(temp)
{
std::swap(image, temp);
SDL_FreeSurface(temp);
}
void apply_surface(int x, int y, SDL_Surface* source, SDL_Surface* destination, SDL_Rect* clip = NULL)
{
SDL_Rect offset = { x, y };
SDL_BlitSurface(source, clip, destination, &offset);
}
// You had duplicated the following code in your program 5 times!
// In fact, three of these contained a bug (it used the rectange width instead of height for the last check)
// Writing the logic once makes it easier to read, test and correct bugs.
bool isPointInsideRectangle(int x, int y, SDL_Rect rectangle)
{
return (x > rectangle.x) && (x < rectangle.x + rectangle.w) && (y > rectangle.y) && (y < rectangle.y + rectangle.h);
}
// Instead of returning a boolean, we can return the screen surface
// This avoids a global variable.
SDL_Surface *init()
{
if(SDL_Init(SDL_INIT_EVERYTHING) == -1)
{
return NULL;
}
SDL_Surface *screen = SDL_SetVideoMode(ScreenWidth, ScreenHeight, ScreenBPP, SDL_SWSURFACE);
if(!screen)
{
// If there is a failure here, we still need to ensure SDL is shut down.
SDL_Quit();
return 0;
}
SDL_WM_SetCaption("Choose your DECISION", NULL);
return screen;
}
void clean_up()
{
SDL_Quit();
}
class Gender
{
public:
Gender();
~Gender();
// Allows us to determine if the instance loaded correctly.
bool isValid() const;
// Handles one event at a time, doesn't draw anything
void handleEvent(SDL_Event event);
// Draw the current state to the screen.
void show(SDL_Surface *screen);
// Should return true if a transition to the next game state is allowed
bool update();
// Note: Don't put internal functions like "clipGender" in the "public" portion
// of a class. They are for internal use only
private:
static const int NUM_CLIPS = 2;
static const int TIMER_DURATION_MS = 1000;
SDL_Surface* surface;
// If choice is -1, then no choice is currently active.
// Otherwise choice is a value between 0 and NUM_CLIPS - 1
// i.e. it can be used as an index to "clips".
//
// The reason I made this an integer is because it is easier to debug.
// You can print the value, or inspect it in a debugger, and the meaning of
// the value is immediately apparent.
int choice;
SDL_Rect clips[NUM_CLIPS];
// This value becomes true when the player makes a selection.
// Once made, the choice cannot be changed
bool selectionMade;
// The timer allows us to wait before transitioning.
// We record the current time when the player makes a choice.
// Then we simply check if TIMER_DURATION_MS has elapsed since this event
Uint32 timer;
};
void PokemonChoice::handleEvent(SDL_Event event)
{
if(event.type == SDL_MOUSEBUTTONDOWN)
{
if(event.button.button == SDL_BUTTON_LEFT)
{
int x = event.motion.x;
int y = event.motion.y;
choice = -1;
for(int i = 0 ; i < NUM_CLIPS ; ++i)
{
if(isPointInsideRectangle(x, y, clips))
{
choice = i;
}
}
}
}
}
int main(int argc, char* args[])
{
SDL_Surface *screen = init();
if(!screen)
{
// When things go wrong, it is better to be alerted to them
cerr << "Failed to start game: " << SDL_GetError() << '\n';
return 1;
}
Because the enumeration is small, I've used if statements instead of switches so you can concentrate on the idea and not the implementation. You can change it to a switch statement later.
The above shows you how you might implement a timed transition between the two states without compromising your application's ability to respond to events. It also checks for more errors, and prints messages allowing you to diagnose these errors if they occur. It shows how to avoid using global variables to implement your program by passing parameters to the functions that need information.