- Home /
Slow Time for a single Rigid Body
I'm working on a project where the player can slow time on certain objects (or in an area), but I can't figure out how to get this to apply to a rigid body. I need the rigidbody to fall/rotate/everything slower while affected, without messing up the rest of the game (anything not affected by the slow, for example). Other than being slower, it should act normally, and other things should interact with it correctly too (such as allowing the player to jump on it as it goes).
Is this possible, and/or how would it be done?
[Edit]:
My first attempt was to try to personally handle the physics update with MovePosition and MoveRotation, manually applying the rigidbody's velocity and angular velocity, like so:
float dt = Time.fixedDeltaTime * timeScale;
rigidbody.MovePosition( transform.position + rigidbody.velocity * dt );
rigidbody.MoveRotation( transform.rotation * Quaternion.Euler( rigidbody.angularVelocity * dt ) );
I'm not 100% sure that this would be the way to do these calculations, but either way it doesn't work, because I can't get the rigidbody to stop updating itself without setting it to kinematic, but then it won't update things like gravity...any thoughts?
[Edit2]:
Is there any way to make a rigidbody stop moving itself, but still respond to all other collisions (so that I could potentially use MovePosition and MoveRotation, with my own timescale, while still maintaining normal behavior when hitting things)? I can make it kinematic, but then I can't get it to collide.
Something like this, maybe ?
rigidbody.velocity /= slowFactor;
No, that wouldn't work...it would either continuously lower the velocity, or only occur once at the start of the effect, and wouldn't change rotation or gravity or anything like that...I want the rigidbody to act slower while affected.
What about adjusting the drag and angularDrag of the affected objects?
Unfortunately, drag would cause it to slow down over time, which wasn't the intention. The ideal situation would have the rigidbody slow down immediately, then speed back up again after the effect ends.
Answer by Nethara · May 21, 2014 at 10:07 PM
Here's the solution that I'm going with (for now, at least). It'll probably need to get tweaked as I move along, and/or optimized (I don't like all the division I have to do), but it seems to work for now. Thanks to HarshadK for links that helped me get this working. The code in Update() is just for testing.
bool first = true;
[SerializeField]
private float _timeScale = 1;
public float timeScale {
get { return _timeScale; }
set {
if( !first )
{
rigidbody.mass *= timeScale;
rigidbody.velocity /= timeScale;
rigidbody.angularVelocity /= timeScale;
}
first = false;
_timeScale = Mathf.Abs(value);
rigidbody.mass /= timeScale;
rigidbody.velocity *= timeScale;
rigidbody.angularVelocity *= timeScale;
}
}
void Awake()
{
timeScale = _timeScale;
}
void Update() {
timeScale = ( Input.GetButton("Fire2") ? 0.25f : 1 );
}
void FixedUpdate () {
float dt = Time.fixedDeltaTime * timeScale;
rigidbody.velocity += Physics.gravity * dt;
}
I took basically the same approach, except your version is more complete, accounting for mass and such. However, I encountered the same problem: Gravity was slower, but still not slow enough to be proportional, causing objects to fall faster (relatively speaking). I honestly don't understand why, but simply dividing by mass seems to give perfect results.
private void FixedUpdate()
{
float delta = Time.fixedDeltaTime * timeScale;
rigidbody.velocity += Physics.gravity / rigidbody.mass * delta;
}
Nice work around! I like it. You don't really need to worry too much about the efficiency either, as long as you're not changing the timescale constantly.
However, what do you think of this version to improve efficiency slightly?
public float timeScale {
get { return _timeScale; }
set {
value = $$anonymous$$athf.Abs(value);
float tempTimeScaleApplied = value/timeScale;
_timeScale = value;
rigidbody.mass /= tempTimeScaleApplied;
rigidbody.velocity *= tempTimeScaleApplied;
rigidbody.angularVelocity *= tempTimeScaleApplied;
}
}
As long as _timeScale always initializes to 1, it should be fine, no? Otherwise you do need a first still.. but it can still reduce the number of maths overall, right? :D
Edit: Just realized that I don't think either of these handle 0 timeScale too well, heh... might want to put some code in to prevent that too... or to handle it as a special case.
Answer by ludiq · Apr 11, 2015 at 03:35 PM
I know this is an old-ish question, but I managed to solve the problem consistently, if you're interested. I published a plugin in the asset store for time control that handles this case and a lot more: [removed, sorry!]
The whole set of calculations is actually quite complicated to make it work in every case, but I'll try to explain it roughly.
Your first hunch was good. You have to modify the velocity by a certain factor. However, since we're touching velocity and not acceleration, we should divide it by the derivative of the time scale, not the time scale itself. In other words:
velocity *= newTimeScale / lastTimeScale
For example, if the object becomes slowed at half speed (0.5) in a frame, we multiply its velocity by 0.5 / 1 = 0.5
. On the next frame, since it stays at 0.5
, we multiply by 0.5 / 0.5 = 1
, that is, we don't actually change it.
You should also apply similar calculations to angular velocity and drag.
Finally, there are some other considerations to factor in for adding forces and handling collisions, but that's too complicated for a single answer. This should get you started, though :)
Sorry, didn't know there was a rule against advertising assets (perhaps the FAQ should be updated?). I removed the link, but I left the rest of the answer, because that's really the way to do it. :)
As it's a paid asset I have allowed one to be published on another thread as it is relevant. It would be inappropriate to duplicate the link on many threads.
There is a section on the forums where you can post a link to your finished product.
I totally understand, I posted it on the forum already, but seeing as it could solve a lot of these questions, I figured I would post it here. I tried removing all the links in my other answers, but feel free to delete them (apparently I can't?). I still believe the FAQ should be updated, because it isn't clear on this topic.
Hey i know this is an old post, but ive trying to build something similar to what it seems you have built already(not the same) but im trying to have a separate section in my code that changes the physics properties when the object is "slowed" would you be able to give me a lead or point me in the direction of what i would need to slow something down if it gets hit with an impulse? is it just different math with the velocity, mass, gravity, etc? or is there more to it? would you be able to point me in the right direction for this?
Answer by HarshadK · May 21, 2014 at 08:51 AM
Based on your [Edit2], you might want to take a look at Rigidbody.Sleep and Rigidbody Sleeping
Also for overall question, check answers to this question: Decreasing timescale for a single object and this one: How do I speed up the simulation of a rigidbody?
I tried using sleep but it doesn't really work, because sleep arrests any movement and zeroes out velocities, plus it wakes up automatically if anything hits it. I tried it, but it didn't work.
The first link isn't what I need...I know how to change the timestep for things that I control myself...the issue is the rigidbody physics built into Unity.
The second link might be a little more useful...I'll play with it and see if I can make it work how I want.
Answer by screenname_taken · May 21, 2014 at 07:24 AM
Try putting the code that worked in decelerating the rigid body into a trigger with OnTriggerStay(). So it will affect it while in there.
OnTriggerEnter() save the acceleration and then restore it with OnTriggerExit().
The issue isn't applying/removing the effect, I can't get Unity's rigidbody physics to slow down for that one item.
Ah. don't think you can mess with just one object's gravity but i'm not sure. Have you tried messing around with its mass?
The solution I ended up going with (marked as answer above) was to turn off Use Gravity on the rigidbody, apply gravity myself (altered by the timescale), increase mass based on the timescale, and reduce velocity and angular velocity ONLY when the timescale is changed. Seems to work pretty well.
Changing the mass alone wouldn't affect gravity (it's just handled as acceleration, not force).
Answer by tzadik10 · Jun 27, 2017 at 05:29 PM
Yo need multiply the TIme.fixedDeltaTIme by Time.timeScale
Example runnig physics on 50 frames
Time.fixedDeltaTime = 0.02F * Time.timeScale;
or
Time.fixedDeltaTime = (1/50) * Time.timeScale;
https://docs.unity3d.com/ScriptReference/Time-timeScale.html
Your answer
Follow this Question
Related Questions
Slow motion for Physics game 2 Answers
Physics behave weirdly after timeScale change 1 Answer
TimeScaling & Rigidbody problem 0 Answers
How to make Rigidbody.AddForce less delayed in Unity3D? 0 Answers
Avoid bouncing when ground move down 1 Answer