1
0
mirror of https://github.com/rwengine/openrw.git synced 2024-09-15 15:02:34 +02:00

Improved game loop to support game speed

This commit is contained in:
husho 2018-05-21 13:13:09 +03:00
parent 5f85de5bd3
commit 214095ba02
3 changed files with 57 additions and 45 deletions

View File

@ -11,7 +11,7 @@ BasicState::BasicState()
, gameMinute{0} , gameMinute{0}
, padMode{0} , padMode{0}
, timeMS{0} , timeMS{0}
, timeScale{0} , timeScale{1.f}
, timeStep{0} , timeStep{0}
, timeStep_unclipped{0} , timeStep_unclipped{0}
, frameCounter{0} , frameCounter{0}

View File

@ -32,9 +32,8 @@ std::map<GameRenderer::SpecialModel, std::string> kSpecialModels = {
{GameRenderer::Arrow, "arrow.dff"}}; {GameRenderer::Arrow, "arrow.dff"}};
namespace { namespace {
constexpr float kPhysicsTimeStep = 1.0f/30.0f; constexpr float kMaxPhysicsSubSteps = 2;
constexpr float kMaxPhysicsSubSteps = 4; } // namespace
}
#define MOUSE_SENSITIVITY_SCALE 2.5f #define MOUSE_SENSITIVITY_SCALE 2.5f
@ -75,7 +74,8 @@ RWGame::RWGame(Logger& log, int argc, char* argv[])
btIDebugDraw::DBG_DrawConstraintLimits); btIDebugDraw::DBG_DrawConstraintLimits);
debug.setShaderProgram(renderer.worldProg.get()); debug.setShaderProgram(renderer.worldProg.get());
data.loadDynamicObjects((config.getGameDataPath() / "data/object.dat").string()); // FIXME: use path data.loadDynamicObjects((config.getGameDataPath() / "data/object.dat")
.string()); // FIXME: use path
data.loadGXT("text/" + config.getGameLanguage() + ".gxt"); data.loadGXT("text/" + config.getGameLanguage() + ".gxt");
@ -249,9 +249,9 @@ void RWGame::handleCheatInput(char symbol) {
// The iPod / Android version of the game (10th year anniversary) spawns random // The iPod / Android version of the game (10th year anniversary) spawns random
// (?) vehicles instead of always rhino // (?) vehicles instead of always rhino
#ifdef RW_GAME_GTA3_ANNIVERSARY #ifdef RW_GAME_GTA3_ANNIVERSARY
// uint16_t vehicleModel = 110; // @todo Which cars are spawned?! // uint16_t vehicleModel = 110; // @todo Which cars are spawned?!
#else #else
// uint16_t vehicleModel = 122; // uint16_t vehicleModel = 122;
#endif #endif
// @todo Spawn rhino // @todo Spawn rhino
// @todo ShowHelpMessage("CHEAT1"); // III / VC: Inputting most // @todo ShowHelpMessage("CHEAT1"); // III / VC: Inputting most
@ -364,7 +364,9 @@ void RWGame::handleCheatInput(char symbol) {
int RWGame::run() { int RWGame::run() {
namespace chrono = std::chrono; namespace chrono = std::chrono;
auto lastFrame = chrono::steady_clock::now(); auto lastFrame = chrono::steady_clock::now();
float deltaTime = GAME_TIMESTEP;
float accumulatedTime = 0.0f; float accumulatedTime = 0.0f;
// Loop until we run out of states. // Loop until we run out of states.
@ -412,37 +414,48 @@ int RWGame::run() {
} }
RW_PROFILE_END(); RW_PROFILE_END();
auto now = chrono::steady_clock::now(); auto currentFrame = chrono::steady_clock::now();
auto deltaTime = chrono::duration<float>(now - lastFrame).count(); auto frameTime =
lastFrame = now; chrono::duration<float>(currentFrame - lastFrame).count();
lastFrame = currentFrame;
if(!world->isPaused()) { // Clamp frameTime, so we won't freeze completely
accumulatedTime += deltaTime; if (frameTime > 0.1f) {
world->dynamicsWorld->stepSimulation(deltaTime * timescale, kMaxPhysicsSubSteps, kPhysicsTimeStep); frameTime = 0.1f;
} }
accumulatedTime += frameTime;
auto deltaTimeWithTimeScale = deltaTime * world->state->basic.timeScale;
RW_PROFILE_BEGIN("Update"); RW_PROFILE_BEGIN("Update");
while (accumulatedTime >= GAME_TIMESTEP && !world->isPaused()) { while (accumulatedTime >= deltaTime && !world->isPaused()) {
if (!StateManager::currentState()) { if (!StateManager::currentState()) {
break; break;
} }
accumulatedTime -= GAME_TIMESTEP;
RW_PROFILE_BEGIN("physics");
world->dynamicsWorld->stepSimulation(
deltaTimeWithTimeScale, kMaxPhysicsSubSteps, deltaTime);
RW_PROFILE_END();
RW_PROFILE_BEGIN("state"); RW_PROFILE_BEGIN("state");
StateManager::get().tick(GAME_TIMESTEP * timescale); StateManager::get().tick(deltaTimeWithTimeScale);
RW_PROFILE_END(); RW_PROFILE_END();
RW_PROFILE_BEGIN("engine"); RW_PROFILE_BEGIN("engine");
tick(GAME_TIMESTEP * timescale); tick(deltaTimeWithTimeScale);
RW_PROFILE_END(); RW_PROFILE_END();
getState()->swapInputState(); getState()->swapInputState();
accumulatedTime -= deltaTime;
} }
RW_PROFILE_END(); RW_PROFILE_END();
RW_PROFILE_BEGIN("Render"); RW_PROFILE_BEGIN("Render");
RW_PROFILE_BEGIN("engine"); RW_PROFILE_BEGIN("engine");
render(1, deltaTime); render(1, frameTime);
RW_PROFILE_END(); RW_PROFILE_END();
RW_PROFILE_BEGIN("state"); RW_PROFILE_BEGIN("state");
@ -514,7 +527,6 @@ void RWGame::tick(float dt) {
state.text.tick(dt); state.text.tick(dt);
if (vm) { if (vm) {
try { try {
vm->execute(dt); vm->execute(dt);
@ -528,7 +540,7 @@ void RWGame::tick(float dt) {
/// @todo this doesn't make sense as the condition /// @todo this doesn't make sense as the condition
if (state.playerObject) { if (state.playerObject) {
currentCam.frustum.update(currentCam.frustum.projection() * currentCam.frustum.update(currentCam.frustum.projection() *
currentCam.getView()); currentCam.getView());
// Use the current camera position to spawn pedestrians. // Use the current camera position to spawn pedestrians.
world->cleanupTraffic(currentCam); world->cleanupTraffic(currentCam);
// Only create new traffic outside cutscenes // Only create new traffic outside cutscenes
@ -683,7 +695,7 @@ void RWGame::renderDebugPaths(float time) {
} }
// Draw Garage bounds // Draw Garage bounds
for (const auto &garage : state.garages) { for (const auto& garage : state.garages) {
btVector3 minColor(1.f, 0.f, 0.f); btVector3 minColor(1.f, 0.f, 0.f);
btVector3 maxColor(0.f, 1.f, 0.f); btVector3 maxColor(0.f, 1.f, 0.f);
btVector3 min(garage.min.x, garage.min.y, garage.min.z); btVector3 min(garage.min.x, garage.min.y, garage.min.z);
@ -698,7 +710,7 @@ void RWGame::renderDebugPaths(float time) {
} }
// Draw vehicle generators // Draw vehicle generators
for (const auto &generator : state.vehicleGenerators) { for (const auto& generator : state.vehicleGenerators) {
btVector3 color(1.f, 0.f, 0.f); btVector3 color(1.f, 0.f, 0.f);
btVector3 position(generator.position.x, generator.position.y, btVector3 position(generator.position.x, generator.position.y,
generator.position.z); generator.position.z);
@ -808,23 +820,25 @@ void RWGame::renderProfile() {
ti.font = 2; ti.font = 2;
ti.size = lineHeight - 2.f; ti.size = lineHeight - 2.f;
ti.baseColour = glm::u8vec3(255); ti.baseColour = glm::u8vec3(255);
std::function<void(const perf::ProfileEntry&, int)> renderEntry = [&]( std::function<void(const perf::ProfileEntry&, int)> renderEntry =
const perf::ProfileEntry& entry, int depth) { [&](const perf::ProfileEntry& entry, int depth) {
int g = 0; int g = 0;
for (auto& event : entry.childProfiles) { for (auto& event : entry.childProfiles) {
auto duration = event.end - event.start; auto duration = event.end - event.start;
float y = 60.f + (depth * (lineHeight + 5.f)); float y = 60.f + (depth * (lineHeight + 5.f));
renderer.drawColour( renderer.drawColour(
perf_colours[(std::hash<std::string>()(entry.label) * (g++)) % perf_colours[(std::hash<std::string>()(entry.label) *
perf_colours.size()], (g++)) %
{xscale * event.start, y, xscale * duration, lineHeight}); perf_colours.size()],
ti.screenPosition.x = xscale * (event.start); {xscale * event.start, y, xscale * duration, lineHeight});
ti.screenPosition.y = y + 2.f; ti.screenPosition.x = xscale * (event.start);
ti.text = GameStringUtil::fromString(event.label + " " + std::to_string(duration) + " us "); ti.screenPosition.y = y + 2.f;
renderer.text.renderText(ti); ti.text = GameStringUtil::fromString(
renderEntry(event, depth + 1); event.label + " " + std::to_string(duration) + " us ");
} renderer.text.renderText(ti);
}; renderEntry(event, depth + 1);
}
};
renderEntry(frame, 0); renderEntry(frame, 0);
ti.screenPosition = glm::vec2(xscale * (16000), 40.f); ti.screenPosition = glm::vec2(xscale * (16000), 40.f);
ti.text = GameStringUtil::fromString(".16 ms"); ti.text = GameStringUtil::fromString(".16 ms");
@ -845,10 +859,10 @@ void RWGame::globalKeyEvent(const SDL_Event& event) {
world->offsetGameTime(30); world->offsetGameTime(30);
break; break;
case SDLK_9: case SDLK_9:
timescale *= 0.5f; world->state->basic.timeScale *= 0.5f;
break; break;
case SDLK_0: case SDLK_0:
timescale *= 2.0f; world->state->basic.timeScale *= 2.0f;
break; break;
case SDLK_F1: case SDLK_F1:
toggle_debug(DebugViewMode::General); toggle_debug(DebugViewMode::General);

View File

@ -3,7 +3,7 @@
#include <chrono> #include <chrono>
//FIXME: should be in rwengine, deeply hidden // FIXME: should be in rwengine, deeply hidden
#include <BulletDynamics/Dynamics/btDiscreteDynamicsWorld.h> #include <BulletDynamics/Dynamics/btDiscreteDynamicsWorld.h>
#include <engine/GameData.hpp> #include <engine/GameData.hpp>
@ -47,8 +47,6 @@ class RWGame : public GameBase {
std::string cheatInputWindow = std::string(32, ' '); std::string cheatInputWindow = std::string(32, ' ');
float timescale = 1.f;
public: public:
RWGame(Logger& log, int argc, char* argv[]); RWGame(Logger& log, int argc, char* argv[]);
~RWGame() override; ~RWGame() override;
@ -76,7 +74,7 @@ public:
return renderer; return renderer;
} }
ScriptMachine *getScriptVM() const { ScriptMachine* getScriptVM() const {
return vm.get(); return vm.get();
} }