Jay Balar / Mbed 2 deprecated 4180_Project_3

Dependencies:   4DGL-uLCD-SE Physac-MBED PinDetect SDFileSystem mbed-rtos mbed

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers hockey.cpp Source File

hockey.cpp

00001 #include "hockey.h"
00002 #include "globals.h"
00003 
00004 // Custom memory allocation methods
00005 #define PHYSAC_NO_THREADS
00006 #define PHYSAC_STANDALONE
00007 #define PHYSAC_IMPLEMENTATION
00008 #define _STDBOOL_H
00009 #include "physac.h"
00010 
00011 // Controls size of gamepieces in the hockey arena. This influences both
00012 // rendering and collision, so be careful adjusting dimensions too small
00013 #define HOCKEY_PUCK_RADIUS 4
00014 #define HOCKEY_PADDLE_W 8
00015 #define HOCKEY_PADDLE_H 24
00016 
00017 // Physics sim properties
00018 //  HOCKEY_PHYSICS_STEPS    number of physics ticks between frames
00019 //  HOCKEY_PHYSICS_DELTA    how many ms the physics engine believes pass per
00020 //                          update. This is arbitrary. Pick what behaves well.
00021 //  HOCKEY_PUCK_VELOCITY    how fact the puck moves in one physics tick
00022 #define HOCKEY_PHYSICS_STEPS 4
00023 #define HOCKEY_PHYSICS_DELTA 1.66
00024 #define HOCKEY_PUCK_VELOCITY (5.0f / HOCKEY_PHYSICS_STEPS)
00025 
00026 // Screen-space constants:
00027 #define SCREEN_WIDTH 128
00028 #define SCREEN_HEIGHT 128
00029 
00030 // Controls the dimensions of the arena. You can adjust the screen margin above
00031 // and below the court, and the width of the goal.
00032 #define HOCKEY_ARENA_TOP 16
00033 #define HOCKEY_ARENA_BOT 127
00034 #define HOCKEY_GOAL_HEIGHT 32
00035 
00036 // Properties derived from other macros
00037 #define _HOCKEY_ARENA_H (HOCKEY_ARENA_BOT - HOCKEY_ARENA_TOP)
00038 #define _HOCKEY_ARENA_MID ((HOCKEY_ARENA_BOT + HOCKEY_ARENA_TOP)/2)
00039 #define _HOCKEY_GOAL_TOP ((_HOCKEY_ARENA_H - HOCKEY_GOAL_HEIGHT)/2 + HOCKEY_ARENA_TOP)
00040 #define _HOCKEY_GOAL_BOT (_HOCKEY_GOAL_TOP + HOCKEY_GOAL_HEIGHT)
00041 
00042 
00043 PhysicsBody puck;
00044 PhysicsBody paddle_a;
00045 PhysicsBody paddle_b;
00046 
00047 int blue_score;
00048 int red_score;
00049 bool serve_direction;   // 0 means toward blue, 1 means red
00050 float input_sensitivity = 4.00f;
00051 
00052 // Helper functions
00053 // @{
00054 int maxOf(int a, int b)
00055 {
00056     return (a > b) ? a : b;
00057 }
00058 int minOf(int a, int b)
00059 {
00060     return (a < b) ? a : b;
00061 }
00062 int clamp(int a, int low, int upp)
00063 {
00064     return minOf(upp, maxOf(a, low));
00065 }
00066 // @}
00067 
00068 /**
00069  * Sets up physics elements within the Physac library
00070  */
00071 void initPhysicsObjects()
00072 {
00073     SetPhysicsTimeStep(HOCKEY_PHYSICS_DELTA);
00074 
00075     puck = CreatePhysicsBodyCircle((Vector2) {
00076         SCREEN_WIDTH/2, SCREEN_HEIGHT/2
00077     }, HOCKEY_PUCK_RADIUS, 1);
00078     puck->enabled = true;
00079     puck->useGravity = false;
00080     puck->restitution = 1.0;
00081     puck->dynamicFriction = 0;
00082     puck->velocity = (Vector2) {
00083         HOCKEY_PUCK_VELOCITY, 0
00084     };
00085 
00086     paddle_a = CreatePhysicsBodyRectangle((Vector2) {
00087         32, _HOCKEY_ARENA_MID
00088     }, HOCKEY_PADDLE_W, HOCKEY_PADDLE_H, 100);
00089     paddle_a->enabled = false; // Disable body state to convert it to static (no dynamics, but collisions)
00090     paddle_a->useGravity = false;
00091     paddle_a->restitution = 1.0;
00092     SetPhysicsBodyRotation(paddle_a, 3.14159 / 6);
00093 
00094     paddle_b = CreatePhysicsBodyRectangle((Vector2) {
00095         96, _HOCKEY_ARENA_MID
00096     }, HOCKEY_PADDLE_W, HOCKEY_PADDLE_H, 100);
00097     paddle_b->enabled = false; // Disable body state to convert it to static (no dynamics, but collisions)
00098     paddle_b->useGravity = false;
00099     paddle_b->restitution = 1.0;
00100     SetPhysicsBodyRotation(paddle_b, 3.14159 / 6);
00101 }
00102 
00103 /**
00104  * Draws the fixed graphics of the game. This is the border of the arena
00105  *
00106  * note: locks `uLCD_mutex`
00107  */
00108 void drawStaticEnvironment()
00109 {
00110     uLCD_mutex.lock();
00111 
00112     // Clear screen
00113     uLCD.cls();
00114 
00115     // Draw arena border
00116     uLCD.line(0, _HOCKEY_GOAL_TOP, 0, HOCKEY_ARENA_TOP, BLUE);
00117     uLCD.line(0, _HOCKEY_GOAL_BOT, 0, HOCKEY_ARENA_BOT, BLUE);
00118     uLCD.line(0, HOCKEY_ARENA_TOP, 63, HOCKEY_ARENA_TOP, BLUE);
00119     uLCD.line(0, HOCKEY_ARENA_BOT, 63, HOCKEY_ARENA_BOT, BLUE);
00120     uLCD.line(127, _HOCKEY_GOAL_TOP, 127, HOCKEY_ARENA_TOP, RED);
00121     uLCD.line(127, _HOCKEY_GOAL_BOT, 127, HOCKEY_ARENA_BOT, RED);
00122     uLCD.line(64, HOCKEY_ARENA_TOP, 127, HOCKEY_ARENA_TOP, RED);
00123     uLCD.line(64, HOCKEY_ARENA_BOT, 127, HOCKEY_ARENA_BOT, RED);
00124 
00125     // Draw score
00126     uLCD.color(BLUE);
00127     uLCD.locate(0, 0);
00128     uLCD.printf("%i", blue_score);
00129     uLCD.color(RED);
00130     uLCD.locate(17, 0);
00131     uLCD.printf("%i", red_score);
00132 
00133     // Draw serve indicator
00134     uLCD.filled_circle(
00135         serve_direction ? SCREEN_WIDTH - 16 : 16,
00136         HOCKEY_ARENA_TOP / 2,
00137         HOCKEY_ARENA_TOP / 6,
00138         serve_direction ? RED : BLUE);
00139 
00140     uLCD_mutex.unlock();
00141 }
00142 
00143 /**
00144  * Redraws the puck and paddles
00145  * Each elements is erased, updated, and redrawn in as short of a window as
00146  * possible.
00147  *
00148  * note: locks `uLCD_mutex`
00149  */
00150 void drawDynamicObjects()
00151 {
00152     static int puck_x = SCREEN_WIDTH / 2,
00153                puck_y = _HOCKEY_ARENA_MID;
00154 
00155     uLCD_mutex.lock();
00156 
00157     // Redraw puck
00158     uLCD.filled_circle(puck_x, puck_y, HOCKEY_PUCK_RADIUS, BLACK);
00159     puck_x = puck->position.x;
00160     puck_y = puck->position.y;
00161     uLCD.filled_circle(puck_x, puck_y, HOCKEY_PUCK_RADIUS, GREEN);
00162 
00163     // Redraw blue paddle
00164     static int pa_x1 = 64,
00165                pa_x2 = 64,
00166                pa_y1 = 64,
00167                pa_y2 = 64;
00168     float sinA = sin(-paddle_a->orient) / 2;
00169     float cosA = cos(-paddle_a->orient) / 2;
00170     uLCD.line(pa_x1, pa_y1, pa_x2, pa_y2, BLACK);
00171     pa_x1 = paddle_a->position.x + HOCKEY_PADDLE_H * sinA;
00172     pa_x2 = paddle_a->position.x - HOCKEY_PADDLE_H * sinA;
00173     pa_y1 = paddle_a->position.y + HOCKEY_PADDLE_H * cosA;
00174     pa_y2 = paddle_a->position.y - HOCKEY_PADDLE_H * cosA;
00175     uLCD.line(pa_x1, pa_y1, pa_x2, pa_y2, BLUE);
00176 
00177     // Redraw red paddle
00178     static int pb_x1 = 64,
00179                pb_x2 = 64,
00180                pb_y1 = 64,
00181                pb_y2 = 64;
00182     float sinB = sin(-paddle_b->orient) / 2;
00183     float cosB = cos(-paddle_b->orient) / 2;
00184     uLCD.line(pb_x1, pb_y1, pb_x2, pb_y2, BLACK);
00185     pb_x1 = paddle_b->position.x + HOCKEY_PADDLE_H * sinB;
00186     pb_x2 = paddle_b->position.x - HOCKEY_PADDLE_H * sinB;
00187     pb_y1 = paddle_b->position.y + HOCKEY_PADDLE_H * cosB;
00188     pb_y2 = paddle_b->position.y - HOCKEY_PADDLE_H * cosB;
00189     uLCD.line(pb_x1, pb_y1, pb_x2, pb_y2, RED);
00190 
00191     uLCD_mutex.unlock();
00192 }
00193 
00194 /**
00195  * Reset the game after a goal.
00196  * If the game will continue, re-draw and reset the pieces.
00197  * If the game is over, display end scene and then suspend the game loop until
00198  *      re-launched.
00199  */
00200 void resetGame()
00201 {
00202     // If game over, draw an end-game screen
00203     if (red_score >= 5 || blue_score >= 5) {
00204         uLCD_mutex.lock();
00205         uLCD.cls();
00206         uLCD.locate(5, 3);
00207         uLCD.color(GREEN);
00208         uLCD.printf("Game Over");
00209 
00210         if (red_score > blue_score) {
00211             uLCD.locate(5, 5);
00212             uLCD.color(RED);
00213             uLCD.printf("Red Wins!");
00214         } else {
00215             uLCD.locate(5, 5);
00216             uLCD.color(BLUE);
00217             uLCD.printf("Blue Wins!");
00218         }
00219 
00220         Thread::wait(2000);
00221 
00222         uLCD.cls();
00223         menu_flag = 0;
00224 
00225         game2 = false;
00226         uLCD_mutex.unlock();
00227     }
00228 
00229     // Otherwise re-draw the game board and reset the pieces
00230     else {
00231         drawStaticEnvironment();
00232 
00233         puck->position.x = SCREEN_WIDTH/2;
00234         puck->position.y = _HOCKEY_ARENA_MID;
00235         puck->velocity.x = HOCKEY_PUCK_VELOCITY * (serve_direction ? 1 : -1);
00236         puck->velocity.y = (float)(rand() % 50) / 100;
00237 
00238         paddle_a->position.x = 32;
00239         paddle_a->position.y = _HOCKEY_ARENA_MID;
00240         SetPhysicsBodyRotation(paddle_a, 0);
00241 
00242         paddle_b->position.x = 96;
00243         paddle_b->position.y = _HOCKEY_ARENA_MID;
00244         SetPhysicsBodyRotation(paddle_b, 0);
00245     }
00246 }
00247 
00248 /**
00249  * Handles puck bouncing off walls, goal scoring, player input, and end-game
00250  * conditions
00251  */
00252 void runGameLogic()
00253 {
00254     // If puck hits the ceiling or floor, reflect across the y component of
00255     // the velocity
00256     if (puck->position.y < HOCKEY_ARENA_TOP + HOCKEY_PUCK_RADIUS + 1) {
00257         puck->velocity.y *= -1;
00258         puck->position.y = maxOf(
00259                                puck->position.y,
00260                                HOCKEY_ARENA_TOP + HOCKEY_PUCK_RADIUS + 1);
00261     } else if (puck->position.y > HOCKEY_ARENA_BOT - HOCKEY_PUCK_RADIUS - 1) {
00262         puck->velocity.y *= -1;
00263         puck->position.y = minOf(
00264                                puck->position.y,
00265                                HOCKEY_ARENA_BOT - HOCKEY_PUCK_RADIUS - 1);
00266     }
00267 
00268     // true if the puck is in the y range corresponding to the goal
00269     bool puckInGoalRange =
00270         puck->position.y > _HOCKEY_GOAL_TOP &&
00271         puck->position.y < _HOCKEY_GOAL_BOT;
00272 
00273     // Puck collides with left or right walls
00274     if (puckInGoalRange == false) {
00275         if (puck->position.x < HOCKEY_PUCK_RADIUS + 1 && puck->position.x > 0) {
00276             puck->velocity.x *= -1;
00277             puck->position.x = maxOf(
00278                                    puck->position.x,
00279                                    HOCKEY_PUCK_RADIUS + 1);
00280         } else if (puck->position.x > SCREEN_WIDTH - HOCKEY_PUCK_RADIUS - 1 && puck->position.x < SCREEN_WIDTH) {
00281             puck->velocity.x *= -1;
00282             puck->position.x = minOf(
00283                                    puck->position.x,
00284                                    SCREEN_WIDTH - HOCKEY_PUCK_RADIUS - 2);
00285         }
00286     }
00287 
00288     // Puck in goals
00289     else {
00290         // SCORE RED
00291         if (puck->position.x <= -HOCKEY_PUCK_RADIUS * 2) {
00292             red_score += 1;
00293             serve_direction = 0;
00294             uLCD.locate(4, 1);
00295             uLCD.color(RED);
00296             uLCD.printf("Red Scores!");
00297             rgB = 1;
00298             Thread::wait(2000);
00299             rgB = 0;
00300             resetGame();
00301         }
00302 
00303         // SCORE BLUE
00304         else if (puck->position.x >= SCREEN_WIDTH + HOCKEY_PUCK_RADIUS * 2) {
00305             blue_score += 1;
00306             serve_direction = 1;
00307             uLCD.locate(3, 1);
00308             uLCD.color(BLUE);
00309             Rgb = 1;
00310             uLCD.printf("Blue Scores!");
00311             Thread::wait(2000);
00312             Rgb = 0;
00313             resetGame();
00314         }
00315     }
00316 }
00317 
00318 /**
00319  * Process user input and update game state
00320  */
00321 void handleInput()
00322 {
00323     // Process input from the game
00324     blue.parseMessage();
00325     if (blue.button[BUTTON_UP]) {
00326         paddle_a->position.y -= input_sensitivity;
00327     }
00328     if (blue.button[BUTTON_DOWN]) {
00329         paddle_a->position.y += input_sensitivity;
00330     }
00331     if (blue.button[BUTTON_LEFT]) {
00332         paddle_a->position.x -= input_sensitivity;
00333     }
00334     if (blue.button[BUTTON_RIGHT]) {
00335         paddle_a->position.x += input_sensitivity;
00336     }
00337     if (blue.button[BUTTON_A]) {
00338         SetPhysicsBodyRotation(paddle_a, paddle_a->orient - input_sensitivity * 0.05);
00339     }
00340     if (blue.button[BUTTON_B]) {
00341         SetPhysicsBodyRotation(paddle_a, paddle_a->orient + input_sensitivity * 0.05);
00342     }
00343 
00344 
00345 
00346     
00347     // Player 2 Input
00348     if (myNav.up()) { // Up
00349         paddle_b->position.y -= input_sensitivity;
00350     }
00351     if (myNav.down()) { // down
00352         paddle_b->position.y += input_sensitivity;
00353     }
00354     if (myNav.left()) { // left
00355         paddle_b->position.x -= input_sensitivity;
00356     }
00357     if (myNav.right()) { // right
00358         paddle_b->position.x += input_sensitivity;
00359     }
00360     if (myNav.fire()) { // rotate
00361         SetPhysicsBodyRotation(paddle_b, paddle_b->orient + 3.14159 / 4);
00362     }
00363 
00364     // Bounds checking. Don't let users steer paddles out of the arena
00365     paddle_a->position.y = clamp(paddle_a->position.y, HOCKEY_ARENA_TOP, HOCKEY_ARENA_BOT);
00366     paddle_a->position.x = clamp(paddle_a->position.x, 1, SCREEN_WIDTH - 1);
00367 
00368     paddle_b->position.y = clamp(paddle_b->position.y, HOCKEY_ARENA_TOP, HOCKEY_ARENA_BOT);
00369     paddle_b->position.x = clamp(paddle_b->position.x, 1, SCREEN_WIDTH - 1);
00370 }
00371 
00372 /**
00373  * The primary game loop for the air hockey game
00374  */
00375 void hockeyGame(void)
00376 {
00377     // Set up Physac objects & simulation
00378     initPhysicsObjects();
00379 
00380     while (true) { 
00381         // Wait until the game starts
00382         while (game2 == false) {
00383            
00384             PRINTF("[HOCKEY] Idle\r\n");
00385             Thread::wait(500);
00386         }
00387         
00388         // Reset game state
00389         red_score = 0;
00390         blue_score = 0;
00391         serve_direction = rand() % 2;
00392         resetGame();
00393 
00394         // Reset screen, draw arena
00395         drawStaticEnvironment();
00396 
00397         Timer timer;
00398         timer.start();
00399 
00400         while (game2) {
00401             float dt = timer.read() * 1000;
00402             timer.reset();
00403 
00404             // Update the physics of the game
00405             for (int i = 0; i < HOCKEY_PHYSICS_STEPS; i++) {
00406                 PhysicsStep();
00407                 runGameLogic();
00408                 if (game2 == false) break;
00409             }
00410 
00411             // Exit loop early if game over after the logic step
00412             if (game2 == false) continue;
00413 
00414             handleInput();
00415 
00416             // Render the game
00417             drawDynamicObjects();
00418 
00419             PRINTF("[HOCKEY] Delta %f\r\n", dt);
00420             Thread::wait(100);
00421         }
00422     }
00423 
00424     ClosePhysics();
00425 }