0
\$\begingroup\$

The waypoints script :

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

public class Waypoints : MonoBehaviour
{
    public Transform objectToMovePrefab;
    public int numberOfObjectsToMove = 1;
    //with this approach, you use GameObjects to represent your waypoints 
    //(they can be empty if you want the waypoint to be invisible)
    [SerializeField] private List<Transform> waypoints;

    private void Start()
    {
        for (int i = 0; i < numberOfObjectsToMove; i++)
        {
            var parent = GameObject.Find("Moving Object Parent");
            var objectToMove = Instantiate(objectToMovePrefab, parent.transform);
            objectToMove.name = "Platfrom";
        }
    }

    public int Count => waypoints.Count;
    public Vector3 GetWaypoint(int index)
    {
        return waypoints[index].position;
    }
}

Then in the editor I added this script to the prefab :

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

public class WaypointsFollower : MonoBehaviour
{
    [SerializeField] private Waypoints waypoints;
    [SerializeField] private float speed = 5f;
    [SerializeField] private bool goBack = false;
    [SerializeField] private float delay;

    private float startTime;
    private int waypointIndex = 0;

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

        waypoints = GameObject.Find("Waypoints").GetComponent<Waypoints>();
    }

    void Update()
    {
        if (Time.time < startTime) return;

        Vector3 waypoint = waypoints.GetWaypoint(waypointIndex);
        //movement
        float distance = speed * Time.deltaTime;
        transform.position = Vector3.MoveTowards(transform.position, waypoint, distance);

        //check if we've reached the waypoint
        float threshold = .1f; //how close is considered having reached the waypoint
        if (Vector3.Distance(transform.position, waypoint) < threshold)
        {
            //wraps back to 0 when we reach last waypoint
            if (goBack)
            {
                waypointIndex = (waypointIndex + 1) % waypoints.Count;
            }
            else
            {
                if (waypointIndex != waypoints.Count - 1)
                    waypointIndex = waypointIndex + 1;
            }
        }
    }
}

The delay is working but it's delaying all the WaypointsFollower's so they move at the same time after the delay.

I want that even if all the Followers have the same delay value for example 3 so the first one will start moving after 3 seconds then the next follower will start moving after another 3 seconds and so on and not that they all delay for 3 seconds and move together but to make delay so there will be spaces between the followers when they are moving.

\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

First: are you sure that you set different value of variable delay for each object/ minion?? Because problem is here: Time.time is a number of seconds that has passed from starting the game. So it will be the same for all objects! So if all minions has variable delay set to 3, and Time.time is equal thought the game - means after 3 seconds Time.time+3 will be exactly equal to each object/minion!

In short, your problem is that you are counting time from beginning of the game, which is equal to all objects.

Second: When I want to delay something I almost always use Coroutines. You can specify the time for delay, and since they are asynchronous, so each object can have its own timer running - independent of each other. Also you can start Coroutines and stop them.

Over the time I found that often even more convenient (less code) is InvokeReapeating or just Invoke - you specify time when to fire a function, and that's it. And again each object can have its own timer. You can also CancelInvoke to stop it from firing.

Update() timers gets messy, especially if a lot is happening inside Update() function.

P.S. I do not normally use Update(). In most cases of delays, waypoints, cooldowns, respawns, etc.- Coroutines or InvokeRepeating or Invoke do the job brilliantly, without affecting the rest of the code.

Here would be example of code sending different minion every three seconds: In your main or manager script you write:

public List<GameObject> minions = new List<GameObject>();

 void Start()
  {
        InvokeRepeating("SendOneMinion", 3, 3);
  }


int i=0;
public void SendOneMinion()
 {
  if (i< minions.Count && minions[i]!=null)
    minions[i].GetComponent<WaypointsFollower>().GoRightNow();
  else
    CancelInvoke();
   i++;
}

Is not that elegant? All you need to do is to add minions to the list. And oh create function GoRightNow() in each minion script..

Another example with coroutine even more elegant:

public List<GameObject> minions = new List<GameObject>();

void Start()
{
    StartCoroutine(SendOneMinion());
}

private IEnumerator SendOneMinion()
{
    
    foreach (GameObject mn in minions)
    {
        yield return new WaitForSeconds(3);
        mn.GetComponent<WaypointsFollower>().GoRightNow();
    }
}

here coroutine is running, and inside a loop there is 3 second delay before sending new minion..

\$\endgroup\$

You must log in to answer this question.

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