How to create a time slow down effect for everything but my player?

Hello again fellow devs !

I have a question, I would like to make a « time slow down » effect where everything but my player is slowed down.

That means I need the physics of my player to remain the same (momentum, gravity, speed etc).
While everything else will have it divided by a certain value.

I was thinking of using Time.timeScale for this.

I would set the timeScale to, say, 0.25f, and then divide everything for my player by Time.timeScale (so 0.25f in this case) to compensate.
Is that the way to go ?

Or do you have a better, safer, efficient, whatever way of doing it ?

Thank you !

Pretty sure the poster above me is a chat bot.

In any case this is one of those situations where introducing your own time-scales might be needed. Probably as simple as a static class with some properties, and using that as an API layer on-top of the Time API.

Well that’s a new one. What in the world would make you think I’m a bot ? oO

Anyway could you please detail your answer ? I’m not sure I understand what you mean ?

Thanks for answering though !

The post before mine was deleted, which was that of a chat GPT esque bot.

Pretty much I’m just saying that you make your own additional layer in between your game and Unity’s time scale, so you can have multiple time-scales, and affect them individually. It’s just a case of Time.delta * MyTimeScale, etc etc, and different parts of your game use different separate time-scales.

Maybe taking a look into multi-scene physics might be worth a look for your use case. Separate out the character into an extra scene to have it simulated in normal speed there. Though it gets tricky if you want your character to interact with the slow scene.

Link:
https://learn.unity.com/tutorial/multi-scene-physics#
https://docs.unity3d.com/Manual/physics-multi-scene.html

You can control the speed of the physics simulation in each scene by disabling auto simulation and advancing it manually.

Hey, so I started trying to do what you said and basically I have this :

// Set the timeScale and the Time.fixedDeltaTime on button press

private void Update() {
      if (RInput.LT.down) {
        Time.timeScale = 1f;
        Time.fixedDeltaTime = 1f / 60f * Time.timeScale;
      }
      if (RInput.RT.down) {
        Time.timeScale = 0.01f;
        Time.fixedDeltaTime = 1f / 60f * Time.timeScale;
      }
    }

// For the player (that should not be slowed down), everywhere I apply velocity, divide the value by Time.timeScale to compensate.

That kinda works, but for a TimeScale of 0.001 for example, it becomes very jittery for some reason.

Am I doing it wrong ?

You’re setting fixedDeltaTime to an extremely small number is why:

The expression 1f / 60f * 0.001f becomes about .0000167.

Try it yourself on a calculator.

When you feed ultra-micro-tiny or huge numbers into existing systems like physics, they become unstable.

Keep everything within one (or perhaps two) orders of magnitude of 1.0 and things will be much happier, eg, 0.10f to 10.0f for scale factors like this. This is just how floating point numbers work.

Floating (float) point imprecision:

Never test floating point (float) quantities for equality / inequality. Here’s why:

https://starmanta.gitbooks.io/unitytipsredux/content/floating-point.html

https://discussions.unity.com/t/851400/4

https://discussions.unity.com/t/843503/4

“Think of [floating point] as JPEG of numbers.” - orionsyndrome on the Unity3D Forums

Yeah I had a hunch that that would be the culprit. I thought I was fine under a magnitude of 10E-6 though :confused:

Then how can I do this ? How can I slow time a thousand times for everything but the player ?

So that everything is 1000 times slower but the player remains the same ?

Did I misunderstand what spiney199 said earlier ?

Or is “reducing the timeScale and then increasing everything else for the player, to compensate” is indeed the right way somehow ?

If you just want to have everything but the player affected by time, why not have all the players code / animator etc, using unscaled time. Maybe im missing something here?

Instead of modifying Time.timeScale directly, you can keep your own variable for timescale and multiply by that. So that way the engine still runs at the default timescale, but the objects you want to slow down are multiplied by YOUR timescale variable.

I think what Spiney was referring to is that you can always add another multiplier to Time.timeScale or even not use Time.timeScale at all (use your own custom value). However, this is only an easy solution for non-physics simulations.

For physics it gets tricky. If your object moves only due to some force you add every frame then you could do it. Though that would only scale that one force.

In general it depends on how your world is working (Physics or Animation).

Physics) If your player is a physics object and thus is moved by the physics engine then you CAN NOT have it simulate slower than the rest of the physics world.

What you can do is lots of shenanegans with taking the player out of the simulation or adding/reducing forces + adding/removing drag to make it look like it’s moving slower.

You can also do what I suggested above: Separate out the character into an extra scene to have it simulated in normal speed there. Though it gets tricky if you want your character to interact with the slow scene (see shenanegans above).

Links:
https://learn.unity.com/tutorial/multi-scene-physics#
https://docs.unity3d.com/Manual/physics-multi-scene.html
You can control the speed of the physics simulation in each scene by disabling auto simulation and advancing it manually.

It’s also important to understand what Time.fixedDeltaTime is. From your code I can’t really tell if you are aware (apologies if you already are). There is lot’s of confusion going around concerning it.

9466769--1330220--upload_2023-11-12_20-4-0.png

It controls how often the physics simulation will update (aka how often the FixedUpdate() is called per time interval). It has no influence on how fast physics objects will move.

As Kurt already mentioned the simulation starts to fall apart for extreme values. Maybe you could try counteract this with increasing the precision settings in the physics section, but even if that works it comes at a price (performance) and may not help. Also the time interval precision of physics steps seems to be float (at least in the API). So you will definitely hit a limit there.

Test script I like to hand out for fixed update testing. Try it on a falling physics object. Toggle “SmallFixedDeltaTime”.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class FixedDeltaTime : MonoBehaviour
{
    public bool SlowMotion = true;
    public bool SmallFixedDeltaTime = false;

    int fixedUpdateCountInFrame;

    void Update()
    {
        Time.timeScale = SlowMotion ? 0.1f : 1f;
        Time.fixedDeltaTime = SmallFixedDeltaTime ? 0.001f : 0.02f;

        Debug.Log("Fixed update calls in this frame: " + fixedUpdateCountInFrame);
        fixedUpdateCountInFrame = 0;
    }

    private void FixedUpdate()
    {
        fixedUpdateCountInFrame++;
    }
}

Animation) If your player is controlled by a script then you can simply use Time.deltaTime to scale the movement or use a custom value to scale the speed of the animation.

Ok so I stopped tweaking Time.timeScale and started using my own and it seems to work (at least what I started to work on with it looks promising).

I’m having an issue though :

At many places, I did stuff like this :

private const float timeout = 1f;

private float startTime;

private void Start() {
  startTime = Time.time;
}

private void FixedUpdate() {
  if (Time.time - startTime >= timeout) // Do stuff
}

It appears Time.time keeps incrementing at the rate of Time.FixedDeltaTime * Time.timeScale

And I’m not sure how to make that work with my own timeScale ?

Don’t use absolute time. Instead make it a countdown (a variable you decrease by the amount of time you wish to have passed per update). It makes reasoning about it easier and it’s also easier to pause (simply stop decreasing the countdown). The amount you decrease the countdown by is the value that you apply your custom scale to. Or you scale up the initial time of the counter amd then decrease normally, whichever fits your needs better.

Ah daaamn I did that in the first place and read somewhere that doing so was better so I changed it all. Ok I’m gonna fix that.

Also the concept works ! Acceleration, velocity, everything is correctly timeScaled for everything except the ones I chose not to. It’s so versatile !

Thanks so much you guys !!

Ok so my time slow down effect works perfectly up to over 100000 times which is quite the improvement since that 1000 times that didn’t work before ! I tried 200000 but things start to stop functioning properly (and stop working completely at about 1 million)

While 100000 is more than enough for a convincing Time Stop effect (even the speed of sound would only travel 3.43 mm/s, which is imperceptible in gameplay), I do wonder though :

In the game Shadow the hedgehog it is possible to have a slowdown effect for any value. Like, I tried about 34 billion (yes, with a « b ») and after letting my GameCube run for about 24 hours, I could indeed notice the movement change which was absolutely minuscule, (and swapping to normal also restore the speed etc).

How does that work there then ?

Probably a precision problem - floats vs doubles

Can’t we use doubles in Unity to do stuff like that ?

I suppose not since I use the built in Velocity which uses Vector3 which is filled with floats.