]> git.localhorst.tv Git - blank.git/commitdiff
experiments with ai states and steering
authorDaniel Karbach <daniel.karbach@localhorst.tv>
Fri, 23 Oct 2015 23:48:02 +0000 (01:48 +0200)
committerDaniel Karbach <daniel.karbach@localhorst.tv>
Fri, 23 Oct 2015 23:48:02 +0000 (01:48 +0200)
src/ai/AIController.hpp
src/ai/IdleState.hpp
src/ai/RoamState.hpp [new file with mode: 0644]
src/ai/ai.cpp
src/world/EntityController.hpp

index 8e5073364ac22f9f2da21841cccadc0937b002a5..c1241969bdef702ada0225d0952184cd4a5fa986 100644 (file)
@@ -1,6 +1,7 @@
 #ifndef BLANK_AI_AICONTROLLER_HPP_
 #define BLANK_AI_AICONTROLLER_HPP_
 
+#include "../app/IntervalTimer.hpp"
 #include "../world/EntityController.hpp"
 
 #include <glm/glm.hpp>
@@ -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;
 
index 76358f8eb6b65aa3f47d169f393eb1596a9f8191..b663d9164fc459ab20be2076476ebd230f912c7a 100644 (file)
@@ -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 (file)
index 0000000..3a560e0
--- /dev/null
@@ -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
index f24d0694e0838222d457471ba1f254a76417471c..343bacf774aebe56d0828e3ab74a079149779f7a 100644 (file)
@@ -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<unsigned int>() % 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();
 }
 
index 526a3222fa4d985fc53094ab7e2c649376dfdd2e..3bfc10bb47b5a4118a587a7a3d8fe1908f78fb20 100644 (file)
@@ -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;
+       }
 
 };