namespace blank {
+class AIState;
class GaloisLFSR;
class AIController
explicit AIController(GaloisLFSR &);
~AIController();
+ void SetState(const AIState &);
+
void Update(Entity &, float dt) override;
glm::vec3 ControlForce(const EntityState &) const override;
static glm::vec3 Heading(const EntityState &) noexcept;
+ void StartFleeing(const Entity &, float speed) noexcept;
+ void StopFleeing() noexcept;
+ bool IsFleeing() const noexcept;
+ const Entity &GetFleeTarget() const noexcept;
+
+ void StartSeeking(const Entity &, float speed) noexcept;
+ void StopSeeking() noexcept;
+ bool IsSeeking() const noexcept;
+ const Entity &GetSeekTarget() const noexcept;
+
+ void StartWandering() noexcept;
+ void StopWandering() noexcept;
+
private:
GaloisLFSR &random;
+ const AIState *state;
- float chase_speed;
+ const Entity *flee_target;
float flee_speed;
- float stop_dist;
- float flee_dist;
+ const Entity *seek_target;
+ float seek_speed;
+
+ bool wandering;
glm::vec3 wander_pos;
float wander_dist;
float wander_radius;
--- /dev/null
+#ifndef BLANK_AI_AISTATE_HPP_
+#define BLANK_AI_AISTATE_HPP_
+
+
+namespace blank {
+
+struct AIState {
+
+ virtual void Enter(AIController &) const = 0;
+ virtual void Update(AIController &, Entity &, float dt) const = 0;
+ virtual void Exit(AIController &) const = 0;
+
+};
+
+}
+
+#endif
--- /dev/null
+#ifndef BLANK_AI_IDLESTATE_HPP_
+#define BLANK_AI_IDLESTATE_HPP_
+
+#include "AIState.hpp"
+
+
+namespace blank {
+
+class IdleState
+: public AIState {
+
+ void Enter(AIController &) const override;
+ void Update(AIController &, Entity &, float dt) const override;
+ void Exit(AIController &) const override;
+
+};
+
+}
+
+#endif
#include "AIController.hpp"
+#include "IdleState.hpp"
#include "../model/geometry.hpp"
#include "../rand/GaloisLFSR.hpp"
namespace blank {
+namespace {
+
+IdleState idle;
+
+}
+
AIController::AIController(GaloisLFSR &rand)
: random(rand)
-, chase_speed(2.0f)
+, state(&idle)
+, flee_target(nullptr)
, flee_speed(-5.0f)
-, stop_dist(10.0f)
-, flee_dist(5.0f)
+, seek_target(nullptr)
+, seek_speed(-5.0f)
+, wandering(false)
, wander_pos(1.0f, 0.0f, 0.0f)
, wander_dist(2.0f)
, wander_radius(1.0f)
, wander_disp(1.0f)
, wander_speed(1.0f) {
-
+ state->Enter(*this);
}
AIController::~AIController() {
+ state->Exit(*this);
+}
+void AIController::SetState(const AIState &s) {
+ state->Exit(*this);
+ state = &s;
+ state->Enter(*this);
}
void AIController::Update(Entity &e, float dt) {
}
glm::vec3 AIController::ControlForce(const EntityState &state) const {
- return (Heading(state) * wander_dist + wander_pos) * wander_speed;
+ glm::vec3 force(0.0f);
+ if (IsFleeing()) {
+ glm::vec3 diff(GetFleeTarget().GetState().Diff(state));
+ if (dot(diff, diff) > std::numeric_limits<float>::epsilon()) {
+ force += normalize(diff) * flee_speed;
+ }
+ }
+ if (IsSeeking()) {
+ glm::vec3 diff(state.Diff(GetSeekTarget().GetState()));
+ if (dot(diff, diff) > std::numeric_limits<float>::epsilon()) {
+ force += normalize(diff) * seek_speed;
+ }
+ }
+ if (wandering) {
+ force += (Heading(state) * wander_dist + wander_pos) * wander_speed;
+ }
+ return force;
}
glm::vec3 AIController::Heading(const EntityState &state) noexcept {
}
}
+void AIController::StartFleeing(const Entity &e, float speed) noexcept {
+ flee_target = &e;
+ flee_speed = speed;
+}
+
+void AIController::StopFleeing() noexcept {
+ flee_target = nullptr;
+}
+
+bool AIController::IsFleeing() const noexcept {
+ return flee_target;
+}
+
+const Entity &AIController::GetFleeTarget() const noexcept {
+ return *flee_target;
+}
+
+void AIController::StartSeeking(const Entity &e, float speed) noexcept {
+ seek_target = &e;
+ seek_speed = speed;
+}
+
+void AIController::StopSeeking() noexcept {
+ seek_target = nullptr;
+}
+
+bool AIController::IsSeeking() const noexcept {
+ return seek_target;
+}
+
+const Entity &AIController::GetSeekTarget() const noexcept {
+ return *seek_target;
+}
+
+void AIController::StartWandering() noexcept {
+ wandering = true;
+}
+void AIController::StopWandering() noexcept {
+ wandering = false;
+}
+
+
+void IdleState::Enter(AIController &ctrl) const {
+ ctrl.StartWandering();
+}
+
+void IdleState::Update(AIController &ctrl, Entity &e, float dt) const {
+}
+
+void IdleState::Exit(AIController &ctrl) const {
+ ctrl.StopWandering();
+}
+
}
next.velocity += delta.velocity * dt;
next.AdjustPosition();
+ if (dot(next.velocity, next.velocity) > entity.MaxVelocity() * entity.MaxVelocity()) {
+ next.velocity = normalize(next.velocity) * entity.MaxVelocity();
+ }
+
EntityDerivative out;
out.position = next.velocity;
out.velocity = CalculateForce(entity, next); // by mass = 1kg
const Entity &entity,
const EntityState &state
) {
- return ControlForce(entity, state) + CollisionForce(entity, state) + Gravity(entity, state);
+ glm::vec3 force(ControlForce(entity, state) + CollisionForce(entity, state) + Gravity(entity, state));
+ if (dot(force, force) > entity.MaxControlForce() * entity.MaxControlForce()) {
+ return normalize(force) * entity.MaxControlForce();
+ } else {
+ return force;
+ }
}
glm::vec3 World::ControlForce(