diff --git a/semestralka1/src/game/game_state.cpp b/semestralka1/src/game/game_state.cpp new file mode 100644 index 0000000..b2eed25 --- /dev/null +++ b/semestralka1/src/game/game_state.cpp @@ -0,0 +1,6 @@ + +// src/game/game_state.cpp +#include "game_state.h" + +GameState g_state; +Mutex g_state_mutex; diff --git a/semestralka1/src/game/game_state.h b/semestralka1/src/game/game_state.h new file mode 100644 index 0000000..37b78fe --- /dev/null +++ b/semestralka1/src/game/game_state.h @@ -0,0 +1,34 @@ +// src/game/game_state.h +#pragma once +#include "mbed.h" +#include "../render/player.h" +#include "../render/player_positioning.h" +#include "../assets/obstacle_crawl_frames.h" + +// POZOR Z OBSTACLE MANAGERA +constexpr int MAX_RENDER_OBSTACLES = 6; + +struct ObstacleRenderData { + int x; + int y; + CrawlObstacleType type; + bool active; +}; + +// All data needed to render one frame +struct GameState { + CharacterPosition player_pos; + MovementType movement; + int frame_index; + + int background_shift; + + ObstacleRenderData obstacles[MAX_RENDER_OBSTACLES]; + + bool game_over; + bool need_redraw; +}; + +// Global shared state and mutex +extern GameState g_state; +extern Mutex g_state_mutex; diff --git a/semestralka1/src/render/loop.cpp b/semestralka1/src/render/loop.cpp index 8822978..bd574aa 100644 --- a/semestralka1/src/render/loop.cpp +++ b/semestralka1/src/render/loop.cpp @@ -6,6 +6,7 @@ #include "mbed.h" #include "player.h" #include "background.h" +#include "../game/game_state.h" #include "../game/state.h" #include "../game/animation.h" #include "../game/collision.h" @@ -22,112 +23,152 @@ extern DigitalOut led; // constexpr int PLAYER_X = 9; constexpr int PLAYER_X = 29; constexpr int PLAYER_Y = 6; +constexpr int STACK_SIZE = 4096; -void render_loop(int speed) { +Thread logic_thread(osPriorityNormal, STACK_SIZE); +Thread render_thread(osPriorityNormal, STACK_SIZE); +void logic_loop() { MovementState player_state; AnimationController animation; ButtonHandler button; MovementController mover(PLAYER_X, VIEW_WIDTH); SpeedController timing; - timing.set_ground_speed(speed); + timing.set_ground_speed(6); CharacterPosition pos = {PLAYER_X, PLAYER_Y}; - - const char *bg_file = "background_dark_inverted.txt"; - bool need_redraw = false; - - int anim_tick_counter = 0; - int tick_counter = 0; - int player_speed = 6; - bool game_over = false; - player_state.set_state(PlayerState::Walk); - CrawlObstacleType type = CrawlObstacleType::Crawl1; - int start_x = VIEW_WIDTH + 10; - spawn_obstacle(type, start_x); - Timer spawn_timer; spawn_timer.start(); + int anim_tick_counter = 0; + int tick_counter = 0; + bool game_over = false; + + // Spawn first obstacle + spawn_obstacle(CrawlObstacleType::Crawl1, VIEW_WIDTH + 10); + while (!game_over) { tick_counter++; - mover.update_position(player_speed, timing.get_ground_speed()); + mover.update_position(6, timing.get_ground_speed()); pos.x = mover.get_position(); + // Spawn periodically if (spawn_timer.elapsed_time() > 2s) { spawn_timer.reset(); - CrawlObstacleType s_type = CrawlObstacleType::Crawl1; - int spawn_x = VIEW_WIDTH; - int idx = spawn_obstacle(s_type, spawn_x); - if (idx >= 0) { - obstacle_pool[idx].active = true; - } + spawn_obstacle(CrawlObstacleType::Crawl1, VIEW_WIDTH); } - need_redraw = false; - ButtonEvent button_event = button.poll(); - - if (button_event == ButtonEvent::Pressed) { + // Handle input + if (button.poll() == ButtonEvent::Pressed) { PlayerState current = player_state.get_state(); - - if (current == PlayerState::Walk || current == PlayerState::Run) { + if (current == PlayerState::Walk || current == PlayerState::Run) player_state.start_crawl(PlayerState::Crawl1); - } else if (current == PlayerState::Crawl1) { + else if (current == PlayerState::Crawl1) player_state.start_crawl(PlayerState::Crawl2); - } - - need_redraw = true; } - // Check if crawl duration expired player_state.update(); - if (animation.tick(speed)) { + bool anim_tick = animation.tick(6); + if (anim_tick) { anim_tick_counter++; + // move obstacles int ground_speed = timing.get_ground_speed(); for (int i = 0; i < MAX_OBSTACLES; i++) { if (obstacle_pool[i].active) { obstacle_pool[i].data.x -= ground_speed; - // deactivate when offscreen if (obstacle_pool[i].data.x + obstacle_pool[i].data.width < 0) obstacle_pool[i].active = false; } } - need_redraw = true; - } - - if (need_redraw) { - player_state.toggle_walk_frame(player_speed, anim_tick_counter); - draw_mask(bg_file, animation.get_shift()); + player_state.toggle_walk_frame(6, anim_tick_counter); FrameSelection frame = player_state.get_frame_selection(); + CharacterPosition draw_pos = + get_aligned_frame_position(pos, frame.movement, frame.frame_index); - CharacterPosition draw_pos = get_aligned_frame_position(pos, frame.movement, frame.frame_index); - draw_character(draw_pos.x, draw_pos.y, frame.movement, frame.frame_index); + // check collision + bool collision = false; + for (int i = 0; i < MAX_OBSTACLES; i++) { + if (obstacle_pool[i].active && + check_collision(pos, frame.movement, obstacle_pool[i].data)) { + collision = true; + break; + } + } - // for (int i = 0; i < MAX_OBSTACLES; i++) { - // if (!obstacle_pool[i].active) - // continue; + g_state_mutex.lock(); - // draw_obstacle(obstacle_pool[i].data.x, obstacle_pool[i].data.y, - // obstacle_pool[i].type); + g_state.player_pos = draw_pos; + g_state.movement = frame.movement; + g_state.frame_index = frame.frame_index; + g_state.background_shift = animation.get_shift(); + g_state.need_redraw = true; + g_state.game_over = collision; - // if (check_collision(pos, frame.movement, obstacle_pool[i].data)) { - // printf("\033[2J\033[H"); - // printf("GAME OVER\r\n"); - // game_over = true; - // break; - // } - // } + for (int i = 0; i < MAX_OBSTACLES; i++) { + g_state.obstacles[i].x = obstacle_pool[i].data.x; + g_state.obstacles[i].y = obstacle_pool[i].data.y; + g_state.obstacles[i].type = obstacle_pool[i].type; + g_state.obstacles[i].active = obstacle_pool[i].active; + } - if (game_over) - break; + g_state_mutex.unlock(); - ThisThread::sleep_for(50ms); + if (collision) + game_over = true; } ThisThread::sleep_for(25ms); } + + // final update + g_state_mutex.lock(); + g_state.game_over = true; + g_state_mutex.unlock(); +} + +void render_loop_thread() { + while (true) { + g_state_mutex.lock(); + bool need = g_state.need_redraw; + bool over = g_state.game_over; + GameState local = g_state; + g_state.need_redraw = false; + g_state_mutex.unlock(); + + if (over) { + printf("\033[2J\033[H"); + printf("GAME OVER\r\n"); + break; + } + + if (need) { + draw_mask("background", local.background_shift); + draw_character(local.player_pos.x, local.player_pos.y, local.movement, + local.frame_index); + for (int i = 0; i < MAX_RENDER_OBSTACLES; i++) { + if (local.obstacles[i].active) + draw_obstacle(local.obstacles[i].x, local.obstacles[i].y, + local.obstacles[i].type); + } + } + + ThisThread::sleep_for(50ms); + } +} + +void render_loop(int speed) { + g_state_mutex.lock(); + g_state.need_redraw = false; + g_state.game_over = false; + g_state_mutex.unlock(); + + logic_thread.start(logic_loop); + render_thread.start(render_loop_thread); + + logic_thread.join(); + render_thread.join(); }