ex6.c (20506B)
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 <X11/Xlib.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 Display *display; 127 Window window; 128 unsigned int win_width = INIT_WIDTH, win_height = INIT_HEIGHT; 129 GC gc, sgc[NUM_RECT]; 130 Atom wm_delete_window; 131 struct rect block[NUM_RECT]; 132 struct rect player; 133 int player_is_falling; 134 int next_menu = START_MENU; 135 136 137 /* function prototypes */ 138 void setup(void); 139 void cleanup(void); 140 void start_menu(void); 141 void game_play(void); 142 void receive_events(int[]); 143 void handle_inputs(int[]); 144 void rect_next_tick(struct rect *, long); 145 int rect_test_collision(struct rect *, struct rect *); 146 void rect_handle_collision_mf(struct rect *, struct rect *); 147 void rect_handle_collision(struct rect *, struct rect *); 148 void rect_handle_collision_elastic(struct rect *, struct rect *); 149 void circle_next_tick(struct circle *, long); 150 int circle_test_collision(struct circle *, struct circle *); 151 void update_falling_status(struct rect *, long); 152 void game_over(void); 153 154 155 void 156 setup(void) 157 { 158 if ((display = XOpenDisplay(NULL)) == NULL){ 159 fprintf(stderr, "ERROR: could not open display\n"); 160 exit(1); 161 } 162 window = XCreateSimpleWindow( 163 display, 164 XDefaultRootWindow(display), 165 0, 0, 166 win_width, win_height, 167 0, 0, 168 0); 169 XStoreName(display, window, "UNKO"); 170 gc = XCreateGC(display, window, 0, NULL); 171 XSetForeground(display, gc, 0x00FFFF); 172 for (int i = 0; i < NUM_RECT; i++){ 173 sgc[i] = XCreateGC(display, window, 0, NULL); 174 XSetForeground(display, sgc[i], 0x00FF00); 175 } 176 177 wm_delete_window = XInternAtom(display, 178 "WM_DELETE_WINDOW", False); 179 XSetWMProtocols(display, window, &wm_delete_window, 1); 180 181 XSelectInput(display, window, 182 ExposureMask | KeyPressMask | KeyReleaseMask); 183 184 XMapWindow(display, window); 185 } 186 187 void 188 start_menu(void) 189 { 190 XEvent event; 191 char *menu_char_q = "press q to quit."; 192 char *menu_char_s = "press <space> to start."; 193 194 XClearArea(display, window, 195 0, 0, // position 196 win_width, win_height, // width and height 197 False); 198 XDrawString(display, window, gc, 199 win_width/2 - strlen(menu_char_q)/2, win_height/2, 200 menu_char_q, strlen(menu_char_q)); 201 XDrawString(display, window, gc, 202 win_width/2 - strlen(menu_char_s)/2, win_height/2 + 20, 203 menu_char_s, strlen(menu_char_s)); 204 205 while (next_menu == START_MENU) { 206 XNextEvent(display, &event); 207 switch (event.type) { 208 case Expose: { 209 XDrawString(display, window, gc, 210 win_width/2 - strlen(menu_char_q)/2, 211 win_height/2, 212 menu_char_q, strlen(menu_char_q)); 213 XDrawString(display, window, gc, 214 win_width/2 - strlen(menu_char_s)/2, 215 win_height/2 + 20, 216 menu_char_s, strlen(menu_char_s)); 217 218 } break; 219 case KeyPress: { 220 switch (XLookupKeysym(&event.xkey, 0)) { 221 case 'q': 222 next_menu = QUIT; 223 break; 224 case ' ': 225 next_menu = GAME_PLAY; 226 break; 227 default: 228 break; 229 } 230 } break; 231 case ClientMessage: { 232 if ((Atom) event.xclient.data.l[0] == wm_delete_window) { 233 next_menu = QUIT; 234 } 235 } break; 236 default: 237 break; 238 } 239 } 240 } 241 242 void 243 receive_events(int key_state[]) 244 { 245 XEvent event; 246 XWindowAttributes wattr; 247 248 while (XPending(display) > 0) { 249 XNextEvent(display, &event); 250 switch (event.type) { 251 case Expose: { 252 XGetWindowAttributes(display, window, &wattr); 253 win_width = wattr.width; 254 win_height = wattr.height; 255 } break; 256 case KeyPress: { 257 switch (XLookupKeysym(&event.xkey, 0)) { 258 case 'q': 259 //next_menu = GAME_OVER; 260 key_state[KEY_Q] = KEY_DOWN; 261 break; 262 case 'd': 263 key_state[KEY_D] = KEY_DOWN; 264 break; 265 case 'a': 266 key_state[KEY_A] = KEY_DOWN; 267 break; 268 case 'w': 269 key_state[KEY_W] = KEY_DOWN; 270 break; 271 case 's': 272 key_state[KEY_S] = KEY_DOWN; 273 break; 274 case ' ': 275 key_state[KEY_SPACE] = KEY_DOWN; 276 break; 277 default: 278 break; 279 } 280 } break; 281 case KeyRelease: { 282 switch (XLookupKeysym(&event.xkey, 0)) { 283 case 'q': 284 key_state[KEY_Q] = KEY_UP; 285 break; 286 case 'd': 287 key_state[KEY_D] = KEY_UP; 288 break; 289 case 'a': 290 key_state[KEY_A] = KEY_UP; 291 break; 292 case 'w': 293 key_state[KEY_W] = KEY_UP; 294 break; 295 case 's': 296 key_state[KEY_S] = KEY_UP; 297 break; 298 case ' ': 299 key_state[KEY_SPACE] = KEY_UP; 300 break; 301 default: 302 break; 303 } 304 } break; 305 case ClientMessage: { 306 if ((Atom) event.xclient.data.l[0] == wm_delete_window) { 307 next_menu = QUIT; 308 } 309 } break; 310 default: 311 break; 312 } 313 } 314 } 315 316 void 317 handle_inputs(int key_state[]) 318 { 319 if (key_state[KEY_Q] == KEY_DOWN){ 320 next_menu = GAME_OVER; 321 return; 322 } 323 if (key_state[KEY_D] == KEY_DOWN) { 324 if (player.vx > 0) { 325 player.ax = 500; 326 } else { 327 player.ax = 1000; 328 } 329 } else if (key_state[KEY_A] == KEY_DOWN) { 330 if (player.vx > 0) { 331 player.ax = -1000; 332 } else { 333 player.ax = -500; 334 } 335 } else { 336 if (player_is_falling) 337 player.ax = -player.vx; 338 else 339 player.ax = -3 * player.vx; 340 } 341 342 if (player.vx < -200) player.vx = -200; 343 if (player.vx > 200) player.vx = 200; 344 /* 345 if (key_state[KEY_S] == KEY_DOWN) 346 player.vy += 300; 347 if (key_state[KEY_W] == KEY_DOWN) 348 player.vy += -300; 349 */ 350 if (!player_is_falling && key_state[KEY_SPACE] == KEY_DOWN) 351 player.vy = -450; 352 } 353 354 void 355 rect_next_tick(struct rect *s, long ndt) // nano second 356 { 357 s->ppx = s->px; 358 s->ppy = s->py; 359 s->vx += s->ax * ndt / 1000 / 1000 / 1000; 360 s->vy += s->ay * ndt / 1000 / 1000 / 1000; 361 s->px += s->vx * ndt / 1000 / 1000 / 1000; 362 s->py += s->vy * ndt / 1000 / 1000 / 1000; 363 364 // bind within the window 365 if (s->px < 0) { 366 s->px = 0; 367 //s->vx *= -1; 368 } 369 if (win_width < s->px + s->w) { 370 s->px = win_width - s->w; 371 //s->vx *= -1; 372 } 373 /* 374 if (s->py < 0) { 375 s->py = 0; 376 s->vy *= -1; 377 } 378 if (win_height < s->py + s->h) { 379 s->py = win_height - s->h; 380 s->vy *= -1; 381 } 382 */ 383 // game over when fall out of the screen 384 if (s->py > win_height) 385 next_menu = GAME_OVER; 386 } 387 388 void 389 circle_next_tick(struct circle *c, long ndt) 390 { 391 c->ppx = c->px; 392 c->ppy = c->py; 393 c->vx += c->ax * ndt / 1000 / 1000 / 1000; 394 c->vy += c->ay * ndt / 1000 / 1000 / 1000; 395 c->px += c->vx * ndt / 1000 / 1000 / 1000; 396 c->py += c->vy * ndt / 1000 / 1000 / 1000; 397 398 // bind within the window 399 if (c->px - c->r < 0) { 400 c->px = c->r; 401 c->vx *= -1; 402 } 403 if (win_width < c->px + c->r) { 404 c->px = win_width - c->r; 405 c->vx *= -1; 406 } 407 if (c->py - c->r < 0) { 408 c->py = c->r; 409 c->vy *= -1; 410 } 411 if (win_height < c->py + c->r) { 412 c->py = win_height - c->r; 413 c->vy *= -1; 414 } 415 } 416 417 int 418 rect_test_collision(struct rect *s1, struct rect *s2) 419 { 420 return s1->px < s2->px + s2->w && s2->px < s1->px + s1->w && 421 s2->py < s1->py + s1->h && s1->py < s2->py + s2->h; 422 } 423 424 int 425 circle_test_collision(struct circle *c1, struct circle *c2) 426 { 427 return (c1->px - c2->px) * (c1->px - c2->px) + 428 (c1->py - c2->py) * (c1->py - c2->py) < 429 (c1->r + c2->r) * (c1->r + c2->r); 430 } 431 432 /* 433 * Handle collision of a moving rect against fixed rect 434 */ 435 void 436 rect_handle_collision_mf(struct rect *sm, struct rect *sf) 437 { 438 if (!rect_test_collision(sm, sf)) 439 return; 440 if (sm->ppx + sm->w <= sf->ppx && sf->px < sm->px + sm->w) { 441 // collisioin from left to right 442 sm->px = sf->px - sm->w; 443 sm->vx = 0; 444 } 445 if (sf->ppx + sf->w <= sm->ppx && sm->px < sf->px + sf->w) { 446 // collision from right to left 447 sm->px = sf->px + sf->w; 448 sm->vx = 0; 449 } 450 451 if (sm->ppy + sm->h <= sf->ppy && sf->py < sm->py + sm->h) { 452 // collision from up to down 453 sm->py = sf->py - sm->h; 454 sm->vy = 0; 455 } 456 if (sf->ppy + sf->h <= sm->ppy && sm->py < sf->py + sf->h) { 457 // collision from dohn to up 458 sm->py = sf->py + sf->h; 459 sm->vy = 0; 460 } 461 462 } 463 464 /* 465 * Handle collision of a moving rect against another moving rect 466 */ 467 void 468 rect_handle_collision_mm(struct rect *s1, struct rect *s2) 469 { 470 if (!rect_test_collision(s1, s2)) 471 return; 472 473 float lapx = min(s1->px + s1->w, s2->px + s2->w) - max(s1->px, s2->px); 474 float lapy = min(s1->py + s1->h, s2->py + s2->h) - max(s1->py, s2->py); 475 476 if (lapx < lapy) { 477 if (s1->px + s1->w < s2->px + s2->w / 2) { 478 s1->px -= lapx / 2; 479 s2->px += lapx / 2; 480 } else { 481 s1->px += lapx / 2; 482 s2->px -= lapx / 2; 483 } 484 } else { 485 if (s1->py + s1->h < s2->py + s2->h / 2) { 486 s1->py -= lapy / 2; 487 s2->py += lapy / 2; 488 } else { 489 s1->py += lapy / 2; 490 s2->py -= lapy / 2; 491 } 492 } 493 } 494 495 void 496 circle_handle_collision_mm(struct circle *c1, struct circle *c2) 497 { 498 if (!circle_test_collision(c1, c2)) 499 return; 500 501 float col_px = c2->px - c1->px; 502 float col_py = c2->py - c1->py; 503 float col_pr = sqrtf(col_px * col_px + col_py * col_py); 504 col_px /= col_pr; 505 col_py /= col_pr; 506 507 c1->px = c1->px - col_px / 2; 508 c1->py = c1->py - col_py / 2; 509 c2->px = c2->px + col_px / 2; 510 c2->py = c2->py + col_py / 2; 511 } 512 513 void 514 rect_handle_collision_elastic(struct rect *s1, struct rect *s2) 515 { 516 if(!rect_test_collision(s1, s2)) 517 return; 518 519 rect_handle_collision_mm(s1, s2); 520 521 float v1, v2; 522 float m1 = s1->m; 523 float m2 = s2->m; 524 525 float lapx = min(s1->px + s1->w, s2->px + s2->w) - max(s1->px, s2->px); 526 float lapy = min(s1->py + s1->h, s2->py + s2->h) - max(s1->py, s2->py); 527 528 if (lapx < lapy) { 529 v1 = s1->vx; 530 v2 = s2->vx; 531 s1->vx = 2*m2/(m1+m2)*v2 + (m1-m2)/(m1+m2)*v1; 532 s2->vx = 2*m1/(m1+m2)*v1 + (m2-m1)/(m1+m2)*v2; 533 } else { 534 v1 = s1->vy; 535 v2 = s2->vy; 536 s1->vy = 2*m2/(m1+m2)*v2 + (m1-m2)/(m1+m2)*v1; 537 s2->vy = 2*m1/(m1+m2)*v1 + (m2-m1)/(m1+m2)*v2; 538 } 539 } 540 541 void 542 circle_handle_collision_elastic(struct circle *c1, struct circle *c2) 543 { 544 if(!circle_test_collision(c1, c2)) 545 return; 546 547 circle_handle_collision_mm(c1, c2); 548 549 float col_px = c2->px - c1->px; 550 float col_py = c2->py - c1->py; 551 float col_pr = sqrtf(col_px * col_px + col_py * col_py); 552 col_px /= col_pr; 553 col_py /= col_pr; 554 float nor_px = col_py; 555 float nor_py = -col_px; 556 557 float m1 = c1->m; 558 float m2 = c2->m; 559 560 float col_1v = c1->vx * col_px + c1->vy * col_py; 561 float col_2v = c2->vx * col_px + c2->vy * col_py; 562 563 float col_1vxn = (2*m2/(m1+m2)*col_2v + (m1-m2)/(m1+m2)*col_1v) * col_px; 564 float col_1vyn = (2*m2/(m1+m2)*col_2v + (m1-m2)/(m1+m2)*col_1v) * col_py; 565 float col_2vxn = (2*m1/(m1+m2)*col_1v + (m2-m1)/(m1+m2)*col_2v) * col_px; 566 float col_2vyn = (2*m1/(m1+m2)*col_1v + (m2-m1)/(m1+m2)*col_2v) * col_py; 567 568 float nor_1vx = nor_px * (c1->vx * nor_px + c1->vy * nor_py); 569 float nor_1vy = nor_py * (c1->vx * nor_px + c1->vy * nor_py); 570 float nor_2vx = nor_px * (c2->vx * nor_px + c2->vy * nor_py); 571 float nor_2vy = nor_py * (c2->vx * nor_px + c2->vy * nor_py); 572 573 c1->vx = col_1vxn + nor_1vx; 574 c1->vy = col_1vyn + nor_1vy; 575 c2->vx = col_2vxn + nor_2vx; 576 c2->vy = col_2vyn + nor_2vy; 577 } 578 579 void 580 update_falling_status(struct rect *player, long ndt) 581 { 582 int collision = 0; 583 struct rect r = {0}; 584 r.ppx = player->ppx; 585 r.ppy = player->ppy; 586 r.px = player->px; 587 r.py = player->py; 588 r.vx = 0; 589 r.vy = 0; 590 r.ax = 0; 591 r.ay = GRAVITY; 592 r.w = player->w; 593 r.h = player->h; 594 r.m = player->m; 595 596 rect_next_tick(&r, ndt); 597 for (int i = 0; i < NUM_RECT; i++) 598 if (rect_test_collision(&r, &block[i])) 599 collision = 1; 600 601 if (collision == 1) 602 player_is_falling = 0; 603 else 604 player_is_falling = 1; 605 } 606 607 void 608 game_play(void) 609 { 610 int key_state[NUM_KEY]; 611 long t0, t1, dt; 612 #ifdef COUNT_FPS 613 int fps_count = 0; 614 #endif 615 struct timespec ts; 616 617 int bi = 0; 618 for (int i = 0; i < WORLD_WIDTH * WORLD_HEIGHT; i++) { 619 if (world_map[i] == 'b') { 620 block[bi].ppx = block[bi].px = i % WORLD_WIDTH * BLOCK_SIZE; 621 block[bi].ppy = block[bi].py = i / WORLD_WIDTH * BLOCK_SIZE; 622 block[bi].ax = 0; 623 block[bi].ay = 0; 624 block[bi].vx = 0; 625 block[bi].vy = 0; 626 block[bi].w = block[bi].h = BLOCK_SIZE; 627 block[bi].m = block[bi].w * block[bi].h; 628 bi++; 629 } else if (world_map[i] == 'p') { 630 player.ppx = player.px = i % WORLD_WIDTH * BLOCK_SIZE; 631 player.ppy = player.py = i / WORLD_WIDTH * BLOCK_SIZE; 632 player.vx = 0; 633 player.vy = 0; 634 player.ax = 0; 635 player.ay = GRAVITY; 636 player.w = player.h = BLOCK_SIZE; 637 player.m = player.w * player.h; 638 } 639 } 640 641 while (next_menu == GAME_PLAY){ 642 clock_gettime(CLOCK_MONOTONIC, &ts); 643 t0 = ts.tv_nsec; 644 receive_events(key_state); 645 handle_inputs(key_state); 646 647 648 clock_gettime(CLOCK_MONOTONIC, &ts); 649 t0 = ts.tv_nsec; 650 651 int collision[NUM_RECT] = {0}; 652 for (int k = 0; k < SUB_TICK; k++) { 653 update_falling_status(&player, 1000 * 1000 * 1000 / FPS / SUB_TICK); 654 if (player_is_falling) 655 player.ay = GRAVITY; 656 else 657 player.ay = 0; 658 rect_next_tick(&player, 1000 * 1000 * 1000 / FPS / SUB_TICK); 659 for (int i = 0; i < NUM_RECT; i++) 660 rect_next_tick(&block[i], 1000 * 1000 * 1000 / FPS / SUB_TICK); 661 662 for (int i = 0; i < NUM_RECT; i++){ 663 rect_handle_collision_mf(&player, &block[i]); 664 if (rect_test_collision(&player, &block[i])) 665 collision[i] = 1; 666 } 667 } 668 for (int i = 0; i < NUM_RECT; i++) 669 if (collision[i] == 1) 670 XSetForeground(display, sgc[i], 0xFFFF00); 671 else 672 XSetForeground(display, sgc[i], 0x90FF90); 673 674 // fix fps 675 // TODO: This method create some strange stripe when 676 // rendered in 60fps on a 60fps monitor. 677 dt = 0; 678 while (dt < 1.0 * 1000 * 1000 * 1000 / FPS){ 679 clock_gettime(CLOCK_MONOTONIC, &ts); 680 t1 = ts.tv_nsec; 681 dt = t1 > t0 ? t1 - t0 : t1 - t0 + 1000 * 1000 * 1000; 682 } 683 #ifdef COUNT_FPS 684 // count fps. 685 fps_count++; 686 if (t1 < t0){ 687 printf("fps: %u\n", fps_count); 688 fps_count = 0; 689 } 690 #endif 691 692 XClearArea(display, window, 693 0, 0, // position 694 win_width, win_height, // width and height 695 False); 696 for (int i = 0; i < NUM_RECT; i++) { 697 XDrawRectangle(display, window, sgc[i], 698 block[i].px, 699 block[i].py, // position 700 block[i].w, block[i].h); 701 } 702 XDrawRectangle(display, window, gc, 703 player.px, 704 player.py, // position 705 player.w, player.h); 706 char status_string[128]; 707 snprintf(status_string, 128, "falling: %d", player_is_falling); 708 XDrawString(display, window, gc, 709 0, 20, 710 status_string, 711 strlen(status_string)); 712 } 713 } 714 715 void 716 game_over(void) 717 { 718 char *menu_char = "GAME OVER"; 719 720 XClearArea(display, window, 721 0, 0, // position 722 win_width, win_height, // width and height 723 False); 724 XDrawString(display, window, gc, 725 win_width/2 - strlen(menu_char)/2, win_height/2, 726 menu_char, strlen(menu_char)); 727 XFlush(display); 728 729 sleep(1); 730 next_menu = START_MENU; 731 } 732 733 void 734 cleanup(void) 735 { 736 XCloseDisplay(display); 737 } 738 739 int 740 main(void) 741 { 742 setup(); 743 while (next_menu != QUIT){ 744 switch (next_menu){ 745 case START_MENU: 746 start_menu(); 747 break; 748 case GAME_PLAY: 749 game_play(); 750 break; 751 case GAME_OVER: 752 game_over(); 753 break; 754 default: 755 break; 756 } 757 } 758 759 cleanup(); 760 return 0; 761 }