Polish up AI

This commit is contained in:
2024-02-13 19:01:51 +01:00
parent 417cf081d7
commit dcd4d1940d
6 changed files with 40 additions and 22 deletions

View File

@@ -15,6 +15,8 @@ float shortestArc(float a, float b) {
} }
BzBTStatus aiMoveTo(AIBlackboard *data, f32 dt) { BzBTStatus aiMoveTo(AIBlackboard *data, f32 dt) {
if (!data->shouldMoveTo)
return BZ_BT_FAIL;
Game *game = ecs_singleton_get_mut(ECS, Game); Game *game = ecs_singleton_get_mut(ECS, Game);
const Vector2 pos = *ecs_get(ECS, data->entity, Position); const Vector2 pos = *ecs_get(ECS, data->entity, Position);
const HitBox hb = *ecs_get(ECS, data->entity, HitBox); const HitBox hb = *ecs_get(ECS, data->entity, HitBox);
@@ -23,12 +25,15 @@ BzBTStatus aiMoveTo(AIBlackboard *data, f32 dt) {
f32 dst = Vector2Distance(center, target); f32 dst = Vector2Distance(center, target);
if (dst < data->proximity) { if (dst < data->proximity) {
ecs_remove(ECS, data->entity, Path); ecs_remove(ECS, data->entity, Path);
data->shouldMoveTo = false;
return BZ_BT_SUCCESS; return BZ_BT_SUCCESS;
} }
if (!ecs_has(ECS, data->entity, Path)) { if (!ecs_has(ECS, data->entity, Path)) {
bool pathfindSuccessful = entitySetPath(data->entity, target, game); bool pathfindSuccessful = entitySetPath(data->entity, target, game);
if (!pathfindSuccessful) if (!pathfindSuccessful) {
data->shouldMoveTo = false;
return BZ_BT_FAIL; return BZ_BT_FAIL;
}
} }
if (ecs_has(ECS, data->entity, Orientation)) { if (ecs_has(ECS, data->entity, Orientation)) {
Orientation *orientation = ecs_get_mut(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; 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) { BzBTStatus aiIsEnemyNearby(AIBlackboard *data, f32 dt) {
if (data->seenEnemy && ecs_is_alive(ECS, data->seenEnemy)) { if (data->seenEnemy && ecs_is_alive(ECS, data->seenEnemy)) {
Position enemyPos = *ecs_get(ECS, data->seenEnemy, Position); Position enemyPos = *ecs_get(ECS, data->seenEnemy, Position);
Position pos = *ecs_get(ECS, data->entity, 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; 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); Position pos = *ecs_get(ECS, data->entity, Position);
HitBox hb = *ecs_get(ECS, data->entity, HitBox); HitBox hb = *ecs_get(ECS, data->entity, HitBox);
Vector2 center = entityGetCenter(pos, hb); Vector2 center = entityGetCenter(pos, hb);
@@ -109,6 +117,10 @@ BzBTStatus aiAttackEnemy(AIBlackboard *data, f32 dt) {
Position enemyPos = *ecs_get(ECS, data->seenEnemy, Position); Position enemyPos = *ecs_get(ECS, data->seenEnemy, Position);
Position pos = *ecs_get(ECS, data->entity, 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); Vector2 dif = Vector2Subtract(enemyPos, pos);
// Overload steering // Overload steering
Steering *steering = ecs_get_mut(ECS, data->entity, 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 enemyPos = *ecs_get(ECS, data->seenEnemy, Position);
Position pos = *ecs_get(ECS, data->entity, 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); Vector2 dif = Vector2Subtract(pos, enemyPos);
// Overload steering // Overload steering
Steering *steering = ecs_get_mut(ECS, data->entity, 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) { if (harvestable.harvestCount < harvestable.harvestLimit) {
// Target still alive, no need to find next harvestable // Target still alive, no need to find next harvestable
data->moveToPos = data->as.worker.harvestPos; data->moveToPos = data->as.worker.harvestPos;
data->shouldMoveTo = true;
return BZ_BT_SUCCESS; return BZ_BT_SUCCESS;
} }
} }
@@ -191,6 +208,7 @@ BzBTStatus aiFindNextHarvestable(AIBlackboard *data, f32 dt) {
data->as.worker.harvestTarget = closest; data->as.worker.harvestTarget = closest;
data->as.worker.harvestPos = closestPos; data->as.worker.harvestPos = closestPos;
data->moveToPos = closestPos; data->moveToPos = closestPos;
data->shouldMoveTo = true;
return BZ_BT_SUCCESS; return BZ_BT_SUCCESS;
} }
@@ -233,6 +251,7 @@ BzBTStatus aiFindNearestStorage(AIBlackboard *data, f32 dt) {
data->as.worker.depositTarget = closest; data->as.worker.depositTarget = closest;
data->moveToPos = getPositionNearBuilding(closest, pos); data->moveToPos = getPositionNearBuilding(closest, pos);
data->shouldMoveTo = true;
return BZ_BT_SUCCESS; return BZ_BT_SUCCESS;
} }

View File

@@ -10,6 +10,7 @@ typedef struct AIBlackboard {
ecs_entity_t entity; ecs_entity_t entity;
ecs_entity_t seenEnemy; ecs_entity_t seenEnemy;
bool shouldMoveTo;
Vector2 moveToPos; Vector2 moveToPos;
union { union {

View File

@@ -90,9 +90,7 @@ ecs_entity_t entityCreateSoldier(const Position position, Player player, Game *g
unit.maxDamage = 10.0f; unit.maxDamage = 10.0f;
unit.attackCooldown = 1.0f; unit.attackCooldown = 1.0f;
ecs_set_ptr(ECS, e, Unit, &unit); ecs_set_ptr(ECS, e, Unit, &unit);
setAIBehaviour(e, game->BTs.unit, &(AIBlackboard) { setAIBehaviour(e, game->BTs.unit, NULL);
.moveToPos = position,
});
return e; return e;
} }
ecs_entity_t entityCreateWarrior(const Position position, Player player, Game *game) { 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.maxDamage = 22.0f;
unit.attackCooldown = 1.8f; unit.attackCooldown = 1.8f;
ecs_set_ptr(ECS, e, Unit, &unit); ecs_set_ptr(ECS, e, Unit, &unit);
setAIBehaviour(e, game->BTs.unit, &(AIBlackboard) { setAIBehaviour(e, game->BTs.unit, NULL);
.moveToPos = position,
});
return e; return e;
} }
@@ -128,9 +124,7 @@ ecs_entity_t entityCreateWorker(const Position position, Player player, Game *ga
.lastChanged = -1000.0f .lastChanged = -1000.0f
}); });
setAIBehaviour(e, game->BTs.unit, &(AIBlackboard) { setAIBehaviour(e, game->BTs.unitEvasive, NULL);
.moveToPos = position,
});
return e; return e;
} }

View File

@@ -274,6 +274,8 @@ bool init(void *userData) {
game->BTs.unit = root; game->BTs.unit = root;
BzBTNode *sel = bzBTCompSelector(nodePool, root); BzBTNode *sel = bzBTCompSelector(nodePool, root);
node = bzBTAction(nodePool, sel, (BzBTActionFn) aiMoveTo);
bzBTNodeSetName(node, "moveTo");
BzBTNode *attackSeq = bzBTCompSequence(nodePool, sel); BzBTNode *attackSeq = bzBTCompSequence(nodePool, sel);
{ {
node = bzBTAction(nodePool, attackSeq, (BzBTActionFn) aiIsEnemyNearby); node = bzBTAction(nodePool, attackSeq, (BzBTActionFn) aiIsEnemyNearby);
@@ -282,9 +284,7 @@ bool init(void *userData) {
node = bzBTAction(nodePool, attackSeq, (BzBTActionFn) aiAttackEnemy); node = bzBTAction(nodePool, attackSeq, (BzBTActionFn) aiAttackEnemy);
bzBTNodeSetName(node, "attackEnemy"); bzBTNodeSetName(node, "attackEnemy");
} }
node = bzBTAction(nodePool, sel, (BzBTActionFn) aiMoveTo); bzBTDecorDelay(nodePool, sel, 0.2f);
bzBTNodeSetName(node, "moveTo");
bzBTDecorDelay(nodePool, sel, 2.0f);
} }
// Unit (evasive) // Unit (evasive)
{ {
@@ -294,6 +294,9 @@ bool init(void *userData) {
game->BTs.unitEvasive = root; game->BTs.unitEvasive = root;
BzBTNode *sel = bzBTCompSelector(nodePool, 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); BzBTNode *evadeSeq = bzBTCompSequence(nodePool, sel);
{ {
node = bzBTAction(nodePool, evadeSeq, (BzBTActionFn) aiIsEnemyNearby); node = bzBTAction(nodePool, evadeSeq, (BzBTActionFn) aiIsEnemyNearby);
@@ -302,9 +305,6 @@ bool init(void *userData) {
node = bzBTAction(nodePool, evadeSeq, (BzBTActionFn) aiEvadeEnemy); node = bzBTAction(nodePool, evadeSeq, (BzBTActionFn) aiEvadeEnemy);
bzBTNodeSetName(node, "evadeEnemy"); bzBTNodeSetName(node, "evadeEnemy");
} }
node = bzBTAction(nodePool, sel, (BzBTActionFn) aiMoveTo);
bzBTNodeSetName(node, "moveTo");
bzBTDecorDelay(nodePool, sel, 2.0f);
} }
// evade // evade
BzBTNode *evade = NULL; BzBTNode *evade = NULL;

View File

@@ -263,8 +263,12 @@ void inputUnitAction(Game *game, InputState *input) {
for (i32 i = 0; i < unitIdx; i++) { for (i32 i = 0; i < unitIdx; i++) {
ecs_entity_t entity = entities[i].entity; 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], .moveToPos = positions[i],
.shouldMoveTo = true,
.proximity = 1.0f, .proximity = 1.0f,
}); });
} }

View File

@@ -406,11 +406,11 @@ void drawMainMenuUI(Game *game, f32 dt) {
if (uiMainMenuButton("Play", true)) { if (uiMainMenuButton("Play", true)) {
setScreen(game, SCREEN_GAME); setScreen(game, SCREEN_GAME);
unloadMap(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/entity_test.tmj", false);
//loadMap(game, "assets/maps/worker_test.tmj", false); //loadMap(game, "assets/maps/worker_test.tmj", false);
//loadMap(game, "assets/maps/battle_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)) { if (uiMainMenuButton("Settings", true)) {
setScreen(game, SCREEN_SETTINGS); setScreen(game, SCREEN_SETTINGS);