diff --git a/src/game.c b/src/game.c index 5fd0b32..275c73c 100644 --- a/src/game.c +++ b/src/game.c @@ -20,10 +20,172 @@ void game_init(game_t* game) { game->state=prestart; game->ticks_per_pixel = SPEED_DEFAULT; game->ticks_leftover =0; -} +} +bool game_check_line_collision(player_t* player, point_t start, point_t end, uint8_t pixels){ -void game_step(game_t* game, uint64_t deltaTime) { + bool line_is_horizontal = (start.y == end.y); + bool player_is_horizontal = (player->direction == left || player->direction == right); + + if(line_is_horizontal == player_is_horizontal){ // if player moves parallel to the line, there is no point in checking + return false; // no collision possible because parallel moving + } + + if(!player_is_horizontal && (player->position.x < min(start.x, end.x) || player->position.x > max(start.x, end.x))){ + return false; // if player is passing by horizontally, no collision possible + } + + if(player_is_horizontal && (player->position.y < min(start.y, end.y) || player->position.y > max(start.y, end.y))){ + return false; // if player is passing by vertically, no collision possible + } + + switch(player->direction){ + case up: + if(player->position.y > start.y && ((int16_t)player->position.y - pixels) <= start.y) { + return true; + } + break; + case down: + if(player->position.y < start.y && ((int16_t)player->position.y + pixels) >= start.y) { + return true; + } + break; + case left: + if(player->position.x > start.x && ((int16_t)player->position.x - pixels) <= start.x) { + return true; + } + break; + case right: + if(player->position.x < start.x && ((int16_t)player->position.x + pixels) >= start.x) { + return true; + } + break; + } + return false; +} + +bool game_player_check_collision(game_t* game, player_t* player, uint8_t pixels){ + + // Check bounding collision + switch(player->direction){ + case up: + if((int16_t)(player->position.y) - pixels <= TFT_GAME_FIELD_TOP){ + return true; // Collision at top + } + break; + case down: + if((int16_t)(player->position.y) + pixels >= (TFT_HEIGHT - TFT_GAME_FIELD_BOTTOM - 1)){ + return true; // Collision at bottom + } + break; + case left: + if((int16_t)(player->position.x) - pixels <= TFT_GAME_FIELD_LEFT){ + return true; // Collision at left + } + break; + case right: + if((int16_t)(player->position.x) + pixels >= (TFT_WIDTH - TFT_GAME_FIELD_RIGHT - 1)){ + return true; // Collision at right + } + break; + } + + // Check collision with players (including self) + for(int i = 0; i < PLAYER_COUNT; i++){ + + player_t* colliding = &(game->player[i]); // pointer to player whose lines we want to check (against opponent or self) + point_t last_point = colliding->past_positions[0]; // start point of the line + + // For each line segment (last_point to curr_point) + for(int j = 1; j < colliding->num_positions; j++ ){ + point_t curr_point = colliding->past_positions[j]; // end point of the line + + if(game_check_line_collision(player, last_point, curr_point, pixels)){ // check if player collides with line segment + return true; + } + + last_point = curr_point; // set new start point + } + + if( player != colliding && // do not check against yourself + game_check_line_collision(player, last_point, colliding->position, pixels)){ // check if player collides with newest segment of opponent + return true; + } + } + + return false; // no collision! +} + +void game_player_update(game_t* game, player_t* player, uint8_t pixels){ + + bool directionChange = false; + + // Check for button presses + if(io_button_has_edge(player->btn_left)) { + player->direction= (player->direction + (4 - 1)) % 4 ; // "decrement enum value" + directionChange = true; + } else if(io_button_has_edge(player->btn_right)) { + player->direction= (player->direction + 1) % 4 ; // "increment enum value" + directionChange = true; + } + + // Check if player is alive + if(player->state != alive){ + return; + } + // Change direction + if(directionChange) { + player_append_position(player,player->position); + } + + if(pixels) { + + // Check if a collision is about to happen + if(game_player_check_collision(game, player, pixels)){ + player->state=dead; + } + + point_t last_point = player->past_positions[player->num_positions-1]; + + switch(player->direction) { + case down: + player->position.y+=pixels; + LCD_DrawRectF( player->position.x, + last_point.y, + PLAYER_WIDTH, + player->position.y - last_point.y, + player->color); + break; + case left: + player->position.x-=pixels; + LCD_DrawRectF( player->position.x, + player->position.y, + last_point.x -player->position.x, + PLAYER_WIDTH, + player->color); + break; + case up: + player->position.y-=pixels; + LCD_DrawRectF( player->position.x, + player->position.y, + PLAYER_WIDTH, + last_point.y - player->position.y, + player->color); + break; + case right: + player->position.x+=pixels; + LCD_DrawRectF( last_point.x, + player->position.y, + player->position.x - last_point.x, + PLAYER_WIDTH, + player->color); + break; + } + } + +} + +bool game_step(game_t* game, uint64_t deltaTime) { static long l = 0; switch(game->state) { case prestart: @@ -37,85 +199,71 @@ void game_step(game_t* game, uint64_t deltaTime) { //wait on game accept response //setup - //void player_init(player_t* player, uint8_t btn_left, uint8_t btn_right, point_t pos, uint16_t color, direction_t direction); - player_init(&(game->player[0]), BTN_PLAYER_1_LEFT, BTN_PLAYER_1_RIGHT, (point_t){.x=10,.y=120}, GUI_COLOR_BLUE, right); - player_init(&(game->player[1]), BTN_PLAYER_2_LEFT, BTN_PLAYER_2_RIGHT, (point_t){.x=230,.y=120}, GUI_COLOR_RED, left); + player_init(&(game->player[0]), + BTN_PLAYER_1_LEFT, + BTN_PLAYER_1_RIGHT, + (point_t){ + .x=(TFT_GAME_FIELD_START_OFFSET + TFT_GAME_FIELD_LEFT), + .y=(((TFT_HEIGHT - TFT_GAME_FIELD_TOP - TFT_GAME_FIELD_BOTTOM) / 2) + TFT_GAME_FIELD_TOP) + }, + GUI_COLOR_BLUE, + right); + player_init(&(game->player[1]), + BTN_PLAYER_2_LEFT, + BTN_PLAYER_2_RIGHT, + (point_t){ + .x=(TFT_WIDTH - 1 - TFT_GAME_FIELD_RIGHT - TFT_GAME_FIELD_START_OFFSET), + .y=(((TFT_HEIGHT - TFT_GAME_FIELD_TOP - TFT_GAME_FIELD_BOTTOM) / 2) + TFT_GAME_FIELD_TOP) + }, + GUI_COLOR_RED, + left); //switch state game->state = running; + LCD_Clear(GUI_COLOR_BLACK); - break; + LCD_DrawRect(TFT_GAME_FIELD_LEFT, + TFT_GAME_FIELD_TOP, + (TFT_WIDTH - TFT_GAME_FIELD_LEFT - TFT_GAME_FIELD_RIGHT - 1), + (TFT_HEIGHT - TFT_GAME_FIELD_TOP - TFT_GAME_FIELD_BOTTOM - 1), + GUI_COLOR_WHITE); + return true; + case running: { uint16_t ticks; uint16_t pixels = 0; if(deltaTime) { - ticks = game->ticks_leftover + deltaTime; pixels = ticks / game->ticks_per_pixel; - game->ticks_leftover = ticks % game->ticks_per_pixel; } + // For each player do ... + bool all_players_dead = true; for(int i = 0; i < PLAYER_COUNT; i++) { + player_t* player = &(game->player[i]); + game_player_update(game, player, pixels); + if(player->state!=dead) { + all_players_dead=false; + } + } - bool directionChange = false; - player_t* player = &(game->player[i]); - - if(io_button_has_edge(player->btn_left)) { - player->direction= (player->direction + (4 - 1)) % 4 ; // "decrement enum value" - directionChange = true; - } else if(io_button_has_edge(player->btn_right)) { - player->direction= (player->direction + 1) % 4 ; // "increment enum value" - directionChange = true; - } - - if(directionChange) { - player_append_position(player,player->position); - } - - if(pixels) { - point_t last_point = player->past_positions[player->num_positions-1]; - switch(player->direction) { - case down: - player->position.y+=pixels; - LCD_DrawRectF( player->position.x, - last_point.y, - PLAYER_WIDTH, - player->position.y - last_point.y, - player->color); - break; - case left: - player->position.x-=pixels; - LCD_DrawRectF( player->position.x, - player->position.y, - last_point.x -player->position.x, - PLAYER_WIDTH, - player->color); - break; - case up: - player->position.y-=pixels; - LCD_DrawRectF( player->position.x, - player->position.y, - PLAYER_WIDTH, - last_point.y - player->position.y, - player->color); - break; - case right: - player->position.x+=pixels; - LCD_DrawRectF( last_point.x, - player->position.y, - player->position.x - last_point.x, - PLAYER_WIDTH, - player->color); - break; - } - } - } - break; + if(all_players_dead) { + game->state=ended; + LCD_Clear(GUI_COLOR_BLACK); + return true; + } else { + return false; + } } + + case ended: + + game->state= prestart; + return false; } } diff --git a/src/game.h b/src/game.h index a6c4916..0e15b36 100644 --- a/src/game.h +++ b/src/game.h @@ -3,13 +3,14 @@ #include #include +#include #include"player.h" #define PLAYER_COUNT 2 #define PLAYER_WIDTH 0 // Don't change #define SPEED_SLOW 10 #define SPEED_FAST 1 -#define SPEED_DEFAULT (SPEED_SLOW) +#define SPEED_DEFAULT (SPEED_FAST) #define BTN_START 0 #define BTN_PLAYER_1_LEFT 3 @@ -17,6 +18,12 @@ #define BTN_PLAYER_2_LEFT 1 #define BTN_PLAYER_2_RIGHT 0 +#define TFT_GAME_FIELD_TOP 20 +#define TFT_GAME_FIELD_BOTTOM 5 +#define TFT_GAME_FIELD_LEFT 5 +#define TFT_GAME_FIELD_RIGHT 5 +#define TFT_GAME_FIELD_START_OFFSET 10 + typedef struct game_s{ //public section @@ -35,7 +42,19 @@ typedef struct game_s{ uint8_t ticks_leftover; } game_t; +/** + @brief Initializes the game object + @param game +*/ void game_init(game_t* game); -void game_step(game_t* game, uint64_t deltaTime); + +/** + @brief Calculates one step of the game + @param game Game to calculate a step for + @param deltaTime Time that passed since the last call to this method (in ticks) + @return true if the next call to this method should be made with a delta time of zero. + +*/ +bool game_step(game_t* game, uint64_t deltaTime); #endif /* GAME_H */ diff --git a/src/main.c b/src/main.c index a3bcd90..c7219a9 100644 --- a/src/main.c +++ b/src/main.c @@ -53,8 +53,11 @@ int main(void) game_init(&gameobj); while(1) { uint64_t curTicks = ticks; - game_step(&gameobj,curTicks-lastTicks); //calculate next game step, and pass it the delta time - lastTicks = curTicks; + if(game_step(&gameobj,curTicks-lastTicks)) { //calculate next game step, and pass it the delta time + lastTicks = ticks; + } else { + lastTicks = curTicks; + } } return 0;