summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorKelly Rauchenberger <fefferburbia@gmail.com>2015-03-13 11:14:01 -0400
committerKelly Rauchenberger <fefferburbia@gmail.com>2015-03-13 11:14:01 -0400
commitb8d62ce8d2f7c8f38aa9c52ab8d7dd32dc3aba64 (patch)
tree42720ef2eefaf42abc25937e9a2a78cd6998f0f6
parent47d9d7884c57c2c14dd363b4ccb0df1dcbb5375e (diff)
downloadtherapy-b8d62ce8d2f7c8f38aa9c52ab8d7dd32dc3aba64.tar.gz
therapy-b8d62ce8d2f7c8f38aa9c52ab8d7dd32dc3aba64.tar.bz2
therapy-b8d62ce8d2f7c8f38aa9c52ab8d7dd32dc3aba64.zip
Fixed my timestep!
http://gafferongames.com/game-physics/fix-your-timestep/
-rw-r--r--src/components.cpp62
-rw-r--r--src/components.h10
-rw-r--r--src/entity.cpp4
-rw-r--r--src/entity.h4
-rw-r--r--src/game.cpp35
-rw-r--r--src/game.h4
6 files changed, 63 insertions, 56 deletions
diff --git a/src/components.cpp b/src/components.cpp index ad0f501..cf82979 100644 --- a/src/components.cpp +++ b/src/components.cpp
@@ -73,10 +73,10 @@ void PhysicsBodyComponent::receive(Game&, Entity&, const Message& msg)
73{ 73{
74 if (msg.type == Message::Type::walkLeft) 74 if (msg.type == Message::Type::walkLeft)
75 { 75 {
76 velocity.first = -1.5; 76 velocity.first = -90;
77 } else if (msg.type == Message::Type::walkRight) 77 } else if (msg.type == Message::Type::walkRight)
78 { 78 {
79 velocity.first = 1.5; 79 velocity.first = 90;
80 } else if (msg.type == Message::Type::stopWalking) 80 } else if (msg.type == Message::Type::stopWalking)
81 { 81 {
82 velocity.first = 0.0; 82 velocity.first = 0.0;
@@ -89,13 +89,30 @@ void PhysicsBodyComponent::receive(Game&, Entity&, const Message& msg)
89 } 89 }
90} 90}
91 91
92void PhysicsBodyComponent::tick(Game&, Entity& entity) 92void PhysicsBodyComponent::tick(Game&, Entity& entity, double dt)
93{ 93{
94 velocity.first += accel.first; 94 // RK4 integration
95 velocity.second += accel.second; 95 auto a = std::make_pair(velocity.first, velocity.second);
96 auto b = std::make_pair(velocity.first + a.first*dt*0.5, velocity.second + a.second*dt*0.5);
97 auto c = std::make_pair(velocity.first + b.first*dt*0.5, velocity.second + b.second*dt*0.5);
98 auto d = std::make_pair(velocity.first + c.first*dt, velocity.second + c.second*dt);
96 99
97 entity.position.first += velocity.first; 100 double dxxdt = 1.0 / 6.0 * (a.first + 2.0*(b.first + c.first) + d.first);
98 entity.position.second += velocity.second; 101 double dxydt = 1.0 / 6.0 * (a.second + 2.0*(b.second + c.second) + d.second);
102
103 entity.position.first += dxxdt * dt;
104 entity.position.second += dxydt * dt;
105
106 velocity.first += accel.first * dt;
107 velocity.second += accel.second * dt;
108
109 // Terminal velocity
110#define TERMINAL_VELOCITY_X (2 * TILE_WIDTH * FRAMES_PER_SECOND)
111#define TERMINAL_VELOCITY_Y (2 * TILE_HEIGHT * FRAMES_PER_SECOND)
112 if (velocity.first < -TERMINAL_VELOCITY_X) velocity.first = -TERMINAL_VELOCITY_X;
113 if (velocity.first > TERMINAL_VELOCITY_X) velocity.first = TERMINAL_VELOCITY_X;
114 if (velocity.second < -TERMINAL_VELOCITY_Y) velocity.second = -TERMINAL_VELOCITY_Y;
115 if (velocity.second > TERMINAL_VELOCITY_Y) velocity.second = TERMINAL_VELOCITY_Y;
99} 116}
100 117
101void PhysicsBodyComponent::detectCollision(Game& game, Entity& entity, Entity& collider, std::pair<double, double> old_position) 118void PhysicsBodyComponent::detectCollision(Game& game, Entity& entity, Entity& collider, std::pair<double, double> old_position)
@@ -192,9 +209,9 @@ void PlayerSpriteComponent::receive(Game&, Entity&, const Message& msg)
192 209
193PlayerPhysicsComponent::PlayerPhysicsComponent() 210PlayerPhysicsComponent::PlayerPhysicsComponent()
194{ 211{
195 jump_velocity = JUMP_VELOCITY(TILE_HEIGHT*4.5, 0.3*FRAMES_PER_SECOND); 212 jump_velocity = JUMP_VELOCITY(TILE_HEIGHT*4.5, 0.3);
196 jump_gravity = JUMP_GRAVITY(TILE_HEIGHT*4.5, 0.3*FRAMES_PER_SECOND); 213 jump_gravity = JUMP_GRAVITY(TILE_HEIGHT*4.5, 0.3);
197 jump_gravity_short = JUMP_GRAVITY(TILE_HEIGHT*3.5, 0.233*FRAMES_PER_SECOND); 214 jump_gravity_short = JUMP_GRAVITY(TILE_HEIGHT*3.5, 0.233);
198 215
199 accel.second = jump_gravity_short; 216 accel.second = jump_gravity_short;
200} 217}
@@ -203,11 +220,11 @@ void PlayerPhysicsComponent::receive(Game&, Entity& entity, const Message& msg)
203{ 220{
204 if (msg.type == Message::Type::walkLeft) 221 if (msg.type == Message::Type::walkLeft)
205 { 222 {
206 velocity.first = -1.5; 223 velocity.first = -90;
207 direction = -1; 224 direction = -1;
208 } else if (msg.type == Message::Type::walkRight) 225 } else if (msg.type == Message::Type::walkRight)
209 { 226 {
210 velocity.first = 1.5; 227 velocity.first = 90;
211 direction = 1; 228 direction = 1;
212 } else if (msg.type == Message::Type::stopWalking) 229 } else if (msg.type == Message::Type::stopWalking)
213 { 230 {
@@ -255,7 +272,7 @@ void PlayerPhysicsComponent::receive(Game&, Entity& entity, const Message& msg)
255 } 272 }
256} 273}
257 274
258void PlayerPhysicsComponent::tick(Game& game, Entity& entity) 275void PlayerPhysicsComponent::tick(Game& game, Entity& entity, double dt)
259{ 276{
260 // If frozen, do nothing 277 // If frozen, do nothing
261 if (frozen) 278 if (frozen)
@@ -268,10 +285,10 @@ void PlayerPhysicsComponent::tick(Game& game, Entity& entity)
268 { 285 {
269 if (direction < 0) 286 if (direction < 0)
270 { 287 {
271 velocity.first = -1.5; 288 velocity.first = -90;
272 } else if (direction > 0) 289 } else if (direction > 0)
273 { 290 {
274 velocity.first = 1.5; 291 velocity.first = 90;
275 } 292 }
276 } 293 }
277 294
@@ -281,21 +298,10 @@ void PlayerPhysicsComponent::tick(Game& game, Entity& entity)
281 accel.second = jump_gravity_short; 298 accel.second = jump_gravity_short;
282 } 299 }
283 300
284 // Apply acceleration
285 velocity.first += accel.first;
286 velocity.second += accel.second;
287
288 // Terminal velocity
289 if (velocity.first < -16) velocity.first = -16;
290 if (velocity.first > 16) velocity.first = 16;
291 if (velocity.second < -16) velocity.second = -16;
292 if (velocity.second > 16) velocity.second = 16;
293
294 // Do the movement 301 // Do the movement
295 std::pair<double, double> old_position = entity.position; 302 std::pair<double, double> old_position = entity.position;
296 entity.position.first += velocity.first; 303 PhysicsBodyComponent::tick(game, entity, dt);
297 entity.position.second += velocity.second; 304
298
299 // Check for collisions 305 // Check for collisions
300 game.detectCollision(entity, old_position); 306 game.detectCollision(entity, old_position);
301} 307}
diff --git a/src/components.h b/src/components.h index f421529..c66db47 100644 --- a/src/components.h +++ b/src/components.h
@@ -19,10 +19,10 @@ class UserMovementComponent : public Component {
19class PhysicsBodyComponent : public Component { 19class PhysicsBodyComponent : public Component {
20 public: 20 public:
21 void receive(Game& game, Entity& entity, const Message& msg); 21 void receive(Game& game, Entity& entity, const Message& msg);
22 void tick(Game& game, Entity& entity); 22 void tick(Game& game, Entity& entity, double dt);
23 void detectCollision(Game& game, Entity& entity, Entity& collider, std::pair<double, double> old_position); 23 void detectCollision(Game& game, Entity& entity, Entity& collider, std::pair<double, double> old_position);
24 24
25 private: 25 protected:
26 std::pair<double, double> velocity; 26 std::pair<double, double> velocity;
27 std::pair<double, double> accel; 27 std::pair<double, double> accel;
28}; 28};
@@ -40,15 +40,13 @@ class PlayerSpriteComponent : public Component {
40 bool dying = false; 40 bool dying = false;
41}; 41};
42 42
43class PlayerPhysicsComponent : public Component { 43class PlayerPhysicsComponent : public PhysicsBodyComponent {
44 public: 44 public:
45 PlayerPhysicsComponent(); 45 PlayerPhysicsComponent();
46 void tick(Game& game, Entity& entity); 46 void tick(Game& game, Entity& entity, double dt);
47 void receive(Game& game, Entity& entity, const Message& msg); 47 void receive(Game& game, Entity& entity, const Message& msg);
48 48
49 private: 49 private:
50 std::pair<double, double> velocity;
51 std::pair<double, double> accel;
52 double jump_velocity; 50 double jump_velocity;
53 double jump_gravity; 51 double jump_gravity;
54 double jump_gravity_short; 52 double jump_gravity_short;
diff --git a/src/entity.cpp b/src/entity.cpp index 38ddffe..2b6cd7f 100644 --- a/src/entity.cpp +++ b/src/entity.cpp
@@ -13,11 +13,11 @@ void Entity::send(Game& game, const Message& msg)
13 } 13 }
14} 14}
15 15
16void Entity::tick(Game& game) 16void Entity::tick(Game& game, double dt)
17{ 17{
18 for (auto component : components) 18 for (auto component : components)
19 { 19 {
20 component->tick(game, *this); 20 component->tick(game, *this, dt);
21 } 21 }
22} 22}
23 23
diff --git a/src/entity.h b/src/entity.h index 04772e9..e22fd10 100644 --- a/src/entity.h +++ b/src/entity.h
@@ -37,7 +37,7 @@ class Entity {
37 public: 37 public:
38 void addComponent(std::shared_ptr<Component> c); 38 void addComponent(std::shared_ptr<Component> c);
39 void send(Game& game, const Message& msg); 39 void send(Game& game, const Message& msg);
40 void tick(Game& game); 40 void tick(Game& game, double dt);
41 void input(Game& game, int key, int action); 41 void input(Game& game, int key, int action);
42 void render(Game& game, Texture& buffer); 42 void render(Game& game, Texture& buffer);
43 void detectCollision(Game& game, Entity& collider, std::pair<double, double> old_position); 43 void detectCollision(Game& game, Entity& collider, std::pair<double, double> old_position);
@@ -53,7 +53,7 @@ class Component {
53 public: 53 public:
54 virtual void receive(Game&, Entity&, const Message&) {} 54 virtual void receive(Game&, Entity&, const Message&) {}
55 virtual void render(Game&, Entity&, Texture&) {} 55 virtual void render(Game&, Entity&, Texture&) {}
56 virtual void tick(Game&, Entity&) {} 56 virtual void tick(Game&, Entity&, double) {}
57 virtual void input(Game&, Entity&, int, int) {} 57 virtual void input(Game&, Entity&, int, int) {}
58 virtual void detectCollision(Game&, Entity&, Entity&, std::pair<double, double>) {} 58 virtual void detectCollision(Game&, Entity&, Entity&, std::pair<double, double>) {}
59}; 59};
diff --git a/src/game.cpp b/src/game.cpp index b2de5e9..a494c5f 100644 --- a/src/game.cpp +++ b/src/game.cpp
@@ -50,17 +50,14 @@ void Game::execute(GLFWwindow* window)
50 Texture buffer(GAME_WIDTH, GAME_HEIGHT); 50 Texture buffer(GAME_WIDTH, GAME_HEIGHT);
51 51
52 double lastTime = glfwGetTime(); 52 double lastTime = glfwGetTime();
53 int nbFrames = 0; 53 const double dt = 0.01;
54 double accumulator = 0.0;
55
54 while (!(shouldQuit || glfwWindowShouldClose(window))) 56 while (!(shouldQuit || glfwWindowShouldClose(window)))
55 { 57 {
56 double currentTime = glfwGetTime(); 58 double currentTime = glfwGetTime();
57 nbFrames++; 59 double frameTime = currentTime - lastTime;
58 if (currentTime - lastTime >= 1.0) 60 lastTime = currentTime;
59 {
60 printf("%f ms/frame\n", 1000.0/double(nbFrames));
61 nbFrames = 0;
62 lastTime += 1.0;
63 }
64 61
65 // Should we load a new world? 62 // Should we load a new world?
66 if (newWorld) 63 if (newWorld)
@@ -74,23 +71,29 @@ void Game::execute(GLFWwindow* window)
74 glfwPollEvents(); 71 glfwPollEvents();
75 72
76 // Tick! 73 // Tick!
77 for (auto entity : entities) 74 accumulator += frameTime;
75 while (accumulator >= dt)
78 { 76 {
79 entity->tick(*this); 77 for (auto entity : entities)
78 {
79 entity->tick(*this, dt);
80 }
81
82 accumulator -= dt;
80 } 83 }
81 84
82 // Do any scheduled tasks 85 // Do any scheduled tasks
83 for (auto& task : scheduled) 86 for (auto& task : scheduled)
84 { 87 {
85 task.first--; 88 task.first -= frameTime;
86 89
87 if (task.first == 0) 90 if (task.first <= 0)
88 { 91 {
89 task.second(); 92 task.second();
90 } 93 }
91 } 94 }
92 95
93 scheduled.remove_if([] (std::pair<int, std::function<void ()>> value) { return value.first == 0; }); 96 scheduled.remove_if([] (std::pair<double, std::function<void ()>> value) { return value.first <= 0; });
94 97
95 // Do rendering 98 // Do rendering
96 buffer.fill(buffer.entirety(), 0, 0, 0); 99 buffer.fill(buffer.entirety(), 0, 0, 0);
@@ -133,9 +136,9 @@ void Game::saveGame(const Map& map, std::pair<double, double> position)
133 save = {&map, position}; 136 save = {&map, position};
134} 137}
135 138
136void Game::schedule(int frames, std::function<void ()>&& callback) 139void Game::schedule(double time, std::function<void ()>&& callback)
137{ 140{
138 scheduled.emplace_front(frames, callback); 141 scheduled.emplace_front(time, callback);
139} 142}
140 143
141void Game::playerDie(Entity& player, const Map& curMap) 144void Game::playerDie(Entity& player, const Map& curMap)
@@ -145,7 +148,7 @@ void Game::playerDie(Entity& player, const Map& curMap)
145 148
146 playSound("../res/Hit_Hurt5.wav", 0.25); 149 playSound("../res/Hit_Hurt5.wav", 0.25);
147 150
148 schedule(FRAMES_PER_SECOND * 0.75, [&] () { 151 schedule(0.75, [&] () {
149 if (&curMap != save.map) 152 if (&curMap != save.map)
150 { 153 {
151 loadMap(*(save.map)); 154 loadMap(*(save.map));
diff --git a/src/game.h b/src/game.h index 4f2f3df..c7c2625 100644 --- a/src/game.h +++ b/src/game.h
@@ -30,7 +30,7 @@ class Game {
30 void loadMap(const Map& map); 30 void loadMap(const Map& map);
31 void detectCollision(Entity& collider, std::pair<double, double> old_position); 31 void detectCollision(Entity& collider, std::pair<double, double> old_position);
32 void saveGame(const Map& map, std::pair<double, double> position); 32 void saveGame(const Map& map, std::pair<double, double> position);
33 void schedule(int frames, std::function<void ()>&& callback); 33 void schedule(double time, std::function<void ()>&& callback);
34 void playerDie(Entity& player, const Map& curMap); 34 void playerDie(Entity& player, const Map& curMap);
35 35
36 private: 36 private:
@@ -43,7 +43,7 @@ class Game {
43 Map m{"../maps/embarass.txt"}; 43 Map m{"../maps/embarass.txt"};
44 Map m2{"../maps/second.txt"}; 44 Map m2{"../maps/second.txt"};
45 Savefile save; 45 Savefile save;
46 std::list<std::pair<int, std::function<void ()>>> scheduled; 46 std::list<std::pair<double, std::function<void ()>>> scheduled;
47 bool shouldQuit = false; 47 bool shouldQuit = false;
48}; 48};
49 49