Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » pacman collision and timer

This thread is locked; no one can reply to it. rss feed Print
pacman collision and timer
stooge_dev
Member #1,797
January 2002
avatar

In my game it seems the collision code doesn't always run properly. So if you run into the blue or green ghosts sometime their face over takes pacman's face. I have the Collision checking function within my Input function and I think that it should be part of the main loop instead. What's a good way to correct this?

Also, could you please tell me if I have the speed counter setup and being used correctly?

#SelectExpand
1void setValues(void); 2void getInput(void); 3void checkCollision(void); 4 5struct ghost ghosts[3]; /* blue, red, green */ 6struct player pacman; 7unsigned int goal = 1000; 8int quit = 0, music = 1; 9unsigned int dots = 0; 10int screenshot = 0; 11int level[15][20]; 12int levelScent[15][20] = {{0}}; /* for bloodhound(green) */ 13unsigned int levelNum = 0; 14int voice, voice1; 15 16int intro_counter = 0; /* timer used for intro and re-used for bonus, and ending */ 17int super_pill_timer = 0; 18 19volatile long speed_counter = 0; /* A long integer which will store the value of the speed counter */ 20time_t t; 21void super_pill_counter() 22{ 23 super_pill_timer++; 24} 25void intro_update_counter() 26{ 27 intro_counter++; 28} 29void increment_speed_counter() /* a function to increment the speed counter*/ 30{ 31 speed_counter++; 32} 33END_OF_FUNCTION(increment_speed_counter); 34 35/*****************************************************************************/ 36int main(int argc, char *argv[]) 37{ 38 char curLevel[20] = ""; 39 int mode = 0; /* 0 = fullscreen, 1 = windowed */ 40 int i; 41 42 for(i = 0; i < argc; i++) 43 { 44 if(strcmp(argv<i>, "-windowed") == 0) 45 mode = 1; 46 } 47 setup(mode); 48 pacman.lives = 3; 49 display_intro(); 50 play_midi(sound[LISTENTOTHEMOCKINGBIRD_MID].dat,TRUE); 51 while(!quit) 52 { 53 54 if(dots == 0) 55 { 56 sprintf(curLevel,"levels\\level%d.lev", levelNum); 57 levelNum++; 58 if(load_level(curLevel) == 1) 59 { quit = 1;} 60 setValues(); 61 intro_counter = 0; 62 super_pill_timer = 0; 63 pacman.flee = 0; 64 } 65 66 poll_keyboard(); 67 if(pacman.lives == 0) 68 quit = 1; 69 while(speed_counter > 0) 70 { 71 if(keypressed()) 72 getInput(); 73 moveAggressor(); 74 moveBloodhound(); 75 moveBloodhound2(); 76 speed_counter--; 77 } 78 drawscreen(); 79 } 80 clear_keybuf(); 81 highscore(); 82 stop_midi(); 83 shutdown(); 84 return 0; 85} 86END_OF_MAIN(); 87/*****************************************************************************/ 88void setValues(void) 89{ 90 pacman.x = pacman.orgX; 91 pacman.y = pacman.orgY; 92 pacman.sprNum = PACMAN1_BMP; 93 ghosts[0].sprNum = BLUEGHOST1_BMP; 94 ghosts[0].x = ghosts[0].orgX; 95 ghosts[0].y = ghosts[0].orgY; 96 ghosts[1].sprNum = GREENGHOST1_BMP; 97 ghosts[1].x = ghosts[1].orgX; 98 ghosts[1].y = ghosts[1].orgY; 99 ghosts[2].sprNum = REDGHOST1_BMP; 100 ghosts[2].x = ghosts[2].orgX; 101 ghosts[2].y = ghosts[2].orgY; 102} 103/*****************************************************************************/ 104void getInput(void) 105{ 106 pacman.oldx = pacman.x; 107 pacman.oldy = pacman.y; 108 109 if(key[KEY_UP] && pacman.y > 0) 110 { // && level[pacman.y-1][pacman.x] != 1 111 pacman.y--; 112 pacman.sprNum = PACMAN4_BMP; 113 pacman.direction = UP; 114 } 115 else if(key[KEY_DOWN] && pacman.y < 14) 116 { // && level[pacman.y+1][pacman.x] != 1 117 pacman.y++; 118 pacman.sprNum = PACMAN1_BMP; 119 pacman.direction = DOWN; 120 } 121 else if(key[KEY_LEFT] && pacman.x > 0) 122 { // && level[pacman.y][pacman.x-1] != 1 123 pacman.x--; 124 pacman.sprNum = PACMAN3_BMP; 125 pacman.direction = LEFT; 126 } 127 else if(key[KEY_RIGHT] && pacman.x < 19) 128 { //&& level[pacman.y][pacman.x+1] != 1 129 pacman.x++; 130 pacman.sprNum = PACMAN2_BMP; 131 pacman.direction = RIGHT; 132 } 133 134 else if(key[KEY_PRTSCR]) 135 { 136 savescreen(); 137 } 138 else if(key[KEY_M]) 139 { 140 if(music == 1) 141 { music = 0; 142 midi_pause(); 143 } 144 else 145 { music = 1; 146 midi_resume(); 147 } 148 } 149 else if(key[KEY_ESC]) 150 { 151 gui_bg_color = 255; 152 gui_fg_color = 0; 153 play_sample(sound[SOUND5_WAV].dat,255,128,1000,FALSE); 154 if(alert("Are you sure", "you want to", "Quit?","&Yes","&No",'y','n') == 1) 155 quit = 1; 156 } 157 158 checkCollision(); 159 level[pacman.y][pacman.x] = 0; 160 levelScent[pacman.oldy][pacman.oldx] = pacman.direction; 161} 162/*****************************************************************************/ 163void checkCollision(void) 164{ 165 int bonusNum = 0; 166 if(level[pacman.y][pacman.x] == 3) 167 { 168 pacman.score+=1; 169 play_sample(sound[SOUND1_WAV].dat,255,128,1000,FALSE); 170 dots--; 171 } 172 else if(level[pacman.y][pacman.x] == 1) 173 { 174 voice_start(voice); 175 pacman.y = pacman.oldy; 176 pacman.x = pacman.oldx; 177 } 178 else if(level[pacman.y][pacman.x] == 8) 179 { super_pill_timer = 0; 180 pacman.score+=40; 181 voice_start(voice1); 182 pacman.flee = 1; 183 } 184 else if(level[pacman.y][pacman.x] == 4) 185 { 186 switch(bonus.sprNum) 187 { 188 case BONUS1_BMP: pacman.score+= 5; // cherry 189 break; 190 case BONUS2_BMP: pacman.score+= 10; // lemon 191 break; 192 case BONUS3_BMP: pacman.score+= 1000; // lime 193 break; 194 } 195 play_sample(sound[SOUND4_WAV].dat,255,128,1000,FALSE); 196 } 197 if((ghosts[0].x == pacman.x) && (ghosts[0].y == pacman.y) && (pacman.flee == 1)) 198 { 199 ghosts[0].x = ghosts[0].orgX; 200 ghosts[0].y = ghosts[0].orgY; 201 pacman.score += 25; 202 } 203 else if((ghosts[1].x == pacman.x) && (ghosts[1].y == pacman.y) && (pacman.flee == 1)) 204 { 205 ghosts[1].x = ghosts[1].orgX; 206 ghosts[1].y = ghosts[1].orgY; 207 pacman.score += 50; 208 } 209 else if((ghosts[2].x == pacman.x) && (ghosts[2].y == pacman.y) && (pacman.flee == 1)) 210 { 211 ghosts[2].x = ghosts[2].orgX; 212 ghosts[2].y = ghosts[2].orgY; 213 pacman.score += 100; 214 } 215 else if(((ghosts[0].x == pacman.x) && (ghosts[0].y == pacman.y)) || 216 ((ghosts[1].x == pacman.x) && (ghosts[1].y == pacman.y)) || 217 ((ghosts[2].x == pacman.x) && (ghosts[2].y == pacman.y))) 218 { 219 pacman.lives--; 220 setValues(); 221 } 222 if((pacman.score / goal) == 1) 223 { pacman.lives++; 224 goal+=1000; 225 if(pacman.lives > 5) 226 pacman.score = 0; 227 } 228 if((intro_counter >= 20) && (level[1][9] != 4) && (level[1][9] != 1)) 229 { 230 if(level[1][9] == 3) 231 dots--; 232 bonusNum = rand() % 3; 233 switch(bonusNum) 234 { 235 case 0: bonus.sprNum = BONUS1_BMP; 236 break; 237 case 1: bonus.sprNum = BONUS2_BMP; 238 break; 239 case 2: bonus.sprNum = BONUS3_BMP; 240 } 241 intro_counter = 0; 242 level[1][9] = 4; 243 } 244 245 if(super_pill_timer == 20 && pacman.flee == 1) 246 { pacman.flee = 0; 247 super_pill_timer = 0; 248 voice_stop(voice1); 249 } 250 else if(pacman.flee == 1) 251 { 252 voice_start(voice1); 253 } 254 255}

[edit]
reduced code
[/edit]

Zaphos
Member #1,468
August 2001

http://alleg.sourceforge.net/help.html#Part%206%20-%20learn%20from%20my%20mistakes See part two. That said:

Your logic timer looks good, but your other timers lack the END_OF macros, as well as volatile typing, and neither the functions nor variables are locked ... this probably won't cause you much trouble but you should go back and put in the extra stuff to be safe anyway.

As to the other question, that's too much code for me to look through.

Thomas Harte
Member #33
April 2000
avatar

You detect a collision when, for a given ghost, ghost.x == pacman.x and also ghost.y == pacman.y. If you think about it, this gives exactly one possible way for ghosts and pacman to be colliding, however if you think about it, there are actually many more ways than this. If pacman.y == ghost.y, but ghost.x = pacman.x+1, for example, the ghost is drawn on top of pacman, but you do not detect a collision. And the ghost may have approached from the right, so there will not yet have been an opportunity to detect a collision. i.e. you have made a fundamental logic error.

The correct way to check for a collision between any one ghost and pacman, would be this :

if(!
   (
    (pacman.x > ghost.x+ghostsprite->w) ||
    (ghost.x > pacman.x+pacmansprite->w) ||

    (pacman.y > ghost.y+ghostsprite->h) ||
    (ghost.y > pacman.y+pacmansprite->h)
   )
  )
{
   /* there has been a collision */
}

This is a form of the seperating planes theorem adapted for 2d sprites contained within boxes. Never mind those scary words, think about it this way :

The ghost and pacman are clearly not colliding if pacman is further to the right than the rightmost point of the ghost.

Also, the ghost and pacman are clearly not colliding if the ghost is further to the right than the rightmost point of pacman.

Think about it for a bit, and you'll see that this forms a complete check for whether pacman and the ghost have collided, if they were both sitting on the same horizontal.

In your game they are not necessarily sitting on the same horizontal, so we need to check y as well. Which is the same thing, but referencing sprite heights and y positions instead.

By the way, you'd save yourself work if you did ghost vs pacman collision checks in a 'for' loop, rather than duplicating code for ghosts[0], ghosts[1] and ghosts[2]. But I'm sure you realise this and were just trying to save a little time in getting to the right results before cleaning up code?

stooge_dev
Member #1,797
January 2002
avatar

No, I wasn't thinking about using a for loop for collision detection.

In your example you are using pixel-to-pixel collision? Where as I'm using tile collision.

Zaphos
Member #1,468
August 2001

Well, even though you posted a rather large amount of code, you didn't post all of it (drawscreen() would be nice to see) ... so I can't be sure of how your code works. However, assuming that pacman does not 'jerk' from tile to tile and also assuming that pacman is not a single pixel, you are NOT using tile collision. You are using "see if two pixels which happen to be parts of two images are colliding" ... which, I think you'll find, is a very very poor method of collision detection.

Harte describes bounding box collision, which is what you should use. Pixel-pixel collision is not mentioned at all ... not quite sure where you got that from. Give his post a close reading.

stooge_dev
Member #1,797
January 2002
avatar

Yes, I move my pacman and ghosts from tile to tile. All my tiles are 32x32 including pacman and the ghosts so I move them tile by time. Bad idea, eh?

1struct player {
2// BITMAP* sprites[4];
3 int sprNum;
4 unsigned int score;
5 unsigned int lives;
6 int orgX; /* original x-coordinate */
7 int orgY; /* original y-coordinate */
8 int oldx;
9 int oldy;
10 int x;
11 int y;
12 int flee;
13 int direction;
14};
15 
16extern struct player pacman;
17 
18void drawscreen(void)
19{
20 clear_to_color(buffer,0);
21 acquire_screen();
22 drawBackground();
23 drawLevel();
24 masked_blit(graphic[pacman.sprNum].dat,buffer,0,0,pacman.x*32,pacman.y*32,32,32);
25 blit(graphic[BGBLOCK_BMP].dat,buffer,0,0,ghosts[0].x*32,ghosts[0].y*32,32,32);
26 masked_blit(graphic[ghosts[0].sprNum].dat,buffer,0,0,ghosts[0].x*32,ghosts[0].y*32,32,32); // blue ghost
27 blit(graphic[BGBLOCK_BMP].dat,buffer,0,0,ghosts[1].x*32,ghosts[1].y*32,32,32);
28 masked_blit(graphic[ghosts[1].sprNum].dat,buffer,0,0,ghosts[1].x*32,ghosts[1].y*32,32,32); // green ghost
29 blit(graphic[BGBLOCK_BMP].dat,buffer,0,0,ghosts[2].x*32,ghosts[2].y*32,32,32);
30 masked_blit(graphic[ghosts[2].sprNum].dat,buffer,0,0,ghosts[2].x*32,ghosts[2].y*32,32,32); // red ghost
31// textprintf(buffer,font,1,1,15,"x:%d y:%d dots:%d pacman.flee: %d",pacman.x,pacman.y, dots, pacman.flee);
32// textprintf(buffer,font,1,1,15,"super_pill_timer:%d pacman.flee: %d",super_pill_timer, pacman.flee);
33 textprintf(buffer,font,1,1,15,"music:%d",music);
34 textprintf(buffer,font,290,1,9,"Lives:%d",pacman.lives);
35 textprintf(buffer,font,290,11,9,"Score:%d",pacman.score);
36 textprintf(buffer,font,290,21,9,"Level:%d",levelNum);
37 blit(buffer,screen,0,0,0,0,640,480);
38 release_screen();
39}
40/*****************************************************************************/
41void drawLevel(void)
42{
43 int i,j;
44 for(i = 0; i < 20; i++)
45 {
46 for(j = 0; j < 15;j++)
47 {
48 switch(level[j]<i>)
49 {
50 case 1 : blit(graphic[BLOCK_BMP].dat,buffer,0,0,i*32,j*32,32,32);
51 break;
52 case 3 : masked_blit(graphic[DOT_BMP].dat,buffer,0,0,i*32,j*32,32,32);
53 break;
54 case 4 : masked_blit(graphic[bonus.sprNum].dat,buffer,0,0,i*32,j*32,32,32);
55 break;
56 case 8 : masked_blit(graphic[BONUS4_BMP].dat,buffer,0,0,i*32,j*32,32,32);
57 break;
58 }
59 }
60 }
61}
62/*****************************************************************************/
63void drawBackground(void)
64{
65 int i,j;
66 for(i = 0; i < 20; i++)
67 {
68 for(j = 0; j < 15;j++)
69 {
70 if(level[j]<i> != 1)
71 blit(graphic[BGBLOCK_BMP].dat,buffer,0,0,i*32,j*32,32,32);
72 
73 }
74 }
75}

[edit]
Do I use bounding box collision for the whole game including the walls?
[/edit]

Go to: