ex7.c (18454B)
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <time.h> 4 #include <unistd.h> 5 #include <string.h> 6 #include <math.h> 7 8 #include "x.h" 9 10 /* macros */ 11 #define INIT_WIDTH (800) 12 #define INIT_HEIGHT (600) 13 #define FPS (60) 14 #define SUB_TICK (4) 15 #define NUM_RECT (200) 16 #define GRAVITY (1000) 17 #define max(a, b) ((a) > (b) ? (a) : (b)) 18 #define min(a, b) ((a) < (b) ? (a) : (b)) 19 #define WORLD_WIDTH (80) 20 #define WORLD_HEIGHT (60) 21 #define BLOCK_SIZE (10) 22 23 // #define COUNT_FPS 24 25 enum keys { 26 KEY_D, 27 KEY_S, 28 KEY_A, 29 KEY_W, 30 KEY_Q, 31 KEY_SPACE, 32 NUM_KEY, //number of keys in this enum 33 }; 34 enum key_state { 35 KEY_UP, 36 KEY_DOWN, 37 }; 38 enum next_menu { 39 START_MENU, 40 GAME_PLAY, 41 GAME_OVER, 42 QUIT, 43 }; 44 45 struct rect { 46 float ppx, ppy; // previous position 47 float px, py; // top left corner 48 float vx, vy; 49 float ax, ay; 50 int w, h; 51 int m; 52 }; 53 54 struct circle { 55 float ppx, ppy; 56 float px, py; 57 float vx, vy; 58 float ax, ay; 59 int r; 60 int m; 61 }; 62 63 char world_map[WORLD_WIDTH * WORLD_HEIGHT + 1] = 64 "................................................................................" 65 "................................................................................" 66 "................................................................................" 67 "................................................................................" 68 "................................................................................" 69 "........b......................................................................." 70 "................................................................................" 71 "................................................................................" 72 "....b..........................................................................." 73 "................................................................................" 74 "................b..............................................................." 75 "..........................................................b..........b.........." 76 "................................................................................" 77 ".......................b........................................................" 78 "...........................................b...................................." 79 "...........................................b...................................." 80 "................................................................................" 81 "..................b............................................................." 82 "................................................................................" 83 "...........................................b...................................." 84 "................................................................................" 85 "................................................................................" 86 "...........................b...................................................." 87 "................................................................................" 88 "................................................................................" 89 "................................................................................" 90 "................................................................................" 91 "................................................................................" 92 "................................................................................" 93 "....................................bbbbbbbbbb.................................." 94 "................................................................................" 95 "................................................................................" 96 "................................................................................" 97 "................................................................................" 98 "................................................................................" 99 "................................................................................" 100 "..............................................bbbbbbbbbb........................" 101 "................................................................................" 102 "................................................................................" 103 "................................................................................" 104 "................................................................................" 105 "....................................bbbbbbbbbb.................................." 106 "................................................................................" 107 "................................................................................" 108 "................................................................................" 109 "................................................................................" 110 "..........................bbbbbbbbbb............................................" 111 "................................................................................" 112 "................................................................................" 113 "................................................................................" 114 "................................................................................" 115 "................bbbbbbbbbb......................................................" 116 "................................................................................" 117 "................................................................................" 118 "...p............................................................................" 119 "bbbbbbbbbbbbbbbbbbbbbbbbb.......bbbbbbbbbbbbbbbbbbbbbbbb...bbbbbbbbbbbbbbbbbbbbb" 120 "........................b.......b......................b...b...................." 121 "........................b.......b......................b...b...................." 122 "........................b.......b......................b...b...................." 123 "........................b.......b......................b...b...................."; 124 125 /* variables */ 126 struct rect block[NUM_RECT]; 127 struct rect player; 128 int player_is_falling; 129 int next_menu = START_MENU; 130 extern int win_width, win_height; 131 132 133 /* function prototypes */ 134 void cleanup(void); 135 void start_menu(void); 136 void game_play(void); 137 void receive_events(int[]); 138 void handle_inputs(int[]); 139 void rect_next_tick(struct rect *, long); 140 int rect_test_collision(struct rect *, struct rect *); 141 void rect_handle_collision_mf(struct rect *, struct rect *); 142 void rect_handle_collision(struct rect *, struct rect *); 143 void rect_handle_collision_elastic(struct rect *, struct rect *); 144 void circle_next_tick(struct circle *, long); 145 int circle_test_collision(struct circle *, struct circle *); 146 void update_falling_status(struct rect *, long); 147 void game_over(void); 148 149 void 150 start_menu(void) 151 { 152 char *menu_char_q = "press q to quit."; 153 char *menu_char_s = "press <space> to start."; 154 155 x_clear_area(); 156 x_draw_string(0x00FFFF, 157 win_width/2 - 10 * strlen(menu_char_q)/2, 158 win_height/2, 159 menu_char_q, strlen(menu_char_q)); 160 x_draw_string(0x00FFFF, 161 win_width/2 - 10 * strlen(menu_char_s)/2, 162 win_height/2 + 20, 163 menu_char_s, strlen(menu_char_s)); 164 165 while (next_menu == START_MENU) { 166 char c; 167 int event = x_next_event(&c); 168 169 switch (event) { 170 case XEXPOSE: 171 x_draw_string(0x00FFFF, 172 win_width/2 - 10 * strlen(menu_char_q)/2, 173 win_height/2, 174 menu_char_q, strlen(menu_char_q)); 175 x_draw_string(0x00FFFF, 176 win_width/2 - 10 * strlen(menu_char_s)/2, 177 win_height/2 + 20, 178 menu_char_s, strlen(menu_char_s)); 179 180 break; 181 case XWINDEL: 182 next_menu = QUIT; 183 break; 184 case XKEYPRESS: 185 switch (c) { 186 case 'q': 187 next_menu = QUIT; 188 break; 189 case ' ': 190 next_menu = GAME_PLAY; 191 break; 192 default: 193 break; 194 } 195 break; 196 default: 197 break; 198 } 199 } 200 } 201 202 void 203 receive_events(int key_state[]) 204 { 205 while (x_pending() > 0) { 206 char c; 207 int event = x_next_event(&c); 208 209 switch (event) { 210 case XWINDEL: 211 next_menu = QUIT; 212 break; 213 case XKEYPRESS: 214 switch (c) { 215 case 'q': 216 key_state[KEY_Q] = KEY_DOWN; 217 break; 218 case 'd': 219 key_state[KEY_D] = KEY_DOWN; 220 break; 221 case 'a': 222 key_state[KEY_A] = KEY_DOWN; 223 break; 224 case 'w': 225 key_state[KEY_W] = KEY_DOWN; 226 break; 227 case 's': 228 key_state[KEY_S] = KEY_DOWN; 229 break; 230 case ' ': 231 key_state[KEY_SPACE] = KEY_DOWN; 232 break; 233 default: 234 break; 235 } 236 break; 237 case XKEYRELEASE: 238 switch (c) { 239 case 'q': 240 key_state[KEY_Q] = KEY_UP; 241 break; 242 case 'd': 243 key_state[KEY_D] = KEY_UP; 244 break; 245 case 'a': 246 key_state[KEY_A] = KEY_UP; 247 break; 248 case 'w': 249 key_state[KEY_W] = KEY_UP; 250 break; 251 case 's': 252 key_state[KEY_S] = KEY_UP; 253 break; 254 case ' ': 255 key_state[KEY_SPACE] = KEY_UP; 256 break; 257 default: 258 break; 259 } 260 break; 261 } 262 } 263 } 264 265 void 266 handle_inputs(int key_state[]) 267 { 268 if (key_state[KEY_Q] == KEY_DOWN){ 269 next_menu = GAME_OVER; 270 return; 271 } 272 if (key_state[KEY_D] == KEY_DOWN) { 273 if (player.vx > 0) { 274 player.ax = 500; 275 } else { 276 player.ax = 1000; 277 } 278 } else if (key_state[KEY_A] == KEY_DOWN) { 279 if (player.vx > 0) { 280 player.ax = -1000; 281 } else { 282 player.ax = -500; 283 } 284 } else { 285 if (player_is_falling) 286 player.ax = -player.vx; 287 else 288 player.ax = -3 * player.vx; 289 } 290 291 if (player.vx < -200) player.vx = -200; 292 if (player.vx > 200) player.vx = 200; 293 /* 294 if (key_state[KEY_S] == KEY_DOWN) 295 player.vy += 300; 296 if (key_state[KEY_W] == KEY_DOWN) 297 player.vy += -300; 298 */ 299 if (!player_is_falling && key_state[KEY_SPACE] == KEY_DOWN) 300 player.vy = -450; 301 } 302 303 void 304 rect_next_tick(struct rect *s, long ndt) // nano second 305 { 306 s->ppx = s->px; 307 s->ppy = s->py; 308 s->vx += s->ax * ndt / 1000 / 1000 / 1000; 309 s->vy += s->ay * ndt / 1000 / 1000 / 1000; 310 s->px += s->vx * ndt / 1000 / 1000 / 1000; 311 s->py += s->vy * ndt / 1000 / 1000 / 1000; 312 313 // bind within the window 314 if (s->px < 0) { 315 s->px = 0; 316 //s->vx *= -1; 317 } 318 if (win_width < s->px + s->w) { 319 s->px = win_width - s->w; 320 //s->vx *= -1; 321 } 322 /* 323 if (s->py < 0) { 324 s->py = 0; 325 s->vy *= -1; 326 } 327 if (win_height < s->py + s->h) { 328 s->py = win_height - s->h; 329 s->vy *= -1; 330 } 331 */ 332 // game over when fall out of the screen 333 if (s->py > win_height) 334 next_menu = GAME_OVER; 335 } 336 337 void 338 circle_next_tick(struct circle *c, long ndt) 339 { 340 c->ppx = c->px; 341 c->ppy = c->py; 342 c->vx += c->ax * ndt / 1000 / 1000 / 1000; 343 c->vy += c->ay * ndt / 1000 / 1000 / 1000; 344 c->px += c->vx * ndt / 1000 / 1000 / 1000; 345 c->py += c->vy * ndt / 1000 / 1000 / 1000; 346 347 // bind within the window 348 if (c->px - c->r < 0) { 349 c->px = c->r; 350 c->vx *= -1; 351 } 352 if (win_width < c->px + c->r) { 353 c->px = win_width - c->r; 354 c->vx *= -1; 355 } 356 if (c->py - c->r < 0) { 357 c->py = c->r; 358 c->vy *= -1; 359 } 360 if (win_height < c->py + c->r) { 361 c->py = win_height - c->r; 362 c->vy *= -1; 363 } 364 } 365 366 int 367 rect_test_collision(struct rect *s1, struct rect *s2) 368 { 369 return s1->px < s2->px + s2->w && s2->px < s1->px + s1->w && 370 s2->py < s1->py + s1->h && s1->py < s2->py + s2->h; 371 } 372 373 int 374 circle_test_collision(struct circle *c1, struct circle *c2) 375 { 376 return (c1->px - c2->px) * (c1->px - c2->px) + 377 (c1->py - c2->py) * (c1->py - c2->py) < 378 (c1->r + c2->r) * (c1->r + c2->r); 379 } 380 381 /* 382 * Handle collision of a moving rect against fixed rect 383 */ 384 void 385 rect_handle_collision_mf(struct rect *sm, struct rect *sf) 386 { 387 if (!rect_test_collision(sm, sf)) 388 return; 389 if (sm->ppx + sm->w <= sf->ppx && sf->px < sm->px + sm->w) { 390 // collisioin from left to right 391 sm->px = sf->px - sm->w; 392 sm->vx = 0; 393 } 394 if (sf->ppx + sf->w <= sm->ppx && sm->px < sf->px + sf->w) { 395 // collision from right to left 396 sm->px = sf->px + sf->w; 397 sm->vx = 0; 398 } 399 400 if (sm->ppy + sm->h <= sf->ppy && sf->py < sm->py + sm->h) { 401 // collision from up to down 402 sm->py = sf->py - sm->h; 403 sm->vy = 0; 404 } 405 if (sf->ppy + sf->h <= sm->ppy && sm->py < sf->py + sf->h) { 406 // collision from dohn to up 407 sm->py = sf->py + sf->h; 408 sm->vy = 0; 409 } 410 411 } 412 413 /* 414 * Handle collision of a moving rect against another moving rect 415 */ 416 void 417 rect_handle_collision_mm(struct rect *s1, struct rect *s2) 418 { 419 if (!rect_test_collision(s1, s2)) 420 return; 421 422 float lapx = min(s1->px + s1->w, s2->px + s2->w) - max(s1->px, s2->px); 423 float lapy = min(s1->py + s1->h, s2->py + s2->h) - max(s1->py, s2->py); 424 425 if (lapx < lapy) { 426 if (s1->px + s1->w < s2->px + s2->w / 2) { 427 s1->px -= lapx / 2; 428 s2->px += lapx / 2; 429 } else { 430 s1->px += lapx / 2; 431 s2->px -= lapx / 2; 432 } 433 } else { 434 if (s1->py + s1->h < s2->py + s2->h / 2) { 435 s1->py -= lapy / 2; 436 s2->py += lapy / 2; 437 } else { 438 s1->py += lapy / 2; 439 s2->py -= lapy / 2; 440 } 441 } 442 } 443 444 void 445 circle_handle_collision_mm(struct circle *c1, struct circle *c2) 446 { 447 if (!circle_test_collision(c1, c2)) 448 return; 449 450 float col_px = c2->px - c1->px; 451 float col_py = c2->py - c1->py; 452 float col_pr = sqrtf(col_px * col_px + col_py * col_py); 453 col_px /= col_pr; 454 col_py /= col_pr; 455 456 c1->px = c1->px - col_px / 2; 457 c1->py = c1->py - col_py / 2; 458 c2->px = c2->px + col_px / 2; 459 c2->py = c2->py + col_py / 2; 460 } 461 462 void 463 rect_handle_collision_elastic(struct rect *s1, struct rect *s2) 464 { 465 if(!rect_test_collision(s1, s2)) 466 return; 467 468 rect_handle_collision_mm(s1, s2); 469 470 float v1, v2; 471 float m1 = s1->m; 472 float m2 = s2->m; 473 474 float lapx = min(s1->px + s1->w, s2->px + s2->w) - max(s1->px, s2->px); 475 float lapy = min(s1->py + s1->h, s2->py + s2->h) - max(s1->py, s2->py); 476 477 if (lapx < lapy) { 478 v1 = s1->vx; 479 v2 = s2->vx; 480 s1->vx = 2*m2/(m1+m2)*v2 + (m1-m2)/(m1+m2)*v1; 481 s2->vx = 2*m1/(m1+m2)*v1 + (m2-m1)/(m1+m2)*v2; 482 } else { 483 v1 = s1->vy; 484 v2 = s2->vy; 485 s1->vy = 2*m2/(m1+m2)*v2 + (m1-m2)/(m1+m2)*v1; 486 s2->vy = 2*m1/(m1+m2)*v1 + (m2-m1)/(m1+m2)*v2; 487 } 488 } 489 490 void 491 circle_handle_collision_elastic(struct circle *c1, struct circle *c2) 492 { 493 if(!circle_test_collision(c1, c2)) 494 return; 495 496 circle_handle_collision_mm(c1, c2); 497 498 float col_px = c2->px - c1->px; 499 float col_py = c2->py - c1->py; 500 float col_pr = sqrtf(col_px * col_px + col_py * col_py); 501 col_px /= col_pr; 502 col_py /= col_pr; 503 float nor_px = col_py; 504 float nor_py = -col_px; 505 506 float m1 = c1->m; 507 float m2 = c2->m; 508 509 float col_1v = c1->vx * col_px + c1->vy * col_py; 510 float col_2v = c2->vx * col_px + c2->vy * col_py; 511 512 float col_1vxn = (2*m2/(m1+m2)*col_2v + (m1-m2)/(m1+m2)*col_1v) * col_px; 513 float col_1vyn = (2*m2/(m1+m2)*col_2v + (m1-m2)/(m1+m2)*col_1v) * col_py; 514 float col_2vxn = (2*m1/(m1+m2)*col_1v + (m2-m1)/(m1+m2)*col_2v) * col_px; 515 float col_2vyn = (2*m1/(m1+m2)*col_1v + (m2-m1)/(m1+m2)*col_2v) * col_py; 516 517 float nor_1vx = nor_px * (c1->vx * nor_px + c1->vy * nor_py); 518 float nor_1vy = nor_py * (c1->vx * nor_px + c1->vy * nor_py); 519 float nor_2vx = nor_px * (c2->vx * nor_px + c2->vy * nor_py); 520 float nor_2vy = nor_py * (c2->vx * nor_px + c2->vy * nor_py); 521 522 c1->vx = col_1vxn + nor_1vx; 523 c1->vy = col_1vyn + nor_1vy; 524 c2->vx = col_2vxn + nor_2vx; 525 c2->vy = col_2vyn + nor_2vy; 526 } 527 528 void 529 update_falling_status(struct rect *player, long ndt) 530 { 531 int collision = 0; 532 struct rect r = {0}; 533 r.ppx = player->ppx; 534 r.ppy = player->ppy; 535 r.px = player->px; 536 r.py = player->py; 537 r.vx = 0; 538 r.vy = 0; 539 r.ax = 0; 540 r.ay = GRAVITY; 541 r.w = player->w; 542 r.h = player->h; 543 r.m = player->m; 544 545 rect_next_tick(&r, ndt); 546 for (int i = 0; i < NUM_RECT; i++) 547 if (rect_test_collision(&r, &block[i])) 548 collision = 1; 549 550 if (collision == 1) 551 player_is_falling = 0; 552 else 553 player_is_falling = 1; 554 } 555 556 void 557 game_play(void) 558 { 559 int key_state[NUM_KEY]; 560 long t0, t1, dt; 561 #ifdef COUNT_FPS 562 int fps_count = 0; 563 #endif 564 struct timespec ts; 565 566 int bi = 0; 567 for (int i = 0; i < WORLD_WIDTH * WORLD_HEIGHT; i++) { 568 if (world_map[i] == 'b') { 569 block[bi].ppx = block[bi].px = i % WORLD_WIDTH * BLOCK_SIZE; 570 block[bi].ppy = block[bi].py = i / WORLD_WIDTH * BLOCK_SIZE; 571 block[bi].ax = 0; 572 block[bi].ay = 0; 573 block[bi].vx = 0; 574 block[bi].vy = 0; 575 block[bi].w = block[bi].h = BLOCK_SIZE; 576 block[bi].m = block[bi].w * block[bi].h; 577 bi++; 578 } else if (world_map[i] == 'p') { 579 player.ppx = player.px = i % WORLD_WIDTH * BLOCK_SIZE; 580 player.ppy = player.py = i / WORLD_WIDTH * BLOCK_SIZE; 581 player.vx = 0; 582 player.vy = 0; 583 player.ax = 0; 584 player.ay = GRAVITY; 585 player.w = player.h = BLOCK_SIZE; 586 player.m = player.w * player.h; 587 } 588 } 589 590 while (next_menu == GAME_PLAY){ 591 clock_gettime(CLOCK_MONOTONIC, &ts); 592 t0 = ts.tv_nsec; 593 receive_events(key_state); 594 handle_inputs(key_state); 595 596 597 clock_gettime(CLOCK_MONOTONIC, &ts); 598 t0 = ts.tv_nsec; 599 600 int collision[NUM_RECT] = {0}; 601 for (int k = 0; k < SUB_TICK; k++) { 602 update_falling_status(&player, 1000 * 1000 * 1000 / FPS / SUB_TICK); 603 if (player_is_falling) 604 player.ay = GRAVITY; 605 else 606 player.ay = 0; 607 rect_next_tick(&player, 1000 * 1000 * 1000 / FPS / SUB_TICK); 608 for (int i = 0; i < NUM_RECT; i++) 609 rect_next_tick(&block[i], 1000 * 1000 * 1000 / FPS / SUB_TICK); 610 611 for (int i = 0; i < NUM_RECT; i++){ 612 rect_handle_collision_mf(&player, &block[i]); 613 if (rect_test_collision(&player, &block[i])) 614 collision[i] = 1; 615 } 616 } 617 618 // fix fps 619 // TODO: This method create some strange stripe when 620 // rendered in 60fps on a 60fps monitor. 621 dt = 0; 622 while (dt < 1.0 * 1000 * 1000 * 1000 / FPS){ 623 clock_gettime(CLOCK_MONOTONIC, &ts); 624 t1 = ts.tv_nsec; 625 dt = t1 > t0 ? t1 - t0 : t1 - t0 + 1000 * 1000 * 1000; 626 } 627 #ifdef COUNT_FPS 628 // count fps. 629 fps_count++; 630 if (t1 < t0){ 631 printf("fps: %u\n", fps_count); 632 fps_count = 0; 633 } 634 #endif 635 636 x_clear_area(); 637 for (int i = 0; i < NUM_RECT; i++) { 638 x_draw_rectangle(0x00FF00, 639 block[i].px, block[i].py, // position 640 block[i].w, block[i].h); 641 } 642 x_draw_rectangle(0x009FFF, 643 player.px, player.py, // position 644 player.w, player.h); 645 char status_string[128]; 646 snprintf(status_string, 128, "falling: %d", player_is_falling); 647 x_draw_string(0x00FFFF, 648 0, 20, 649 status_string, 650 strlen(status_string)); 651 } 652 } 653 654 void 655 game_over(void) 656 { 657 char *menu_char = "GAME OVER"; 658 659 x_clear_area(); 660 x_draw_string(0x00FFFF, 661 win_width/2 - 10 * strlen(menu_char)/2, win_height/2, 662 menu_char, strlen(menu_char)); 663 x_flush(); 664 665 sleep(1); 666 next_menu = START_MENU; 667 } 668 669 int 670 main(void) 671 { 672 win_width = 800; 673 win_height = 600; 674 x_setup_window(0, 0, win_width, win_height, 0x000000, "UNKO"); 675 while (next_menu != QUIT){ 676 switch (next_menu){ 677 case START_MENU: 678 start_menu(); 679 break; 680 case GAME_PLAY: 681 game_play(); 682 break; 683 case GAME_OVER: 684 game_over(); 685 break; 686 default: 687 break; 688 } 689 } 690 691 x_clean_up(); 692 return 0; 693 }