Copy Position and Rotation with Delay

Im trying to copy a master object position and rotation in another object with delay.

I would like to realize the delay of the timing to start move and rotation instead of the speed of move and rotation itself.

It can be imagined that writing the master position and rotation in the array for each frame and reading the array by offsetting the array by the delayed seconds.

But I can’t find obvious implementation method.

Thanks in advance.

If you want to delay something, the two most straightforward methods are InvokeRepeating or using a Coroutine and WaitforSeconds.

No, Coroutine and Invoke methods should be used in the Awake or Start method, which is not appropriate in this case.
I need to manipulate the positions and rotations that are updated in the Update method.

Here is the code.

using UnityEngine;
using System.Collections;

public class RingBuffer : MonoBehaviour {
  
    public int delayTime = 2;
    public int fps = 30;
    public GameObject masterObject;
    public GameObject slaveObject;

    private int ringSize;
    private int inIndexer;
    private int outIndexer;
    private Transform[] ringBuffer;

    void Awake () {
        Application.targetFrameRate = 30;
    }

    void Start () {
        ringSize = delayTime * fps;
        ringBuffer = new Transform[ringSize];
        inIndexer = ringSize - 1;
        outIndexer = 0;

        int i = 0;
        while (i < ringSize) {
            ringBuffer [i] = masterObject.transform;
            i++;
        }
    }

    void Update () {
        ringBuffer [inIndexer] = masterObject.transform;

        Vector3 temp = slaveObject.transform.position;
        temp.x = ringBuffer [outIndexer].position.x;
        slaveObject.transform.position = temp;
        //slaveObject.transform.position = Vector3.Lerp (slaveObject.transform.position, temp, Time.deltaTime*2);

        outIndexer = (outIndexer + 1) % ringSize;
        inIndexer = (inIndexer + 1) % ringSize;
    }
}

Have you noticed that your Awake() is misspelled?

I fixed it but the result did not change.

I don’t understand what you want. You claim you want a delay, but describe transitioning from one point to another.

A shifting register, where you PUSH transformations of master object and POP transformation to slave object, could solve this.

However you need to use it in fixed update and use target framerate to get consistent results (independant of default framerate (which is “as many as possible”)).

Then the only thing you’ll need to look out for is the size of the register.

Thank you for your quick reply.

I just want to do it, but can not be realized with the circular buffer as above?

If we assume 30 fps, we can set targetFrameRate to 30 and Fixed Timestep to 0.03 and do processing within FixedUpdate method?

This thread is the same as what I want to do…
https://forum.unity3d.com/threads/following-object-with-delay.468475/

create a custom class container that holds a Vector3 (for position) a Quaternion (for rotation) and a float (for timestamp) and use a Queue of this class.

Unless the Transforms interact with physics they don’t need to be in FixedUpdate. Pooling and skipping to the youngest register thats older than the delay will handle it just fine.

and yes the size of the register will need to be cared for. this is largely dependent on how large you set the delay.

TranformState

public class TransformState
{
    public Vector3 position;
    public Quaternion rotation;
    public float timeStamp;

    public TransformState(Vector3 position, Quaternion rotation, float timeStamp)
    {
        this.position = position;
        this.rotation = rotation;
        this.timeStamp = timeStamp;
    }

    public TransformState(Transform transform)
    {
        SetState(transform);
    }

    public SetState(Transform transform)
    {
        this.position = transform.position;
        this.rotation = transform.rotation;
        this.timeStamp = Time.time;
    }
}

Queue Code (with pooling)

private Queue<TransformState> frameStates = new Queue<TransformState>();
private List<TransformState> pooledStates = new List<TransformState>();

private void Update()
{
    TransformState state = null;
    if(pooledStates.Count>1)
    {
        state = pooledStates[0];
        pooledStates.Remove(state);

        state.SetState(transform);
    }
    else
    {
        state = new TransformState(transform);
    }

    frameState.Enqueue(state);

    TransformState delayState = null;
    while(frameState.Count>0)
    {
        var peekState = frameState.Peek();
        if(peekState == null || (state.timeStamp - peekState.timeStamp) < delay) break;
    



        delayState = frameState.Dequeue();
        pooledStates.Add(delayState);
    }

    if(delayState != null)
    {
        var slaveTransform = slaveObject.transform;
        slaveTransform.position = delayState.position;
        slaveTransform.rotation = delayState.rotation;
    }
}

Hi, Thanks for attaching script.

I applied it to the scene based on your code.
Both position and rotation will move to follow the master object, but the slave object flickers.

Since the error occurs when writing the SetState() in the constructor of the TransformState script and declaring the delayState in the Queue script, I am assigning an instance of TransformState, but is this a problem?

3097868–233844–TransformQueue.cs (1.08 KB)
3097868–233845–TransformState.cs (539 Bytes)

Flicker has been eliminated for the moment once you stop declaring the var and make it a member variable of the class.

oh, that was a lazy copy/paste… i fixed line 16 in TransformState and line 21 in the Queue Code. I wrote all this directly in the post so I didn’t have a compiler proof-read it.

1 Like

Thank you for reply.
It worked!
There is another question, how can I set an individual delay time for each slaveobject when I increase slaveobject?

Similar to TransformState, write a custom class that holds a single Slave’s transform and its delay. Your script will hold a list of these objects. populate the list via the inspector. then in either Awake, Start, or preferably OnEnable, sort the list by delay in descending order (you can use Linq for this). then take the Queue code I wrote and wrap lines 22-39 in a for loop that iterates through your sorted slaves. change a couple lines withing the code i posted so that they are using the each slaves delay and transform instead of the values I wrote to.

Thanks for the detailed explanation.

I tried but it does not work.
I could grasp the atmosphere what Queue code is doing but I do not know how to modify it.

I created a TransformState class for Slave, attached it to SlaveObject, put them together in a list. I have not tried descending order yet.

How to combine the SlaveObject stored in the List with the TransformState of the existing Master in the Queue code?

In the TransformState of Master that already exists in the Queue code, the comparison target is Master itself, but does Slave need to be processed in the same way?
Or is there a need to compare between Master’s transform and Slave’s transform?

Sorry, I could not think simply and it got confused.