void Update(Entity &, float dt) override;
- glm::vec3 ControlForce(const EntityState &) const override;
+ glm::vec3 ControlForce(const Entity &, const EntityState &) const override;
static glm::vec3 Heading(const EntityState &) noexcept;
bool IsSeeking() const noexcept;
const Entity &GetSeekTarget() const noexcept;
- void StartWandering() noexcept;
+ /// start wandering randomly at given speed
+ /// the trajectory is modified by targetting a blip on a sphere
+ /// in front of the entity which moves randomly
+ /// the displacement is given (roughly) in units per second
+ void StartWandering(
+ float speed,
+ float distance = 2.0f,
+ float radius = 1.0f,
+ float displacement = 1.0f
+ ) noexcept;
void StopWandering() noexcept;
private:
bool wandering;
glm::vec3 wander_pos;
+ float wander_speed;
float wander_dist;
float wander_radius;
float wander_disp;
- float wander_speed;
};
, timer(64)
, despawn_range(128 * 128)
, spawn_distance(16 * 16)
-, max_entities(16)
+, max_entities(32)
, chunk_range(4)
, model_offset(0)
, model_length(models.size()) {
: random(rand)
, state(&idle)
, flee_target(nullptr)
-, flee_speed(-5.0f)
+, flee_speed(5.0f)
, seek_target(nullptr)
-, seek_speed(-5.0f)
+, seek_speed(5.0f)
, wandering(false)
, wander_pos(1.0f, 0.0f, 0.0f)
+, wander_speed(1.0f)
, wander_dist(2.0f)
-, wander_radius(1.0f)
-, wander_disp(1.0f)
-, wander_speed(1.0f) {
+, wander_radius(1.5f)
+, wander_disp(1.0f) {
state->Enter(*this);
}
void AIController::Update(Entity &e, float dt) {
// movement: for now, wander only
- glm::vec3 displacement(
- random.SNorm() * wander_disp,
- random.SNorm() * wander_disp,
- random.SNorm() * wander_disp
- );
- if (dot(displacement, displacement) > std::numeric_limits<float>::epsilon()) {
- wander_pos = normalize(wander_pos + displacement * dt) * wander_radius;
+ if (wandering) {
+ glm::vec3 displacement(
+ random.SNorm() * wander_disp,
+ random.SNorm() * wander_disp,
+ random.SNorm() * wander_disp
+ );
+ if (!iszero(displacement)) {
+ wander_pos = normalize(wander_pos + displacement * dt) * wander_radius;
+ }
}
if (e.Moving()) {
}
}
-glm::vec3 AIController::ControlForce(const EntityState &state) const {
+glm::vec3 AIController::ControlForce(const Entity &entity, const EntityState &state) const {
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 (MaxOutForce(force, TargetVelocity(normalize(diff) * flee_speed, state, 0.5f), entity.MaxControlForce())) {
+ return force;
}
}
if (IsSeeking()) {
glm::vec3 diff(state.Diff(GetSeekTarget().GetState()));
- if (dot(diff, diff) > std::numeric_limits<float>::epsilon()) {
- force += normalize(diff) * seek_speed;
+ if (MaxOutForce(force, TargetVelocity(normalize(diff) * seek_speed, state, 0.5f), entity.MaxControlForce())) {
+ return force;
}
}
if (wandering) {
- force += (Heading(state) * wander_dist + wander_pos) * wander_speed;
+ 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;
+ }
}
return force;
}
return *seek_target;
}
-void AIController::StartWandering() noexcept {
+void AIController::StartWandering(
+ float speed,
+ float distance,
+ float radius,
+ float displacement
+) noexcept {
wandering = true;
+ wander_speed = speed;
+ wander_dist = distance;
+ wander_radius = radius;
+ wander_disp = displacement;
}
void AIController::StopWandering() noexcept {
wandering = false;
void IdleState::Enter(AIController &ctrl) const {
- ctrl.StartWandering();
+ ctrl.StartWandering(1.0f);
}
void IdleState::Update(AIController &ctrl, Entity &e, float dt) const {
#define BLANK_MODEL_GEOMETRY_H_
#include <algorithm>
+#include <limits>
#include <glm/glm.hpp>
}
+template <class T>
+inline bool iszero(const T &v) {
+ return dot(v, v) < std::numeric_limits<typename T::value_type>::epsilon();
+}
+
+
template<class T>
T manhattan_distance(const glm::tvec3<T> &a, const glm::tvec3<T> &b) {
glm::tvec3<T> diff(abs(a - b));
void SetMovement(const glm::vec3 &) noexcept;
const glm::vec3 &GetMovement() const noexcept { return move_dir; }
- glm::vec3 ControlForce(const EntityState &) const override;
+ glm::vec3 ControlForce(const Entity &, const EntityState &) const override;
/// turn the controlled entity's head by given pitch and yaw deltas
void TurnHead(float pitch, float yaw) noexcept;
Invalidate();
}
-glm::vec3 PlayerController::ControlForce(const EntityState &s) const {
- glm::vec3 target(rotateY(move_dir * player.GetEntity().MaxVelocity(), s.yaw) - s.velocity);
- return target * player.GetEntity().MaxControlForce();
+glm::vec3 PlayerController::ControlForce(const Entity &e, const EntityState &s) const {
+ return TargetVelocity(rotateY(move_dir * e.MaxVelocity(), s.yaw), s, 5.0f);
}
void PlayerController::TurnHead(float dp, float dy) noexcept {
#ifndef BLANK_WORLD_ENTITYCONTROLLER_HPP_
#define BLANK_WORLD_ENTITYCONTROLLER_HPP_
+#include "EntityState.hpp"
+
#include <glm/glm.hpp>
namespace blank {
class Entity;
-class EntityState;
struct EntityController {
virtual void Update(Entity &, float dt) = 0;
- virtual glm::vec3 ControlForce(const EntityState &) const = 0;
+ virtual glm::vec3 ControlForce(const Entity &, const EntityState &) const = 0;
+
+
+ /// try to add as much of add to out so it doesn't exceed max
+ /// returns true if it's maxed out
+ static bool MaxOutForce(
+ glm::vec3 &out,
+ const glm::vec3 &add,
+ float max
+ ) noexcept;
+ /// give a force that makes state's velocity converge with given target velocity
+ /// over 1/n seconds
+ static inline glm::vec3 TargetVelocity(
+ const glm::vec3 &target,
+ const EntityState &state,
+ float n
+ ) noexcept {
+ return (target - state.velocity) * n;
+ }
};
glm::vec3 Entity::ControlForce(const EntityState &s) const noexcept {
if (HasController()) {
- return GetController().ControlForce(s);
+ return GetController().ControlForce(*this, s);
} else {
return -s.velocity;
}
}
+bool EntityController::MaxOutForce(
+ glm::vec3 &out,
+ const glm::vec3 &add,
+ float max
+) noexcept {
+ if (iszero(add) || any(isnan(add))) {
+ return false;
+ }
+ float current = iszero(out) ? 0.0f : length(out);
+ float remain = max - current;
+ if (remain <= 0.0f) {
+ return true;
+ }
+ float additional = length(add);
+ if (additional > remain) {
+ out += normalize(add) * remain;
+ return true;
+ } else {
+ out += add;
+ return false;
+ }
+}
+
EntityState::EntityState()
: chunk_pos(0)