[Bf-blender-cvs] [aae93ae] master: BGE: Improve clock management

Arnaud Degroote noreply at git.blender.org
Sat Dec 12 02:49:23 CET 2015


Commit: aae93ae4c6c13e2b30ba272e24354dd020880868
Author: Arnaud Degroote
Date:   Sat Dec 12 02:37:42 2015 +0100
Branches: master
https://developer.blender.org/rBaae93ae4c6c13e2b30ba272e24354dd020880868

BGE: Improve clock management

This patch improves clock management in BGE, to be able to accelerate /
slow the time, and also to finely synchronize clock with external
engines. Several new python functions have been added and existence ones
have been improved for that purpose. Now we have:

- getClockTime(): Get the current BGE render time, in seconds. The BGE
render time is the simulation time corresponding to the next scene that
will be rendered.

- getFrameTime(): Get the current BGE frame time, in seconds. The BGE
frame time is the simulation time corresponding to the current call of
the logic system. Generally speaking, it is what the user is interested
in.

- getRealTime(): Get the number of real (system-clock) seconds elapsed
since the beginning of the simulation.

- getTimeScale(): Get the time multiplier between real-time and
simulation time. The default value is 1.0. A value greater than 1.0
means that the simulation is going faster than real-time, a value lower
than 1.0 means that the simulation is going slower than real-time.

- setTimeScale(time_scale): Set the time multiplier between real-time
and simulation time. A value greater than 1.0 means that the simulation
is going faster than real-time, a value lower than 1.0 means that the
simulation is going slower than real-time. Note that a too large value
may lead to some physics instabilities.

- getUseExternalClock(): Get if the BGE use the inner BGE clock, or rely
or on an external clock. The default is to use the inner BGE clock.

- setUseExternalClock(use_external_clock): Set if the BGE use the inner
BGE clock, or rely or on an external clock. If the user selects the use
of an external clock, he should call regularly the setClockTime method.

- setClockTime(new_time): Set the next value of the simulation clock. It
is preferable to use this method from a custom main function in python,
as calling it in the logic block can easily lead to a blocked system (if
the time does not advance enough to run at least the next logic step).

Rationale are described more precisely in the thread
http://lists.blender.org/pipermail/bf-gamedev/2013-November/000165.html.

See also T37640

Reviewers: sybren, panzergame, #game_engine, lordloki, moguri

Reviewed By: sybren, panzergame, #game_engine, lordloki, moguri

Subscribers: moguri, hg1, sybren, panzergame, dfelinto, lordloki

Projects: #game_engine

Maniphest Tasks: T37640

Differential Revision: https://developer.blender.org/D728

===================================================================

M	doc/python_api/rst/bge.logic.rst
M	source/gameengine/Ketsji/KX_KetsjiEngine.cpp
M	source/gameengine/Ketsji/KX_KetsjiEngine.h
M	source/gameengine/Ketsji/KX_PythonInit.cpp

===================================================================

diff --git a/doc/python_api/rst/bge.logic.rst b/doc/python_api/rst/bge.logic.rst
index b119bdd..3f35901 100644
--- a/doc/python_api/rst/bge.logic.rst
+++ b/doc/python_api/rst/bge.logic.rst
@@ -378,6 +378,76 @@ General functions
 
    Render next frame (if Python has control)
 
+**********************
+Time related functions
+**********************
+
+.. function:: getClockTime()
+
+    Get the current BGE render time, in seconds. The BGE render time is the
+    simulation time corresponding to the next scene that will be rendered.
+
+    :rtype: double
+
+.. function:: getFrameTime()
+
+    Get the current BGE frame time, in seconds. The BGE frame time is the
+    simulation time corresponding to the current call of the logic system.
+    Generally speaking, it is what the user is interested in.
+
+    :rtype: double
+
+.. function:: getRealTime()
+
+    Get the number of real (system-clock) seconds elapsed since the beginning
+    of the simulation.
+
+    :rtype: double
+
+.. function:: getTimeScale()
+
+    Get the time multiplier between real-time and simulation time. The default
+    value is 1.0. A value greater than 1.0 means that the simulation is going
+    faster than real-time, a value lower than 1.0 means that the simulation is
+    going slower than real-time.
+
+    :rtype: double
+
+.. function:: setTimeScale(time_scale)
+
+    Set the time multiplier between real-time and simulation time. A value
+    greater than 1.0 means that the simulation is going faster than real-time,
+    a value lower than 1.0 means that the simulation is going slower than
+    real-time. Note that a too large value may lead to some physics
+    instabilities.
+
+    :arg time_scale: The new time multiplier.
+
+.. function:: getUseExternalClock()
+
+    Get if the BGE use the inner BGE clock, or rely or on an external
+    clock. The default is to use the inner BGE clock.
+
+    :rtype: bool
+
+.. function:: setUseExternalClock(use_external_clock)
+
+    Set if the BGE use the inner BGE clock, or rely or on an external
+    clock. If the user selects the use of an external clock, he should call
+    regularly the setClockTime method.
+
+    :arg use_external_clock: the new setting
+
+.. function:: setClockTime(new_time)
+
+    Set the next value of the simulation clock. It is preferable to use this
+    method from a custom main function in python, as calling it in the logic
+    block can easily lead to a blocked system (if the time does not advance
+    enough to run at least the next logic step).
+
+    :arg new_time: the next value of the BGE clock (in second).
+    
+
 *****************
 Utility functions
 *****************
diff --git a/source/gameengine/Ketsji/KX_KetsjiEngine.cpp b/source/gameengine/Ketsji/KX_KetsjiEngine.cpp
index 5f36a98..fa924f7 100644
--- a/source/gameengine/Ketsji/KX_KetsjiEngine.cpp
+++ b/source/gameengine/Ketsji/KX_KetsjiEngine.cpp
@@ -128,6 +128,7 @@ KX_KetsjiEngine::KX_KetsjiEngine(KX_ISystem* system)
 	m_bInitialized(false),
 	m_activecam(0),
 	m_bFixedTime(false),
+	m_useExternalClock(false),
 	
 	m_firstframe(true),
 	
@@ -135,6 +136,8 @@ KX_KetsjiEngine::KX_KetsjiEngine(KX_ISystem* system)
 	m_clockTime(0.f),
 	m_previousClockTime(0.f),
 	m_previousAnimTime(0.f),
+	m_timescale(1.0f),
+	m_previousRealTime(0.0f),
 
 
 	m_exitcode(KX_EXIT_REQUEST_NO_REQUEST),
@@ -411,6 +414,7 @@ void KX_KetsjiEngine::StartEngine(bool clearIpo)
 	m_clockTime = m_kxsystem->GetTimeInSeconds();
 	m_frameTime = m_kxsystem->GetTimeInSeconds();
 	m_previousClockTime = m_kxsystem->GetTimeInSeconds();
+	m_previousRealTime = m_kxsystem->GetTimeInSeconds();
 
 	m_firstframe = true;
 	m_bInitialized = true;
@@ -554,7 +558,7 @@ void KX_KetsjiEngine::EndFrame()
 
 bool KX_KetsjiEngine::NextFrame()
 {
-	double timestep = 1.0/m_ticrate;
+	double timestep =  m_timescale / m_ticrate;
 	double framestep = timestep;
 	//	static hidden::Clock sClock;
 
@@ -563,12 +567,43 @@ bool KX_KetsjiEngine::NextFrame()
 	//float dt = sClock.getTimeMicroseconds() * 0.000001f;
 	//sClock.reset();
 
-	if (m_bFixedTime) {
-		m_clockTime += timestep;
-	}
-	else {
-		// m_clockTime += dt;
-		m_clockTime = m_kxsystem->GetTimeInSeconds();
+	/*
+	 * Clock advancement. There is basically three case:
+	 *   - m_useExternalClock is true, the user is responsible to advance the time
+	 *   manually using setClockTime, so here, we do not do anything.
+	 *   - m_useExternalClock is false, m_bFixedTime is true, we advance for one
+	 *   timestep, which already handle the time scaling parameter
+	 *   - m_useExternalClock is false, m_bFixedTime is false, we consider how much
+	 *   time has elapsed since last call and we scale this time by the time
+	 *   scaling parameter. If m_timescale is 1.0 (default value), the clock
+	 *   corresponds to the computer clock.
+	 *
+	 * Once clockTime has been computed, we will compute how many logic frames
+	 * will be executed before the next rendering phase (which will occur at "clockTime").
+	 * The game time elapsing between two logic frames (called framestep)
+	 * depends on several variables:
+	 *   - ticrate 
+	 *   - max_physic_frame
+	 *   - max_logic_frame
+	 * XXX The logic over computation framestep is definitively not clear (and
+	 * I'm not even sure it is correct). If needed frame is strictly greater
+	 * than max_physics_frame, we are doing a jump in game time, but keeping
+	 * framestep = 1 / ticrate, while if frames is greater than
+	 * max_logic_frame, we increase framestep.
+	 *
+	 * XXX render.fps is not considred anywhere.
+	 */
+	if (!m_useExternalClock) {
+		if (m_bFixedTime) {
+			m_clockTime += timestep;
+		}
+		else {
+			double current_time = m_kxsystem->GetTimeInSeconds();
+			double dt = current_time - m_previousRealTime;
+			m_previousRealTime = current_time;
+			// m_clockTime += dt;
+			m_clockTime += dt * m_timescale;
+		}
 	}
 	
 	double deltatime = m_clockTime - m_frameTime;
@@ -579,16 +614,14 @@ bool KX_KetsjiEngine::NextFrame()
 		return false;
 	}
 
-
 	// Compute the number of logic frames to do each update (fixed tic bricks)
-	int frames =int(deltatime*m_ticrate+1e-6);
+	int frames = int(deltatime * m_ticrate / m_timescale + 1e-6);
 //	if (frames>1)
 //		printf("****************************************");
 //	printf("dt = %f, deltatime = %f, frames = %d\n",dt, deltatime,frames);
 	
 //	if (!frames)
 //		PIL_sleep_ms(1);
-	
 	KX_SceneList::iterator sceneit;
 	
 	if (frames>m_maxPhysicsFrame)
@@ -1756,6 +1789,10 @@ void KX_KetsjiEngine::SetUseFixedTime(bool bUseFixedTime)
 	m_bFixedTime = bUseFixedTime;
 }
 
+void KX_KetsjiEngine::SetUseExternalClock(bool useExternalClock)
+{
+	m_useExternalClock = useExternalClock;
+}
 
 void	KX_KetsjiEngine::SetAnimRecordMode(bool animation_record, int startFrame)
 {
@@ -1783,6 +1820,11 @@ bool KX_KetsjiEngine::GetUseFixedTime(void) const
 	return m_bFixedTime;
 }
 
+bool KX_KetsjiEngine::GetUseExternalClock(void) const
+{
+	return m_useExternalClock;
+}
+
 double KX_KetsjiEngine::GetSuspendedDelta()
 {
 	return m_suspendeddelta;
@@ -1798,6 +1840,16 @@ void KX_KetsjiEngine::SetTicRate(double ticrate)
 	m_ticrate = ticrate;
 }
 
+double KX_KetsjiEngine::GetTimeScale() const
+{
+	return m_timescale;
+}
+
+void KX_KetsjiEngine::SetTimeScale(double timescale)
+{
+	m_timescale = timescale;
+}
+
 int KX_KetsjiEngine::GetMaxLogicFrame()
 {
 	return m_maxLogicFrame;
@@ -1838,6 +1890,11 @@ double KX_KetsjiEngine::GetClockTime(void) const
 	return m_clockTime;
 }
 
+void KX_KetsjiEngine::SetClockTime(double externalClockTime)
+{
+	m_clockTime = externalClockTime;
+}
+
 double KX_KetsjiEngine::GetFrameTime(void) const
 {
 	return m_frameTime;
diff --git a/source/gameengine/Ketsji/KX_KetsjiEngine.h b/source/gameengine/Ketsji/KX_KetsjiEngine.h
index ec855be..3b8cec2 100644
--- a/source/gameengine/Ketsji/KX_KetsjiEngine.h
+++ b/source/gameengine/Ketsji/KX_KetsjiEngine.h
@@ -103,16 +103,19 @@ private:
 	bool				m_bInitialized;
 	int					m_activecam;
 	bool				m_bFixedTime;
+	bool				m_useExternalClock;
 	
 	
 	bool				m_firstframe;
 	int					m_currentFrame;
 
-	double				m_frameTime;//discrete timestamp of the 'game logic frame'
-	double				m_clockTime;//current time
-	double				m_previousClockTime;//previous clock time
-	double				m_previousAnimTime; //the last time animations were updated
+	double				m_frameTime; // current logic game time
+	double				m_clockTime; // game time for the next rendering step
+	double				m_previousClockTime; // game time of the previous rendering step
+	double				m_previousAnimTime; //game time when the animations were last updated
 	double				m_remainingTime;
+	double				m_timescale; // time scaling parameter. if > 1.0, time goes faster than real-time. If < 1.0, times goes slower than real-time.
+	double				m_previousRealTime;
 
 	static int				m_maxLogicFrame;	/* maximum number of consecutive logic frame */
 	static int				m_maxPhysicsFrame;	/* maximum number of consecutive physics frame */
@@ -297,15 +300,37 @@ public:
 	bool GetUseFixedTime(void) const;
 
 	/**
-	 * Returns current render frame clock time
+	 * Sets if the BGE relies on a external clock or its own internal clock
+	 */
+	void SetUseExternalClock(bool bUseExternalClock);
+
+	/**
+	 * Returns if we rely on an external clock
+	 * \return Current setting
+	 */
+	bool GetUseExternalClock(void) const;
+
+	/**
+	 * Returns next render frame game time
 	 */
 	double GetClockTime(void) const;
+
+	/**
+	 * Set the next render frame game time. It will impact also frame time, as
+	 * this one is derived from clocktime
+	 */
+	void SetClockTime(double externalClockTime);
+
 	/**
-	 * Returns current logic frame clock time
+	 * Returns current logic frame game time
 	 */
 	double GetFrameTime(void) const;
 
+	/**
+	 * Returns the real (system) time
+	 */
 	double GetRealTime(void) const;
+
 	/**
 	 * Returns the difference between the local time of the scene (when it
 	 * was running and not suspended) and the "curtime"
@@ -361,6 +386,16 @@ public:
 	 */
 	static double GetAverageFrameRate();
 
+	/**
+	 * Gets the time scale multiplier 
+	 */
+	double GetTimeScale() const;
+
+	/**
+	 * Sets the time scale multiplier
+	 */
+	void SetTimeScale(double scale);
+
 	static void SetExitKey(short key);
 
 	static 

@@ Diff output truncated at 10240 characters. @@




More information about the Bf-blender-cvs mailing list