Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: 4DGL-uLCD-SE Physac-MBED PinDetect SDFileSystem mbed-rtos mbed
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 }
Generated on Fri Dec 9 2022 21:28:44 by
1.7.2