0
\$\begingroup\$

I am using the script below to move a transform to quickly follow a target (the player), and come to a stop close to the player.

When I move the player around, the transform is following the player, keeping the 1.5 distance.

To ensure the transform can keep up with the player, I set the moveSpeed to 5, But when I do that, the transform jitters/stutters when moving.

If I set the moveSpeed to 1 for example, the transform will move smoothly but will not keep up with the player - the player has to stop moving and wait for it to catch up. But increasing the speed makes the stutter return.

Why is this stutter happening, and how can I make the movement smooth?

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.UI;

public class Follow : MonoBehaviour
{
    public Transform targetToFollow;
    public Text text;
    public Text text1;
    public float lookAtRotationSpeed;
    public float moveSpeed;

    private float minMoveSpeed = 0f;
    private Vector3 originPos;

    // Start is called before the first frame update
    void Start()
    {
        originPos = targetToFollow.position;
    }

    // Update is called once per frame
    void Update()
    {
        Vector3 lTargetDir = targetToFollow.position - transform.position;
        lTargetDir.y = 0.0f;
        transform.rotation = Quaternion.RotateTowards(transform.rotation,
            Quaternion.LookRotation(lTargetDir), Time.time * lookAtRotationSpeed);

        var distance = Vector3.Distance(transform.position, targetToFollow.position);

        text.text = "Transform Distance From Target " + distance.ToString();

        float ms = moveSpeed;

        if (distance > 5f)
        {
            ms = moveSpeed + 0.5f;
        }
        else if (distance < 1.5f)
        {
            ms = Mathf.Max(minMoveSpeed, ms - 0.3f);
        }
        else
        {
            //transform.position = Vector3.MoveTowards(transform.position, targetToFollow.position, Time.deltaTime * ms);
        }

        if (distance < 0.5f && originPos == targetToFollow.position)
        {
            ms = 0f;
        }

        if (distance > 1.5f)
            transform.position = Vector3.MoveTowards(transform.position, targetToFollow.position, Time.deltaTime * ms);

        originPos = targetToFollow.position;
    }
}
\$\endgroup\$

1 Answer 1

0
\$\begingroup\$

You have two hard cutoffs in your code:

if (distance > 5f)
{
    ms = moveSpeed + 0.5f;
}

This switches to a higher seed if the object is more than 5 units away.

if (distance > 1.5f)
    transform.position = Vector3.MoveTowards(transform.position, targetToFollow.position, Time.deltaTime * ms);

This moves the object only if the object is more than 1.5 units away.

Consider what happens when your distance is close to one of these thresholds.

  • On frame 1, I'm 1.51 units away.

    That's more than 1.5, so I take a step of .16 units, now I'm 1.34 units away

  • On frame 2, my target has moved .1 units further away, so now I'm 1.44 units from it.

    That's less than 1.5, so I don't move.

  • On frame 3, my target has moved .1 units again, so now I'm 1.54 units from it.

    That's more than 1.5, so I take a step of .16 units, now I'm 1.38 units away.

  • On frame 5, my target has moved .1 units again, so now I'm 1.48 units from it.

    That's less than 1.5, so I don't move.

You can see, instead of approaching at a slow rate to keep pace with the object I'm following, I stop and start. On alternating frames I find myself too far (so I move and overshoot) or too close (so I stop), creating a jerky, stuttering movement.

The same thing can happen more subtly at the 5 unit cutoff, where I alternate between taking big steps (because I'm just past 5 units) and small steps (because I'm just under 5 units).

You can solve the hard movement cutoff by aiming for the place where you would want to stop. That way you take a smaller move when you're very close, rather than overshooting.

float followRadius = 1.5f;

// Compute a position no further than followRadius away from our target.
Vector3 fromTarget = Vector3.ClampMagnitude(-lTargetDir.normalized, followRadius);    
Vector3 stopPoint = targetToFollow.position + fromTarget;

// Move as far as we can at our speed ms to reach the stopPoint, without overshooting.
transform.position = Vector3.MoveTowards(transform.position, stopPoint, Time.deltaTime * ms);

To solve the sharp speed change you get at the 5 unit mark, you can blend the speed gradually over a longer distance, something like...

float fastRadius = 5f;
float speedBoost = 0.5f;

float speedBlend= Mathf.Clamp01((distance - followRadius)/(fastRadius-followRadius));

ms = moveSpeed + speedBlend * speedBoost;

This has you moving at your normal move speed when close to the followRadius, and at your faster speed when at/beyond your fastRadius, with a smooth acceleration/deceleration in between.

\$\endgroup\$
11
  • \$\begingroup\$ Yes I see the hard cutoffs there. When I'm using your solution instead my code the transform is not staying close to the player when the player is moving. The transform move but the player is moving faster so the distance get bigger and I need to stop moving the player and waiting to the transform to get closer. In my code the transform is staying close to the player by 1.5 when moving the player. Should I keep my code and add to it your solution ? \$\endgroup\$
    – Daniel Lip
    Dec 19, 2020 at 21:42
  • \$\begingroup\$ You can increase the moveSpeed or speedBoost variables if the transform is not moving fast enough for your needs. \$\endgroup\$
    – DMGregory
    Dec 19, 2020 at 21:46
  • \$\begingroup\$ I tried it again and it stutter again. but when I changed the function from Update to FixedUpdate it's working smooth now. Could be the problem is that the target the player that I move have a Rigidbody component ? Not the transform in the Follow script but the targetToFollow have a Rigidbody. \$\endgroup\$
    – Daniel Lip
    Dec 19, 2020 at 22:02
  • \$\begingroup\$ Yes, because FixedUpdate and physics tick at a different interval than the Update function, leading to judder between the two. Don't forget to include important details like that in your question! \$\endgroup\$
    – DMGregory
    Dec 19, 2020 at 22:04
  • \$\begingroup\$ I will include this detail/s next time. Thank you very much. \$\endgroup\$
    – Daniel Lip
    Dec 19, 2020 at 22:28

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .