: canvas(c)
, world(w)
, tiles(t)
-, focus(Vector<float>(5, 5), 2)
-, cam(c.Size(), focus.Pos())
+, ctrl()
+, focus(5, 5)
+, target(focus, 2)
+, cam(c.Size(), focus)
, last(SDL_GetTicks())
, running(false)
, paused(false) {
}
+void Application::Control(Entity &e) {
+ ctrl.Control(e);
+}
+
+void Application::Relinquish() {
+ ctrl.Relinquish();
+}
+
+
void Application::Run() {
running = true;
while (running) {
void Application::OnKeyDown(const SDL_KeyboardEvent &e) {
switch (e.keysym.sym) {
- case SDLK_UP:
- focus.MoveUp();
+ case SDLK_w:
+ target.MoveUp();
+ break;
+ case SDLK_s:
+ target.MoveDown();
break;
- case SDLK_DOWN:
- focus.MoveDown();
+ case SDLK_a:
+ ctrl.MoveLeft();
+ target.MoveLeft();
break;
- case SDLK_LEFT:
- focus.MoveLeft();
+ case SDLK_d:
+ ctrl.MoveRight();
+ target.MoveRight();
break;
- case SDLK_RIGHT:
- focus.MoveRight();
+ case SDLK_SPACE:
+ ctrl.StartJump();
break;
case SDLK_p:
paused = !paused;
void Application::OnKeyUp(const SDL_KeyboardEvent &e) {
switch (e.keysym.sym) {
- case SDLK_UP:
- focus.StopUp();
+ case SDLK_w:
+ target.StopUp();
break;
- case SDLK_DOWN:
- focus.StopDown();
+ case SDLK_s:
+ target.StopDown();
break;
- case SDLK_LEFT:
- focus.StopLeft();
+ case SDLK_a:
+ ctrl.StopLeft();
+ target.StopLeft();
break;
- case SDLK_RIGHT:
- focus.StopRight();
+ case SDLK_d:
+ ctrl.StopRight();
+ target.StopRight();
+ break;
+ case SDLK_SPACE:
+ ctrl.StopJump();
break;
default:
break;
void Application::Update(int dt) {
const float delta = dt / 1e3;
+ ctrl.Update(delta);
+ target.Update(delta);
+ focus = ctrl.Controlling()
+ ? ctrl.Controlled().bounds.Center()
+ : target.Pos();
cam.Update(delta);
- world.Update(dt);
- focus.Update(delta);
+ for (int i = 0; i < dt; ++i) {
+ world.Update(1e-3);
+ }
}
canvas.SetColor(entityColor);
for (const Entity &e : world.Entities()) {
- const Vector<float> pos(e.Bounds().Left(), e.Bounds().Top());
- const Vector<float> size(e.Bounds().Size());
+ const Vector<float> pos(e.bounds.Left(), e.bounds.Top());
+ const Vector<float> size(e.bounds.Size());
canvas.OutlineRect(cam.ToScreen(pos), cam.ToScale(size));
}
}
void Application::RenderUI() {
constexpr Color outlineColor(0x00, 0x00, 0xFA);
- constexpr Color focusColor(0xFA, 0xFA, 0x00);
+ constexpr Color targetColor(0xFA, 0xFA, 0x00);
canvas.SetColor(outlineColor);
canvas.Grid(cam.ToScreen(Vector<int>(0, 0)), cam.ToScale(world.Size()), cam.ToScale(Vector<float>(1, 1)));
- canvas.SetColor(focusColor);
- canvas.Cross(cam.ToScreen(focus.Pos()), 15);
+ canvas.SetColor(targetColor);
+ canvas.Cross(cam.ToScreen(target.Pos()), 15);
}
}
#ifndef ORBI_APPLICATION_H_
#define ORBI_APPLICATION_H_
+#include "Controller.h"
#include "../graphics/Camera.h"
#include "../graphics/Moveable.h"
#include "../graphics/Texture.h"
public:
Application(Canvas &, World &, Tileset &);
+ void Control(Entity &);
+ void Relinquish();
+
public:
void Run();
World &world;
Tileset &tiles;
- Moveable<float> focus;
+ Controller ctrl;
+ Vector<float> focus;
+ Moveable<float> target;
Camera cam;
Uint32 last;
--- /dev/null
+#include "Controller.h"
+
+#include "../world/Entity.h"
+#include "../graphics/const.h"
+
+#include <algorithm>
+
+
+namespace orbi {
+
+Controller::Controller(Entity *ent)
+: e(ent)
+, moving(0)
+, moveAcc(3.5)
+, moveTerm(5)
+, jumping(false)
+, jumpVel(-5) {
+
+}
+
+void Controller::Update(float delta) {
+ if (!e) return;
+
+ if (moving) {
+ if (std::abs(e->acc.x) < moveTerm) {
+ e->acc.x = sigma(moving) * moveAcc;
+ } else {
+ e->acc.x = 0;
+ }
+ } else {
+ e->acc.x = sigma(e->vel.x) * -moveAcc;
+ }
+
+ if (jumping && e->onGround) {
+ e->vel.y += jumpVel;
+ jumping = false;
+ }
+}
+
+
+void Controller::MoveLeft() {
+ moving -= 1;
+}
+
+void Controller::StopLeft() {
+ moving += 1;
+}
+
+void Controller::MoveRight() {
+ moving += 1;
+}
+
+void Controller::StopRight() {
+ moving -= 1;
+}
+
+void Controller::StartJump() {
+ jumping = true;
+}
+
+void Controller::StopJump() {
+ jumping = false;
+}
+
+}
--- /dev/null
+#ifndef ORBI_CONTROLLER_H_
+#define ORBI_CONTROLLER_H_
+
+namespace orbi {
+
+class Entity;
+
+class Controller {
+
+public:
+ explicit Controller(Entity * = nullptr);
+
+ void Control(Entity &ent) { e = &ent; }
+ void Relinquish() { e = nullptr; }
+
+ bool Controlling() const { return e; }
+ const Entity &Controlled() const { return *e; }
+
+ void Update(float delta);
+
+public:
+ void MoveLeft();
+ void StopLeft();
+ void MoveRight();
+ void StopRight();
+
+ void StartJump();
+ void StopJump();
+
+private:
+ Entity *e;
+
+ int moving;
+ float moveAcc;
+ float moveTerm;
+
+ bool jumping;
+ float jumpVel;
+
+};
+
+}
+
+#endif
world.SetTile(Vector<int>(9, 6), Tile(0));
Entity e;
- e.Bounds() = AABB(Vector<float>(5, 2), Vector<float>(2, 3));
- world.AddEntity(e);
+ e.bounds = AABB(Vector<float>(5, 0), Vector<float>(2, 3));
+ Entity &player = world.AddEntity(e);
Application app(canv, world, tiles);
+ app.Control(player);
app.Run();
return 0;
void Move(Vector<float> delta) { bounds.Move(delta); }
public:
- AABB &Bounds() { return bounds; }
- const AABB &Bounds() const { return bounds; }
-
-private:
AABB bounds;
Vector<float> vel;
Vector<float> acc;
float mass = 1.0f;
float elast = 0.75f;
+ bool onGround = false;
+
};
}
}
-void World::Update(int delta) {
- for (int i = 0; i < delta; ++i) {
- const float dt = 1e-3;
- for (Entity &e : entities) {
- e.Update(dt, gravity, terminal);
-
- const AABB &b = e.Bounds();
-
- // world bounds collision
- if (b.Top() < 0) e.Move(Vector<float>(0, -b.Top()));
- if (b.Right() > size.x) e.Move(Vector<float>(size.x - b.Right(), 0));
- if (b.Bottom() > size.y) e.Move(Vector<float>(0, size.y - b.Bottom()));
- if (b.Left() < 0) e.Move(Vector<float>(-b.Left(), 0));
-
- const Vector<int> cBegin(b.Left(), b.Top());
- const Vector<int> cEnd(b.Right(), b.Bottom());
-
- Vector<float> topResponse;
- for (Vector<int> pos(cBegin); pos.x < cEnd.x; ++pos.x) {
- if (TileAt(pos).IsSolid()) {
- topResponse = Vector<float>(0, pos.y + 1 - b.Top());
- break;
- }
- }
- Vector<float> bottomResponse;
- for (Vector<int> pos(cBegin.x, cEnd.y); pos.x < cEnd.x; ++pos.x) {
- if (TileAt(pos).IsSolid()) {
- bottomResponse = Vector<float>(0, pos.y - b.Bottom());
- break;
- }
- }
- if (!IsZero(topResponse)) {
- if (IsZero(bottomResponse)) {
- e.Move(topResponse);
+void World::Update(float dt) {
+ for (Entity &e : entities) {
+ e.Update(dt, gravity, terminal);
+ e.onGround = false;
+
+ const AABB &b = e.bounds;
+
+ // world bounds collision
+ if (b.Top() < 0) {
+ e.Move(Vector<float>(0, -b.Top()));
+ e.vel.y = 0;
+ }
+ if (b.Right() > size.x) {
+ e.Move(Vector<float>(size.x - b.Right(), 0));
+ e.vel.x = 0;
+ }
+ if (b.Bottom() > size.y) {
+ e.Move(Vector<float>(0, size.y - b.Bottom()));
+ e.vel.y = 0;
+ e.onGround = true;
+ }
+ if (b.Left() < 0) {
+ e.Move(Vector<float>(-b.Left(), 0));
+ e.vel.x = 0;
+ }
+
+ const Vector<int> cBegin(b.Left(), b.Top());
+ const Vector<int> cEnd(b.Right() + 1, b.Bottom() + 1);
+
+ Vector<float> min;
+ Vector<float> max;
+
+ for (Vector<int> pos(cBegin); pos.y < cEnd.y; ++pos.y) {
+ for (pos.x = cBegin.x; pos.x < cEnd.x; ++pos.x) {
+ if (!TileAt(pos).IsSolid()) continue;
+ const AABB tBounds(pos, Vector<float>(1, 1));
+ Vector<float> pos;
+ Vector<float> norm;
+ Vector<float> depth;
+ if (!e.bounds.Intersects(tBounds, pos, norm, depth)) {
+ continue;
}
- } else if (!IsZero(bottomResponse)) {
- e.Move(bottomResponse);
+ if (depth.x < min.x) min.x = depth.x;
+ if (depth.x > max.x) max.x = depth.x;
+ if (depth.y < min.y) min.y = depth.y;
+ if (depth.y > max.y) max.y = depth.y;
}
+ }
- Vector<float> leftResponse;
- for (Vector<int> pos(cBegin); pos.y < cEnd.y; ++pos.y) {
- if (TileAt(pos).IsSolid()) {
- leftResponse = Vector<float>(pos.x + 1 - b.Left(), 0);
- break;
- }
+ Vector<float> resp;
+ if (min.x != 0) {
+ if (max.x == 0) {
+ resp.x = min.x;
}
- Vector<float> rightResponse;
- for (Vector<int> pos(cEnd.x, cBegin.y); pos.y < cEnd.y; ++pos.y) {
- if (TileAt(pos).IsSolid()) {
- rightResponse = Vector<float>(pos.x - b.Right(), 0);
- break;
- }
+ } else {
+ resp.x = max.x;
+ }
+ if (min.y != 0) {
+ if (max.y == 0) {
+ resp.y = min.y;
}
- if (!IsZero(leftResponse)) {
- if (IsZero(rightResponse)) {
- e.Move(leftResponse);
- }
- } else if (!IsZero(rightResponse)) {
- e.Move(rightResponse);
+ } else {
+ resp.y = max.y;
+ }
+ e.Move(resp);
+ if (resp.x != 0) {
+ e.vel.x = 0;
+ }
+ if (resp.y != 0) {
+ e.vel.y = 0;
+ if (resp.y < 0) {
+ e.onGround = true;
}
}
}
Vector<int> Size() const { return size; }
public:
- void Update(int dt);
+ void Update(float dt);
bool InBounds(Vector<int> pos) const
{ return pos.x > 0 && pos.y > 0 && pos.x < size.x && pos.y < size.y; }