My Game runs Slow on PIII Machine
armond

I got a 192 MB RAM and 933 Mhz PIII Machine. I was wondering why would my game run so slow on PIII Machines or below(although still acceptable)... I suspect this line...

install_int(GameTimer::incrementGameTicker, 10);

My game deoesn't seem to run at constant speed... Here is my ticker function...

void GameTimer::incrementGameTicker()
{
game_ticker++;
}
END_OF_FUNCTION(incrementGameTicker);

Thanks!

Jeff Bernard

It runs so slow because you are telling the timer to be really slow. Instead you should try install_int_ex() as it gives better options for setting the speed of the timer (I like BPS_TO_TIMER). Or you can try to just fiddling with that 10, lowering it, until you get a speed that you like.

Audric

This 100Hz timer (10ms) should run with no problem at all.
If something runs at varying speed, it's in your main program.
If you comment out the heaviest video operations (for example: background drawing), does it change the apparent speed?

armond

Hi! Sorry for the late response. I actually tried turning off everything leaving the ship to be the only thing visible. The scrolling background does take some time to process but it doesn't seem that heavy at all. I will try install_int_ex() and give you guys some feedback asap. Thanks!

weapon_S

Is the PIII the computer you compiled the program on?

armond

weapon_S

Nope, it's not. But I was told that it wouldn't have any effect even if I compiled on that machine. I just run the binary there...

Tobias Dammers

Care to share some more code? As in, a 20-100 line example that compiles as-is and reproduces the problem?

Richard Phipps

Give us more details. If you are trying to scroll a 32bit screen on less than a 933Mhz Machine then I'm not surprised if you start getting slowdown.

armond

Sure thing. Here is the code for my PlayState class which functions as the main entry of the game. Perhaps you could point out some problems here...

#SelectExpand
1#include "globals.h" 2 3PlayState::PlayState() 4{ 5 currentLevel = 1; 6 7 GameManager::game_score = 0; 8 9 //create buffer image 10 GameManager::buffer = create_bitmap(SCREEN_W, SCREEN_H); 11 12 //load sprites 13 BitmapManager::addBitmap(SHIP_SPRITE, "graphics/fs.bmp"); 14 BitmapManager::addBitmap(VULCAN_SPRITE, "graphics/vulcan.bmp"); 15 BitmapManager::addBitmap(ASTEROID_SPRITE_FLYING, "graphics/asteroid_flying.bmp"); 16 BitmapManager::addBitmap(ASTEROID_SPRITE_DYING, "graphics/asteroid_destroyed.bmp"); 17 BitmapManager::addBitmap(BULLET_HEAD_SPRITE_FLYING, "graphics/bullet_head_flying.bmp"); 18 BitmapManager::addBitmap(SPRITE_EXPLOSION, "graphics/explosion.bmp"); 19 20 //load SFX 21 SoundManager::addSample(VULCAN_GUN_FIRE, "sfx/vulcan.wav"); 22 23 //ship = new Ship(BitmapManager::SHIP_SPRITE, 50, 50, 50, 32, 1); 24 //Ship(int x, int y, int w, int h, int speed, int recoveryRate, int recoveryTime, Status stat, int lfe); 25 ship = new Ship(50, 50, 50, 45, 3, 5, 1000, ALIVE, MAX_PLAYER_LIFE); 26 27 //add a weapon for the ship 28 ship->addWeapon(VULCAN_GUN, WeaponFactory::getWeapon(VULCAN_GUN) ); 29 ship->setCurrentWeapon(VULCAN_GUN); 30 ship->move(0, (SCREEN_H / 2) ); 31} 32 33PlayState::~PlayState() 34{ 35 BitmapManager::removeAll(); 36 37 destroy_bitmap(GameManager::buffer); 38 39 delete ship; 40} 41 42PlayState* PlayState::getInstance() 43{ 44 if (instance == NULL) 45 { 46 instance = new PlayState(); 47 } 48 49 return instance; 50} 51 52void PlayState::init() 53{ 54} 55 56void PlayState::pause() 57{ 58} 59 60void PlayState::run() 61{ 62 GameStateManager::pushState( GameOverState::getInstance() ); 63 GameStateManager::getCurrentState()->run();//--execute run method 64 GameStateManager::popState(); //--pop the element on top 65 66 loadLevel(currentLevel); 67 68 //begin main game loop 69 while ( !key[KEY_ESC] ) 70 { 71 72 while (GameTimer::game_ticker > 0) 73 { 74 //move left 75 if ( key[KEY_A] ) 76 { 77 ship->move( ship->x - ship->getSpeed(), ship->y ); 78 if ( ship->x <= 0 ) 79 { 80 ship->move( ship->x + ship->getSpeed(), ship->y ); 81 } 82 } 83 //move down 84 if ( key[KEY_S] ) 85 { 86 ship->move( ship->x, ship->y + ship->getSpeed() ); 87 88 if ( (ship->y + ship->height) >= SCREEN_H ) 89 { 90 ship->move( ship->x, ship->y - ship->getSpeed() ); 91 } 92 } 93 //move right 94 if ( key[KEY_D] ) 95 { 96 ship->move( ship->x + ship->getSpeed(), ship->y ); 97 98 if ( (ship->x + ship->width) >= SCREEN_W ) 99 { 100 ship->move( ship->x - ship->getSpeed(), ship->y ); 101 } 102 } 103 //move up 104 if ( key[KEY_W] ) 105 { 106 ship->move( ship->x, ship->y - ship->getSpeed() ); 107 108 if ( ship->y <= 20 ) 109 { 110 ship->move( ship->x, ship->y + ship->getSpeed() ); 111 } 112 } 113 //fire weapon 114 if ( key[KEY_K] ) 115 { 116 ship->fireWeapon(); 117 } 118 //change weapon 119 if ( key[KEY_TAB] ) 120 { 121 //ship->fireWeapon(); 122 } 123 //fire special 124 if ( key[KEY_L] ) 125 { 126 } 127 128 //exit to menu 129 if ( key[KEY_ESC] ) 130 { 131 break; 132 } 133 134 //scroll the background 135 bg->update(); 136 137 //animate the ship 138 ship->update(); 139 140 bg->draw(); 141 142 //move all projectiles 143 for (std::vector<Projectile*>::iterator it = ProjectileFactory::projectilePool.begin(); it != ProjectileFactory::projectilePool.end(); ++it) 144 { 145 if ( (*it)->isVisible() == true ) 146 { 147 Projectile* p = *it; 148 p->update(); //(*it)->update(); 149 p->draw(); //(*it)->draw(); 150 151 //check for collision on bullet and enemies 152 for(std::vector<Enemy*>::iterator eit = EnemyFactory::enemyPool.begin(); eit != EnemyFactory::enemyPool.end(); ++eit) 153 { 154 Enemy* e = *eit; 155 156 //projectile hits an enemy 157 if ( p->collides(e) && p->isActive() && e->isAlive() ) 158 { 159 e->takeDamage(1); //TODO: change 1 to weapon power 160 p->makeInactive(); 161 162 //player killed the enemy 163 if ( !( e->isAlive() ) ) 164 { 165 GameManager::game_score += 10; 166 } 167 168 //exit the inner loop 169 break; 170 } 171 } 172 173 if ( !( p->isActive() ) ) 174 { 175 Projectile* p = *it; 176 it = ProjectileFactory::projectilePool.erase(it); 177 // we must decrement, because it will be incremented when the loop starts over again 178 --it; 179 delete p; 180 } 181 182 } 183 else 184 { 185 Projectile* p = *it; 186 it = ProjectileFactory::projectilePool.erase(it); 187 // we must decrement, because it will be incremented when the loop starts over again 188 --it; 189 delete p; 190 } 191 } 192 193 //move all enemies 194 for(std::vector<Enemy*>::iterator it = EnemyFactory::enemyPool.begin(); it != EnemyFactory::enemyPool.end(); ++it) 195 { 196 Enemy* e = *it; 197 198 if ( e->isAlive() || e->isDying() ) 199 { 200 e->update(); 201 e->draw(); 202 203 if ( e->isAlive() && ship->collides(e) ) 204 { 205 ship->takeDamage(10); //TODO: change to enemy attack power 206 e->takeDamage(1); 207 } 208 } 209 } 210 211 //recharge weapon 212 ship->getCurrentWeapon()->recharge(); 213 214 //control game speed 215 GameTimer::game_ticker--; 216 217 ship->draw(); 218 219 textout_ex(GameManager::buffer, font, "Life: ", 10, 10, makecol(0, 0, 255), -1); 220 //drawLifeBar(int x, int y, int length, height, BITMAP* bitmap); 221 drawLifeBar(50, 0, ship->getLife() , 20, GameManager::buffer); 222 223 textprintf_centre_ex(GameManager::buffer, font, SCREEN_W/2, 10, makecol(255, 255, 255), makecol(0, 0, 0), "Current Weapon: %s", ship->getCurrentWeapon()->getWeaponName() ); 224 textprintf_centre_ex(GameManager::buffer, font, (SCREEN_W - (SCREEN_W/8) ), 10, makecol(255, 255, 255), makecol(0, 0, 0), "Score: %d", GameManager::game_score); 225 226 textprintf_centre_ex(GameManager::buffer, font, SCREEN_W/2, 176, makecol(255, 255, 255), makecol(0, 0, 0), "num=%d", GameTimer::game_ticker); 227 228 //blit buffer 229 GameManager::blitBuffer(); 230 } 231 232 } 233 234 unloadLevel(currentLevel); 235} 236 237void PlayState::update() 238{ 239} 240 241void PlayState::clean() 242{ 243} 244 245void PlayState::loadLevel(int level) 246{ 247 PACKFILE* levelMap; 248 249 switch(level) 250 { 251 case 1: 252 SoundManager::addSample(METEOR_DESTROYED, "sfx/meteordestroyed.wav"); 253 254 levelMap = pack_fopen("levels/level_01.lvl", "r"); 255 256 createLevel(levelMap); 257 258 bg = new GameBackground(BitmapManager::addBitmap(SPACE_BACKGROUND, "graphics/bg.bmp"), HORIZONTAL_LEFT, 3, SCREEN_W, SCREEN_H); 259 260 break; 261 default: 262 break; 263 } 264 265 if (levelMap) 266 { 267 pack_fclose(levelMap); 268 } 269} 270 271void PlayState::createLevel(PACKFILE* packFile) 272{ 273 std::string line; 274 char buf[255]; 275 276 int currX = 0; 277 int currY = 0; 278 int xIncrement = (SCREEN_W / 20); 279 int yIncrement = (SCREEN_H / 20); 280 281 if (!packFile) 282 { 283 allegro_message("COULD NOT CREATE LEVEL!: %s", allegro_error); 284 } 285 286 while ( pack_feof(packFile) == 0 ) 287 { 288 //read a line from the file (levelMap) 289 line = pack_fgets(buf, sizeof(buf), packFile); 290 291 //loop through each character in the line 292 for (int i = 0; i < line.length(); i++) 293 { 294 //only an empty space 295 if ( line.at(i) == ' ') 296 { 297 currX += xIncrement; 298 } 299 else //create an enemy object 300 { 301 Enemy* e = EnemyFactory::getEnemy( line.at(i) ); 302 //set initial position of the enemy 303 e->move(currX, currY); 304 EnemyFactory::enemyPool.push_back(e); 305 306 e = NULL; 307 308 currX += xIncrement; 309 } 310 } 311 312 currX = 0; 313 currY += yIncrement; 314 } 315} 316 317void PlayState::unloadLevel(int level) 318{ 319 switch(level) 320 { 321 case 1: 322 std::vector<Enemy*>().swap(EnemyFactory::enemyPool); 323 delete bg; 324 break; 325 default: 326 break; 327 } 328} 329 330void PlayState::drawLifeBar(int x, int y, int length, int height, BITMAP* bitmap) 331{ 332 //rectfill(BITMAP *bmp, int x1, int y1, int x2, int y2, int color); 333 if (length <= 0) 334 { 335 length = 0; 336 } 337 338 rectfill(bitmap, x, y, (x + length), height, 255); 339} 340 341PlayState* PlayState::instance; 342int PlayState::currentLevel;

If you're interested on how the PlayState is loaded, here's my main cpp file.

#SelectExpand
1#include "globals.h" 2 3//declare all functions used 4int initAllegro(); 5void deinitAllegro(); 6 7int main(int* argc, char* argv[]) 8{ 9 //initialize allegro 10 if ( initAllegro() == -1 ) 11 { 12 return -1; 13 } 14 15 //push states to gameManager stack 16 GameStateManager::pushState( PlayState::getInstance() ); 17 18 19 //loop through gameManager stack 20 GameStateManager::getCurrentState()->run();//--execute run method 21 GameStateManager::popState(); //--pop the element on top 22 23 24 //deinitialize allegro 25 deinitAllegro(); 26 27 return 0; 28} 29END_OF_MAIN() 30 31int initAllegro() 32{ 33 allegro_init(); 34 install_keyboard(); 35 36 install_timer(); 37 38 set_color_depth(GameManager::COLOR_DEPTH); 39 40 if ( set_gfx_mode(GFX_AUTODETECT, GameManager::GAME_WIDTH, GameManager::GAME_HEIGHT, 0, 0) != 0 ) 41 { 42 allegro_message("Graphics Mode Could Not Be Set!: %s", allegro_error); 43 return -1; 44 } 45 46 if ( install_sound(DIGI_AUTODETECT, MIDI_AUTODETECT, 0) ) 47 { 48 allegro_message("Sound Error: %s", allegro_error); 49 } 50 51 set_volume(255, 255); 52 53 LOCK_VARIABLE(GameTimer::game_ticker); 54 LOCK_FUNCTION(GameTimer::incrementGameTicker); 55 56 install_int(GameTimer::incrementGameTicker, 10); 57 58 return 0; 59} 60 61void deinitAllegro() 62{ 63 allegro_exit(); 64}

Here are the constants that I used for the game width, height and color depth.

#SelectExpand
1#include "globals.h" 2 3GameManager::GameManager() 4{ 5} 6 7GameManager::~GameManager() 8{ 9} 10 11void GameManager::blitBuffer() 12{ 13 acquire_screen(); 14 15 blit(buffer, screen, 0, 0, 0, 0, GAME_WIDTH, GAME_HEIGHT); 16 17 release_screen(); 18 19 clear_bitmap(buffer); 20} 21 22BITMAP* GameManager::buffer; 23 24const short int GameManager::GAME_WIDTH = 640; 25const short int GameManager::GAME_HEIGHT = 480; 26const short int GameManager::COLOR_DEPTH = 16; 27 28int GameManager::game_score;

Andrei Ellman
Quote:

//move all projectiles
for (std::vector<Projectile*>::iterator it = ProjectileFactory::projectilePool.begin(); it != ProjectileFactory::projectilePool.end(); ++it)
[...]
//check for collision on bullet and enemies
for(std::vector<Enemy*>::iterator eit = EnemyFactory::enemyPool.begin(); eit != EnemyFactory::enemyPool.end(); ++eit)

Howmany enemies and projectyles do you have? If checking each projectyle against each enemy, the execution-time can grow very fast according to howmany of each there are.

AE.

Tobias Dammers

I agree with Andrei here.
Although game logic is usually the least of your problems, in this case you might want to optimize.
There are numerous optimization techniques you can use here, and virtually all of them try to reduce the number of iterations (read: try to loop through less objects). This is better than optimizing the collision test itself (rule of thumb: optimize algorithms, not code).
The key to this common problem is space partitioning, which means that you divide the total space in which objects can lie into regions, and you only test objects against each other that share a region. The art is to define your regions in such a way that they are easy and fast to calculate, and that they don't produce double checking. Here are a few approaches:
1. Axis sort. Sort all your objects by one of the cardinal axes (x or y). Check in both directions, and stop iterating when you hit an object that is too far away. This will usually restrict checking to the nearest 10 or so objects.
2. Tilemap. Divide your scene into a tile grid (you don't need to use this for drawing, but if you draw using a tilemap anyway, you may want to add the collision detection features to it while you're at it), and give each tile a list (a vector is probably your best choice, or maybe even a static array - just be careful not to produce overflows) with pointers to all checkable objects that touch it. If your tile size is a power of 2, then finding the tile a character is on is as trivial as a binary & with tilesize-1. Downside: enormous memory consumption, and you need to be careful with those character lists. Upside: you only check against 4 tiles max (provided your character isn't larger than the tile size).
3. Quadtrees. Google for it. The idea is that you divide your space into 4 sectors, and divide those 4 again, and so on, until a given minimum size is reached, OR a maximum number of characters is found in the sector. When checking for collisions, you only check against objects that share a sector with the current object. This is the hardest to code, but it is the most efficient one for complex scenes, with very little overhead and adaptable to the most uneven distributions of objects (e.g. a hundred objects crowding in one corner of the screen).

armond

Hi! I just tried doing

install_int_ex(GameTimer::incrementGameTicker, BPS_TO_TIMER(100) );

doesn't make any change...

I haven't tried Tobias Dammers's suggestion yet. But even when I don't fire any bullet(no instance created) and there is only 1 enemy it's still slow... Slow as in not as fast when I'm in a P-IV Machine... When I remove the scrolling background, it does get a little fast. But still not that fast... Here is the code for my scrolling bg BTW... I'd like to point out that this(the scrolling bg) isn't necessarily my problem.

1#include "globals.h"
2 
3 
4GameBackground::GameBackground(BITMAP* bitmap, BackgroundScrollType scrollType, int scrollSpeed, int x, int y)
5{
6 this->bitmap = bitmap;
7 this->scrollType = scrollType;
8 this->scrollSpeed = scrollSpeed;
9 this->x = x;
10 this->y = y;
11
12 tmpBuffer = create_bitmap(SCREEN_W, SCREEN_H);
13}
14 
15GameBackground::~GameBackground()
16{
17 destroy_bitmap(tmpBuffer);
18}
19 
20void GameBackground::update()
21{
22 scrollBackground();
23}
24 
25void GameBackground::scrollBackground()
26{
27 switch(scrollType)
28 {
29 case HORIZONTAL_LEFT:
30 scrollLeft();
31 break;
32 default:
33 break;
34 }
35}
36 
37void GameBackground::draw()
38{
39 draw_sprite(GameManager::buffer, tmpBuffer, 0, 0);
40}
41 
42void GameBackground::setScrollSpeed(int speed)
43{
44 scrollSpeed = speed;
45}
46 
47void GameBackground::scrollLeft()
48{
49 x -= scrollSpeed;
50
51 int bw = bitmap->w;
52 int bh = bitmap->h;
53
54 int xOffSet = (SCREEN_W + bw);
55
56 if ( x > bw || x < -(bw) )
57 {
58 x = 0;
59 }
60
61 int newX = 0;
62
63 for (int i = -(bw); i < (xOffSet); i += bw)
64 {
65 newX = (x + i);
66
67 for (int j = 0; j < SCREEN_H; j += bh)
68 {
69 //draw tile
70 //Optimization: draw only parts that are visible to the screen
71 if ( ( newX + bw) > 0 )
72 {
73 draw_sprite(tmpBuffer, bitmap, newX, j);
74 }
75 else
76 {
77 break;
78 }
79
80 }
81 }
82}

I'm scrolling an 200x200 24-bit bitmap and drawing it repeatedly on screen to create an illusion of a scrolling background.

Audric

24 bit bitmap? But your screen is 16bit... try to avoid run-time color conversion.

If the animation is still choppy, the background drawing is not at fault and no optimization there will solve the problem.
I guess the final blit to screen takes the biggest toll, but you can't easily reduce this cost (dirty rectangles on a 840x680 hardware scrolling bitmap? Seems far-fetched)

Last step: profile, using your compiler's options.

edit: I just noticed you used draw_sprite() both times: If what you're copying is opaque, just blit() it, it will be quite faster.

Richard Phipps

On low end machines drawing a 24 bit bitmap to the screen every frame is just slow at high resolutions. There is not much you can do if you have scrolling on such a machine. Looking into Video bitmaps might be your best bet..

armond

Thank you for your reply! Could you please explain what you meant by "Looking into Video bitmaps might be your best bet.."?

Richard Phipps

Copying data from video bitmaps to the screen can be much faster that copying from a normal BITMAP if your card supports hardware acceleration. There is a section in the manual on it and many threads if you do a search on these forums. :)

armond

Hi! I quick search from google tells me that you're talking about page flipping...?

Richard Phipps

That's related, look in the manual in the gfx / bitmap sections.

nonnus29

Armond, your noob code doesn't look like noob code. I'm not complaining, it's a nice change.... ;D

Thread #590085. Printed from Allegro.cc