diff options
author | Kelly Rauchenberger <fefferburbia@gmail.com> | 2015-03-13 11:14:01 -0400 |
---|---|---|
committer | Kelly Rauchenberger <fefferburbia@gmail.com> | 2015-03-13 11:14:01 -0400 |
commit | b8d62ce8d2f7c8f38aa9c52ab8d7dd32dc3aba64 (patch) | |
tree | 42720ef2eefaf42abc25937e9a2a78cd6998f0f6 /src | |
parent | 47d9d7884c57c2c14dd363b4ccb0df1dcbb5375e (diff) | |
download | therapy-b8d62ce8d2f7c8f38aa9c52ab8d7dd32dc3aba64.tar.gz therapy-b8d62ce8d2f7c8f38aa9c52ab8d7dd32dc3aba64.tar.bz2 therapy-b8d62ce8d2f7c8f38aa9c52ab8d7dd32dc3aba64.zip |
Fixed my timestep!
http://gafferongames.com/game-physics/fix-your-timestep/
Diffstat (limited to 'src')
-rw-r--r-- | src/components.cpp | 62 | ||||
-rw-r--r-- | src/components.h | 10 | ||||
-rw-r--r-- | src/entity.cpp | 4 | ||||
-rw-r--r-- | src/entity.h | 4 | ||||
-rw-r--r-- | src/game.cpp | 35 | ||||
-rw-r--r-- | src/game.h | 4 |
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 | ||
92 | void PhysicsBodyComponent::tick(Game&, Entity& entity) | 92 | void 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 | ||
101 | void PhysicsBodyComponent::detectCollision(Game& game, Entity& entity, Entity& collider, std::pair<double, double> old_position) | 118 | void 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 | ||
193 | PlayerPhysicsComponent::PlayerPhysicsComponent() | 210 | PlayerPhysicsComponent::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 | ||
258 | void PlayerPhysicsComponent::tick(Game& game, Entity& entity) | 275 | void 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 { | |||
19 | class PhysicsBodyComponent : public Component { | 19 | class 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 | ||
43 | class PlayerPhysicsComponent : public Component { | 43 | class 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 | ||
16 | void Entity::tick(Game& game) | 16 | void 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 | ||
136 | void Game::schedule(int frames, std::function<void ()>&& callback) | 139 | void 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 | ||
141 | void Game::playerDie(Entity& player, const Map& curMap) | 144 | void 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 | ||