From 064341e2eb7f0badfee89380caae4c5cb8c053c4 Mon Sep 17 00:00:00 2001 From: En Yi Date: Mon, 2 Oct 2023 22:21:41 +0800 Subject: [PATCH] Initial implementation of camera system Changelog: - Implement effectively a PI controller --- scenes/constants.h | 1 + scenes/editor_scene.c | 8 +++--- scenes/game_scene.c | 2 +- scenes/game_systems.c | 58 ++++++++++++++++++++++++++++++++---------- scenes/scene_impl.h | 13 +++++++++- scenes/scene_systems.c | 10 +++++--- water_test.c | 4 +-- 7 files changed, 71 insertions(+), 25 deletions(-) diff --git a/scenes/constants.h b/scenes/constants.h index d8b6993..95884f1 100644 --- a/scenes/constants.h +++ b/scenes/constants.h @@ -15,6 +15,7 @@ #define VIEWABLE_MAP_HEIGHT 16 #endif +#define DELTA_T 0.017 #define GRAV_ACCEL 1500 #define JUMP_SPEED 70 #define MOVE_ACCEL 1300 diff --git a/scenes/editor_scene.c b/scenes/editor_scene.c index a924463..c11f580 100644 --- a/scenes/editor_scene.c +++ b/scenes/editor_scene.c @@ -86,13 +86,13 @@ static void level_scene_render_func(Scene_t* scene) TileGrid_t tilemap = data->tilemap; Entity_t* p_ent; - Vector2 min = GetScreenToWorld2D((Vector2){data->game_rec.x, data->game_rec.y}, data->cam); + Vector2 min = GetScreenToWorld2D((Vector2){data->game_rec.x, data->game_rec.y}, data->camera.cam); Vector2 max = GetScreenToWorld2D( (Vector2){ data->game_rec.x + data->game_rec.width, data->game_rec.y + data->game_rec.height }, - data->cam + data->camera.cam ); min = Vector2Scale(min, 1.0f/tilemap.tile_size); max = Vector2Scale(max, 1.0f/tilemap.tile_size); @@ -103,7 +103,7 @@ static void level_scene_render_func(Scene_t* scene) BeginTextureMode(data->game_viewport); ClearBackground(WHITE); - BeginMode2D(data->cam); + BeginMode2D(data->camera.cam); for (int tile_y = min.y; tile_y <= max.y; tile_y++) { for (int tile_x = min.x; tile_x <= max.x; tile_x++) @@ -533,7 +533,7 @@ static void toggle_block_system(Scene_t* scene) && raw_mouse_pos.y < data->game_rec.height ) { - Vector2 mouse_pos = GetScreenToWorld2D(raw_mouse_pos, data->cam); + Vector2 mouse_pos = GetScreenToWorld2D(raw_mouse_pos, data->camera.cam); unsigned int tile_idx = get_tile_idx(mouse_pos.x, mouse_pos.y, &tilemap); if (tile_idx >= (tilemap.n_tiles - tilemap.width)) return; if (tile_idx == last_tile_idx) return; diff --git a/scenes/game_scene.c b/scenes/game_scene.c index 253d748..23e57fb 100644 --- a/scenes/game_scene.c +++ b/scenes/game_scene.c @@ -19,7 +19,7 @@ static void level_scene_render_func(Scene_t* scene) BeginTextureMode(data->game_viewport); ClearBackground(WHITE); - BeginMode2D(data->cam); + BeginMode2D(data->camera.cam); for (size_t i = 0; i < tilemap.n_tiles; ++i) { char buffer[6] = {0}; diff --git a/scenes/game_systems.c b/scenes/game_systems.c index 17c0bc8..3f4f4c7 100644 --- a/scenes/game_systems.c +++ b/scenes/game_systems.c @@ -1125,7 +1125,7 @@ void movement_update_system(Scene_t* scene) LevelSceneData_t* data = &(CONTAINER_OF(scene, LevelScene_t, scene)->data); TileGrid_t tilemap = data->tilemap; // Update movement - float delta_time = 0.017; // TODO: Will need to think about delta time handling + float delta_time = DELTA_T; // TODO: Will need to think about delta time handling CTransform_t * p_ctransform; unsigned long ent_idx; sc_map_foreach(&scene->ent_manager.component_map[CTRANSFORM_COMP_T], ent_idx, p_ctransform) @@ -1795,29 +1795,59 @@ void sprite_animation_system(Scene_t* scene) void camera_update_system(Scene_t* scene) { - LevelScene_t* lvl_scene = CONTAINER_OF(scene, LevelScene_t, scene); + LevelSceneData_t* data = &CONTAINER_OF(scene, LevelScene_t, scene)->data; Entity_t* p_player; - const int width = lvl_scene->data.game_rec.width; - const int height = lvl_scene->data.game_rec.height; + const int width = data->game_rec.width; + const int height =data->game_rec.height; + data->camera.cam.offset = (Vector2){ width/2.0f, height/2.0f }; + + Vector2 target_pos = {0}; + Vector2 target_vel = {0}; sc_map_foreach_value(&scene->ent_manager.entities_map[PLAYER_ENT_TAG], p_player) { CTransform_t* p_ctransform = get_component(p_player, CTRANSFORM_COMP_T); - lvl_scene->data.cam.offset = (Vector2){ width/2.0f, height/2.0f }; - lvl_scene->data.cam.target = p_ctransform->position; + target_pos = p_ctransform->position; + target_vel = p_ctransform->velocity; + CMovementState_t* p_movement = get_component(p_player, CMOVEMENTSTATE_T); + target_pos.x += (p_movement->x_dir == 1) ? width/4: -width/4; } + + // Mass-Spring damper update + Vector2 x = Vector2Subtract(target_pos, data->camera.cam.target); + Vector2 v = Vector2Subtract(data->camera.current_vel, target_vel); + //Vector2 F = Vector2Scale(x, data->camera.k); + Vector2 F = + Vector2Subtract( + Vector2Scale(x, data->camera.k), + Vector2Scale(v, data->camera.c) + ); + + // Kinematics update + const float dt = DELTA_T; + Vector2 a_dt = Vector2Scale(F, dt/data->camera.mass); + data->camera.cam.target = + Vector2Add( + data->camera.cam.target, + Vector2Add( + Vector2Scale(data->camera.current_vel, dt), + Vector2Scale(a_dt, dt*0.5f) + ) + ); + data->camera.current_vel = Vector2Add(data->camera.current_vel, a_dt); + Vector2 max = GetWorldToScreen2D( (Vector2){ - fmax(lvl_scene->data.tilemap.width * TILE_SIZE, lvl_scene->data.game_rec.width), - fmax(lvl_scene->data.tilemap.height * TILE_SIZE, lvl_scene->data.game_rec.height) + fmax(data->tilemap.width * TILE_SIZE, data->game_rec.width), + fmax(data->tilemap.height * TILE_SIZE, data->game_rec.height) }, - lvl_scene->data.cam + data->camera.cam ); - Vector2 min = GetWorldToScreen2D((Vector2){0, 0}, lvl_scene->data.cam); + //Vector2 min = GetWorldToScreen2D((Vector2){0, 0}, data->camera.cam); - if (max.x < width) lvl_scene->data.cam.offset.x = width - (max.x - width/2.0f); - if (max.y < height) lvl_scene->data.cam.offset.y = height - (max.y - height/2.0f); - if (min.x > 0) lvl_scene->data.cam.offset.x = width/2.0f - min.x; - if (min.y > 0) lvl_scene->data.cam.offset.y = height/2.0f - min.y; + //if (max.x < width) data->camera.cam.offset.x = width - (max.x - width/2.0f); + //if (max.y < height) data->camera.cam.offset.y = height - (max.y - height/2.0f); + //if (min.x > 0) data->camera.cam.offset.x = width/2.0f - min.x; + //if (min.y > 0) data->camera.cam.offset.y = height/2.0f - min.y; } void level_end_detection_system(Scene_t* scene) diff --git a/scenes/scene_impl.h b/scenes/scene_impl.h index 6415baa..e5fb873 100644 --- a/scenes/scene_impl.h +++ b/scenes/scene_impl.h @@ -27,11 +27,22 @@ typedef struct CoinCounter uint16_t total; }CoinCounter_t; +typedef struct LevelCamera { + Camera2D cam; + Vector2 target_pos; + //Vector2 prev_pos; + Vector2 current_vel; + float mass; + float c; // damping factor + float k; // spring constant +}LevelCamera_t; + typedef struct LevelSceneData { TileGrid_t tilemap; RenderTexture2D game_viewport; Rectangle game_rec; - Camera2D cam; + //Camera2D cam; + LevelCamera_t camera; Sprite_t* tile_sprites[MAX_TILE_SPRITES]; LevelPack_t* level_pack; unsigned int current_level; diff --git a/scenes/scene_systems.c b/scenes/scene_systems.c index a9ad852..f14d9c9 100644 --- a/scenes/scene_systems.c +++ b/scenes/scene_systems.c @@ -10,9 +10,13 @@ void init_level_scene_data(LevelSceneData_t* data, uint32_t max_tiles, Tile_t* t //data->game_rec = (Rectangle){25, 25, VIEWABLE_MAP_WIDTH*TILE_SIZE, VIEWABLE_MAP_HEIGHT*TILE_SIZE}; data->game_viewport = LoadRenderTexture(view_zone.width, view_zone.height); data->game_rec = view_zone; - data->cam = (Camera2D){0}; - data->cam.rotation = 0.0f; - data->cam.zoom = 1.0f; + //data->camera.cam = (Camera2D){0}; + memset(&data->camera, 0, sizeof(LevelCamera_t)); + data->camera.cam.rotation = 0.0f; + data->camera.cam.zoom = 1.0f; + data->camera.mass = 0.6f; + data->camera.c = 2.4f; + data->camera.k = 0.1f; data->tilemap.max_tiles = max_tiles; if (tiles != NULL) diff --git a/water_test.c b/water_test.c index 2fdd72f..b418981 100644 --- a/water_test.c +++ b/water_test.c @@ -34,7 +34,7 @@ static void level_scene_render_func(Scene_t* scene) BeginTextureMode(data->game_viewport); ClearBackground(WHITE); - BeginMode2D(data->cam); + BeginMode2D(data->camera.cam); for (size_t i = 0; i < tilemap.n_tiles; ++i) { char buffer[6] = {0}; @@ -240,7 +240,7 @@ static void toggle_block_system(Scene_t* scene) && raw_mouse_pos.y < data->game_rec.height ) { - Vector2 mouse_pos = GetScreenToWorld2D(raw_mouse_pos, data->cam); + Vector2 mouse_pos = GetScreenToWorld2D(raw_mouse_pos, data->camera.cam); unsigned int tile_idx = get_tile_idx(mouse_pos.x, mouse_pos.y, &tilemap); if (tile_idx >= MAX_N_TILES) return; if (tile_idx == last_tile_idx) return;