Define custom Time.deltaTime. Any suggestions??

Hi, I usually don’t post so I’ll try to be as clearer as possible.

Premise:

I’m working on a project in which I want to move a player sprite based on music. It’s something like geometry dash, but I want to try to put slopes and different obstacles and animations. Lets say it’s an hybrid between geometry dash and the music levels of Rayman Legends. If the picture is not right there’s a game called <> that’s preatty similar to the idea of the project.

The question:

This is the script of my Music Controller:

public class Scr_MusicController : MonoBehaviour
{
    public static Scr_MusicController instance;

    // Variable Initialization    

    void Awake() {
        instance = this;  
        au_musicSource = GetComponent<AudioSource>();
        secPerBeat = 60f/bpm; // 1bpm = 1/60 [Hz] so 1 secPerBeat = 1/60bpm = 60s/60

        // unitPerBeat is a normalized time quantity (1/x [1/s])
        unitPerBeat = 1/secPerBeat;
    }

    // Update is called once per frame
    void Update()
    {
        if(!gameHasStarted){
            if(Keyboard.current.enterKey.isPressed){
                songStart = (float) (AudioSettings.dspTime);
                au_musicSource.Play();
                gameHasStarted = true;
            }
        }

        if(!au_musicSource.isPlaying) return;
        songPosition = (float) (AudioSettings.dspTime - songStart);
        songPositionInBeats = Mathf.FloorToInt( songPosition / secPerBeat);
    }
}

It controls when the music starts and it calculates each frame the songPosition from the start. I’ve used AudioSettings.dspTime because I read that it works on a “different time level” than whatever Time.* function.

The idea I’ve used for the player movement is the sequent:

public class Scr_PlayerMovement_Legacy : MonoBehaviour
{

    Rigidbody2D com_rb2D;

[Header("Velocity Parameters")]
    public float velocityMultiplier;
    float unitPerBeat;
    public float offsetPosition;
 
    void Awake() {
        com_rb2D = GetComponent<Rigidbody2D>();
        offsetPosition = this.transform.position.x;
    }

    void Start(){

        // secPerBeat [Hz = 1/s]
        // unitPerBeat is a normalized velocity (1/x [m/s]) 1f per beat
        // unitPerBeat times a number gives the real velocity at which the player moves
        unitPerBeat = 1 / Scr_MusicController.instance.secPerBeat;
    }

    void Update() {
        if(!Scr_MusicController.instance.gameHasStarted){
            return;
        }

        // Adding the offsetPosition to the PlayerMovement so that it starts from the correct position
        // This can be rewritten so that less calculations must be taken in occurrence each beat
        // On the x axis x_moveAmount = uPB [1/s] * songPos [s] * velocity [m/s]

        com_rb2D.MovePosition(Vector2.right * (offsetPosition + unitPerBeat * (Scr_MusicController.instance.songPosition) * velocityMultiplier) );
    }

}

My problem now is:
I found out that when implementing the y movement with some physics the player sprite stutters. I am aware that MovePosition should be used in FixedUpdate() and not in Update, but even then, nothing changes.

I thought that the problem could be of two kinds:

  • Using different time implementations for the movement on the x axis and the y axis
  • Since I’m not using discrete values when updating movement on the x axis each frame the computation of each frame is basically finalposition = startingposition + position calculated based on songposition

I have gotten around the problem by moving the platforms instead of the player, but I worry that having 3 minutes long levels would require a lot of intern computations moving a lot of platforms or big chuncks of SpriteShapes.

My question:
Do you have any suggestions on how I could define discrete time variations for each frame, based on the time difference in each frame between AudioSettings.dspTime calls or more simply songposition values in each frame??

I tried something like this:

public SongInfo: Monobehaviour
{

    double currentInterval;
    double songStart, songPosition;
    double lastValue, currentValue;

    void Start(){
        songStart = AudioSettings.dspTime;
    }
    void Update(){
        currentInterval = songPosition - lastValue;
        songPosition = AudioSettings.dspTime - songStart;
    }

    IEnumerator GetLastValue(){
         yield return new WaitForEndOfFrame();
         lastValue = songPosition;
    }
}

but as I get from debugging even if using WaitForEndOfFrame() it doesn’t necessarely means that the code after yield return is called at the end of all the script calls in the current frame. I have looked at the Unity Order of Execution but still I can’t get my mind around it.

Do you have Rigidbody interpolation turned on?

By default, physics (and therefore physics object movement) is independent of the display frame rate, which causes stutter.

1 Like

Sorry I didn’t mention it. Yes I have Kinematic Rigidbody2D set on, since it is said to use a kinematic rigidbody when using MovePosition.
I have implemented physics with Raycasting so it might also be a problem of collision, but my guess would be that the calculations required for collisions, delay the visual frame update so the distance moved each frame is discrete, therefore the “stutter”. To understand better what I mean with stutter: it is visible that the object is moving back and forth between the old position and the current position.

That’s not at all what they meant. They meant the interpolate setting:
8953989--1229541--upload_2023-4-17_19-42-48.png
Set that to interpolate and you should get smooth movement.

2 Likes

Sounds like you’ve misunderstood a few things here:

  • Physics doesn’t run per-frame by default, it runs during the FixedUpdate. Calling MovePosition per-frame is pointless because it’ll only move to the new position when the simulation executes. You need to do this per fixed-update and turn-on Rigidbody2D interpolation (see above). You say you are aware of this but ignore because “reasons”. :slight_smile:

  • Rigidbody2D.MovePosition takes an absolute world position to move to, not a relative position. Your code looks all kinds of wrong there i.e. “com_rb2D.MovePosition(Vector2.right * (offsetPosition + unitPerBeat * (Scr_MusicController.instance.songPosition) * velocityMultiplier) );”

The above is basic stuff though so either you skipped this when learning 2D physics or the “stutter” is visibly something else so perhaps you should show it in a video.

1 Like

Ofc I’m stupid and I didn’t get what he meant. Not having the editor at hand at the time of the reply I said stupid things x(.
I checked and the Interpolation is on interpolate.

I researched how FixedUpdate works and why it is used for physics, some tutorials, forums and I even read the documentation because I wanted to understand in which code is executed and the interaction between the different Methods. I understood that whatever code is in FixedUpdate is “calculated prior” to the frame execution since the timestep is fixed so it can predict what to do in the following step, and that’s why physics is calculated in FixedUpdate.

I researched how the MovePosition works. I know that if you would use MovePosition using normal velocity calculations you would do something like:

rigidbody.MovePosition( rigidbody.position + velocity * Time.deltaTime )

The thing is that since MovePosition uses an absolute position value meaning that it is calculated from the (0,0,0), the position value I calculate is somewhat correct or absolute since the the songPosition is a float that goes from [0,song.length] meaning that each Update I calculate the position at time t:

pos(t) = velocity * songPosition(t)

not being a discrete value that I add each time. What I guessed since everything works nice as long as I don’t have time different movements on x axis and y axis is that something is delaying the position update on the x axis.

I have to be more precise. I calculate songPosition in Update and use MovePosition in FixedUpdate (as shown below). I know I shouldn’t do it. Still, I experimented trying to calculate songPosition in FixedUpdate and nothing changed.

My problem occurs when having movement on the y axis that I coded as I would normally do since it doesn’t need to be synced to the music.

Following are the parts involving movement on the y axis:

void Movement(){
        // Adding the x_offsetPosition to the PlayerMovement so that it starts from the correct position
        // This can be rewritten so that less calculations must be taken in occurrence each beat
        // On the x axis x_moveAmount = uPB [1/s] * songPos [s] * velocity [m/s]
        moveAmount.x = x_offsetPosition + unitPerBeat * Scr_MusicController.instance.songPosition * velocity.x;
        moveAmount.y = velocity.y * Time.fixedDeltaTime;
    }
    void BelowCollisions(){

        float dirY = Mathf.Sign(velocity.y * Time.fixedDeltaTime);
        float rayLength = Mathf.Abs(velocity.y * Time.fixedDeltaTime) + skinWidth; // Inset by skin width

        for(int i = 0; i < verticalRayCount; i++){
            // This adds a rayOrigin based on spacing
            // also taking into account the movement made on the x axis
            Vector2 newRayOrigin = rayCastOrigins.bottomLeft + Vector2.right * (verticalRaySpacing *i - raycastOffset);
            RaycastHit2D hit2D = Physics2D.Raycast(newRayOrigin, Vector2.up * dirY, rayLength, msk_ground);

            Debug.DrawRay(newRayOrigin, Vector2.up * dirY, Color.blue);

            if(hit2D && dirY == -1){
                if(i == 0) Debug.Log("HIT");
                moveAmount.y = (hit2D.distance - skinWidth) * dirY; // still understanding how this works
                if(i == 7) Debug.Log("Current moveAmount: " + moveAmount.y + "\n" + "Current Hit Distance: " + hit2D.distance);
                rayLength = hit2D.distance;
                collisions.below = true;    // If the ray shrinks to the block the collision is activated
            } else {
                if(i==0){
                    collisions.below = false;
                }
            }
        }
 void FixedUpdate() {
        if(!Scr_MusicController.instance.gameHasStarted) return;
        Movement();
        CheckCollisions();
 
        if(!collisions.below){
            velocity.y -= gravity * Time.fixedDeltaTime;
        } else {
            velocity.y = -1f; // This way the player is "constantly falling" and the collision gets detected
        }

        com_rb2D.MovePosition(new Vector2(moveAmount.x, com_rb2D.position.y + moveAmount.y));
    }

It might just be that using com_rb2D.MovePosition(new Vector2(moveAmount.x, com_rb2D.position.y + moveAmount.y)); creates the problem, only having to use the current position for the y axis but not on the x axis doesn’t seems a right thing to do.
That’s why I was asking if you had any suggestions to define a discrete amount of time based on dspTime so that I can update the position on both x and y axis at the same time.

Furthermore if u try to move an obj with this line of code:
com_rb2D.MovePosition(Vector2.right * (offsetPosition + unitPerBeat * (Scr_MusicController.instance.songPosition) * velocityMultiplier) )

it will be almost on pace with music even if I know it’s not a proper calculation of position since u have: m/s * 1/s * s. But in videogames not all has to work as with real physics. Or am I wrong?? xD.

I know I seem to be asking you to basically solve my problem and I don’t really want to sound like that. Thanks a lot for your time and patience.

Here is a clip in the hope u see what I mean:

https://www.youtube.com/watch?v=9MTXQ6C2cjg

FixedUpdate is always behind Update(), by a variable amount of up to fixedDeltaTime. That is probably what you are seeing.

To solve it you’re going to have to understand how the two update loops interact with one another. Or, in this case, I’d strongly consider a few alternatives:

  1. Change to a variable physics timestep and synchronise it with standard updates.
  2. Turn on whatever kinematic contacts you require, then move things via the Transform in Update.
  3. Possibly just making the physics fixed timestep smaller would help? This will never be perfect and has a significant computational cost, but might be ok if your game is fairly simple? I’d consider doing this first just to see if it does improve the stutter, or if it’s coming from something else.
1 Like

I won’t be able to work on this stuff for a while, so sorry for not replying.

Nontheless, thanks for help. As soon as I manage to make something work I’ll post a reply with what I’ve found out