main.c (8293B)
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <time.h> 4 #include <unistd.h> 5 #include <string.h> 6 7 #include "main.h" 8 #include "world_map.h" 9 10 /* macros */ 11 #define FPS (60) 12 #define SUB_TICK (4) 13 #define NUM_BLOCK (200) 14 #define GRAVITY (1000) 15 16 /* 17 #define COUNT_FPS 18 */ 19 20 enum keys { 21 KEY_D, 22 KEY_S, 23 KEY_A, 24 KEY_W, 25 KEY_Q, 26 KEY_SPACE, 27 NUM_KEY, //number of keys in this enum 28 }; 29 enum key_state { 30 KEY_UP, 31 KEY_DOWN, 32 }; 33 enum next_menu { 34 START_MENU, 35 GAME_PLAY, 36 GAME_OVER, 37 QUIT, 38 }; 39 40 /* variables */ 41 struct Object block[NUM_BLOCK]; 42 struct Object player; 43 int player_is_falling; 44 enum next_menu next_menu = START_MENU; 45 46 47 /* function prototypes */ 48 /* menus */ 49 void start_menu(void); 50 void game_play(void); 51 void game_over(void); 52 /* events */ 53 void receive_events(enum key_state[]); 54 void handle_inputs(enum key_state[]); 55 /* sort */ 56 void sort_blocks(int colliding_blocks[], int distance[], int count); 57 58 void 59 start_menu(void) 60 { 61 char *menu_char_q = "press q to quit."; 62 char *menu_char_s = "press <space> to start."; 63 64 x_clear_area(); 65 x_draw_string(0x00FFFF, 66 WORLD_WIDTH * BLOCK_SIZE/2 - 10 * strlen(menu_char_q)/2, 67 WORLD_HEIGHT * BLOCK_SIZE/2, 68 menu_char_q, strlen(menu_char_q)); 69 x_draw_string(0x00FFFF, 70 WORLD_WIDTH * BLOCK_SIZE/2 - 10 * strlen(menu_char_s)/2, 71 WORLD_HEIGHT * BLOCK_SIZE/2 + 20, 72 menu_char_s, strlen(menu_char_s)); 73 74 while (next_menu == START_MENU) { 75 char c; 76 int event = x_next_event(&c); 77 78 switch (event) { 79 case XEXPOSE: 80 x_draw_string(0x00FFFF, 81 WORLD_WIDTH * BLOCK_SIZE/2 - 10 * strlen(menu_char_q)/2, 82 WORLD_HEIGHT * BLOCK_SIZE/2, 83 menu_char_q, strlen(menu_char_q)); 84 x_draw_string(0x00FFFF, 85 WORLD_WIDTH * BLOCK_SIZE/2 - 10 * strlen(menu_char_s)/2, 86 WORLD_HEIGHT * BLOCK_SIZE/2 + 20, 87 menu_char_s, strlen(menu_char_s)); 88 89 break; 90 case XWINDEL: 91 next_menu = QUIT; 92 break; 93 case XKEYPRESS: 94 switch (c) { 95 case 'q': 96 next_menu = QUIT; 97 break; 98 case ' ': 99 next_menu = GAME_PLAY; 100 break; 101 default: 102 break; 103 } 104 break; 105 default: 106 break; 107 } 108 } 109 } 110 111 void 112 game_play(void) 113 { 114 enum key_state key_state[NUM_KEY]; 115 long t0, t1, dt; 116 #ifdef COUNT_FPS 117 int fps_count = 0; 118 #endif 119 struct timespec ts; 120 121 int bi = 0; 122 for (int i = 0; i < WORLD_WIDTH * WORLD_HEIGHT; i++) { 123 if (world_map[i] == 'b') { 124 block[bi].pp.x = block[bi].p.x = i % WORLD_WIDTH * BLOCK_SIZE; 125 block[bi].pp.y = block[bi].p.y = i / WORLD_WIDTH * BLOCK_SIZE; 126 block[bi].a.x = 0; 127 block[bi].a.y = 0; 128 block[bi].v.x = 0; 129 block[bi].v.y = 0; 130 block[bi].body.rectangle.w = BLOCK_SIZE; 131 block[bi].body.rectangle.h = BLOCK_SIZE; 132 block[bi].m = block[bi].body.rectangle.w * 133 block[bi].body.rectangle.h; 134 bi++; 135 } else if (world_map[i] == 'p') { 136 player.pp.x = player.p.x = i % WORLD_WIDTH * BLOCK_SIZE; 137 player.pp.y = player.p.y = i / WORLD_WIDTH * BLOCK_SIZE; 138 player.v.x = 0; 139 player.v.y = 0; 140 player.a.x = 0; 141 player.a.y = GRAVITY; 142 player.body.rectangle.w = player.body.rectangle.h = BLOCK_SIZE; 143 player.m = player.body.rectangle.w * player.body.rectangle.h; 144 } 145 } 146 147 while (next_menu == GAME_PLAY){ 148 clock_gettime(CLOCK_MONOTONIC, &ts); 149 t0 = ts.tv_nsec; 150 receive_events(key_state); 151 handle_inputs(key_state); 152 153 154 clock_gettime(CLOCK_MONOTONIC, &ts); 155 t0 = ts.tv_nsec; 156 157 for (int k = 0; k < SUB_TICK; k++) { 158 player_is_falling = object_is_falling(&player, block, NUM_BLOCK); 159 if (player_is_falling) 160 player.a.y = GRAVITY; 161 else 162 player.a.y = 0; 163 next_tick(&player, 1e9 / FPS / SUB_TICK); 164 165 // game over when fall out of the screen 166 if (player.p.y > WORLD_HEIGHT * BLOCK_SIZE) { 167 next_menu = GAME_OVER; 168 break; 169 } 170 // bind within the world 171 if (player.p.x < 0) { 172 player.p.x = 0; 173 } 174 if (WORLD_WIDTH * BLOCK_SIZE < player.p.x + 175 player.body.rectangle.w) { 176 player.p.x = WORLD_WIDTH * BLOCK_SIZE - 177 player.body.rectangle.w; 178 } 179 180 181 for (int i = 0; i < NUM_BLOCK; i++) 182 next_tick(&block[i], 1e9 / FPS / SUB_TICK); 183 184 int colliding_blocks[9]; 185 int distance[9]; 186 int j = 0; 187 for (int i = 0; i < NUM_BLOCK; i++) 188 if (test_collision(&player, &block[i])) { 189 colliding_blocks[j] = i; 190 distance[j] = (player.p.x - block[i].p.x) * 191 (player.p.x - block[i].p.x) + 192 (player.p.y - block[i].p.y) * 193 (player.p.y - block[i].p.y); 194 j++; 195 } 196 sort_blocks(colliding_blocks, distance, j); 197 for (int i = 0; i < j; i++) 198 handle_collision_mf(&player, &block[colliding_blocks[i]]); 199 } 200 201 // fix fps 202 // TODO: This method create some strange stripe when 203 // rendered in 60fps on a 60fps monitor. 204 dt = 0; 205 while (dt < 1.0 * 1e9 / FPS){ 206 clock_gettime(CLOCK_MONOTONIC, &ts); 207 t1 = ts.tv_nsec; 208 dt = t1 > t0 ? t1 - t0 : t1 - t0 + 1e9; 209 } 210 #ifdef COUNT_FPS 211 // count fps. 212 fps_count++; 213 if (t1 < t0){ 214 printf("fps: %u\n", fps_count); 215 fps_count = 0; 216 } 217 #endif 218 219 x_clear_area(); 220 for (int i = 0; i < NUM_BLOCK; i++) { 221 x_draw_rectangle(0x00FF00, 222 block[i].p.x, block[i].p.y, // position 223 block[i].body.rectangle.w, 224 block[i].body.rectangle.h); 225 } 226 x_draw_rectangle(0x009FFF, 227 player.p.x, player.p.y, // position 228 player.body.rectangle.w, player.body.rectangle.h); 229 char status_string[128]; 230 snprintf(status_string, 128, "falling: %d", player_is_falling); 231 x_draw_string(0x00FFFF, 232 0, 20, 233 status_string, 234 strlen(status_string)); 235 } 236 } 237 238 void 239 game_over(void) 240 { 241 char *menu_char = "GAME OVER"; 242 243 x_clear_area(); 244 x_draw_string(0x00FFFF, 245 WORLD_WIDTH * BLOCK_SIZE/2 - 10 * strlen(menu_char)/2, 246 WORLD_HEIGHT * BLOCK_SIZE/2, 247 menu_char, strlen(menu_char)); 248 x_flush(); 249 250 sleep(1); 251 next_menu = START_MENU; 252 } 253 254 void 255 receive_events(enum key_state key_state[]) 256 { 257 while (x_pending() > 0) { 258 char c; 259 int event = x_next_event(&c); 260 261 switch (event) { 262 case XWINDEL: 263 next_menu = QUIT; 264 break; 265 case XKEYPRESS: 266 switch (c) { 267 case 'q': 268 key_state[KEY_Q] = KEY_DOWN; 269 break; 270 case 'd': 271 key_state[KEY_D] = KEY_DOWN; 272 break; 273 case 'a': 274 key_state[KEY_A] = KEY_DOWN; 275 break; 276 case 'w': 277 key_state[KEY_W] = KEY_DOWN; 278 break; 279 case 's': 280 key_state[KEY_S] = KEY_DOWN; 281 break; 282 case ' ': 283 key_state[KEY_SPACE] = KEY_DOWN; 284 break; 285 default: 286 break; 287 } 288 break; 289 case XKEYRELEASE: 290 switch (c) { 291 case 'q': 292 key_state[KEY_Q] = KEY_UP; 293 break; 294 case 'd': 295 key_state[KEY_D] = KEY_UP; 296 break; 297 case 'a': 298 key_state[KEY_A] = KEY_UP; 299 break; 300 case 'w': 301 key_state[KEY_W] = KEY_UP; 302 break; 303 case 's': 304 key_state[KEY_S] = KEY_UP; 305 break; 306 case ' ': 307 key_state[KEY_SPACE] = KEY_UP; 308 break; 309 default: 310 break; 311 } 312 break; 313 } 314 } 315 } 316 317 void 318 handle_inputs(enum key_state key_state[]) 319 { 320 if (key_state[KEY_Q] == KEY_DOWN){ 321 next_menu = GAME_OVER; 322 return; 323 } 324 if (key_state[KEY_D] == KEY_DOWN) { 325 if (player.v.x > 0) { 326 player.a.x = 500; 327 } else { 328 player.a.x = 1000; 329 } 330 } else if (key_state[KEY_A] == KEY_DOWN) { 331 if (player.v.x > 0) { 332 player.a.x = -1000; 333 } else { 334 player.a.x = -500; 335 } 336 } else { 337 if (player_is_falling) 338 player.a.x = -player.v.x; 339 else 340 player.a.x = -3 * player.v.x; 341 } 342 343 if (player.v.x < -200) player.v.x = -200; 344 if (player.v.x > 200) player.v.x = 200; 345 /* 346 if (key_state[KEY_S] == KEY_DOWN) 347 player.v.y += 300; 348 if (key_state[KEY_W] == KEY_DOWN) 349 player.v.y += -300; 350 */ 351 if (!player_is_falling && key_state[KEY_SPACE] == KEY_DOWN) 352 player.v.y = -450; 353 } 354 355 void 356 swap(int list[], int i, int j) 357 { 358 int tmp = list[i]; 359 list[i] = list[j]; 360 list[j] = tmp; 361 } 362 363 void 364 sort_blocks(int colliding_blocks[], int distance[], int count) 365 { 366 for (int i = 0; i < count - 1; i++) { 367 for (int j = i + 1; j < count; j++) { 368 if (distance[i] > distance[j]) { 369 swap(colliding_blocks, i, j); 370 swap(distance, i, j); 371 } 372 } 373 } 374 } 375 376 377 int 378 main(void) 379 { 380 x_setup_window(0, 0, 381 WORLD_WIDTH * BLOCK_SIZE, WORLD_HEIGHT * BLOCK_SIZE, 382 0x000000, "UNKO"); 383 while (next_menu != QUIT){ 384 switch (next_menu){ 385 case START_MENU: 386 start_menu(); 387 break; 388 case GAME_PLAY: 389 game_play(); 390 break; 391 case GAME_OVER: 392 game_over(); 393 break; 394 default: 395 break; 396 } 397 } 398 399 x_clean_up(); 400 return 0; 401 }