SDL_Event - Custom events

Started by
8 comments, last by gregrampage 15 years, 10 months ago
Hello all I was wondering whether the SDL_Event structure can hold more than one event. When I use SDL_PollEvent(), I use it in a while() loop. Does this mean there are more events stored in the structure (like a buffer)? Could I theoretically flush the buffer? The reason I'm asking is because I'm thinking about using custom events to have my classes talk to eachother. My main game loop resides inside the Tetris class, but my input handling is done from the Player class. If Player recognises P, Tetris is the one that should pause. Ideally, I would just like something like this:


while( SDL_PollEvent( &event ) ) {

    player.handleInput( &event );

    if( event.type == SDL_USEREVENT ) {

        if( event.code == REQUEST_PAUSE )
          timer.pause();

        if( event.code == REQUEST_EXIT )
          quitloop = true;
    }
}

Obviously, Player::handleInput would also be taking care of the input used to do player-ish stuff.
Advertisement
The SDL_Event structure was designed to be used with SDL events only.

If you want your newly defined events to also contain the SDL ones- use layering/composition, like so.

struct Tetris_Event{    //Custom data here...    SDL_Event sdlEvent;};
.

My advice to you however is to review your design a little. Classes should have well defined responsibilities & who is responsible for handling "user input" in your example is somewhat confusing.

You could have the Tetris (or say, TetrisGame) class process all the user input (SDL_Events) events. Then- say you needed to rotate the active piece- TetrisGame detects an SDL_Event.KEY_PRESS_WHATEVER & then sends off a RotatePiece_Event (containing information like the direction of rotation) to the appropriate TetrisPiece class (which in turn is responsible for the actual rotation).
Quote:Original post by Metro_Mystery
The SDL_Event structure was designed to be used with SDL events only.


Actually SDL does provide one event type that users can use for their own uses.

That doesn't invalidate what you said though. c4c0d3m0n, using the SDL event mechanism is a bad way to do what you are trying to do. Objects should talk to each other by calling member functions.
Quote:Original post by Simian Man
Actually SDL does provide one event type that users can use for their own uses.


Ahh SDL_UserEvent... Whoops! Even so, as we've both said; it's a very bad idea using it!
Alright then, I'll handle the input through Tetris.

My Player class holds all the player-data, including the current piece. It's starting to become funny how many times I'm calling a flipping function (for turning a piece).

void Player::flipcw(){    curr_tetrominoe.flipcw();}void Tetrominoe::flipcw(){    form.flipcw()}void Matrix::flipcw(){    // Only flip if the matrix is square    if( w == h ) {        // Make a quick copy of the current matrix        Matrix tmp( *this );        // Transform!        for( int x = 0; x < w; ++x ) {            for( int y = 0; y < h; ++y ) {                this->set( x, y, tmp.get( y, h-x-1 ) );            }        }    }}
Tetrominoe is a 4x4-Matrix full of 0 and 1, defining the form, and an int defining the type


I was hoping to save one flipcw() function by having the events parsed on to the Player and then the Player handling the userinput (which in a way makes sence, the input is for the Player, not the Game).
Quote:Original post by c4c0d3m0n
It's starting to become funny how many times I'm calling a flipping function (for turning a piece).


From what I can see, you're delegating responsibility- & it's actually part of good design. If it doesn't make sense for someone to handle an event, pass it down to someone else who may be able to.

If your Player class holds all the tetris piece data, perhaps GameBoard would be a better name. That way you can be sure it isn't the GameBoard's responsibility to handle SDL events but rather the TetrisGame (engine). Generally, a Player class holds things like the player's name, score etc. You can have this too, just design a class and have an instance as a member of the TetrisGame- along with the GameBoard (after all, a TetrisGame HAS a player and a GameBoard).
In order to simplify my design a bit, I got rid of the idea that there has to be a Player that plays Tetris. I just put all the Player stuff in Tetris, so that I'm actually programming the game now, and not a game idea and a player to play this idea. Also, this passing down of responsibilities now suddenly seems to pay off, for a tetrominoe is now always allowed to flip (when something is in the way for example). Here is my current Tetris class

////// tetris.h//////#ifndef TETRIS_H#define TETRIS_H#include "graphics.h"#include "font.h"#include <SDL/SDL.h>#include "matrix.h"#include "tetrominoe.h"/// ////// ////// Tetris ////// ////// ///class Tetris{public:    //Tetris();    Tetris( Graphics* pointer = NULL, int width = 10, int height = 20 );    //~Tetris();    void play();private:    // Game stuff    Matrix playfield;    Tetrominoe curr_tetro;    Tetrominoe next_tetro;    int tetro_posx;    int tetro_posy;    int player_score;    int player_level;    int player_lines;    // Operational stuff    void passTetro();    bool moveDown();    bool moveLeft();    bool moveRight();    void drop();    bool flipCW();    bool flipCCW();    // Functional stuff    Graphics* gfxptr;    Font font;    SDL_Event event;};#endif // TETRIS_H


bool Tetris::flipCW(){    // Let's flip prematurely    curr_tetro.flipcw();    // The flag to check whether we did any inapropriate flipping    bool damage = false;    // Let's check is we aren't flipping out of bounds    if( tetro_posx + curr_tetro.freeSpaceLeft() + 1 <= 0 )      damage = true;    if( tetro_posx + TETRO_SIZE - curr_tetro.freeSpaceRight() - 1 >= playfield.width() )      damage = true;    if( tetro_posy + TETRO_SIZE - curr_tetro.freeSpaceBottom() - 1 >= playfield.height() )      damage = true;    // Now we need to check wether we are doing any damage to the field    if( !damage ) {        for( int i = 0; i < TETRO_SIZE; ++i ) {            for( int j = 0; j < TETRO_SIZE; ++j ) {                // If the tetro contains a piece on the coordinates                if( curr_tetro.read( j, i ) > 0 ) {                    // If the field has something where the piece is now                    if( playfield.get( tetro_posx + j, tetro_posy + i ) > 0 ) {                        damage = true;                    }                }            }        }    }    // We need to fix the damage    if( damage ) {        curr_tetro.flipccw();        return false;    }    // We flipped already    return true;}bool Tetris::flipCCW(){    // Let's flip prematurely    curr_tetro.flipccw();    // The flag to check whether we did any inapropriate flipping    bool damage = false;    // Let's check is we aren't flipping out of bounds    if( tetro_posx + curr_tetro.freeSpaceLeft() + 1 <= 0 )      damage = true;    if( tetro_posx + TETRO_SIZE - curr_tetro.freeSpaceRight() - 1 >= playfield.width() )      damage = true;    if( tetro_posy + TETRO_SIZE - curr_tetro.freeSpaceBottom() - 1 >= playfield.height() )      damage = true;    // Now we need to check wether we are doing any damage to the field    if( !damage ) {        for( int i = 0; i < TETRO_SIZE; ++i ) {            for( int j = 0; j < TETRO_SIZE; ++j ) {                // If the tetro contains a piece on the coordinates                if( curr_tetro.read( j, i ) > 0 ) {                    // If the field has something where the piece is now                    if( playfield.get( tetro_posx + j, tetro_posy + i ) > 0 ) {                        damage = true;                    }                }            }        }    }    // We need to fix the damage    if( damage ) {        curr_tetro.flipcw();        return false;    }    // We flipped already    return true;}


Here are my flipping functions. I don't like them, right now they are basically the same, except for calling flipcw() or flipccw(). I don't like big chunks of code repeating liek that. Is there a better way to solve this? (Maybe through function pointers?)
Yes, but be very careful. Using function pointers often complicates code, which would go against your attempt t simplify things.

The easiest way to clean your code up is to implement one flip function in terms of the other.

For example, if flipCW just rotates the pieces- you could implement flip CCW like this.

bool Tetris::flipCCW(){    flipCW();    flipCW();    flipCW();}


as rotating counter-clockwise once in tetris is the same as rotating clockwise three times.

Hope that helps :).
That wouldn't work though. Look at how FlipCW() works. The function will only flip if there is room to flip. There could be a situation where flipping clockwise is impossible, but flipping counter-clockwise is not. Seeing FlipCW() wouldn't work, because there is something in the way, it would automatically mean FlipCCW() doesn't work either, which is wrong.

I had another thought on this problem, I might just change the function to something like this:

enum rotation_directions { CW, CCW };void Tetris::flip( int way ){    // Let's flip prematurely    if( way == CW )      curr_tetro.flipcw();    else      curr.tetro.flipccw();    // ... etc}
Quote:Original post by c4c0d3m0n

I had another thought on this problem, I might just change the function to something like this:

*** Source Snippet Removed ***


I was going to suggest something like that. Or keep your original set up but take the repeated code from both functions and put it into a new function and call that in both of them. Something like CheckForDamage() or whatever you would want to call it.

No need for a complicated solution when a simple one works just fine.

This topic is closed to new replies.

Advertisement