From 1ff06abdd48183b5994725695c53a54158eb71d3 Mon Sep 17 00:00:00 2001 From: Joseph Ferano Date: Sat, 6 Jan 2024 09:09:37 +0700 Subject: [PATCH] Select knights by dragging rect over them --- game_data.h | 4 +- main.c | 183 +++++++++++++++++++++++++++++++----------------- tinyswords.todo | 8 ++- 3 files changed, 124 insertions(+), 71 deletions(-) diff --git a/game_data.h b/game_data.h index 714056d..1bb243f 100644 --- a/game_data.h +++ b/game_data.h @@ -23,11 +23,11 @@ const u16 knight_sprite_size = 192; const Vector2 knight_origin = {knight_sprite_size / 2, knight_sprite_size / 2 + 35}; -const Size knight_col_size = {85, 95}; +const Size knight_col_size = {50, 75}; // TODO: We probably want this as a helper function somewhere const Rectangle knight_colrect_select = { -knight_col_size.width / 2, - -knight_col_size.height / 2 - 40, + -knight_col_size.height / 2 - 35, knight_col_size.width, knight_col_size.height, }; diff --git a/main.c b/main.c index 63a2347..1c2b2d3 100644 --- a/main.c +++ b/main.c @@ -36,21 +36,21 @@ typedef enum Direction { typedef struct { Point position; - Point origin; - Vector2 velocity; - Direction look_dir; - KnightState state; + u8 look_dir; + u8 state; + u8 selected; } Knight; typedef struct { int frame_count; - // Rectangle* sprite_rects; Point camera_position; - int selected_knight; - PointOption *target_points; + PointOption selected_point; Knight *knights; SpriteAnimationPlayback* anim_playbacks; + Knight *selected_knights; + + PointOption selection_mouse_start_pos; int entity_count; } GameState; @@ -80,29 +80,81 @@ Assets Init() { } void Update(GameState *game, Camera2D *cam, float dt) { - if (0 && IsMouseButtonPressed(0)) { + if (IsMouseButtonPressed(0)) { Point mouse_pos = GetScreenToWorld2D(GetMousePosition(), *cam); - // TODO: This kind of sucks that we're doing all the math here to calculate - // the position of the collider, we need a helper that calculates origin offset - Rectangle mouse_select_area = { - game->knights[0].position.x + knight_colrect_select.x, - game->knights[0].position.y + knight_colrect_select.y, - knight_colrect_select.width, - knight_colrect_select.height, - }; - if (CheckCollisionPointRec(mouse_pos, mouse_select_area)) { - // game->selected_knight = &game->knights[0]; - } else { - game->selected_knight = -1; + game->selection_mouse_start_pos = (PointOption){ .tag = SOME, .some.point = mouse_pos }; + } + + if (game->selection_mouse_start_pos.tag == SOME) { + Point start_pos = game->selection_mouse_start_pos.some.point; + Point current_pos = GetScreenToWorld2D(GetMousePosition(), *cam); + + if (Vector2DistanceSqr(current_pos, start_pos) >= 100.0f) { + f32 width = current_pos.x - start_pos.x; + f32 height = current_pos.y - start_pos.y; + f32 x = width >= 0.0f ? start_pos.x : current_pos.x; + f32 y = height >= 0.0f ? start_pos.y : current_pos.y; + Rectangle rect = {x, y, fabs(width), fabs(height)}; + for (int i = 0; i < game->entity_count; i++) { + Rectangle mouse_select_area = { + game->knights[i].position.x + knight_colrect_select.x, + game->knights[i].position.y + knight_colrect_select.y, + knight_colrect_select.width, + knight_colrect_select.height, + }; + game->knights[i].selected = + CheckCollisionRecs(rect, mouse_select_area); + } } } + if (IsMouseButtonReleased(0)) { + // TODO: This is what should happen when the distance is too small + // Point start_pos = game->selection_mouse_start_pos.some.point; + Point start_pos = game->selection_mouse_start_pos.some.point; + Point current_pos = GetScreenToWorld2D(GetMousePosition(), *cam); + + if (Vector2DistanceSqr(current_pos, start_pos) >= 100.0f) { + f32 width = current_pos.x - start_pos.x; + f32 height = current_pos.y - start_pos.y; + f32 x = width >= 0.0f ? start_pos.x : current_pos.x; + f32 y = height >= 0.0f ? start_pos.y : current_pos.y; + Rectangle rect = {x, y, fabs(width), fabs(height)}; + for (int i = 0; i < game->entity_count; i++) { + Rectangle mouse_select_area = { + game->knights[i].position.x + knight_colrect_select.x, + game->knights[i].position.y + knight_colrect_select.y, + knight_colrect_select.width, + knight_colrect_select.height, + }; + game->knights[i].selected = + CheckCollisionRecs(rect, mouse_select_area); + } + } else { + // TODO: This kind of sucks that we're doing all the math here to calculate + // the position of the collider, we need a helper that calculates origin offset + int k_idx = -1; + for (int i = 0; i < game->entity_count; i++) { + Rectangle mouse_select_area = { + game->knights[i].position.x + knight_colrect_select.x, + game->knights[i].position.y + knight_colrect_select.y, + knight_colrect_select.width, + knight_colrect_select.height, + }; + if (CheckCollisionPointRec(current_pos, mouse_select_area)) { + k_idx = i; + } + game->knights[i].selected = false; + } + if (k_idx != -1) { + game->knights[k_idx].selected = true; + } + } + game->selection_mouse_start_pos = (PointOption){ .tag = NONE, }; + } if (IsMouseButtonPressed(1)) { Point mouse_pos = GetScreenToWorld2D(GetMousePosition(), *cam); - if (game->selected_knight != -1) { - game->target_points[game->selected_knight] = - (PointOption){ .tag = SOME, .some.point = mouse_pos }; - } + game->selected_point = (PointOption){ .tag = SOME, .some.point = mouse_pos }; } const float cam_move_speed = 1050.0f * dt; @@ -122,13 +174,13 @@ void Update(GameState *game, Camera2D *cam, float dt) { cam_vel = Vector2Normalize(cam_vel); game->camera_position = Vector2Scale(cam_vel, cam_move_speed); cam->offset = Vector2Add(cam->offset, game->camera_position); - if (IsKeyPressed(KEY_SPACE) && game->selected_knight != -1) { - int k_idx = game->selected_knight; - if (game->knights[k_idx].state != KNIGHT_ATTACKING) { - game->knights[k_idx].state = KNIGHT_ATTACKING; - PlayAnimation(ANIM_KNIGHT_ATTACK_SIDE1, knight_anims, &game->anim_playbacks[k_idx]); - } - } + // if (IsKeyPressed(KEY_SPACE) && game->selected_knight != -1) { + // int k_idx = game->selected_knight; + // if (game->knights[k_idx].state != KNIGHT_ATTACKING) { + // game->knights[k_idx].state = KNIGHT_ATTACKING; + // PlayAnimation(ANIM_KNIGHT_ATTACK_SIDE1, knight_anims, &game->anim_playbacks[k_idx]); + // } + // } if (IsKeyPressed(KEY_F1)) { global_debug_mode = !global_debug_mode; } @@ -140,10 +192,10 @@ void Update(GameState *game, Camera2D *cam, float dt) { for (int i = 0; i < game->entity_count; i++) { Vector2 input_vel = {0}; Knight *knight = &game->knights[i]; - if (game->target_points[i].tag == SOME) { - Vector2 target = game->target_points[game->selected_knight].some.point; + if (game->selected_point.tag == SOME && knight->selected) { + Vector2 target = game->selected_point.some.point; if (Vector2DistanceSqr(target, knight->position) < 2.5f) { - game->target_points[i].tag = NONE; + game->selected_point.tag = NONE; } else { input_vel.x = knight->position.x - target.x < 0.0f ? 1 : -1; knight->position = Vector2MoveTowards(knight->position, target, 4.0f); @@ -172,10 +224,8 @@ void Update(GameState *game, Camera2D *cam, float dt) { } } } - } - - // Sort the entities using Insertion Sort. - for (int i = 1; i < game->entity_count; i++) { + if (i == 0) continue; + // Sort the entities by y position using Insertion Sort int j = i; while (j > 0 && game->knights[j].position.y < game->knights[j - 1].position.y) { // Swap position @@ -186,16 +236,6 @@ void Update(GameState *game, Camera2D *cam, float dt) { SpriteAnimationPlayback temp_a = game->anim_playbacks[j - 1]; game->anim_playbacks[j - 1] = game->anim_playbacks[j]; game->anim_playbacks[j] = temp_a; - // Swap target points - PointOption temp_p = game->target_points[j - 1]; - game->target_points[j - 1] = game->target_points[j]; - game->target_points[j] = temp_p; - // Swap selected knight - if (game->selected_knight == j) { - game->selected_knight = j - 1; - } else if (game->selected_knight == j - 1) { - game->selected_knight = j; - } j--; } } @@ -229,8 +269,8 @@ void Draw(GameState *game, Assets assets, Camera2D cam, float dt) { } } - if (game->target_points[game->selected_knight].tag == SOME) { - Vector2 marker_pos = game->target_points[game->selected_knight].some.point; + if (game->selected_point.tag == SOME) { + Vector2 marker_pos = game->selected_point.some.point; marker_pos.x -= assets.textures[TEX_TARGET_RETICLE].width / 2; marker_pos.y -= assets.textures[TEX_TARGET_RETICLE].height / 2; D_DrawTextureV(assets.textures[TEX_TARGET_RETICLE], marker_pos, WHITE); @@ -253,18 +293,30 @@ void Draw(GameState *game, Assets assets, Camera2D cam, float dt) { Rectangle dest_rect = {knight->position.x, knight->position.y, 192, 192}; Texture2D tex = assets.textures[TEX_KNIGHT]; D_DrawTexturePro(tex, src_rect, dest_rect, knight_origin, 0.0f, WHITE); + if (knight->selected == true) { + Rectangle knight_col_area = { + knight->position.x + knight_colrect_select.x, + knight->position.y + knight_colrect_select.y, + knight_colrect_select.width, + knight_colrect_select.height, + }; + // Color color = game->selected_knight == NULL ? RED : GREEN; + DrawRectangleLinesEx(knight_col_area, 2.0f, GREEN); + } } - if (game->selected_knight != -1) { - Vector2 kpos = game->knights[game->selected_knight].position; - Rectangle knight_col_area = { - kpos.x + knight_colrect_select.x, - kpos.y + knight_colrect_select.y, - knight_colrect_select.width, - knight_colrect_select.height, - }; - // Color color = game->selected_knight == NULL ? RED : GREEN; - DrawRectangleLinesEx(knight_col_area, 2.0f, GREEN); + if (game->selection_mouse_start_pos.tag == SOME) { + Point start_pos = game->selection_mouse_start_pos.some.point; + Point current_pos = GetScreenToWorld2D(GetMousePosition(), cam); + + if (Vector2DistanceSqr(current_pos, start_pos) >= 100.0f) { + f32 width = current_pos.x - start_pos.x; + f32 height = current_pos.y - start_pos.y; + f32 x = width >= 0.0f ? start_pos.x : current_pos.x; + f32 y = height >= 0.0f ? start_pos.y : current_pos.y; + Rectangle rect = {x, y, fabs(width), fabs(height)}; + DrawRectangleLinesEx(rect, 4.0f, WHITE); + } } Vector2 world = GetScreenToWorld2D(GetMousePosition(), cam); @@ -281,8 +333,10 @@ int main(void) { printf("Knight Size: %ld \n", sizeof(Knight)); printf("SpriteAnimationPlayback Size: %ld \n", sizeof(SpriteAnimationPlayback)); - printf("Knight Size: %ld \n", sizeof(Point)); - printf("Knight Size: %ld \n", sizeof(PointOption)); + printf("Point Size: %ld \n", sizeof(Point)); + printf("Point Option Size: %ld \n", sizeof(PointOption)); + printf("Direction Size: %ld \n", sizeof(Direction)); + printf("KnightState Size: %ld \n", sizeof(KnightState)); InitWindow(screen_width, screen_height, "raylib [core] example - basic window"); @@ -305,8 +359,7 @@ int main(void) { GameState game = {0}; game.knights = calloc(MAX_KNIGHTS, sizeof(Knight)); game.anim_playbacks = calloc(MAX_KNIGHTS, sizeof(SpriteAnimationPlayback)); - game.target_points = calloc(MAX_KNIGHTS, sizeof(PointOption)); - const int entities = 1024; + const int entities = MAX_KNIGHTS; for (int i = 0; i < entities; i++) { int rand_x = GetRandomValue(360, 1250); @@ -318,7 +371,6 @@ int main(void) { game.anim_playbacks[i].current_frame = rand_frame; } game.entity_count = entities; - game.selected_knight = 0; while (!WindowShouldClose()) { game.frame_count++; @@ -339,7 +391,6 @@ int main(void) { free(game.knights); free(game.anim_playbacks); - free(game.target_points); for (int i = 0; i < TEXTURES_BUF_SIZE; i++) { UnloadTexture(assets.textures[i]); } diff --git a/tinyswords.todo b/tinyswords.todo index b058013..dddcb7e 100644 --- a/tinyswords.todo +++ b/tinyswords.todo @@ -15,9 +15,11 @@ * DONE Implement basic collider system * DONE Left click on knight to select, right click to move * DONE Mouse cursor texture and target on the ground texture -* TODO Handle multiple knights on the map at the same time -* TODO Ability to attack an entity, like a dummy -* TODO State to handle attacking when you right click a target +* DONE Optimize Animation struct +* DONE Handle multiple knights on the map at the same time +* DONE Knight y-depth sorting * TODO Select multiple units and make them all move * TODO Flocking/Steering behaviors so many knights move in formation * TODO Steering behavior so knights surround a target with spacing +* TODO State to handle attacking when you right click a target +* TODO Ability to attack an entity, like a dummy