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 | ||
