Sound update
Dependencies: 4DGL-uLCD-SE Physac-MBED PinDetect SDFileSystem mbed-rtos mbed
hockey/hockey.cpp
- Committer:
- jaybalar
- Date:
- 21 months ago
- Revision:
- 31:b08cc3c126d6
- Parent:
- 28:bccd14334bb9
File content as of revision 31:b08cc3c126d6:
#include "hockey.h" #include "globals.h" // Custom memory allocation methods #define PHYSAC_NO_THREADS #define PHYSAC_STANDALONE #define PHYSAC_IMPLEMENTATION #define _STDBOOL_H #include "physac.h" // Controls size of gamepieces in the hockey arena. This influences both // rendering and collision, so be careful adjusting dimensions too small #define HOCKEY_PUCK_RADIUS 4 #define HOCKEY_PADDLE_W 8 #define HOCKEY_PADDLE_H 24 // Physics sim properties // HOCKEY_PHYSICS_STEPS number of physics ticks between frames // HOCKEY_PHYSICS_DELTA how many ms the physics engine believes pass per // update. This is arbitrary. Pick what behaves well. // HOCKEY_PUCK_VELOCITY how fact the puck moves in one physics tick #define HOCKEY_PHYSICS_STEPS 4 #define HOCKEY_PHYSICS_DELTA 1.66 #define HOCKEY_PUCK_VELOCITY (5.0f / HOCKEY_PHYSICS_STEPS) // Screen-space constants: #define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 128 // Controls the dimensions of the arena. You can adjust the screen margin above // and below the court, and the width of the goal. #define HOCKEY_ARENA_TOP 16 #define HOCKEY_ARENA_BOT 127 #define HOCKEY_GOAL_HEIGHT 32 // Properties derived from other macros #define _HOCKEY_ARENA_H (HOCKEY_ARENA_BOT - HOCKEY_ARENA_TOP) #define _HOCKEY_ARENA_MID ((HOCKEY_ARENA_BOT + HOCKEY_ARENA_TOP)/2) #define _HOCKEY_GOAL_TOP ((_HOCKEY_ARENA_H - HOCKEY_GOAL_HEIGHT)/2 + HOCKEY_ARENA_TOP) #define _HOCKEY_GOAL_BOT (_HOCKEY_GOAL_TOP + HOCKEY_GOAL_HEIGHT) PhysicsBody puck; PhysicsBody paddle_a; PhysicsBody paddle_b; int blue_score; int red_score; bool serve_direction; // 0 means toward blue, 1 means red float input_sensitivity = 4.00f; // Helper functions // @{ int maxOf(int a, int b) { return (a > b) ? a : b; } int minOf(int a, int b) { return (a < b) ? a : b; } int clamp(int a, int low, int upp) { return minOf(upp, maxOf(a, low)); } // @} /** * Sets up physics elements within the Physac library */ void initPhysicsObjects() { SetPhysicsTimeStep(HOCKEY_PHYSICS_DELTA); puck = CreatePhysicsBodyCircle((Vector2) { SCREEN_WIDTH/2, SCREEN_HEIGHT/2 }, HOCKEY_PUCK_RADIUS, 1); puck->enabled = true; puck->useGravity = false; puck->restitution = 1.0; puck->dynamicFriction = 0; puck->velocity = (Vector2) { HOCKEY_PUCK_VELOCITY, 0 }; paddle_a = CreatePhysicsBodyRectangle((Vector2) { 32, _HOCKEY_ARENA_MID }, HOCKEY_PADDLE_W, HOCKEY_PADDLE_H, 100); paddle_a->enabled = false; // Disable body state to convert it to static (no dynamics, but collisions) paddle_a->useGravity = false; paddle_a->restitution = 1.0; SetPhysicsBodyRotation(paddle_a, 3.14159 / 6); paddle_b = CreatePhysicsBodyRectangle((Vector2) { 96, _HOCKEY_ARENA_MID }, HOCKEY_PADDLE_W, HOCKEY_PADDLE_H, 100); paddle_b->enabled = false; // Disable body state to convert it to static (no dynamics, but collisions) paddle_b->useGravity = false; paddle_b->restitution = 1.0; SetPhysicsBodyRotation(paddle_b, 3.14159 / 6); } /** * Draws the fixed graphics of the game. This is the border of the arena * * note: locks `uLCD_mutex` */ void drawStaticEnvironment() { uLCD_mutex.lock(); // Clear screen uLCD.cls(); // Draw arena border uLCD.line(0, _HOCKEY_GOAL_TOP, 0, HOCKEY_ARENA_TOP, BLUE); uLCD.line(0, _HOCKEY_GOAL_BOT, 0, HOCKEY_ARENA_BOT, BLUE); uLCD.line(0, HOCKEY_ARENA_TOP, 63, HOCKEY_ARENA_TOP, BLUE); uLCD.line(0, HOCKEY_ARENA_BOT, 63, HOCKEY_ARENA_BOT, BLUE); uLCD.line(127, _HOCKEY_GOAL_TOP, 127, HOCKEY_ARENA_TOP, RED); uLCD.line(127, _HOCKEY_GOAL_BOT, 127, HOCKEY_ARENA_BOT, RED); uLCD.line(64, HOCKEY_ARENA_TOP, 127, HOCKEY_ARENA_TOP, RED); uLCD.line(64, HOCKEY_ARENA_BOT, 127, HOCKEY_ARENA_BOT, RED); // Draw score uLCD.color(BLUE); uLCD.locate(0, 0); uLCD.printf("%i", blue_score); uLCD.color(RED); uLCD.locate(17, 0); uLCD.printf("%i", red_score); // Draw serve indicator uLCD.filled_circle( serve_direction ? SCREEN_WIDTH - 16 : 16, HOCKEY_ARENA_TOP / 2, HOCKEY_ARENA_TOP / 6, serve_direction ? RED : BLUE); uLCD_mutex.unlock(); } /** * Redraws the puck and paddles * Each elements is erased, updated, and redrawn in as short of a window as * possible. * * note: locks `uLCD_mutex` */ void drawDynamicObjects() { static int puck_x = SCREEN_WIDTH / 2, puck_y = _HOCKEY_ARENA_MID; uLCD_mutex.lock(); // Redraw puck uLCD.filled_circle(puck_x, puck_y, HOCKEY_PUCK_RADIUS, BLACK); puck_x = puck->position.x; puck_y = puck->position.y; uLCD.filled_circle(puck_x, puck_y, HOCKEY_PUCK_RADIUS, GREEN); // Redraw blue paddle static int pa_x1 = 64, pa_x2 = 64, pa_y1 = 64, pa_y2 = 64; float sinA = sin(-paddle_a->orient) / 2; float cosA = cos(-paddle_a->orient) / 2; uLCD.line(pa_x1, pa_y1, pa_x2, pa_y2, BLACK); pa_x1 = paddle_a->position.x + HOCKEY_PADDLE_H * sinA; pa_x2 = paddle_a->position.x - HOCKEY_PADDLE_H * sinA; pa_y1 = paddle_a->position.y + HOCKEY_PADDLE_H * cosA; pa_y2 = paddle_a->position.y - HOCKEY_PADDLE_H * cosA; uLCD.line(pa_x1, pa_y1, pa_x2, pa_y2, BLUE); // Redraw red paddle static int pb_x1 = 64, pb_x2 = 64, pb_y1 = 64, pb_y2 = 64; float sinB = sin(-paddle_b->orient) / 2; float cosB = cos(-paddle_b->orient) / 2; uLCD.line(pb_x1, pb_y1, pb_x2, pb_y2, BLACK); pb_x1 = paddle_b->position.x + HOCKEY_PADDLE_H * sinB; pb_x2 = paddle_b->position.x - HOCKEY_PADDLE_H * sinB; pb_y1 = paddle_b->position.y + HOCKEY_PADDLE_H * cosB; pb_y2 = paddle_b->position.y - HOCKEY_PADDLE_H * cosB; uLCD.line(pb_x1, pb_y1, pb_x2, pb_y2, RED); uLCD_mutex.unlock(); } /** * Reset the game after a goal. * If the game will continue, re-draw and reset the pieces. * If the game is over, display end scene and then suspend the game loop until * re-launched. */ void resetGame() { // If game over, draw an end-game screen if (red_score >= 5 || blue_score >= 5) { uLCD_mutex.lock(); uLCD.cls(); uLCD.locate(5, 3); uLCD.color(GREEN); uLCD.printf("Game Over"); if (red_score > blue_score) { uLCD.locate(5, 5); uLCD.color(RED); uLCD.printf("Red Wins!"); } else { uLCD.locate(5, 5); uLCD.color(BLUE); uLCD.printf("Blue Wins!"); } Thread::wait(2000); uLCD.cls(); menu_flag = 0; game2 = false; uLCD_mutex.unlock(); } // Otherwise re-draw the game board and reset the pieces else { drawStaticEnvironment(); puck->position.x = SCREEN_WIDTH/2; puck->position.y = _HOCKEY_ARENA_MID; puck->velocity.x = HOCKEY_PUCK_VELOCITY * (serve_direction ? 1 : -1); puck->velocity.y = (float)(rand() % 50) / 100; paddle_a->position.x = 32; paddle_a->position.y = _HOCKEY_ARENA_MID; SetPhysicsBodyRotation(paddle_a, 0); paddle_b->position.x = 96; paddle_b->position.y = _HOCKEY_ARENA_MID; SetPhysicsBodyRotation(paddle_b, 0); } } /** * Handles puck bouncing off walls, goal scoring, player input, and end-game * conditions */ void runGameLogic() { // If puck hits the ceiling or floor, reflect across the y component of // the velocity if (puck->position.y < HOCKEY_ARENA_TOP + HOCKEY_PUCK_RADIUS + 1) { puck->velocity.y *= -1; puck->position.y = maxOf( puck->position.y, HOCKEY_ARENA_TOP + HOCKEY_PUCK_RADIUS + 1); } else if (puck->position.y > HOCKEY_ARENA_BOT - HOCKEY_PUCK_RADIUS - 1) { puck->velocity.y *= -1; puck->position.y = minOf( puck->position.y, HOCKEY_ARENA_BOT - HOCKEY_PUCK_RADIUS - 1); } // true if the puck is in the y range corresponding to the goal bool puckInGoalRange = puck->position.y > _HOCKEY_GOAL_TOP && puck->position.y < _HOCKEY_GOAL_BOT; // Puck collides with left or right walls if (puckInGoalRange == false) { if (puck->position.x < HOCKEY_PUCK_RADIUS + 1 && puck->position.x > 0) { puck->velocity.x *= -1; puck->position.x = maxOf( puck->position.x, HOCKEY_PUCK_RADIUS + 1); } else if (puck->position.x > SCREEN_WIDTH - HOCKEY_PUCK_RADIUS - 1 && puck->position.x < SCREEN_WIDTH) { puck->velocity.x *= -1; puck->position.x = minOf( puck->position.x, SCREEN_WIDTH - HOCKEY_PUCK_RADIUS - 2); } } // Puck in goals else { // SCORE RED if (puck->position.x <= -HOCKEY_PUCK_RADIUS * 2) { red_score += 1; serve_direction = 0; uLCD.locate(4, 1); uLCD.color(RED); uLCD.printf("Red Scores!"); rgB = 1; Thread::wait(2000); rgB = 0; resetGame(); } // SCORE BLUE else if (puck->position.x >= SCREEN_WIDTH + HOCKEY_PUCK_RADIUS * 2) { blue_score += 1; serve_direction = 1; uLCD.locate(3, 1); uLCD.color(BLUE); Rgb = 1; uLCD.printf("Blue Scores!"); Thread::wait(2000); Rgb = 0; resetGame(); } } } /** * Process user input and update game state */ void handleInput() { // Process input from the game blue.parseMessage(); if (blue.button[BUTTON_UP]) { paddle_a->position.y -= input_sensitivity; } if (blue.button[BUTTON_DOWN]) { paddle_a->position.y += input_sensitivity; } if (blue.button[BUTTON_LEFT]) { paddle_a->position.x -= input_sensitivity; } if (blue.button[BUTTON_RIGHT]) { paddle_a->position.x += input_sensitivity; } if (blue.button[BUTTON_A]) { SetPhysicsBodyRotation(paddle_a, paddle_a->orient - input_sensitivity * 0.05); } if (blue.button[BUTTON_B]) { SetPhysicsBodyRotation(paddle_a, paddle_a->orient + input_sensitivity * 0.05); } // Player 2 Input if (myNav.up()) { // Up paddle_b->position.y -= input_sensitivity; } if (myNav.down()) { // down paddle_b->position.y += input_sensitivity; } if (myNav.left()) { // left paddle_b->position.x -= input_sensitivity; } if (myNav.right()) { // right paddle_b->position.x += input_sensitivity; } if (myNav.fire()) { // rotate SetPhysicsBodyRotation(paddle_b, paddle_b->orient + 3.14159 / 4); } // Bounds checking. Don't let users steer paddles out of the arena paddle_a->position.y = clamp(paddle_a->position.y, HOCKEY_ARENA_TOP, HOCKEY_ARENA_BOT); paddle_a->position.x = clamp(paddle_a->position.x, 1, SCREEN_WIDTH - 1); paddle_b->position.y = clamp(paddle_b->position.y, HOCKEY_ARENA_TOP, HOCKEY_ARENA_BOT); paddle_b->position.x = clamp(paddle_b->position.x, 1, SCREEN_WIDTH - 1); } /** * The primary game loop for the air hockey game */ void hockeyGame(void) { // Set up Physac objects & simulation initPhysicsObjects(); while (true) { // Wait until the game starts while (game2 == false) { PRINTF("[HOCKEY] Idle\r\n"); Thread::wait(500); } // Reset game state red_score = 0; blue_score = 0; serve_direction = rand() % 2; resetGame(); // Reset screen, draw arena drawStaticEnvironment(); Timer timer; timer.start(); while (game2) { float dt = timer.read() * 1000; timer.reset(); // Update the physics of the game for (int i = 0; i < HOCKEY_PHYSICS_STEPS; i++) { PhysicsStep(); runGameLogic(); if (game2 == false) break; } // Exit loop early if game over after the logic step if (game2 == false) continue; handleInput(); // Render the game drawDynamicObjects(); PRINTF("[HOCKEY] Delta %f\r\n", dt); Thread::wait(100); } } ClosePhysics(); }