Use ticks (at the physics frame rate) for time measurement, which

avoids potential floating point errors (in networking).
This commit is contained in:
hiker 2018-02-21 22:18:45 +11:00
parent 208c5eb6d8
commit 27b5409487
15 changed files with 112 additions and 88 deletions

View File

@ -179,6 +179,7 @@ public:
void load (const std::string &filename);
const std::string &getMainMenuPicture(int n);
const std::string &getBackgroundPicture(int n);
void getAllScores(std::vector<int> *all_scores, int num_karts);
// ------------------------------------------------------------------------
/** Returns the default kart properties for each kart. */

View File

@ -167,8 +167,8 @@ void Camera::setupCamera()
m_aspect = (float)((float)(m_viewport.getWidth()) / (float)(m_viewport.getHeight()));
m_scaling = core::vector2df(
irr_driver->getActualScreenSize().Width / m_viewport.getWidth() ,
irr_driver->getActualScreenSize().Height / m_viewport.getHeight());
float(irr_driver->getActualScreenSize().Width) / m_viewport.getWidth() ,
float(irr_driver->getActualScreenSize().Height) / m_viewport.getHeight());
m_fov = DEGREE_TO_RAD * stk_config->m_camera_fov
[race_manager->getNumLocalPlayers() > 0 ?

View File

@ -464,7 +464,7 @@ void Powerup::hitBonusBox(const Item &item, int add_info)
// Check if rubber ball is the current power up held by the kart. If so,
// reset the bBallCollectTime to 0 before giving new powerup.
if(m_type == PowerupManager::POWERUP_RUBBERBALL)
powerup_manager->setBallCollectTime(0);
powerup_manager->setBallCollectTicks(0);
// Check if two bouncing balls are collected less than getRubberBallTimer()
//seconds apart. If yes, then call getRandomPowerup again. If no, then break.
@ -474,8 +474,8 @@ void Powerup::hitBonusBox(const Item &item, int add_info)
{
new_powerup = powerup_manager->getRandomPowerup(position, &n);
if(new_powerup != PowerupManager::POWERUP_RUBBERBALL ||
( World::getWorld()->getTimeSinceStart() - powerup_manager->getBallCollectTime()) >
RubberBall::getTimeBetweenRubberBalls() )
( World::getWorld()->getTicksSinceStart() - powerup_manager->getBallCollectTicks()) >
RubberBall::getTicksBetweenRubberBalls() )
break;
}
}
@ -486,7 +486,7 @@ void Powerup::hitBonusBox(const Item &item, int add_info)
}
if(new_powerup == PowerupManager::POWERUP_RUBBERBALL)
powerup_manager->setBallCollectTime(World::getWorld()->getTime());
powerup_manager->setBallCollectTicks(World::getWorld()->getTimeTicks());
// Always add a new powerup in ITEM_MODE_NEW (or if the kart
// doesn't have a powerup atm).

View File

@ -113,7 +113,7 @@ private:
float m_all_max_turn_angle[POWERUP_MAX];
/** Last time the bouncing ball was collected */
float m_rubber_ball_collect_time;
int m_rubber_ball_collect_ticks;
public:
/** The mesh for each model (if the powerup has a model), e.g. a switch
@ -165,8 +165,8 @@ public:
float getMaxTurnAngle (int type) const {return m_all_max_turn_angle[type];}
const btVector3&
getExtend (int type) const {return m_all_extends[type];}
float getBallCollectTime() const {return m_rubber_ball_collect_time;}
void setBallCollectTime(float time) {m_rubber_ball_collect_time=time;}
int getBallCollectTicks() const {return m_rubber_ball_collect_ticks;}
void setBallCollectTicks(int ticks) {m_rubber_ball_collect_ticks=ticks;}
};

View File

@ -47,7 +47,7 @@ float RubberBall::m_st_max_height_difference;
float RubberBall::m_st_fast_ping_distance;
float RubberBall::m_st_early_target_factor;
int RubberBall::m_next_id = 0;
float RubberBall::m_time_between_balls;
float RubberBall::m_ticks_between_balls;
// Debug only, so that we can get a feel on how well balls are aiming etc.
@ -272,7 +272,7 @@ void RubberBall::init(const XMLNode &node, scene::IMesh *rubberball)
m_st_max_height_difference = 10.0f;
m_st_fast_ping_distance = 50.0f;
m_st_early_target_factor = 1.0f;
m_time_between_balls = 15;
m_ticks_between_balls = 15 * stk_config->m_physics_fps;
if(!node.get("interval", &m_st_interval))
Log::warn("powerup", "No interval specified for rubber ball.");
@ -306,9 +306,10 @@ void RubberBall::init(const XMLNode &node, scene::IMesh *rubberball)
if(!node.get("early-target-factor", &m_st_early_target_factor))
Log::warn("powerup",
"No early-target-factor specified for rubber ball.");
if(!node.get("time-between-balls", &m_time_between_balls))
if(!node.get("time-between-balls", &m_ticks_between_balls))
Log::warn("powerup",
"No time-between-balls specified for rubber ball.");
m_ticks_between_balls *= stk_config->m_physics_fps;
Flyable::init(node, rubberball, PowerupManager::POWERUP_RUBBERBALL);
} // init

View File

@ -84,7 +84,7 @@ private:
/** Timer before another rubber ball can be picked up. This is to ensure
* that there are not too many rubber balls on the track in races with many
* karts. */
static float m_time_between_balls;
static float m_ticks_between_balls;
/** This factor is used to influence how much the rubber ball should aim
* at its target early. It used the 'distance to center of track' of its
@ -205,7 +205,7 @@ public:
virtual bool updateAndDelete(float dt);
virtual bool hit(AbstractKart* kart, PhysicalObject* obj=NULL);
virtual void setAnimation(AbstractKartAnimation *animation);
static float getTimeBetweenRubberBalls() {return m_time_between_balls;}
static float getTicksBetweenRubberBalls() {return m_ticks_between_balls;}
// ------------------------------------------------------------------------
/** This object does not create an explosion, all affects on
* karts are handled by this hit() function. */

View File

@ -48,7 +48,7 @@ AIBaseController::AIBaseController(AbstractKart *kart)
void AIBaseController::reset()
{
m_stuck = false;
m_collision_times.clear();
m_collision_ticks.clear();
} // reset
//-----------------------------------------------------------------------------
@ -237,12 +237,12 @@ void AIBaseController::crashed(const Material *m)
// the track again if it is stuck (i.e. time for the push back plus
// time for the AI to accelerate and hit the terrain again).
const unsigned int NUM_COLLISION = 3;
const float COLLISION_TIME = 1.5f;
const int COLLISION_TICKS = 3*stk_config->m_physics_fps/2;
float time = World::getWorld()->getTimeSinceStart();
if(m_collision_times.size()==0)
int ticks = World::getWorld()->getTicksSinceStart();
if(m_collision_ticks.size()==0)
{
m_collision_times.push_back(time);
m_collision_ticks.push_back(ticks);
return;
}
@ -252,23 +252,24 @@ void AIBaseController::crashed(const Material *m)
// collisions to happen). The time of 0.2 seconds was experimentally
// found, typically it takes 0.5 seconds for a kart to be pushed back
// from the terrain and accelerate to hit the same terrain again.
if(time - m_collision_times.back() < 0.2f)
if(5 * (ticks - m_collision_ticks.back()) < stk_config->m_physics_fps)
return;
// Remove all outdated entries, i.e. entries that are older than the
// collision time plus 1 second. Older entries must be deleted,
// otherwise a collision that happened (say) 10 seconds ago could
// contribute to a stuck condition.
while(m_collision_times.size()>0 &&
time - m_collision_times[0] > 1.0f+COLLISION_TIME)
m_collision_times.erase(m_collision_times.begin());
while(m_collision_ticks.size()>0 &&
ticks - m_collision_ticks[0] > stk_config->m_physics_fps
+COLLISION_TICKS )
m_collision_ticks.erase(m_collision_ticks.begin());
m_collision_times.push_back(time);
m_collision_ticks.push_back(ticks);
// Now detect if there are enough collision records in the
// specified time interval.
if(time - m_collision_times.front() > COLLISION_TIME
&& m_collision_times.size()>=NUM_COLLISION)
if(ticks - m_collision_ticks.front() > COLLISION_TICKS &&
m_collision_ticks.size()>=NUM_COLLISION )
{
// We can't call m_kart->forceRescue here, since crased() is
// called during physics processing, and forceRescue() removes the

View File

@ -35,7 +35,7 @@ private:
/** Stores the last N times when a collision happened. This is used
* to detect when the AI is stuck, i.e. N collisions happened in
* a certain period of time. */
std::vector<float> m_collision_times;
std::vector<int> m_collision_ticks;
/** A flag that is set during the physics processing to indicate that
* this kart is stuck and needs to be rescued. */

View File

@ -358,7 +358,7 @@ void Kart::reset()
m_has_started = false;
m_bounce_back_time = 0.0f;
m_brake_time = 0.0f;
m_time_last_crash = 0.0f;
m_ticks_last_crash = 0;
m_speed = 0.0f;
m_smoothed_speed = 0.0f;
m_current_lean = 0.0f;
@ -1052,7 +1052,8 @@ void Kart::collectedItem(Item *item, int add_info)
*/
float Kart::getStartupBoost() const
{
float t = World::getWorld()->getTimeSinceStart();
float t = float(World::getWorld()->getTicksSinceStart())
/ stk_config->m_physics_fps;
std::vector<float> startup_times = m_kart_properties->getStartupTime();
for (unsigned int i = 0; i < startup_times.size(); i++)
{
@ -2175,9 +2176,10 @@ void Kart::crashed(const Material *m, const Vec3 &normal)
*/
void Kart::playCrashSFX(const Material* m, AbstractKart *k)
{
if(World::getWorld()->getTimeSinceStart()-m_time_last_crash < 0.5f) return;
int ticks_since_start = World::getWorld()->getTicksSinceStart();
if(ticks_since_start-m_ticks_last_crash < 0.5f) return;
m_time_last_crash = World::getWorld()->getTimeSinceStart();
m_ticks_last_crash = ticks_since_start;
// After a collision disable the engine for a short time so that karts
// can 'bounce back' a bit (without this the engine force will prevent
// karts from bouncing back, they will instead stuck towards the obstable).

View File

@ -223,7 +223,7 @@ protected:
SFXBuffer *m_crash_sounds[CRASH_SOUND_COUNT];
SFXBuffer *m_goo_sound;
SFXBuffer *m_boing_sound;
float m_time_last_crash;
int m_ticks_last_crash;
RaceManager::KartType m_type;
/** To prevent using nitro in too short bursts */

View File

@ -625,26 +625,24 @@ void ThreeStrikesBattle::addKartLife(unsigned int id)
void ThreeStrikesBattle::spawnSpareTireKarts()
{
if (m_spare_tire_karts.empty() ||
getTimeSinceStart() < m_next_sta_spawn_time)
getTicksSinceStart() < m_next_sta_spawn_time)
return;
const float period =
race_manager->getDifficulty() == RaceManager::DIFFICULTY_BEST ? 40.0f :
race_manager->getDifficulty() == RaceManager::DIFFICULTY_HARD ? 30.0f :
race_manager->getDifficulty() == RaceManager::DIFFICULTY_MEDIUM ?
25.0f : 20.0f;
const float inc_factor =
race_manager->getDifficulty() == RaceManager::DIFFICULTY_BEST ? 0.7f :
race_manager->getDifficulty() == RaceManager::DIFFICULTY_HARD ? 0.65f :
race_manager->getDifficulty() == RaceManager::DIFFICULTY_MEDIUM ?
0.6f : 0.55f;
// Spawn spare tire kart when necessary
// The lifespan for sta: inc_factor / period * 1000 / 2
// So in easier mode the sta lasts longer than spawn period
const float lifespan = inc_factor / period * 1000;
m_next_sta_spawn_time = lifespan + (getTimeSinceStart() * inc_factor) +
getTimeSinceStart();
float inc_factor, lifespan;
switch (race_manager->getDifficulty())
{
case RaceManager::DIFFICULTY_BEST: inc_factor = 0.7f; lifespan = 17.5f; break;
case RaceManager::DIFFICULTY_HARD: inc_factor = 0.65f; lifespan = 21.66f; break;
case RaceManager::DIFFICULTY_EASY: inc_factor = 0.6f; lifespan = 24.0f; break;
default: inc_factor = 0.55f; lifespan = 27.5f; break;
}
// Spawn spare tire kart when necessary
m_next_sta_spawn_time = lifespan*stk_config->m_physics_fps
+ getTicksSinceStart() * inc_factor
+ getTicksSinceStart();
int kart_has_few_lives = 0;
for (unsigned int i = 0; i < m_kart_info.size(); i++)
{

View File

@ -307,7 +307,7 @@ void World::reset()
irr_driver->reset();
//Reset the Rubber Ball Collect Time to some negative value.
powerup_manager->setBallCollectTime(-100);
powerup_manager->setBallCollectTicks(-100);
} // reset
//-----------------------------------------------------------------------------

View File

@ -61,8 +61,9 @@ WorldStatus::WorldStatus()
void WorldStatus::reset()
{
m_time = 0.0f;
m_auxiliary_timer = 0.0f;
m_count_up_timer = 0.0f;
m_time_ticks = 0;
m_auxiliary_ticks = 0;
m_count_up_ticks = 0;
m_engines_started = false;
@ -136,6 +137,7 @@ void WorldStatus::startEngines()
void WorldStatus::setClockMode(const ClockType mode, const float initial_time)
{
m_clock_mode = mode;
m_time_ticks = initial_time * stk_config->m_physics_fps;
m_time = initial_time;
} // setClockMode
@ -152,7 +154,7 @@ void WorldStatus::enterRaceOverState()
return;
m_phase = DELAY_FINISH_PHASE;
m_auxiliary_timer = 0.0f;
m_auxiliary_ticks = 0;
} // enterRaceOverState
//-----------------------------------------------------------------------------
@ -188,7 +190,7 @@ void WorldStatus::updateTime(const float dt)
// tilt way too much. A separate setup phase for the first frame
// simplifies this handling
case SETUP_PHASE:
m_auxiliary_timer = 0.0f;
m_auxiliary_ticks= 0;
m_phase = TRACK_INTRO_PHASE;
if (m_play_track_intro_sound)
@ -203,14 +205,14 @@ void WorldStatus::updateTime(const float dt)
return; // Do not increase time
case TRACK_INTRO_PHASE:
m_auxiliary_timer += dt;
m_auxiliary_ticks++;
if (UserConfigParams::m_artist_debug_mode &&
race_manager->getNumberOfKarts() -
race_manager->getNumSpareTireKarts() == 1 &&
race_manager->getTrackName() != "tutorial")
{
m_auxiliary_timer += dt * 6;
m_auxiliary_ticks += 6;
}
// Work around a bug that occurred on linux once:
@ -220,21 +222,22 @@ void WorldStatus::updateTime(const float dt)
// long, we use the aux timer to force the next phase
// after 3.5 seconds.
if (m_track_intro_sound->getStatus() == SFXBase::SFX_PLAYING &&
m_auxiliary_timer < 3.5f)
2*m_auxiliary_ticks < 7*stk_config->m_physics_fps)
return; // Do not increase time
// Wait before ready phase if sounds are disabled
if (!UserConfigParams::m_sfx && m_auxiliary_timer < 3.0f)
if (!UserConfigParams::m_sfx &&
m_auxiliary_ticks < 3*stk_config->m_physics_fps )
return;
if (!m_play_track_intro_sound)
{
startEngines();
if (m_auxiliary_timer < 3.0f)
if (m_auxiliary_ticks < 3*stk_config->m_physics_fps)
return; // Do not increase time
}
m_auxiliary_timer = 0.0f;
m_auxiliary_ticks = 0;
if (m_play_ready_set_go_sounds)
m_prestart_sound->play();
@ -268,7 +271,7 @@ void WorldStatus::updateTime(const float dt)
case READY_PHASE:
startEngines();
if (m_auxiliary_timer > 1.0)
if (m_auxiliary_ticks > stk_config->m_physics_fps)
{
if (m_play_ready_set_go_sounds)
{
@ -278,7 +281,7 @@ void WorldStatus::updateTime(const float dt)
m_phase = SET_PHASE;
}
m_auxiliary_timer += dt;
m_auxiliary_ticks++;
// In artist debug mode, when without opponents, skip the
// ready/set/go counter faster
@ -287,12 +290,12 @@ void WorldStatus::updateTime(const float dt)
race_manager->getNumSpareTireKarts() == 1 &&
race_manager->getTrackName() != "tutorial")
{
m_auxiliary_timer += dt*6;
m_auxiliary_ticks += 6;
}
return; // Do not increase time
case SET_PHASE:
if (m_auxiliary_timer > 2.0)
if (m_auxiliary_ticks > 2*stk_config->m_physics_fps)
{
// set phase is over, go to the next one
m_phase = GO_PHASE;
@ -305,7 +308,7 @@ void WorldStatus::updateTime(const float dt)
onGo();
}
m_auxiliary_timer += dt;
m_auxiliary_ticks++;
// In artist debug mode, when without opponents,
// skip the ready/set/go counter faster
@ -314,24 +317,25 @@ void WorldStatus::updateTime(const float dt)
race_manager->getNumSpareTireKarts() == 1 &&
race_manager->getTrackName() != "tutorial")
{
m_auxiliary_timer += dt*6;
m_auxiliary_ticks += 6;
}
return; // Do not increase time
case GO_PHASE :
if (m_auxiliary_timer>2.5f && music_manager->getCurrentMusic() &&
// 2.5 seconds
if (2*m_auxiliary_ticks>5*stk_config->m_physics_fps &&
music_manager->getCurrentMusic() &&
!music_manager->getCurrentMusic()->isPlaying())
{
music_manager->startMusic();
}
if (m_auxiliary_timer > 3.0f) // how long to display the 'go' message
// how long to display the 'go' message
if (m_auxiliary_ticks > 3 * stk_config->m_physics_fps)
{
m_phase = MUSIC_PHASE;
}
m_auxiliary_timer += dt;
m_auxiliary_ticks++;
// In artist debug mode, when without opponents,
// skip the ready/set/go counter faster
@ -340,7 +344,7 @@ void WorldStatus::updateTime(const float dt)
race_manager->getNumSpareTireKarts() == 1 &&
race_manager->getTrackName() != "tutorial")
{
m_auxiliary_timer += dt*6;
m_auxiliary_ticks += 6;
}
break; // Now the world time starts
@ -352,12 +356,13 @@ void WorldStatus::updateTime(const float dt)
UserConfigParams::m_race_now = false;
}
// how long to display the 'music' message
if (m_auxiliary_timer>stk_config->m_music_credit_time)
if (m_auxiliary_ticks > stk_config->m_music_credit_time
* stk_config->m_physics_fps )
{
m_phase = RACE_PHASE;
}
m_auxiliary_timer += dt;
m_auxiliary_ticks++;
break;
case RACE_PHASE:
// Nothing to do for race phase, switch to delay finish phase
@ -365,10 +370,11 @@ void WorldStatus::updateTime(const float dt)
break;
case DELAY_FINISH_PHASE:
{
m_auxiliary_timer += dt;
m_auxiliary_ticks++;
// Change to next phase if delay is over
if (m_auxiliary_timer > stk_config->m_delay_finish_time)
if (m_auxiliary_ticks > stk_config->m_delay_finish_time
* stk_config->m_physics_fps )
{
m_phase = RESULT_DISPLAY_PHASE;
terminateRace();
@ -396,26 +402,29 @@ void WorldStatus::updateTime(const float dt)
case CLOCK_CHRONO:
if (!device->getTimer()->isStopped())
{
m_time += dt;
m_count_up_timer += dt;
m_time_ticks++;
m_time = float(m_time_ticks) / stk_config->m_physics_fps;
m_count_up_ticks++;
}
break;
case CLOCK_COUNTDOWN:
// stop countdown when race is over
if (m_phase == RESULT_DISPLAY_PHASE || m_phase == FINISH_PHASE)
{
m_time_ticks = 0;
m_time = 0.0f;
m_count_up_timer = 0.0f;
m_count_up_ticks = 0;
break;
}
if (!device->getTimer()->isStopped())
{
m_time -= dt;
m_count_up_timer += dt;
m_time_ticks--;
m_time = float(m_time_ticks) / stk_config->m_physics_fps;
m_count_up_ticks++;
}
if(m_time <= 0.0)
if(m_time_ticks <= 0.0)
{
// event
countdownReachedZero();
@ -445,6 +454,7 @@ void WorldStatus::startReadySetGo()
*/
void WorldStatus::setTime(const float time)
{
m_time_ticks = time * stk_config->m_physics_fps;
m_time = time;
} // setTime

View File

@ -91,6 +91,9 @@ protected:
/** Elasped/remaining time in seconds. */
double m_time;
/** Time in number of ticks (in terms of physics time steps). */
int m_time_ticks;
/** If the start race should be played, disabled in cutscenes. */
bool m_play_racestart_sounds;
@ -115,15 +118,17 @@ private:
/**
* Remember previous phase e.g. on pause
*/
Phase m_previous_phase;
Phase m_previous_phase;
/**
* Counts time during the initial 'ready/set/go' phase, or at the end of a race.
* This timer basically kicks in when we need to calculate non-race time like labels.
*/
float m_auxiliary_timer;
int m_auxiliary_ticks;
float m_count_up_timer;
/** Special counter to count ticks since start (in terms of physics
* timestep size). */
int m_count_up_ticks;
bool m_engines_started;
void startEngines();
@ -180,7 +185,12 @@ public:
// ------------------------------------------------------------------------
/** Returns the current race time. */
float getTime() const { return (float)m_time; }
float getTime() const { return (float)m_time; }
// ------------------------------------------------------------------------
/** Returns the current race time in time ticks (i.e. based on the physics
* time step size). */
int getTimeTicks() const { return m_time_ticks; }
// ------------------------------------------------------------------------
/** Will be called to notify your derived class that the clock,
@ -192,8 +202,8 @@ public:
virtual void onGo() {};
// ------------------------------------------------------------------------
/** Get the time since start regardless of which way the clock counts */
float getTimeSinceStart() const { return m_count_up_timer; }
/** Get the ticks since start regardless of which way the clock counts */
int getTicksSinceStart() const { return m_count_up_ticks; }
// ------------------------------------------------------------------------
void setReadyToRace() { m_server_is_ready = true; }

View File

@ -493,7 +493,8 @@ void RaceGUIBase::drawGlobalMusicDescription()
gui::IGUIFont* font = GUIEngine::getFont();
float race_time = World::getWorld()->getTimeSinceStart();
float race_time = World::getWorld()->getTicksSinceStart()
/ stk_config->m_physics_fps;
// ---- Manage pulsing effect
// 3.0 is the duration of ready/set (TODO: don't hardcode)