ECE 2036 Project
Dependencies: mbed wave_player 4DGL-uLCD-SE
game.cpp@2:2042f29de6b7, 2019-11-21 (annotated)
- Committer:
- abraha2d
- Date:
- Thu Nov 21 16:10:57 2019 +0000
- Revision:
- 2:2042f29de6b7
- Parent:
- 1:c18c231cb299
Because other peeps is wanting dis...
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
rconnorlawson | 0:cf4396614a79 | 1 | #include "game.h" |
rconnorlawson | 0:cf4396614a79 | 2 | |
rconnorlawson | 0:cf4396614a79 | 3 | #include "globals.h" |
rconnorlawson | 0:cf4396614a79 | 4 | #include "physics.h" |
rconnorlawson | 0:cf4396614a79 | 5 | #include "wall.h" |
rconnorlawson | 0:cf4396614a79 | 6 | |
rconnorlawson | 0:cf4396614a79 | 7 | |
rconnorlawson | 0:cf4396614a79 | 8 | /** Erases the ball from the screen by drawing over it with the background color. */ |
rconnorlawson | 0:cf4396614a79 | 9 | void erase_ball(Ball* ball) |
rconnorlawson | 0:cf4396614a79 | 10 | { |
abraha2d | 2:2042f29de6b7 | 11 | // Draw background color over previously drawn ball location |
abraha2d | 2:2042f29de6b7 | 12 | uLCD.filled_circle(ball->x, ball->y, radius, BLACK); |
rconnorlawson | 0:cf4396614a79 | 13 | } |
rconnorlawson | 0:cf4396614a79 | 14 | |
rconnorlawson | 0:cf4396614a79 | 15 | /** Draws the ball on the screen at the updated location (according to the state) */ |
rconnorlawson | 0:cf4396614a79 | 16 | void draw_ball(Ball* ball, Physics* state) |
rconnorlawson | 0:cf4396614a79 | 17 | { |
abraha2d | 2:2042f29de6b7 | 18 | // Draw ball in its updated location |
abraha2d | 2:2042f29de6b7 | 19 | uLCD.filled_circle(state->px, state->py, radius, WHITE); |
abraha2d | 2:2042f29de6b7 | 20 | // Save that updated ball position for later erasing |
abraha2d | 2:2042f29de6b7 | 21 | ball->x = state->px; |
abraha2d | 2:2042f29de6b7 | 22 | ball->y = state->py; |
abraha2d | 2:2042f29de6b7 | 23 | } |
abraha2d | 2:2042f29de6b7 | 24 | |
abraha2d | 2:2042f29de6b7 | 25 | /** Draws a hole on the screen */ |
abraha2d | 2:2042f29de6b7 | 26 | void draw_hole(Hole* hole) |
abraha2d | 2:2042f29de6b7 | 27 | { |
abraha2d | 2:2042f29de6b7 | 28 | if (hole->should_draw) { |
abraha2d | 2:2042f29de6b7 | 29 | // Draw hole in its location |
abraha2d | 2:2042f29de6b7 | 30 | uLCD.filled_circle(hole->x, hole->y, radius - 1, RED); |
abraha2d | 2:2042f29de6b7 | 31 | hole->should_draw = 0; |
abraha2d | 2:2042f29de6b7 | 32 | } |
abraha2d | 2:2042f29de6b7 | 33 | } |
abraha2d | 2:2042f29de6b7 | 34 | |
abraha2d | 2:2042f29de6b7 | 35 | /** Checks whether the ball has hit a hole */ |
abraha2d | 2:2042f29de6b7 | 36 | int do_hole(Hole* hole, Physics* curr) |
abraha2d | 2:2042f29de6b7 | 37 | { |
abraha2d | 2:2042f29de6b7 | 38 | double dist = sqrt(pow(curr->px - hole->x, 2) + pow(curr->py - hole->y, 2)); |
abraha2d | 2:2042f29de6b7 | 39 | if (dist < radius * 2) { |
abraha2d | 2:2042f29de6b7 | 40 | hole->should_draw = 1; |
abraha2d | 2:2042f29de6b7 | 41 | } |
abraha2d | 2:2042f29de6b7 | 42 | if (dist < radius) { |
abraha2d | 2:2042f29de6b7 | 43 | return 2; |
abraha2d | 2:2042f29de6b7 | 44 | } |
abraha2d | 2:2042f29de6b7 | 45 | return 0; |
abraha2d | 2:2042f29de6b7 | 46 | } |
abraha2d | 2:2042f29de6b7 | 47 | |
abraha2d | 2:2042f29de6b7 | 48 | /** Draws the goal on the screen */ |
abraha2d | 2:2042f29de6b7 | 49 | void draw_goal(Goal* goal) |
abraha2d | 2:2042f29de6b7 | 50 | { |
abraha2d | 2:2042f29de6b7 | 51 | if (goal->should_draw) { |
abraha2d | 2:2042f29de6b7 | 52 | // Draw goal in its location |
abraha2d | 2:2042f29de6b7 | 53 | uLCD.filled_circle(goal->x, goal->y, radius + 1, GREEN); |
abraha2d | 2:2042f29de6b7 | 54 | goal->should_draw = 0; |
abraha2d | 2:2042f29de6b7 | 55 | } |
abraha2d | 2:2042f29de6b7 | 56 | } |
abraha2d | 2:2042f29de6b7 | 57 | |
abraha2d | 2:2042f29de6b7 | 58 | /** Checks whether the ball has hit the goal */ |
abraha2d | 2:2042f29de6b7 | 59 | int do_goal(Goal* goal, Physics* curr) |
abraha2d | 2:2042f29de6b7 | 60 | { |
abraha2d | 2:2042f29de6b7 | 61 | double dist = sqrt(pow(curr->px - goal->x, 2) + pow(curr->py - goal->y, 2)); |
abraha2d | 2:2042f29de6b7 | 62 | if (dist < radius * 2) { |
abraha2d | 2:2042f29de6b7 | 63 | goal->should_draw = 1; |
abraha2d | 2:2042f29de6b7 | 64 | } |
abraha2d | 2:2042f29de6b7 | 65 | if (dist < radius) { |
abraha2d | 2:2042f29de6b7 | 66 | return 1; |
abraha2d | 2:2042f29de6b7 | 67 | } |
abraha2d | 2:2042f29de6b7 | 68 | return 0; |
rconnorlawson | 0:cf4396614a79 | 69 | } |
rconnorlawson | 0:cf4396614a79 | 70 | |
rconnorlawson | 0:cf4396614a79 | 71 | /** Reads inputs to the game, such as accelerometer and buttons */ |
rconnorlawson | 0:cf4396614a79 | 72 | GameInputs read_inputs() |
rconnorlawson | 0:cf4396614a79 | 73 | { |
rconnorlawson | 0:cf4396614a79 | 74 | GameInputs inputs = {0}; |
rconnorlawson | 0:cf4396614a79 | 75 | |
abraha2d | 2:2042f29de6b7 | 76 | // Get acceleration vector from accelerometer |
abraha2d | 2:2042f29de6b7 | 77 | acc.readXYZGravity(&inputs.ax, &inputs.ay, &inputs.az); |
rconnorlawson | 0:cf4396614a79 | 78 | |
rconnorlawson | 0:cf4396614a79 | 79 | return inputs; |
rconnorlawson | 0:cf4396614a79 | 80 | } |
rconnorlawson | 0:cf4396614a79 | 81 | |
abraha2d | 2:2042f29de6b7 | 82 | int lrpb = left_pb || right_pb; |
abraha2d | 2:2042f29de6b7 | 83 | int lpb = 1; |
abraha2d | 2:2042f29de6b7 | 84 | int upb = up_pb; |
abraha2d | 2:2042f29de6b7 | 85 | int dpb = down_pb; |
abraha2d | 2:2042f29de6b7 | 86 | |
abraha2d | 2:2042f29de6b7 | 87 | int update_game(DLinkedList* arena, Physics* curr, GameInputs inputs, DLinkedList* checkpoints, float delta) |
rconnorlawson | 0:cf4396614a79 | 88 | { |
abraha2d | 2:2042f29de6b7 | 89 | |
abraha2d | 2:2042f29de6b7 | 90 | // Cheat codes! |
abraha2d | 2:2042f29de6b7 | 91 | if (left_pb == 0 && right_pb == 0 && lrpb == 1) { |
abraha2d | 2:2042f29de6b7 | 92 | lpb = 1; |
abraha2d | 2:2042f29de6b7 | 93 | uLCD.cls(); |
abraha2d | 2:2042f29de6b7 | 94 | uLCD.printf("\n\n\n\n Cheat code!"); |
abraha2d | 2:2042f29de6b7 | 95 | wait(0.5); |
abraha2d | 2:2042f29de6b7 | 96 | return 30; |
abraha2d | 2:2042f29de6b7 | 97 | } |
abraha2d | 2:2042f29de6b7 | 98 | |
abraha2d | 2:2042f29de6b7 | 99 | // Help! |
abraha2d | 2:2042f29de6b7 | 100 | if (left_pb == 1 && lpb == 0) { |
abraha2d | 2:2042f29de6b7 | 101 | lpb = 1; |
abraha2d | 2:2042f29de6b7 | 102 | return 32; |
abraha2d | 2:2042f29de6b7 | 103 | } |
abraha2d | 2:2042f29de6b7 | 104 | |
abraha2d | 2:2042f29de6b7 | 105 | // Save/restore state |
abraha2d | 2:2042f29de6b7 | 106 | if (up_pb == 0 && upb == 1) { |
abraha2d | 2:2042f29de6b7 | 107 | greenLED = 1; |
abraha2d | 2:2042f29de6b7 | 108 | Physics* saveState = (Physics*) malloc(sizeof(Physics)); |
abraha2d | 2:2042f29de6b7 | 109 | *saveState = *curr; |
abraha2d | 2:2042f29de6b7 | 110 | insertTail(checkpoints, saveState); |
abraha2d | 2:2042f29de6b7 | 111 | } |
abraha2d | 2:2042f29de6b7 | 112 | if (down_pb == 0 && dpb == 1) { |
abraha2d | 2:2042f29de6b7 | 113 | Physics* restoreState = (Physics*) getTail(checkpoints); |
abraha2d | 2:2042f29de6b7 | 114 | if (restoreState != NULL) { |
abraha2d | 2:2042f29de6b7 | 115 | *curr = *restoreState; |
abraha2d | 2:2042f29de6b7 | 116 | } |
abraha2d | 2:2042f29de6b7 | 117 | } |
abraha2d | 2:2042f29de6b7 | 118 | |
abraha2d | 2:2042f29de6b7 | 119 | // Save previous inputs |
abraha2d | 2:2042f29de6b7 | 120 | lrpb = left_pb || right_pb; |
abraha2d | 2:2042f29de6b7 | 121 | lpb = left_pb; |
abraha2d | 2:2042f29de6b7 | 122 | upb = up_pb; |
abraha2d | 2:2042f29de6b7 | 123 | dpb = down_pb; |
abraha2d | 2:2042f29de6b7 | 124 | |
rconnorlawson | 0:cf4396614a79 | 125 | /////////////////////////////// |
rconnorlawson | 0:cf4396614a79 | 126 | // Prepare for physics update |
rconnorlawson | 0:cf4396614a79 | 127 | /////////////////////////////// |
rconnorlawson | 0:cf4396614a79 | 128 | // Make a copy of the current state for modification |
rconnorlawson | 0:cf4396614a79 | 129 | Physics next = *curr; |
abraha2d | 2:2042f29de6b7 | 130 | |
rconnorlawson | 0:cf4396614a79 | 131 | // No acceleration unless the ArenaElements apply them. (Newton's 1st law) |
rconnorlawson | 0:cf4396614a79 | 132 | next.ax = next.ay = 0.0; |
rconnorlawson | 0:cf4396614a79 | 133 | |
abraha2d | 2:2042f29de6b7 | 134 | |
rconnorlawson | 0:cf4396614a79 | 135 | // Loop over all arena elements |
rconnorlawson | 0:cf4396614a79 | 136 | ArenaElement* elem = (ArenaElement*)getHead(arena); |
rconnorlawson | 0:cf4396614a79 | 137 | do { |
rconnorlawson | 0:cf4396614a79 | 138 | switch(elem->type) { |
rconnorlawson | 0:cf4396614a79 | 139 | case WALL: |
rconnorlawson | 0:cf4396614a79 | 140 | do_wall(&next, curr, (Wall*) elem, delta); |
rconnorlawson | 0:cf4396614a79 | 141 | break; |
rconnorlawson | 0:cf4396614a79 | 142 | case BALL: |
abraha2d | 2:2042f29de6b7 | 143 | next.ax += inputs.ay * ACCELERATION; |
abraha2d | 2:2042f29de6b7 | 144 | next.ay += inputs.ax * ACCELERATION; |
rconnorlawson | 0:cf4396614a79 | 145 | forward_euler(&next, delta); |
rconnorlawson | 0:cf4396614a79 | 146 | break; |
abraha2d | 2:2042f29de6b7 | 147 | case HOLE: |
abraha2d | 2:2042f29de6b7 | 148 | if (do_hole((Hole*) elem, curr)) { |
abraha2d | 2:2042f29de6b7 | 149 | return 31; |
abraha2d | 2:2042f29de6b7 | 150 | } |
abraha2d | 2:2042f29de6b7 | 151 | break; |
abraha2d | 2:2042f29de6b7 | 152 | case GOAL: |
abraha2d | 2:2042f29de6b7 | 153 | if (do_goal((Goal*) elem, curr)) { |
abraha2d | 2:2042f29de6b7 | 154 | return 1; |
abraha2d | 2:2042f29de6b7 | 155 | } |
abraha2d | 2:2042f29de6b7 | 156 | break; |
rconnorlawson | 0:cf4396614a79 | 157 | default: |
rconnorlawson | 0:cf4396614a79 | 158 | break; |
rconnorlawson | 0:cf4396614a79 | 159 | } |
rconnorlawson | 0:cf4396614a79 | 160 | } while(elem = (ArenaElement*)getNext(arena)); |
rconnorlawson | 0:cf4396614a79 | 161 | |
rconnorlawson | 0:cf4396614a79 | 162 | // Last thing! Update state, so it will be saved for the next iteration. |
rconnorlawson | 0:cf4396614a79 | 163 | *curr = next; |
abraha2d | 2:2042f29de6b7 | 164 | |
rconnorlawson | 0:cf4396614a79 | 165 | // Zero means we aren't done yet |
abraha2d | 2:2042f29de6b7 | 166 | // 1 means we've hit the goal |
abraha2d | 2:2042f29de6b7 | 167 | // 31 means we've hit a pothole |
abraha2d | 2:2042f29de6b7 | 168 | // 32 means we need help! |
abraha2d | 2:2042f29de6b7 | 169 | // 30 means we used the cheat code |
rconnorlawson | 0:cf4396614a79 | 170 | return 0; |
rconnorlawson | 0:cf4396614a79 | 171 | } |
rconnorlawson | 0:cf4396614a79 | 172 | |
rconnorlawson | 0:cf4396614a79 | 173 | int run_game(DLinkedList* arena, Physics* state) |
rconnorlawson | 0:cf4396614a79 | 174 | { |
rconnorlawson | 0:cf4396614a79 | 175 | // Initialize game loop timers |
rconnorlawson | 0:cf4396614a79 | 176 | int tick, phys_tick, draw_tick; |
rconnorlawson | 0:cf4396614a79 | 177 | Timer timer; |
rconnorlawson | 0:cf4396614a79 | 178 | timer.start(); |
rconnorlawson | 0:cf4396614a79 | 179 | tick = timer.read_ms(); |
rconnorlawson | 0:cf4396614a79 | 180 | phys_tick = tick; |
rconnorlawson | 0:cf4396614a79 | 181 | draw_tick = tick; |
rconnorlawson | 0:cf4396614a79 | 182 | |
rconnorlawson | 0:cf4396614a79 | 183 | // Initialize debug counters |
rconnorlawson | 0:cf4396614a79 | 184 | int count = 0; |
rconnorlawson | 0:cf4396614a79 | 185 | int count2 = 0; |
rconnorlawson | 0:cf4396614a79 | 186 | |
abraha2d | 2:2042f29de6b7 | 187 | // Initialize checkpoint list |
abraha2d | 2:2042f29de6b7 | 188 | DLinkedList* checkpoints = create_dlinkedlist(); |
abraha2d | 2:2042f29de6b7 | 189 | |
rconnorlawson | 0:cf4396614a79 | 190 | // Initial draw of the game |
rconnorlawson | 0:cf4396614a79 | 191 | uLCD.background_color(BLACK); |
rconnorlawson | 0:cf4396614a79 | 192 | uLCD.cls(); |
rconnorlawson | 0:cf4396614a79 | 193 | |
abraha2d | 2:2042f29de6b7 | 194 | // Save start time |
abraha2d | 2:2042f29de6b7 | 195 | set_time(0); |
abraha2d | 2:2042f29de6b7 | 196 | time_t startTime = time(NULL); |
abraha2d | 2:2042f29de6b7 | 197 | |
rconnorlawson | 0:cf4396614a79 | 198 | /////////////////// |
rconnorlawson | 0:cf4396614a79 | 199 | // Main game loop |
rconnorlawson | 0:cf4396614a79 | 200 | /////////////////// |
rconnorlawson | 0:cf4396614a79 | 201 | while(1) { |
rconnorlawson | 0:cf4396614a79 | 202 | // Read timer to determine how long the last loop took |
rconnorlawson | 0:cf4396614a79 | 203 | tick = timer.read_ms(); |
abraha2d | 2:2042f29de6b7 | 204 | |
rconnorlawson | 0:cf4396614a79 | 205 | /////////////////// |
rconnorlawson | 0:cf4396614a79 | 206 | // Physics Update |
rconnorlawson | 0:cf4396614a79 | 207 | /////////////////// |
rconnorlawson | 0:cf4396614a79 | 208 | // Rate limit: 1 ms |
rconnorlawson | 1:c18c231cb299 | 209 | int diff = tick - phys_tick; |
rconnorlawson | 1:c18c231cb299 | 210 | if (diff < 1) continue; |
rconnorlawson | 0:cf4396614a79 | 211 | phys_tick = tick; |
rconnorlawson | 0:cf4396614a79 | 212 | |
rconnorlawson | 0:cf4396614a79 | 213 | // Compute elapsed time in milliseconds |
rconnorlawson | 1:c18c231cb299 | 214 | float delta = diff*1e-3; |
rconnorlawson | 0:cf4396614a79 | 215 | |
rconnorlawson | 0:cf4396614a79 | 216 | // Read inputs |
rconnorlawson | 0:cf4396614a79 | 217 | GameInputs inputs = read_inputs(); |
rconnorlawson | 0:cf4396614a79 | 218 | |
rconnorlawson | 0:cf4396614a79 | 219 | // Update game state |
abraha2d | 2:2042f29de6b7 | 220 | int done = update_game(arena, state, inputs, checkpoints, delta); |
abraha2d | 2:2042f29de6b7 | 221 | if (done == 1) return 30 - (time(NULL) - startTime); |
abraha2d | 2:2042f29de6b7 | 222 | if (done != 0) return done; |
rconnorlawson | 0:cf4396614a79 | 223 | |
rconnorlawson | 0:cf4396614a79 | 224 | // Debug: Count physics updates |
rconnorlawson | 0:cf4396614a79 | 225 | count2++; |
rconnorlawson | 0:cf4396614a79 | 226 | |
rconnorlawson | 0:cf4396614a79 | 227 | ////////////////// |
rconnorlawson | 0:cf4396614a79 | 228 | // Render update |
rconnorlawson | 0:cf4396614a79 | 229 | ////////////////// |
rconnorlawson | 0:cf4396614a79 | 230 | // Rate limit: 40ms |
rconnorlawson | 0:cf4396614a79 | 231 | if(tick - draw_tick < 40) continue; |
rconnorlawson | 0:cf4396614a79 | 232 | draw_tick = tick; |
rconnorlawson | 0:cf4396614a79 | 233 | |
rconnorlawson | 0:cf4396614a79 | 234 | // Erase moving stuff |
rconnorlawson | 0:cf4396614a79 | 235 | ArenaElement* elem = (ArenaElement*)getHead(arena); |
rconnorlawson | 0:cf4396614a79 | 236 | do { |
rconnorlawson | 0:cf4396614a79 | 237 | switch(elem->type) { |
rconnorlawson | 0:cf4396614a79 | 238 | case BALL: |
rconnorlawson | 0:cf4396614a79 | 239 | erase_ball((Ball*) elem); |
rconnorlawson | 0:cf4396614a79 | 240 | break; |
abraha2d | 2:2042f29de6b7 | 241 | default: |
abraha2d | 2:2042f29de6b7 | 242 | break; |
rconnorlawson | 0:cf4396614a79 | 243 | } |
rconnorlawson | 0:cf4396614a79 | 244 | } while(elem = (ArenaElement*)getNext(arena)); |
rconnorlawson | 0:cf4396614a79 | 245 | |
rconnorlawson | 0:cf4396614a79 | 246 | // Draw everything |
rconnorlawson | 0:cf4396614a79 | 247 | elem = (ArenaElement*)getHead(arena); |
rconnorlawson | 0:cf4396614a79 | 248 | do { |
rconnorlawson | 0:cf4396614a79 | 249 | switch(elem->type) { |
rconnorlawson | 0:cf4396614a79 | 250 | case WALL: |
rconnorlawson | 0:cf4396614a79 | 251 | draw_wall((Wall*) elem); |
rconnorlawson | 0:cf4396614a79 | 252 | break; |
rconnorlawson | 0:cf4396614a79 | 253 | case BALL: |
rconnorlawson | 0:cf4396614a79 | 254 | draw_ball((Ball*) elem, state); |
rconnorlawson | 0:cf4396614a79 | 255 | break; |
abraha2d | 2:2042f29de6b7 | 256 | case HOLE: |
abraha2d | 2:2042f29de6b7 | 257 | draw_hole((Hole*) elem); |
abraha2d | 2:2042f29de6b7 | 258 | break; |
abraha2d | 2:2042f29de6b7 | 259 | case GOAL: |
abraha2d | 2:2042f29de6b7 | 260 | draw_goal((Goal*) elem); |
abraha2d | 2:2042f29de6b7 | 261 | break; |
rconnorlawson | 0:cf4396614a79 | 262 | default: |
rconnorlawson | 0:cf4396614a79 | 263 | break; |
rconnorlawson | 0:cf4396614a79 | 264 | } |
rconnorlawson | 0:cf4396614a79 | 265 | } while(elem = (ArenaElement*)getNext(arena)); |
rconnorlawson | 0:cf4396614a79 | 266 | |
abraha2d | 2:2042f29de6b7 | 267 | uLCD.line(0, 0, 127, 0, 0); |
abraha2d | 2:2042f29de6b7 | 268 | uLCD.line(0, 0, (30 - (time(NULL) - startTime)) * (127/30), 0, GREEN); |
abraha2d | 2:2042f29de6b7 | 269 | |
rconnorlawson | 0:cf4396614a79 | 270 | /////////////// |
rconnorlawson | 0:cf4396614a79 | 271 | // Debug info |
rconnorlawson | 0:cf4396614a79 | 272 | /////////////// |
rconnorlawson | 0:cf4396614a79 | 273 | // Displays rate info in the top corner |
rconnorlawson | 0:cf4396614a79 | 274 | // First number is total time to update and render this frame |
rconnorlawson | 0:cf4396614a79 | 275 | // Second number is how many physics iterations between drawing frames |
rconnorlawson | 0:cf4396614a79 | 276 | // Only displayed every 10th render update (roughly 2.5 Hz) |
rconnorlawson | 0:cf4396614a79 | 277 | // TODO: Take this out before you turn your code in! |
abraha2d | 2:2042f29de6b7 | 278 | //if ((count = (count+1)%10) == 0) { |
abraha2d | 2:2042f29de6b7 | 279 | // uLCD.locate(0, 0); |
abraha2d | 2:2042f29de6b7 | 280 | // uLCD.printf("%d %d \r\n", timer.read_ms()-tick, count2); |
abraha2d | 2:2042f29de6b7 | 281 | //} |
abraha2d | 2:2042f29de6b7 | 282 | |
rconnorlawson | 0:cf4396614a79 | 283 | // Reset physics iteration counter after every render update |
rconnorlawson | 0:cf4396614a79 | 284 | count2 = 0; |
rconnorlawson | 0:cf4396614a79 | 285 | } |
rconnorlawson | 0:cf4396614a79 | 286 | } |