From: Daniel Karbach Date: Fri, 23 Oct 2015 23:48:02 +0000 (+0200) Subject: experiments with ai states and steering X-Git-Url: http://git.localhorst.tv/?a=commitdiff_plain;h=f0a20986c573c4df1eb1212333489252c4b30efa;p=blank.git experiments with ai states and steering --- diff --git a/src/ai/AIController.hpp b/src/ai/AIController.hpp index 8e50733..c124196 100644 --- a/src/ai/AIController.hpp +++ b/src/ai/AIController.hpp @@ -1,6 +1,7 @@ #ifndef BLANK_AI_AICONTROLLER_HPP_ #define BLANK_AI_AICONTROLLER_HPP_ +#include "../app/IntervalTimer.hpp" #include "../world/EntityController.hpp" #include @@ -26,6 +27,20 @@ public: static glm::vec3 Heading(const EntityState &) noexcept; + /// schedule a decision in the next minimum ± variance seconds + void CueDecision( + float minimum, + float variance + ) noexcept; + /// check if the scheduled decision is due already + bool DecisionDue() const noexcept; + /// random choice of 0 to num_choices - 1 + unsigned int Decide(unsigned int num_choices) noexcept; + + void EnterHalt(float speed) noexcept; + void ExitHalt() noexcept; + bool IsHalted() const noexcept; + void StartFleeing(const Entity &, float speed) noexcept; void StopFleeing() noexcept; bool IsFleeing() const noexcept; @@ -47,11 +62,17 @@ public: float displacement = 1.0f ) noexcept; void StopWandering() noexcept; + bool IsWandering() const noexcept; private: GaloisLFSR &random; const AIState *state; + FineTimer decision_timer; + + bool halted; + float halt_speed; + const Entity *flee_target; float flee_speed; diff --git a/src/ai/IdleState.hpp b/src/ai/IdleState.hpp index 76358f8..b663d91 100644 --- a/src/ai/IdleState.hpp +++ b/src/ai/IdleState.hpp @@ -6,6 +6,11 @@ namespace blank { +/// stand around and do nothing +/// occasionally look in a different direction +/// start roaming at random +/// start chasing a player if one comes near + class IdleState : public AIState { diff --git a/src/ai/RoamState.hpp b/src/ai/RoamState.hpp new file mode 100644 index 0000000..3a560e0 --- /dev/null +++ b/src/ai/RoamState.hpp @@ -0,0 +1,24 @@ +#ifndef BLANK_AI_ROAMSTATE_HPP_ +#define BLANK_AI_ROAMSTATE_HPP_ + +#include "AIState.hpp" + + +namespace blank { + +/// randomly waltz about the landscape +/// hold at random +/// start chasing a player if one comes near + +class RoamState +: public AIState { + + void Enter(AIController &) const override; + void Update(AIController &, Entity &, float dt) const override; + void Exit(AIController &) const override; + +}; + +} + +#endif diff --git a/src/ai/ai.cpp b/src/ai/ai.cpp index f24d069..343bacf 100644 --- a/src/ai/ai.cpp +++ b/src/ai/ai.cpp @@ -1,5 +1,6 @@ #include "AIController.hpp" #include "IdleState.hpp" +#include "RoamState.hpp" #include "../model/geometry.hpp" #include "../rand/GaloisLFSR.hpp" @@ -17,12 +18,16 @@ namespace blank { namespace { IdleState idle; +RoamState roam; } AIController::AIController(GaloisLFSR &rand) : random(rand) , state(&idle) +, decision_timer(1.0f) +, halted(false) +, halt_speed(1.0f) , flee_target(nullptr) , flee_speed(5.0f) , seek_target(nullptr) @@ -47,7 +52,9 @@ void AIController::SetState(const AIState &s) { } void AIController::Update(Entity &e, float dt) { - // movement: for now, wander only + decision_timer.Update(dt); + state->Update(*this, e, dt); + if (wandering) { glm::vec3 displacement( random.SNorm() * wander_disp, @@ -69,6 +76,9 @@ void AIController::Update(Entity &e, float dt) { } glm::vec3 AIController::ControlForce(const Entity &entity, const EntityState &state) const { + if (IsHalted()) { + return Halt(state, halt_speed); + } glm::vec3 force(0.0f); if (IsFleeing()) { glm::vec3 diff(GetFleeTarget().GetState().Diff(state)); @@ -82,7 +92,7 @@ glm::vec3 AIController::ControlForce(const Entity &entity, const EntityState &st return force; } } - if (wandering) { + if (IsWandering()) { glm::vec3 wander_target(normalize(Heading(state) * wander_dist + wander_pos) * wander_speed); if (MaxOutForce(force, TargetVelocity(wander_target, state, 2.0f), entity.MaxControlForce())) { return force; @@ -100,6 +110,35 @@ glm::vec3 AIController::Heading(const EntityState &state) noexcept { } } +void AIController::CueDecision( + float minimum, + float variance +) noexcept { + decision_timer = FineTimer(minimum + variance * random.SNorm()); + decision_timer.Start(); +} + +bool AIController::DecisionDue() const noexcept { + return decision_timer.HitOnce(); +} + +unsigned int AIController::Decide(unsigned int num_choices) noexcept { + return random.Next() % num_choices; +} + +void AIController::EnterHalt(float speed) noexcept { + halted = true; + halt_speed = speed; +} + +void AIController::ExitHalt() noexcept { + halted = false; +} + +bool AIController::IsHalted() const noexcept { + return halted; +} + void AIController::StartFleeing(const Entity &e, float speed) noexcept { flee_target = &e; flee_speed = speed; @@ -146,19 +185,62 @@ void AIController::StartWandering( wander_radius = radius; wander_disp = displacement; } + void AIController::StopWandering() noexcept { wandering = false; } +bool AIController::IsWandering() const noexcept { + return wandering; +} + void IdleState::Enter(AIController &ctrl) const { - ctrl.StartWandering(1.0f); + ctrl.EnterHalt(2.0f); + ctrl.StartWandering(0.001f, 1.1f); + ctrl.CueDecision(10.0f, 5.0f); } void IdleState::Update(AIController &ctrl, Entity &e, float dt) const { + // TODO: check for players + if (!ctrl.DecisionDue()) return; + + unsigned int d = ctrl.Decide(10); + if (d == 0) { + // .1 chance to start going + ctrl.SetState(roam); + } else if (d < 3) { + // .2 chance of looking around + ctrl.ExitHalt(); + } else { + ctrl.EnterHalt(2.0f); + } + ctrl.CueDecision(10.0f, 5.0f); } void IdleState::Exit(AIController &ctrl) const { + ctrl.ExitHalt(); + ctrl.StopWandering(); +} + +void RoamState::Enter(AIController &ctrl) const { + ctrl.StartWandering(1.0f); + ctrl.CueDecision(10.0f, 5.0f); +} + +void RoamState::Update(AIController &ctrl, Entity &e, float dt) const { + // TODO: check for players + if (!ctrl.DecisionDue()) return; + + unsigned int d = ctrl.Decide(10); + if (d == 0) { + // .1 chance of idling + ctrl.SetState(idle); + } + ctrl.CueDecision(10.0f, 5.0f); +} + +void RoamState::Exit(AIController &ctrl) const { ctrl.StopWandering(); } diff --git a/src/world/EntityController.hpp b/src/world/EntityController.hpp index 526a322..3bfc10b 100644 --- a/src/world/EntityController.hpp +++ b/src/world/EntityController.hpp @@ -35,6 +35,13 @@ struct EntityController { ) noexcept { return (target - state.velocity) * n; } + /// give a force that makes state come to a halt over 1/n seconds + static inline glm::vec3 Halt( + const EntityState &state, + float n + ) noexcept { + return state.velocity * -n; + } };