Target track algorithm treating Rigidbody differently?

Hello! I’m a second year GameDev programming student at a university, and recently I created a Shoot em’ up prototype as part of one of our assignments. (“Write-all-the-code-yourself”-type assignment.)

My instructor said it was good enough, but I’m really bothered with some issues I’m having with a target tracking/lead algorithm implementation, in which turrets have a tendency to swing sharply and almost dance when doing sudden movements. I realize this is down to the fact that I’m implementing a bog-standard quadratic equation based on assumed zero acceleration.

My original movement control system was based on translations, with the velocity vector for the targeting algorithm being derived from the differential of the players position between frames, divided by frame time. Both the controls and target tracking are running in Update();.

Here’s where things get a little weird. I started experimenting with moving over to a RigidBody based motion system, utilizing rb.AddForce, and capping speeds with rb.Velocity. As is proper all rigidbody operations are being carried out in FixedUpate(); while the target track remains in Update(); To my astonishment the target tracking is now working ‘flawlessly’… No odd dancing movements or overshoots, perfectly smooth tracking.

Now the story could have ended there, but I don’t much care for the slight ‘jittering’ that arises from the ships movement be run on a fixed timestep, rather than frame time, so here I am trying to make sense over what is happening under the hood? I did note that if I moved the target tracking to fixed update also, it behaves the same as when I was using translation-based movement, in regards to the rigidbody.

So I’m stuck here trying to figure out, how to replicate the conditions between rigidbody motion in FixedUpdate and target tracking in Update, so that I can apply it to my translation-based movement, and take advantage of the smoother motion. Can anyone offer any insight?

Thanks!

About the slight jittering, simply enable Interpolation in the rigidbody and the motion will be perfectly smooth in frame time.

I can’t tell about the other discrepancies you’re observing. Maybe you’re using Time.fixedDeltaTime instead of Time.deltaTime somewhere? You should always use Time.deltaTime unless there’s a strong reason to do otherwise. Time.deltaTime always returns the correct value depending on where you’re reading it from (Update or FixedUpdate).

I haven’t used Time.fixedDeltaTime anywhere.

I tried using Interpolation, as I had read that it’s supposed to do just that, but all it did was make the ship barely capable of moving. =/

This is my thruster function for RigidBody based motion.

    private void FireThrustersRB()
    {
        if (forwardThrust)
            rb.AddForce(transform.forward * Thrust);
        else
            if (rb.velocity.z > BreakingThrusterThreshold && !reverseThrust)
                    rb.AddForce(-transform.forward * BreakingThrust);

        if (reverseThrust)
            rb.AddForce(-transform.forward * Thrust);
        else
            if (rb.velocity.z < -BreakingThrusterThreshold && !forwardThrust)
                rb.AddForce(transform.forward * BreakingThrust);

        if (starboardThrust)
            rb.AddForce(transform.right * Thrust);
        else
            if (rb.velocity.x > BreakingThrusterThreshold && !portThrust)
                rb.AddForce(-transform.right * BreakingThrust);

        if (portThrust)
            rb.AddForce(-transform.right * Thrust);
        else
            if (rb.velocity.x < -BreakingThrusterThreshold && !starboardThrust)
                rb.AddForce(transform.right * BreakingThrust);

        if (rb.velocity.magnitude > MaxSpeed)
            rb.velocity = rb.velocity.normalized * MaxSpeed;

        RestrictMovementToCameraRB(); //Keeps the player inside bounds & applies opposing force to prevent 'stickyness'.
    }

That sounds like you are breaking interpolation during Update in a very specific way.
Could you share the code you’re using for the ship movement?

By enabling interpolation, the transform position will be lerped using the previous rb position (previous simulation) as the “starting position” and the most recent position (last simulation) as the “target position”.

Something very important to have in mind is that, by modifying the transform.position during Update, you will force this property to be “dirty” (it works in the same way for rotation). At some point, right before FixedUpdate (don’t know where exactly), Unity checks if the transform.position is dirty. If so, rb position changes to that value:

rb.position = transform.position;

Only when you disable interpolation is when both values are allowed to be different. I’m assuming this check is part of the interpolation process in some way (?)

Example:
Do something like this during Update …

void Update()
{
   Vector3 position = transform.position;  // save the current position (which should be the interpolation "starting position")
   transform.position += Vector3.up;  // position is "dirty" now
   transform.position = position; // go back to "starting position"
}

… Now the ship should feel heavy.

1 Like

I’d start by leaving only the “if (…) rb.AddForce()” parts and remove or comment out everything in the “else” sentences. Ensure everything so far is working properly: good motion, interpolation, speed limit… You may brake by applying opposite thrust in this test.

To me, the problems are in those complicated “else if” parts of your algorithm. I think you should review it. You may target a specific speed by applying a force proportional to the difference with the current speed. Something like this pseudocode:

float force = someNumber * (targetSpeed - currentSpeed);
force = Mathf.Clamp(force, -MaxForce, MaxForce);
rb.AddForce(directionVector * force);

Note that targetSpeed may be zero. In that case the force will counteract any current speed until this one is zero. You may then simply specify a target speed for the code to calculate and apply the force to reach that speed.

In technical terms (ask your instructor) this is applying the P term in a PID.

Thank you for the replies, a lot of food for thought.

That actually explains a lot. While all input-based movement has been moved over to the rigidbody, I still have translation-based movement keeping the ship moving with the camera. I guess what I should do is add the camera velocity to the rigidbody, rather than translate the position.

It also does explain the oddness with the targeting algorithm working better with the rigidbody, as for most of the frame updates, in between the physics steps, only the translation is occuring. And at 500-600 fps, that update at 0.02 seconds would be just a blip! This since the algorithm assumed 0 aceleration.(its only meant to keep people from cheesing the enemies by sitting still.)

My thanks!

I think I understand, it would also greatly simplify my trying to adapt the control system to axis-based input, via gamepad/joystick, by letting the input value determine the target speed. Thanks!

1 Like