Game Development Community

Slow Motion Method

by Apurva Amin · in Torque X 2D · 11/02/2007 (8:06 am) · 16 replies

Hi,

I remember reading somewhere about a method that paused the whole scenegraph. Is there something similar to make everything run in slow motion? If not, what would be the easiest way to slow everything down rather than having to keep track of all objects and decrease all of their speeds?

Thanks

#1
11/02/2007 (9:38 am)
I really don't think there's an easy way out here. I thought something like ProcessList.Instance.TickMS = 40; might work. But it doesn't slow anything down, it just make everything more 'jittery'. Do you just want to slow down all moving things (with a velocity)? Or do you really want to slow down everything, like player input, ProcessTick()-based decisions, and GUI updates too?

John K.
#2
11/04/2007 (4:07 pm)
I want to slow down the velocity of all objects. Similar to a bullet time effect.
Thanks
#3
11/04/2007 (8:51 pm)
This may work:

TorqueGameEngine.Instance.GameTimeScale
#4
11/06/2007 (5:18 pm)
Rwillis, great call!!! I added this._engineComponent.GameTimeScale = 0.5f; right after the scene loads.
Everything slows done really smoothly without sacrificing the input response time - very Matrix-like!

John K.
#5
11/09/2007 (3:52 am)
Ooh, nice! Thanks a lot guys.
#6
05/06/2010 (6:39 pm)
is there any way to access this from inside a scene object's component?
#7
05/06/2010 (9:06 pm)
found it

Game.Instance.Engine.GameTimeScale = 0.5f;
#8
05/07/2010 (7:22 am)
@Steve...just FYI, this impacts all objects at the engine level.

In my title I have a TimeManagerComponent that I can use at the SceneObject level to allow for slow motion, no motion, bullet time, etc.

I use this factor in conjunction with the dt argument passed into ProcessTick() to speed up or slow down the SceneObject's perception of the passage of time.

--RB
#9
05/07/2010 (11:18 am)
That sounds better Ron, can you share your component with us?
#10
05/07/2010 (11:19 am)
hmmm.... thats pretty interesting.

being able to isolate specific objects to slow down individually is pretty swank.

but wouldn't I have to modify every component's process tick to take this into account?
#11
05/07/2010 (12:25 pm)
It's a couple of different components. I'll dig them up and assemble them into a post and try to explain the interactions.

My forum post is a bit oversimplified, but it encapsulates the point.

@Steve...to answer your last question:

There's a static class involved that basically allows a scene object to request its own effective time factor.

I did it this way because I did not want to have each ProcessTick method have to be sensitive to, or do a FindComponent on, the time manager component.

Since each component has a reference to its own scene object, you can call the static method like so:
TimeManager.GetLocalTimeFactor(this.SceneObject);

TimeManager.GetLocalTimeFactor() is a public static method, so you don't need an instance of it. It returns a float.

So instead of using dt in my ProcessTick methods, I use:

(TimeManager.GetLocalTimeFactor(this.SceneObject) * dt)

It's a pretty useful feature, I use it for a "freeze" projectile which stops enemies in their tracks when they get hit by the projectile. But it has a configurable time factor. On my freeze projectile, it's zero, but you could set it to 0.5 to slow the enemies to half speed. You can also apply it to a player power-up to double the speed of the main player. It has an effective time as well that says how long the effects last. But I guess it could fairly easily be made to be permanent.

I'll put everything together and post here. I may not be accurately reflecting the names of the classes and their methods. But the gist is the same.

--RB
#12
05/10/2010 (5:39 am)
Hey guys...sorry I didn't post my code here. I was looking through it to prepare for a post, and I realized it tied into a 3rd component...and it started getting really hairy.

Let me summarize it and maybe give some pseudo code and you can get the gist of what it does with out see having to see the actual code.

Basically there are a few things in my game that affect the passage of time. Most game elements are affected (I refer to these as "Time Managed" elements) but some are not (such as the main player).

I have a class called TimeKeeper which has a static data member called TimeKeeper.TimeFactor. It's a float that under normal conditions is set to 1.0, but in a slow motion condition where all time managed components are affected it might be set to 0.5 (or 0.0 in a stop motion condition). Think of this as the global time factor.

The TimeKeeper class also has a Dictionary of local time factors defined thus:

private Dictionary<T2DSceneObject, float> _localTimeFactors;

But more about that later.

In the movement and AI ProcessTick component methods for my NPCs, they use the TimeKeeper.TimeFactor value (indirectly through a method call discussed later) to determine the speed of movement and decision making. It effectively alters their notion of the passage of time on a global scale.

Additionally, I have a LocalTimeFactor component that has a TimeFactor float value and a TimeToLive float value. The TimeToLive (TTL) value indicates how long the local time factor is in effect. So I can make it so that on collision with my "slow motion bullet" the bad guys experience a LocalTimeFactor of 0.5 for 10 seconds.

In the ProcessTick method of the LocalTimeFactor component, I check to see if the TimeToLive is a positive value. If so, Then I call:

TimeKeeper.SetLocalTimeFactor(this.SceneObject, this.TimeFactor);

Where this is the LocalTimeFactor component attached to the scene object. This method call adds the scene object to the TimeKeeper._localTimeFactors dictionary (using SetLocalTimeFactor() as an accessor method).

When TimeToLive is not positive, I call:

TimeKeeper.RemoveLocalTimeFactor(this.SceneObject);

This method call removes SceneObject from the TimeKeeper._localTimeFactors dictionary.

Then in my components various ProcessTick methods, wherever time passage is a factor I call:

TimeKeeper.GetLocalTimeFactor(SceneObject);

The GetLocalTimeFactor() method returns a float and looks for SceneObject in the TimeKeeper._localTimeFactors dictionary. If SceneObject is found, then the value returned is

// Always use the global time factor and "stack it" with the local time factor.
TimeKeeper.TimeFactor * _localTimeFactors[SceneObject];

If SceneObject is not found in the dictionary, then the value returned is:

// Just return the global time factor
TimeKeeper.TimeFactor;

If you look at my previous post, you can see how this is used in conjunction with the ProcessTick() method's dt argument to give the SceneObject's perceived delta in time since the last call to ProcessTick().

So in my case, the values are stacked. There's a global time factor and a local time factor and multiplying them together gives the SceneObject's perception of the passage of time.

I know the actual code might have been more useful, but it includes and ties into other elements of my game code that are not important to this discussion. It's not a very complex system to work out, and I hope the description here allows you enough insight to replicate the process.

If you need any more assistance on this, I'll try to keep active on this thread.

Good luck!
--RB
#13
02/18/2011 (5:36 am)
Has anyone actually worked through on this and tried to replicate Ron's system? In the future I may try to work out something similar only because I've found that my design may call for moments when I need to stop/slow individual objects in the scene.

Still prototyping so we may scrap/change that before I ever get to that point though.
#14
02/18/2011 (6:55 am)
So much for just setting $timescale, eh?

I used to pause TGE games by going:
$timescale = 0;

$timescale = 0.5; // half speed

$timescale = 150; // Super fast.

I don't know if this works in TGB and it probably isn't final production code but it might work for a quick and dirty prototype.

EDIT:
I just checked and $timescale does work in TGB. IIRC it doesn't pause audio but it does work for everything else.
#15
02/18/2011 (7:49 am)
*nods* Ron's approach is nice in that it manages a local and global timescale. So that different objects can move at different rates, so it would be like setting timescale = 0 for objects that become 'frozen' in place, while letting other objects continue to move or so on.

I'm sure I could do something with it when I have the time using Ron's pseudocode and a few cups of coffee. Just figured I'd ask if anyone took initiative since people seemed very interested when this thread was active.
#16
02/18/2011 (9:44 am)
We've only used TorqueGameEngine.Instance.GameTimeScale ... and it's to pause the game. For simply pausing the game I think setting timescale to 0 would probably be about as good as any other way... if you're needing more control over individual objects then Ron's approach looks pretty cool.

As a side note we do some scheduling in FishCraft and I realized there are two different options:
Game.Instance.Engine.RealTimeSchedule
Game.Instance.Engine.GameTimeSchedule

GameTimeSchedule is effected by GameTimeScale which solved a lot of my issues when pausing the game because it also pauses the scheduled task. For more info on implementing check out this older thread: www.garagegames.com/community/forums/viewthread/68706