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
Diff: hockey/hockey.cpp
- Revision:
- 12:5d913b57da7c
- Parent:
- 9:4e6fae5f9b23
- Child:
- 16:6cf744b2623a
--- a/hockey/hockey.cpp Tue Nov 29 23:05:30 2022 +0000 +++ b/hockey/hockey.cpp Wed Nov 30 21:20:21 2022 +0000 @@ -1,7 +1,5 @@ -#include "mbed.h" -#include "rtos.h" #include "hockey.h" -#include "uLCD_4DGL.h" +#include "globals.h" #define PHYSAC_NO_THREADS #define PHYSAC_STANDALONE @@ -9,123 +7,316 @@ #define _STDBOOL_H #include "physac.h" -uLCD_4DGL uLCD(p9,p10,p30); -Serial pc(USBTX, USBRX); +#define SCREEN_WIDTH 128 +#define SCREEN_HEIGHT 128 + +// 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 -void thread2(void) -{ - uLCD.baudrate(BAUD_3000000); - wait(.05); +// 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_GOAL_TOP ((_HOCKEY_ARENA_H - HOCKEY_GOAL_HEIGHT)/2 + HOCKEY_ARENA_TOP) +#define _HOCKEY_GOAL_BOT (_HOCKEY_GOAL_TOP + HOCKEY_GOAL_HEIGHT) - //-------------------------------------------------------------------------------------- - int screenWidth = 128; - int screenHeight = 128; +int blue_score = 0; +int red_score = 0; + +PhysicsBody puck; +PhysicsBody paddle_a; +PhysicsBody paddle_b; + +float input_sensitivity = 0.01f; - PhysicsBody puck = CreatePhysicsBodyCircle((Vector2) { - screenWidth/2, screenHeight/2 - }, 4, 10); +int maxOf(int a, int b) +{ + return (a > b) ? a : b; +} + +int minOf(int a, int b) +{ + return (a < b) ? a : b; +} + +void initPhysicsObjects() +{ + 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.0; puck->velocity = (Vector2) { 5, 0 }; - - - // Create paddle_a rectangle physics body - PhysicsBody paddle_a = CreatePhysicsBodyRectangle((Vector2) { + paddle_a = CreatePhysicsBodyRectangle((Vector2) { 32, 64 - }, 8, 24, 10); + }, 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); - PhysicsBody paddle_b = CreatePhysicsBodyRectangle((Vector2) { + paddle_b = CreatePhysicsBodyRectangle((Vector2) { 96, 64 - }, 8, 24, 10); + }, 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 + */ +void drawStaticEnvironment() +{ + 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); - // Simulation Loop - Timer timer; - timer.start(); + 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); +} + +/** + * Redraws the puck and paddles + * Each elements is erased, updated, and redrawn in as short of a window as + * possible. + */ +void drawDynamicObjects() +{ + static int puck_x = SCREEN_WIDTH / 2, + puck_y = SCREEN_WIDTH / 2; + + // Redraw puck if moved + if (puck_x != puck->position.x || puck_y != puck->position.y) { + // erase + uLCD.filled_circle(puck_x, puck_y, HOCKEY_PUCK_RADIUS, BLACK); - deltaTime = 1.66; + // update + puck_x = puck->position.x; + puck_y = puck->position.y; + + // redraw + 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; - while (true) { - float dt = timer.read() * 1000; - timer.reset(); + 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); +} - accumulator += dt; - if (accumulator >= deltaTime) { - PhysicsStep(); - accumulator -= deltaTime; +void resetGame() +{ + if (red_score >= 5 || blue_score >= 5) { + uLCD.cls(); + uLCD.printf("Game Over"); + + if (red_score > blue_score) { + uLCD.printf("Red Wins"); + } else { + uLCD.printf("Blue Wins"); } - if (puck->position.y < 6 || puck->position.y > 122) { - puck->velocity.y *= -1; - } else if ((puck->position.y < 42 || puck->position.y > 86) && - (puck->position.x < 6 || puck->position.x > 122)) { - puck->velocity.x *= -1; - } + Thread::wait(2000); + + game2 = false; + } else { + uLCD.cls(); + drawStaticEnvironment(); - float phys_time = timer.read() * 1000; + puck->position.x = SCREEN_WIDTH/2; + puck->position.y = SCREEN_HEIGHT/2; + paddle_a->position.x = 32; + paddle_a->position.y = 64; + SetPhysicsBodyRotation(paddle_a, 0); + + paddle_b->position.x = 96; + paddle_b->position.y = 64; + SetPhysicsBodyRotation(paddle_b, 0); + } +} + - // Draw created physics bodies - int a_count = GetPhysicsShapeVerticesCount(paddle_a->id); - for (int j = 0; j < a_count; j++) { - Vector2 vertexA = GetPhysicsShapeVertex(paddle_a, j); - int jj = (((j + 1) < a_count) ? (j + 1) : 0); - Vector2 vertexB = GetPhysicsShapeVertex(paddle_a, jj); - uLCD.line(vertexA.x, vertexA.y, vertexB.x, vertexB.y, BLUE); +/** + * + */ +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->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->velocity.x *= -1; + puck->position.x = minOf( + puck->position.x, + SCREEN_WIDTH - HOCKEY_PUCK_RADIUS - 2); } - int b_count = GetPhysicsShapeVerticesCount(paddle_b->id); - for (int j = 0; j < b_count; j++) { - Vector2 vertexA = GetPhysicsShapeVertex(paddle_b, j); - int jj = (((j + 1) < b_count) ? (j + 1) : 0); - Vector2 vertexB = GetPhysicsShapeVertex(paddle_b, jj); - uLCD.line(vertexA.x, vertexA.y, vertexB.x, vertexB.y, RED); + } + + // Puck in goals + else { + // SCORE RED + if (puck->position.x < -HOCKEY_PUCK_RADIUS * 2) { + red_score += 1; + uLCD.printf("Red Scores"); + uLCD.printf(" %i - %i", blue_score, red_score); + Thread::wait(2000); + resetGame(); + } + + // SCORE BLUE + else if (puck->position.x > SCREEN_WIDTH + HOCKEY_PUCK_RADIUS*2) { + blue_score += 1; + uLCD.printf("Blue Scores"); + uLCD.printf(" %i - %i", blue_score, red_score); + Thread::wait(2000); + resetGame(); + } + } +} + +void hockeyGame(void) +{ + printf("Entering thread 2\r\n"); + while (true) { + + while (game2 == false) { + printf("Waiting thread 2\r\n"); + Thread::wait(500); } - uLCD.filled_circle(puck->position.x, puck->position.y, 4, GREEN); + printf("Starting Game 2 - Air Hockey\r\n"); + + // Reset game variables + red_score = 0; + blue_score = 0; - /*int bodiesCount = GetPhysicsBodiesCount(); - for (int i = 0; i < bodiesCount; i++) { - PhysicsBody body = GetPhysicsBody(i); + // Reset screen, draw arena + uLCD.baudrate(BAUD_3000000); + uLCD.cls(); + drawStaticEnvironment(); + + // Set up Physac objects & simulation + initPhysicsObjects(); - if (body != NULL) { - int vertexCount = GetPhysicsShapeVerticesCount(i); - for (int j = 0; j < vertexCount; j++) { - // Get physics bodies shape vertices to draw lines - // Note: GetPhysicsShapeVertex() already calculates rotation transformations - Vector2 vertexA = GetPhysicsShapeVertex(body, j); + Timer timer; + timer.start(); + + deltaTime = 1.66; + + while (game2) { + float dt = timer.read() * 1000; + timer.reset(); - int jj = (((j + 1) < vertexCount) ? (j + 1) : 0); // Get next vertex or first to close the shape - Vector2 vertexB = GetPhysicsShapeVertex(body, jj); + // Update the physics of the game + accumulator += dt; + if (accumulator >= deltaTime) { + PhysicsStep(); + accumulator -= deltaTime; + + runGameLogic(); + } + float phys_time = timer.read() * 1000; - uLCD.line(vertexA.x, vertexA.y, vertexB.x, vertexB.y, GREEN); // Draw a line between two vertex positions - } - } - }*/ - float render_time = timer.read()*1000 - phys_time; - + // Render the game + drawDynamicObjects(); + float render_time = timer.read()*1000 - phys_time; - pc.printf("[%2.2f] Phys: %4.4f/%4.4f Render: %4.4f | %4.2f %4.2f\r\n", - dt, - phys_time, - accumulator, - render_time, - puck->position.x, - puck->position.y); + // Process input from the game + blue.parseMessage(); + if (blue.button[BUTTON_UP]) { + paddle_a->position.y -= dt * input_sensitivity; + printf("UP\r\n"); + } + if (blue.button[BUTTON_DOWN]) { + paddle_a->position.y += dt * input_sensitivity; + printf("DOWN\r\n"); + } + if (blue.button[BUTTON_LEFT]) { + paddle_a->position.x -= dt * input_sensitivity; + printf("LEFT\r\n"); + } + if (blue.button[BUTTON_RIGHT]) { + paddle_a->position.x += dt * input_sensitivity; + printf("RIGHT\r\n"); + } + if (blue.button[BUTTON_A]) { + SetPhysicsBodyRotation(paddle_a, paddle_a->orient - dt * input_sensitivity * 0.05); + printf("CW\r\n"); + } + if (blue.button[BUTTON_B]) { + SetPhysicsBodyRotation(paddle_a, paddle_a->orient + dt * input_sensitivity * 0.05); + printf("CCW\r\n"); + } + + float input_time = timer.read()*1000 - render_time - phys_time; + } + + ClosePhysics(); // Unitialize physics } - - ClosePhysics(); // Unitialize physics - } \ No newline at end of file