0
\$\begingroup\$

This script below helps me to to tell the follower object to copy exact movements of the leader object, I found this script in Unity forums and it works pretty well.

My question is how can I add some kind speed variable to it; how can I tell the follower object to move at lower speed?

 public class delayTracker : MonoBehaviour
 {
     float progress;
     const int MAX_FPS = 60;

     public Transform leader;
     public float lagSeconds = 0.5f;

     Vector3[] _positionBuffer;
     float[] _timeBuffer;
     int _oldestIndex;
     int _newestIndex;

     // Use this for initialization
     void Start()
     {
         int bufferLength = Mathf.CeilToInt(lagSeconds * MAX_FPS);
         _positionBuffer = new Vector3[bufferLength];
         _timeBuffer = new float[bufferLength];

         _positionBuffer[0] = _positionBuffer[1] = leader.position;
         _timeBuffer[0] = _timeBuffer[1] = Time.time;

         _oldestIndex = 0;
         _newestIndex = 1;
     }

     void LateUpdate()
     {
         // Insert newest position into our cache.
         // If the cache is full, overwrite the latest sample.
         int newIndex = (_newestIndex + 1) % _positionBuffer.Length;
         if (newIndex != _oldestIndex)
             _newestIndex = newIndex;

         _positionBuffer[_newestIndex] = leader.position;
         _timeBuffer[_newestIndex] = Time.time;

         // Skip ahead in the buffer to the segment containing our target time.
         float targetTime = Time.time - lagSeconds;
         int nextIndex;
         while (_timeBuffer[nextIndex = (_oldestIndex + 1) % _timeBuffer.Length] < targetTime)
             _oldestIndex = nextIndex;

         // Interpolate between the two samples on either side of our target time.
         float span = _timeBuffer[nextIndex] - _timeBuffer[_oldestIndex];
         float progress = 0f;
         if (span > 0f)
         {
             progress = (targetTime - _timeBuffer[_oldestIndex]) / span;
         }

         transform.position = Vector3.Lerp(_positionBuffer[_oldestIndex], _positionBuffer[nextIndex], progress );
     }

     void OnDrawGizmos()
     {
         if (_positionBuffer == null || _positionBuffer.Length == 0)
             return;

         Gizmos.color = Color.grey;

         Vector3 oldPosition = _positionBuffer[_oldestIndex];
         int next;
         for (int i = _oldestIndex; i != _newestIndex; i = next)
         {
             next = (i + 1) % _positionBuffer.Length;
             Vector3 newPosition = _positionBuffer[next];
             Gizmos.DrawLine(oldPosition, newPosition);
             oldPosition = newPosition;
         }
     }
 }
\$\endgroup\$
  • \$\begingroup\$ If it's moving slower you'd need an infinite sized buffer to remember all the movement, since the follower is consuming moves slower than the leader is producing them. If you want to copy movement, then it had to be the same speed. If you want a follow behavior, there's lots of other strategies you can take. \$\endgroup\$ – gjh33 May 7 at 14:09
  • \$\begingroup\$ i just need the follower to follow for about 3 seconds at the speed that i want and i need it to be exactly going where ever the leader goes.do you think there is another way to do this? \$\endgroup\$ – h00man13 May 7 at 17:46
  • \$\begingroup\$ Please don't change the title to say "answered" — our system doesn't look for particular strings in the title to sort out answered/unanswered questions. Instead, the way to mark that a solution worked for you is to click the green checkmark near the voting buttons on the answer. That will mark it as an "accepted" answer. \$\endgroup\$ – DMGregory May 21 at 11:48
3
\$\begingroup\$

I created a tiny project for you, h00man13 !

It doesn't use timers; instead, uses distance to determine how far the follower can be to "forget" the leader. It is smart enough to keep cpu overhead low (using sqrmagnitude, for example).

You can also set different speeds for the followers, and they also rotate towards the target (if you want). In the example, you have 3 different followers, with != speeds.

Hope it helps. The code is simple, you'll get the idea when you read it, but feel free to ask for more, if you want.

Here you have it: https://github.com/burkard/FollowTheLeader

If you prefer, use the following 2 scripts:

LeaderObject.cs

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

public class LeaderObject : MonoBehaviour
{
    [Tooltip("when objects reach this distance, they stop")]
    public float stayAwayDistance;

    [Tooltip("when objects are inside this range, they follow me")]
    public float startFollowingDistance;

    [Tooltip("Minimum travelled distance to record position, in meters -- lower values are more precise, but use MUCH MORE memory, and create more jaggy movements! ")]
    public float moveThreshold; // the followers will keep it's own records

    void Start()
    {
        if (stayAwayDistance == 0f || startFollowingDistance == 0f || moveThreshold == 0f) Debug.LogWarning("If you don't set a startFollowingDistance (and stayAway) for " + this.gameObject.name + ", the followers will start to follow IMEDIATELLY! Is this what you want?");
    }

    void Update()
    {

    }

    private void OnDrawGizmos()
    {
        Gizmos.color = Color.red;
        Gizmos.DrawWireSphere(transform.position, stayAwayDistance);

        Gizmos.color = Color.green;
        Gizmos.DrawWireSphere(transform.position, startFollowingDistance);
    }
}

FollowerObject.cs

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

public enum WhatToLook{ target, nextPosition }

public class FollowerObject : MonoBehaviour
{
    public GameObject target;

    [Tooltip("Moving speed, in meters per second")]
    public float movingSpeed;
    [Tooltip("The higher the speed, the quicker they turn to the target")]
    public float turningSpeed;

    [Tooltip("The delay, in seconds, until it starts to follow the target")]
    public float startDelay = 0f;

    [Tooltip("Should I stop when reach the target, or follow the whole path?")]
    public bool stopWhenReachedTarget;

    [Header("Buffer size")]
    [Tooltip("The higher the number, the bigger the path will be - but more memory will be used! It will use the last X positions (the prior ones will be discarded)")]
    public int bufferSize = 100;

    [Header("What should I look at while I'm moving?")]
    public WhatToLook lookAt;

    private float startTime = 0f;

    private Vector3 relativePos;
    private Quaternion rotation;

    private List<Vector3> desiredPositions;
    private bool isFollowing;

    private static float startFollowingSqrDistance, stayAwaySqrDistance, currentSqrDistance, sqrMoveThreshold;

    void Start()
    {
        if (target == null || movingSpeed <= 0f || turningSpeed == 0f) Debug.LogError("Error - please set target and speed for " + this.name);
        desiredPositions = new List<Vector3>();
        isFollowing = false;

        if (startFollowingSqrDistance == 0f) startFollowingSqrDistance = Vector3.SqrMagnitude(
                new Vector3(target.GetComponent<LeaderObject>().startFollowingDistance, 0f, 0f)
            );

        if (stayAwaySqrDistance == 0f) stayAwaySqrDistance = Vector3.SqrMagnitude(
                new Vector3(target.GetComponent<LeaderObject>().stayAwayDistance, 0f, 0f)
            );

        if (sqrMoveThreshold == 0f) sqrMoveThreshold = Vector3.SqrMagnitude(
                new Vector3(target.GetComponent<LeaderObject>().moveThreshold, 0f, 0f)
            );
    }

    void Update()
    {
        currentSqrDistance = Vector3.SqrMagnitude(target.transform.position - transform.position);
        if (currentSqrDistance <= startFollowingSqrDistance)
        {
            RecordTargetPosition();

            if (!isFollowing) startTime = Time.time + startDelay;
            isFollowing = true;
            if (currentSqrDistance > stayAwaySqrDistance || !stopWhenReachedTarget) MoveToTarget();
            else isFollowing = false;
        }
        else
        {
            if (currentSqrDistance > stayAwaySqrDistance || !stopWhenReachedTarget) MoveToTarget();
        }

        if (isFollowing)
        {
            LookToTarget();
        }
        else
        {
            if (desiredPositions.Count > bufferSize) desiredPositions.RemoveAt(0);
        }
        if (desiredPositions.Count == 0) isFollowing = false;
    }

    void RecordTargetPosition()
    {
        // record target's position ONLY IF target has moved beyond its threshold:
        if (desiredPositions.Count == 0) desiredPositions.Add(target.transform.position);

        if (desiredPositions.Count > 0 && Vector3.SqrMagnitude(desiredPositions[desiredPositions.Count - 1] - target.transform.position) >= sqrMoveThreshold)
            desiredPositions.Add(target.transform.position);
    }

    void LookToTarget()
    {
        if (lookAt == WhatToLook.target || desiredPositions.Count == 0)
            relativePos = target.transform.position - transform.position;
        else
            relativePos = desiredPositions[0] - transform.position;

        transform.rotation = Quaternion.RotateTowards(transform.rotation, Quaternion.LookRotation(relativePos, Vector3.up), turningSpeed * Time.deltaTime);
    }

    void MoveToTarget()
    {
        if (Time.time > startTime && desiredPositions.Count > 0)
        {
            relativePos = desiredPositions[0] - transform.position;

            if (relativePos.sqrMagnitude <= 0.1f)
            {
                desiredPositions.RemoveAt(0);
            }

            transform.position += relativePos.normalized * movingSpeed * Time.deltaTime;
        }
    }
}
\$\endgroup\$
  • \$\begingroup\$ this is real good thank you so much,so i tested your scene...and just there were some tiny issue.when i change the speed of followers to some higher value like 30 or 40 the follower object , while it's moving,it starts jittering and shaking somehow. \$\endgroup\$ – h00man13 May 10 at 9:21
  • \$\begingroup\$ @h00man13, sorry for the late answer; are you still with this issue? Try reducing the "moveThreshold" (of the LeaderObject), for a lower value. Keep in mind that this will use much more memory (not a problem if you're targeting PC, but probably not good for mobile if you're using a lot of Followers). If you use, say, up to 10 followers, probably a moveThresold of 0.01 (event 0.005) will work fine. \$\endgroup\$ – Fabio May 15 at 14:13
  • \$\begingroup\$ @h00man13 or try increasing bufferSize to a very high number (you can remove this line: if (desiredPositions.Count > bufferSize) desiredPositions.RemoveAt(0); to test; if it stops the jitter, then there it is! (the buffer should increase, or be removed). \$\endgroup\$ – Fabio May 15 at 19:09
  • \$\begingroup\$ thanks a lot man it worked just fine \$\endgroup\$ – h00man13 May 17 at 15:43
  • 1
    \$\begingroup\$ @h00man13, press the checkmark on answer and it will turn green. You also will get +2 rep for it, and he will get +15. \$\endgroup\$ – trollingchar May 21 at 8:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.