From dcd4d1940d97d05ad551e3b35dd85658a5e58e54 Mon Sep 17 00:00:00 2001 From: Klemen Plestenjak Date: Tue, 13 Feb 2024 19:01:51 +0100 Subject: [PATCH] Polish up AI --- game/ai_actions.c | 27 +++++++++++++++++++++++---- game/ai_actions.h | 1 + game/entity_factory.c | 12 +++--------- game/main.c | 12 ++++++------ game/systems/s_input.c | 6 +++++- game/systems/s_ui.c | 4 ++-- 6 files changed, 40 insertions(+), 22 deletions(-) diff --git a/game/ai_actions.c b/game/ai_actions.c index f26a10f..ea6ace4 100644 --- a/game/ai_actions.c +++ b/game/ai_actions.c @@ -15,6 +15,8 @@ float shortestArc(float a, float b) { } BzBTStatus aiMoveTo(AIBlackboard *data, f32 dt) { + if (!data->shouldMoveTo) + return BZ_BT_FAIL; Game *game = ecs_singleton_get_mut(ECS, Game); const Vector2 pos = *ecs_get(ECS, data->entity, Position); const HitBox hb = *ecs_get(ECS, data->entity, HitBox); @@ -23,12 +25,15 @@ BzBTStatus aiMoveTo(AIBlackboard *data, f32 dt) { f32 dst = Vector2Distance(center, target); if (dst < data->proximity) { ecs_remove(ECS, data->entity, Path); + data->shouldMoveTo = false; return BZ_BT_SUCCESS; } if (!ecs_has(ECS, data->entity, Path)) { bool pathfindSuccessful = entitySetPath(data->entity, target, game); - if (!pathfindSuccessful) + if (!pathfindSuccessful) { + data->shouldMoveTo = false; return BZ_BT_FAIL; + } } if (ecs_has(ECS, data->entity, Orientation)) { Orientation *orientation = ecs_get_mut(ECS, data->entity, Orientation); @@ -47,18 +52,21 @@ BzBTStatus aiResetElapsed(AIBlackboard *data, f32 dt) { return BZ_BT_SUCCESS; } -#define ENEMY_NEARBY_DST 26.0f +#define ENEMY_NEARBY_DST 30.0f +#define ENEMY_CHASE_THRESH 25.0f +#define ENEMY_EVADE_THRESH 30.0f BzBTStatus aiIsEnemyNearby(AIBlackboard *data, f32 dt) { if (data->seenEnemy && ecs_is_alive(ECS, data->seenEnemy)) { Position enemyPos = *ecs_get(ECS, data->seenEnemy, Position); Position pos = *ecs_get(ECS, data->entity, Position); - if (Vector2Distance(enemyPos, pos) > ENEMY_NEARBY_DST) + if (Vector2Distance(enemyPos, pos) < ENEMY_NEARBY_DST) return BZ_BT_SUCCESS; + data->seenEnemy = 0; } - const f32 range = 20.0f; + const f32 range = 30.0f; Position pos = *ecs_get(ECS, data->entity, Position); HitBox hb = *ecs_get(ECS, data->entity, HitBox); Vector2 center = entityGetCenter(pos, hb); @@ -109,6 +117,10 @@ BzBTStatus aiAttackEnemy(AIBlackboard *data, f32 dt) { Position enemyPos = *ecs_get(ECS, data->seenEnemy, Position); Position pos = *ecs_get(ECS, data->entity, Position); + f32 dst = Vector2Distance(pos, enemyPos); + if (dst > ENEMY_CHASE_THRESH) + return BZ_BT_FAIL; + Vector2 dif = Vector2Subtract(enemyPos, pos); // Overload steering Steering *steering = ecs_get_mut(ECS, data->entity, Steering); @@ -124,6 +136,10 @@ BzBTStatus aiEvadeEnemy(AIBlackboard *data, f32 dt) { Position enemyPos = *ecs_get(ECS, data->seenEnemy, Position); Position pos = *ecs_get(ECS, data->entity, Position); + f32 dst = Vector2Distance(pos, enemyPos); + if (dst > ENEMY_EVADE_THRESH) + return BZ_BT_FAIL; + Vector2 dif = Vector2Subtract(pos, enemyPos); // Overload steering Steering *steering = ecs_get_mut(ECS, data->entity, Steering); @@ -141,6 +157,7 @@ BzBTStatus aiFindNextHarvestable(AIBlackboard *data, f32 dt) { if (harvestable.harvestCount < harvestable.harvestLimit) { // Target still alive, no need to find next harvestable data->moveToPos = data->as.worker.harvestPos; + data->shouldMoveTo = true; return BZ_BT_SUCCESS; } } @@ -191,6 +208,7 @@ BzBTStatus aiFindNextHarvestable(AIBlackboard *data, f32 dt) { data->as.worker.harvestTarget = closest; data->as.worker.harvestPos = closestPos; data->moveToPos = closestPos; + data->shouldMoveTo = true; return BZ_BT_SUCCESS; } @@ -233,6 +251,7 @@ BzBTStatus aiFindNearestStorage(AIBlackboard *data, f32 dt) { data->as.worker.depositTarget = closest; data->moveToPos = getPositionNearBuilding(closest, pos); + data->shouldMoveTo = true; return BZ_BT_SUCCESS; } diff --git a/game/ai_actions.h b/game/ai_actions.h index de4823d..7e2d132 100644 --- a/game/ai_actions.h +++ b/game/ai_actions.h @@ -10,6 +10,7 @@ typedef struct AIBlackboard { ecs_entity_t entity; ecs_entity_t seenEnemy; + bool shouldMoveTo; Vector2 moveToPos; union { diff --git a/game/entity_factory.c b/game/entity_factory.c index ab0c73e..942d2a7 100644 --- a/game/entity_factory.c +++ b/game/entity_factory.c @@ -90,9 +90,7 @@ ecs_entity_t entityCreateSoldier(const Position position, Player player, Game *g unit.maxDamage = 10.0f; unit.attackCooldown = 1.0f; ecs_set_ptr(ECS, e, Unit, &unit); - setAIBehaviour(e, game->BTs.unit, &(AIBlackboard) { - .moveToPos = position, - }); + setAIBehaviour(e, game->BTs.unit, NULL); return e; } ecs_entity_t entityCreateWarrior(const Position position, Player player, Game *game) { @@ -107,9 +105,7 @@ ecs_entity_t entityCreateWarrior(const Position position, Player player, Game *g unit.maxDamage = 22.0f; unit.attackCooldown = 1.8f; ecs_set_ptr(ECS, e, Unit, &unit); - setAIBehaviour(e, game->BTs.unit, &(AIBlackboard) { - .moveToPos = position, - }); + setAIBehaviour(e, game->BTs.unit, NULL); return e; } @@ -128,9 +124,7 @@ ecs_entity_t entityCreateWorker(const Position position, Player player, Game *ga .lastChanged = -1000.0f }); - setAIBehaviour(e, game->BTs.unit, &(AIBlackboard) { - .moveToPos = position, - }); + setAIBehaviour(e, game->BTs.unitEvasive, NULL); return e; } diff --git a/game/main.c b/game/main.c index 8123cf5..dacb1f4 100644 --- a/game/main.c +++ b/game/main.c @@ -274,6 +274,8 @@ bool init(void *userData) { game->BTs.unit = root; BzBTNode *sel = bzBTCompSelector(nodePool, root); + node = bzBTAction(nodePool, sel, (BzBTActionFn) aiMoveTo); + bzBTNodeSetName(node, "moveTo"); BzBTNode *attackSeq = bzBTCompSequence(nodePool, sel); { node = bzBTAction(nodePool, attackSeq, (BzBTActionFn) aiIsEnemyNearby); @@ -282,9 +284,7 @@ bool init(void *userData) { node = bzBTAction(nodePool, attackSeq, (BzBTActionFn) aiAttackEnemy); bzBTNodeSetName(node, "attackEnemy"); } - node = bzBTAction(nodePool, sel, (BzBTActionFn) aiMoveTo); - bzBTNodeSetName(node, "moveTo"); - bzBTDecorDelay(nodePool, sel, 2.0f); + bzBTDecorDelay(nodePool, sel, 0.2f); } // Unit (evasive) { @@ -294,6 +294,9 @@ bool init(void *userData) { game->BTs.unitEvasive = root; BzBTNode *sel = bzBTCompSelector(nodePool, root); + node = bzBTAction(nodePool, sel, (BzBTActionFn) aiMoveTo); + bzBTNodeSetName(node, "moveTo"); + //bzBTDecorDelay(nodePool, sel, 0.2f); BzBTNode *evadeSeq = bzBTCompSequence(nodePool, sel); { node = bzBTAction(nodePool, evadeSeq, (BzBTActionFn) aiIsEnemyNearby); @@ -302,9 +305,6 @@ bool init(void *userData) { node = bzBTAction(nodePool, evadeSeq, (BzBTActionFn) aiEvadeEnemy); bzBTNodeSetName(node, "evadeEnemy"); } - node = bzBTAction(nodePool, sel, (BzBTActionFn) aiMoveTo); - bzBTNodeSetName(node, "moveTo"); - bzBTDecorDelay(nodePool, sel, 2.0f); } // evade BzBTNode *evade = NULL; diff --git a/game/systems/s_input.c b/game/systems/s_input.c index a7c30a7..808c945 100644 --- a/game/systems/s_input.c +++ b/game/systems/s_input.c @@ -263,8 +263,12 @@ void inputUnitAction(Game *game, InputState *input) { for (i32 i = 0; i < unitIdx; i++) { ecs_entity_t entity = entities[i].entity; - setAIBehaviour(entity, game->BTs.unit, &(AIBlackboard) { + BzBTNode *BT = game->BTs.unit; + if (ecs_has(ECS, entity, Worker)) + BT = game->BTs.unitEvasive; + setAIBehaviour(entity, BT, &(AIBlackboard) { .moveToPos = positions[i], + .shouldMoveTo = true, .proximity = 1.0f, }); } diff --git a/game/systems/s_ui.c b/game/systems/s_ui.c index 6ed3be4..920e5f4 100644 --- a/game/systems/s_ui.c +++ b/game/systems/s_ui.c @@ -406,11 +406,11 @@ void drawMainMenuUI(Game *game, f32 dt) { if (uiMainMenuButton("Play", true)) { setScreen(game, SCREEN_GAME); unloadMap(game); - loadMap(game, "assets/maps/tree_test.tmj", false); + //loadMap(game, "assets/maps/tree_test.tmj", false); //loadMap(game, "assets/maps/entity_test.tmj", false); //loadMap(game, "assets/maps/worker_test.tmj", false); //loadMap(game, "assets/maps/battle_test.tmj", false); - //loadMap(game, "assets/maps/map_01.tmj", false); + loadMap(game, "assets/maps/map_01.tmj", false); } if (uiMainMenuButton("Settings", true)) { setScreen(game, SCREEN_SETTINGS);