--- /dev/null
+#include "Autopilot.h"
+
+#include "../entity/Ship.h"
+#include "../graphics/const.h"
+#include "../graphics/Camera.h"
+#include "../graphics/Canvas.h"
+#include "../graphics/Color.h"
+
+#include <iostream>
+using namespace std;
+
+
+namespace space {
+
+Autopilot::Autopilot(Ship &ctrl, const Vector<float> &target)
+: ctrl(&ctrl)
+, target(&target) {
+
+}
+
+
+void Autopilot::Update(float deltaT) {
+ const Vector<float> deltaP(*target - ctrl->pos);
+ const float distance = Length(deltaP);
+ const Vector<float> deltaPnorm(deltaP / distance);
+
+ const Vector<float> dir = ctrl->Dir();
+
+ const float faceTarget = Dot(deltaPnorm, dir);
+ const float maxAcc = faceTarget < 0
+ ? ctrl->MaxFwdAcc()
+ : ctrl->MaxRevAcc();
+
+ const float speed = Length(ctrl->vel);
+ const Vector<float> velNorm = ctrl->vel / speed;
+ const float onTarget = Dot(deltaPnorm, velNorm);
+
+ const float linTTH = speed / maxAcc;
+ const float linDTH = (maxAcc * linTTH * linTTH) / 2;
+
+ planFrom = Vector<float>(ctrl->pos);
+ planTo = Vector<float>(*target);
+ if (speed > 0 && onTarget < 1) {
+ planFrom += velNorm * linDTH;
+ planTo -= velNorm * linDTH;
+ }
+ const Vector<float> plan(planTo - planFrom);
+ const float planLen = Length(plan);
+ const Vector<float> planNorm(plan / planLen);
+
+ float facePlan = Dot(planNorm, dir);
+ ctrl->linThrottle = facePlan * facePlan;
+
+ const float absRotVel = std::abs(ctrl->rotVel);
+ const int sigRotVel = sigma(ctrl->rotVel);
+ const float rotMaxAcc = ctrl->MaxRotAcc();
+ const float rotTTH = absRotVel / rotMaxAcc;
+ const float rotDTH = (rotMaxAcc * rotTTH * rotTTH) / 2;
+
+ float planAngle =
+ std::atan2(planNorm.y, planNorm.x) - std::atan2(dir.y, dir.x);
+ if (planAngle > PI) planAngle -= PI2;
+ if (planAngle < -PI) planAngle += PI2;
+ const float absPlanAngle = std::acos(facePlan);
+ const int sigPlanAngle = sigma(planAngle);
+
+ // pos rot = clockwise
+ // neg rot = counter clockwise
+
+ if (sigPlanAngle == 0) {
+ // pointing straight at the plan
+ } else if (sigPlanAngle == sigRotVel) {
+ // turning in the right direction
+ if (rotDTH >= absPlanAngle) {
+ // overshooting
+ ctrl->rotThrottle = -sigPlanAngle;
+ } else {
+ ctrl->rotThrottle = sigPlanAngle;
+ }
+ } else {
+ // turning in the wrong direction
+ ctrl->rotThrottle = sigPlanAngle;
+ }
+}
+
+
+void Autopilot::Render(Canvas &canv, const Camera &cam) const {
+ constexpr Color tgtColor(0xFF, 0x00, 0x00);
+ constexpr Color velColor(0x00, 0x00, 0xFF);
+ constexpr Color planColor(0x00, 0xFF, 0x00);
+
+ Vector<float> screenPos(cam.ToScreen(ctrl->pos));
+ Vector<float> screenTgt(cam.ToScreen(*target));
+ Vector<float> screenPlanFrom(cam.ToScreen(planFrom));
+ Vector<float> screenPlanTo(cam.ToScreen(planTo));
+
+ canv.SetColor(tgtColor);
+ canv.Arrow(screenPos, screenTgt);
+
+ canv.SetColor(velColor);
+ canv.Arrow(screenPos, screenPlanFrom);
+ canv.Arrow(screenTgt, screenPlanTo);
+
+ canv.SetColor(planColor);
+ canv.Arrow(screenPlanFrom, screenPlanTo);
+
+ cout << "max acc: " << ctrl->MaxFwdAcc() << ", distance: " << Length(*target - ctrl->pos) << endl;
+}
+
+}
--- /dev/null
+#ifndef SPACE_AUTOPILOT_H_
+#define SPACE_AUTOPILOT_H_
+
+#include "../graphics/Vector.h"
+
+
+namespace space {
+
+class Camera;
+class Canvas;
+class Ship;
+
+class Autopilot {
+
+public:
+ Autopilot(Ship &ctrl, const Vector<float> &target);
+
+public:
+ void Update(float deltaT);
+
+ void Render(Canvas &, const Camera &) const;
+
+private:
+ Ship *ctrl;
+ const Vector<float> *target;
+
+ // cache members for debug drawing
+ Vector<float> planFrom;
+ Vector<float> planTo;
+
+};
+
+}
+
+#endif
, univ(Vector<int>(10, 10), Vector<int>(10, 10), Vector<int>(10, 10))
, focus(Vector<float>(500, 500), 500)
, cam(c.Size(), focus.Pos())
+, controlled(univ.AddShip(Ship()))
+, autopilot(*controlled, focus.Pos())
+, apEnabled(false)
, last(SDL_GetTicks())
-, running(false) {
- controlled = univ.AddShip(Ship());
+, running(false)
+, paused(false) {
+
}
void Application::Loop(int delta) {
HandleEvents();
- Update(delta);
+ if (!paused) {
+ Update(delta);
+ }
Render();
canvas.Present();
}
case SDLK_x:
cam.StartShrink();
break;
+ case SDLK_c:
+ apEnabled = !apEnabled;
+ break;
+ case SDLK_p:
+ paused = !paused;
+ break;
default:
break;
}
void Application::Update(int dt) {
const float delta = dt / 1e3;
- controlled->rotThrottle = control.x;
- controlled->linThrottle = -control.y;
+ if (apEnabled) {
+ autopilot.Update(delta);
+ } else {
+ controlled->rotThrottle = control.x;
+ controlled->linThrottle = -control.y;
+ }
cam.Update(delta);
univ.Update(delta);
focus.SetSpeed(500 / cam.Zoom());
canvas.SetColor(entityColor);
for (const Ship &s : univ.Ships()) {
const Vector<float> direction = s.Dir();
- const Vector<int> position = cam.ToScreen(Vector<float>(s.area * univ.areaSize) + s.pos);
+ const Vector<int> position = cam.ToScreen(s.pos);
const Vector<int> nose = position + Vector<int>(direction * 15.0f);
const Vector<int> left = position + Vector<int>((Rotate90(direction) * 8.0f) - (direction * 4.0f));
const Vector<int> right = position + Vector<int>((Rotate270(direction) * 8.0f) - (direction * 4.0f));
canvas.Line(position, nose);
canvas.Quad(nose, left, position, right);
}
+
+ autopilot.Render(canvas, cam);
}
}
#ifndef SPACE_APPLICATION_H_
#define SPACE_APPLICATION_H_
+#include "../ai/Autopilot.h"
#include "../graphics/Camera.h"
#include "../graphics/Moveable.h"
#include "../graphics/Vector.h"
Ship *controlled;
Vector<int> control;
+ Autopilot autopilot;
+ bool apEnabled;;
+
Uint32 last;
bool running;
+ bool paused;
};
#ifndef SPACE_SHIP_H_
#define SPACE_SHIP_H_
+#include "../graphics/const.h"
#include "../graphics/Vector.h"
#include <cmath>
float revForce = 1;
float rotForce = 1;
- Vector<int> area;
-
Vector<float> pos;
Vector<float> vel;
Vector<float> Dir() const {
return Vector<float>::FromPolar(1, orient);
}
+ float MaxFwdAcc() const {
+ return linForce / mass;
+ }
+ float MaxRevAcc() const {
+ return revForce / mass;
+ }
+ float MaxLinAcc() const {
+ return (linThrottle < 0 ? MaxRevAcc() : MaxFwdAcc());
+ }
Vector<float> Acc() const {
- float force = (linThrottle < 0 ? revForce : linForce);
- return Dir() * force / mass * linThrottle;
+ return Dir() * MaxLinAcc() * linThrottle;
}
float orient = 0;
float rotVel = 0;
+ float MaxRotAcc() const {
+ return rotForce / mass;
+ }
float RotAcc() const {
- return rotForce / mass * rotThrottle;
+ return MaxRotAcc() * rotThrottle;
}
float linThrottle = 0;
void Update(float delta) {
rotVel += RotAcc() * delta;
orient += rotVel * delta;
+ while (orient < 0) orient += PI2;
+ while (orient > PI2) orient -= PI2;
vel += Acc() * delta;
pos += vel * delta;
}
Vector<int>(pos.x, pos.y + extent));
}
+void Canvas::Arrow(Vector<int> from, Vector<int> to) {
+ Line(from, to);
+ Vector<float> delta(to - from);
+ delta = delta / Length(delta);
+
+ Line(to, to + Vector<int>(Rotate90(delta) * 5.0f - (delta * 5.0f)));
+ Line(to, to + Vector<int>(Rotate270(delta) * 5.0f - (delta * 5.0f)));
+}
+
void Canvas::Triangle(Vector<int> v1, Vector<int> v2, Vector<int> v3) {
SDL_Point points[4] = { v1, v2, v3, v1 };
SDL_RenderDrawLines(canv, points, 4);
void Dot(Vector<int> pos);
void Cross(Vector<int> pos, int extent);
+ void Arrow(Vector<int> from, Vector<int> to);
void Triangle(Vector<int>, Vector<int>, Vector<int>);
void Quad(Vector<int>, Vector<int>, Vector<int>, Vector<int>);
}
+template<class Scalar>
+constexpr Scalar Cross2D(Vector<Scalar> lhs, Vector<Scalar> rhs) {
+ return (lhs.x * rhs.y) - (lhs.y * rhs.x);
+}
template<class Scalar>
constexpr Scalar Dot(Vector<Scalar> lhs, Vector<Scalar> rhs) {
return (lhs.x * rhs.x) + (lhs.y * rhs.y);
--- /dev/null
+#ifndef SPACE_CONST_H_
+#define SPACE_CONST_H_
+
+namespace space {
+
+// this is so very darn long so it can easily be changed to double
+constexpr float PI = 3.141592653589793238462643383279502884;
+constexpr float PI2 = 2 * PI;
+
+template<class T>
+constexpr int sigma(T v) {
+ return v > 0 ? 1 : (v < 0 ? -1 : 0);
+}
+
+}
+
+#endif
void Universe::Update(float delta) {
for (Ship &s : ships) {
s.Update(delta);
- while (s.pos.x > areaSize.x) {
- s.pos.x -= areaSize.x;
- ++s.area.x;
- }
- while (s.pos.x < 0) {
- s.pos.x += areaSize.x;
- --s.area.x;
- }
- while (s.pos.y > areaSize.y) {
- s.pos.y -= areaSize.y;
- ++s.area.y;
- }
- while (s.pos.y < 0) {
- s.pos.y += areaSize.y;
- --s.area.y;
- }
}
}