Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » game state and level transition approaches

This thread is locked; no one can reply to it. rss feed Print
game state and level transition approaches
Aniconic
Member #15,133
May 2013

Hey there everybody!

Although I registered this account quite some time ago, I decided to start working on a 2D shooter project I've been conceptualizing for a while now and had some questions about code design. Specifically, as it pertains to game states and levels.

So far, I have a base entity class with a working player class, projectile, and parallel scrolling background class, all designed to be updated and rendered using lists and polymorphism. However, I've hit a few roadblocks as to how I should continue.

My question is whats the best approach for scripting enemy encounters for levels? I've looked around online but cannot seem to find any good tutorials or examples.

EDIT: I suppose I should kind of elaborate on this a bit. by enemy encounters, I meant designing levels in such a way that enemy spawns can be queued in some way based on the current level the player is on. My goal is to eventually design the game in such a way that I can use a level editor to make new levels and load them as files in the game. I was hoping someone might be able to point me in the right direction.

Also, I was wondering if someone could provide me with some advice for an effective approach for creating different game states for menus and such? I was thinking of using multiple entity lists instead of the single one I currently have, one for each state. Then I'd use condition branching in the main loop to determine how input behaves and which entity list to loop through for update/render. I'm pretty sure this would work, but my gut tells me there might be a more flexible approach that I might be missing. Any advice would be greatly appreciated!

If it helps, here is my main loop atm. Feel free to pick it apart. The more feedback I can get, the better!

#SelectExpand
1#include <allegro5/allegro.h> 2#include <allegro5/allegro_image.h> 3#include <allegro5/allegro_primitives.h> 4#include <allegro5/allegro_font.h> 5#include <allegro5/allegro_ttf.h> 6#include <allegro5/allegro_audio.h> 7#include <allegro5/allegro_acodec.h> 8#include <list> 9 10 11#include "globals.h" 12#include "entity.h" 13#include "background.h" 14#include "player.h" 15#include "bullet.h" 16#include "pointer.h" 17 18#include "imageMgr.h" 19 20//---local globals---// 21 22//--Entity lists--// 23std::list<entity *> entities; 24std::list<entity *>::iterator iter; 25std::list<entity *>::iterator iter2; 26 27//--project globals--// 28imageMgr *image = new imageMgr; 29 30background *bg = new background(); 31//background *fg = new background(); 32 33player *ship = new player(); 34pointer *cursor = new pointer(); 35 36 37 38//Project function prototypes 39float __cdecl cursorX(); 40float __cdecl cursorY(); 41 42int main(int argc, char **argv) { 43 44 //Shell variables 45 bool done = false; 46 bool render = false; 47 bool keys[5] = {false, false, false, false, false}; 48 49 float gameTime = 0; 50 int frames = 0; 51 int gameFPS = 0; 52 53 //Allegro variables 54 ALLEGRO_DISPLAY *display = NULL; 55 ALLEGRO_EVENT_QUEUE *event_queue = NULL; 56 ALLEGRO_TIMER *timer; 57 ALLEGRO_FONT *font18; 58 59 //Allegro init functions 60 if(!al_init()) { 61 fprintf(stderr, "Failed to initialize allegro!\n"); 62 return -1; 63 } 64 65 display = al_create_display(WIDTH, HEIGHT); 66 if(!display) { 67 fprintf(stderr, "Failed to create display!\n"); 68 return -1; 69 } 70 71 //Addon install functions 72 al_install_keyboard(); 73 al_install_mouse(); 74 al_init_image_addon(); 75 al_init_font_addon(); 76 al_init_ttf_addon(); 77 al_init_primitives_addon(); 78 79 //Project initialization 80 image->init(); 81 82 font18 = al_load_font("Fonts/arial.ttf", 18, 0); 83 84 bg->init(1, image->bg); 85 entities.push_back(bg); 86 87 cursor->init(WIDTH/2,HEIGHT/2, image->cursor); 88 entities.push_back(cursor); 89 90 ship->init(&cursorX, &cursorY, image->player); 91 entities.push_back(ship); 92 93 //Timer init and setup 94 event_queue = al_create_event_queue(); 95 timer = al_create_timer(1.0/60); 96 97 al_register_event_source(event_queue, al_get_timer_event_source(timer)); 98 al_register_event_source(event_queue, al_get_keyboard_event_source()); 99 al_register_event_source(event_queue, al_get_mouse_event_source()); 100 101 al_start_timer(timer); 102 103 al_hide_mouse_cursor(display); 104 105 while (!done) { 106 107 ALLEGRO_EVENT ev; 108 al_wait_for_event(event_queue, &ev); 109 110 //Event decision branch 111 if (ev.type == ALLEGRO_EVENT_KEY_DOWN) { 112 switch(ev.keyboard.keycode) { 113 case ALLEGRO_KEY_ESCAPE: 114 done = true; 115 break; 116 case ALLEGRO_KEY_LEFT: 117 keys[LEFT] = true; 118 break; 119 case ALLEGRO_KEY_RIGHT: 120 keys[RIGHT] = true; 121 break; 122 case ALLEGRO_KEY_UP: 123 keys[UP] = true; 124 break; 125 case ALLEGRO_KEY_DOWN: 126 keys[DOWN] = true; 127 break; 128 } 129 } 130 else if (ev.type == ALLEGRO_EVENT_KEY_UP) { 131 switch(ev.keyboard.keycode) { 132 case ALLEGRO_KEY_ESCAPE: 133 done = false; 134 break; 135 case ALLEGRO_KEY_LEFT: 136 keys[LEFT] = false; 137 break; 138 case ALLEGRO_KEY_RIGHT: 139 keys[RIGHT] = false; 140 break; 141 case ALLEGRO_KEY_UP: 142 keys[UP] = false; 143 break; 144 case ALLEGRO_KEY_DOWN: 145 keys[DOWN] = false; 146 break; 147 } 148 } 149 else if (ev.type == ALLEGRO_EVENT_MOUSE_AXES) { 150 cursor->setX(ev.mouse.x); 151 cursor->setY(ev.mouse.y); 152 } 153 else if (ev.type == ALLEGRO_EVENT_MOUSE_BUTTON_DOWN) { 154 bullet *Bullet1 = new bullet(ship->getTurret1X(), 155 ship->getTurret1Y(), ship->getTurret1Angle(), 20, PLAYER_BULLET); 156 entities.push_back(Bullet1); 157 bullet *Bullet2 = new bullet(ship->getTurret2X(), 158 ship->getTurret2Y(), ship->getTurret2Angle(), 20, PLAYER_BULLET); 159 entities.push_back(Bullet2); 160 } 161 // Update Game 162 else if (ev.type == ALLEGRO_EVENT_TIMER) { 163 164 render = true; 165 166 //Update Frames per second 167 frames++; 168 if(al_current_time() - gameTime >= 1) { 169 gameTime = al_current_time(); 170 gameFPS = frames; 171 frames = 0; 172 } 173 174 //Player Movement Buffer here. 175 ship->playerMovement(keys[UP], keys[DOWN], keys[LEFT], keys[RIGHT]); 176 177 178 //Update objects 179 for (iter = entities.begin(); iter != entities.end(); ++iter) 180 (*iter)->update(); 181 182 //Collisions 183 for(iter = entities.begin(); iter != entities.end(); ++iter) { 184 if( ! (*iter)->Collidable() ) continue; 185 for(iter2 = iter; iter2 != entities.end(); ++iter2) { 186 if( !(*iter2)->Collidable() ) continue; 187 if( (*iter)->getID() == (*iter2)->getID()) continue; 188 if( (*iter)->checkCollision( (*iter2))) { 189 (*iter)->collided( (*iter2)->getID()); 190 (*iter2)->collided( (*iter)->getID()); 191 //something died 192 break; 193 } 194 } 195 } 196 197 //remove dead objects. 198 for (iter = entities.begin(); iter != entities.end();) { 199 if (! (*iter)->getAlive()) { 200 delete(*iter); 201 iter = entities.erase(iter); 202 } 203 else 204 iter++; 205 } 206 } 207 208 //Render 209 if (render && al_is_event_queue_empty(event_queue)) { 210 render = false; 211 212 //Render objects. 213 //int count = 0; 214 for (iter = entities.begin(); iter != entities.end(); ++iter) { 215 //++count; 216 (*iter)->render(); 217 } 218 //std::cout << "entities: " << count << "\n"; 219 220 //Render overlay 221 al_draw_textf(font18, al_map_rgb(255, 255, 255), WIDTH - 70, HEIGHT - 20, 0, "FPS: %i", gameFPS); 222 223 //Flip render buffers. 224 al_flip_display(); 225 al_clear_to_color(al_map_rgb(0,0,0)); 226 } 227 } 228 229 //Destroy project objects 230 for (iter = entities.begin(); iter != entities.end(); ++iter) { 231 (*iter)->destroy(); 232 delete (*iter); 233 iter = entities.erase(iter); 234 } 235 236 //Destroy allegro objects 237 al_destroy_font(font18); 238 al_destroy_timer(timer); 239 al_destroy_event_queue(event_queue); 240 delete image; 241 al_destroy_display(display); 242 243 244 return EXIT_SUCCESS; 245}; 246 247float __cdecl cursorX() { 248 return cursor->getX(); 249}; 250 251float __cdecl cursorY() { 252 return cursor->getY(); 253};

Thanks for your time guys. :)

Schyfis
Member #9,752
May 2008
avatar

I suppose it depends on what exactly you want your game to feel like, but I see two ways of doing it:

1. Keep a list of spawns and a list of delays. Spawn all enemies contained in the group spawns[i], wait for delays[i] seconds, increment, repeat.

2. Keep a list of spawns. Spawn all enemies contained in the group spawns[i]. When all enemies in spawns[i] have been destroyed, increment and repeat.

I think you could also mix those two strategies, if you want to go a more advanced route.

________________________________________________________________________________________________________
[freedwill.us]
[unTied Games]

furinkan
Member #10,271
October 2008
avatar

Personally, I'm not a fan of using polymorphism for trivial cases like this. Compositions are a lot easier, IMO.

If you want to spawn enemy ships you have a couple options:

1) Player can't move beyond screen: enemies show up at timed intervals.
2) Player scrolls the screen: enemies are at specific coordinates.

Either way, what you want to do is to distill the data that makes each enemy unique. Let's take the first example, because that one is simpler IMO.

In order to make an enemy appear we need to know how far along the screen you want him to show up, when he shows up, what type of enemy, and possibly what level. I'll represent this data as a struct, but you can do classes or whathaveyou.

struct gen_enemy {
    unsigned int secs; //how long until he shows up
    float x; //how far along the screen
    unsigned int type; //the type (good use for enumeration?)
    unsigned int lvl; //level if applicable
    //more custom data
}

Basically each level will consist of a list of these structs. Every second, have your code check the list for new enemy arrivals (checked vs secs since the start of the level). If it is time to make new friends... er... enemies place enemy of type, x pixels over, and have him start doing his thing.

Remove enemies when they fall below the screen or die.

The level ends when both the list is empty and there are no enemies alive.

As for game states, you will have to think ahead about that. It is rarely covered, but all that it takes is planning!

EDIT:

Ah! Schyfis beat me to it.

Note that despite our similar answers we both had slightly different ideas as to what to do and how to implement it. The trick is to break your problems down in the manner you see fit.

Go to: