2
\$\begingroup\$

Is it possible to delay the effects of a script that is attached to a game object?

I have 2 characters in a scene, both receiving live motion capture data, so they are both animated identically. I have rotated one by 180 degrees and placed it in front of the other one, to create the impression that they are copying/mirroring each other.

Each has several scripts attached to them, of course...

Now, I would like to be able to delay one character a bit (e.g. 1 sec) to make it look more realistic, as if one character is observing the other character and then copying its exact movements.

Is it possible to do this by somehow using the Invoke method on the other character's Update perhaps? Or any other possibilities to achieve what I described?

EDIT :: I used a buffering mechanism, as suggested by all the helpful comments and answers, instead of delaying, because delaying would have resulted in the same network data being used, except after a delay, but I needed the same old data being used to animate a second character to create the impression of a delayed copying...

private Queue<Vector3[]> posQueue;
private Queue<Quaternion[]> rotQueue;
int delayedFrames = 30;

void Start()
{
    posQueue = new Queue<Vector3[]>();
    rotQueue = new Queue<Quaternion[]>();
}

void Update()
{
    Vector3[] latestPositions;
    Quaternion[] latestOrientations;

    if (mvnActors.getLatestPose(actorID-1, out latestPositions, out latestOrientations))
    {
        posQueue.Enqueue(latestPositions);
        rotQueue.Enqueue(latestOrientations);

        if ((posQueue.Count > delayedFrames) && (rotQueue.Count > delayedFrames))
        {
            Vector3[] delayedPos = posQueue.Dequeue();
            Quaternion[] delayedRot = rotQueue.Dequeue();

            updateMvnActor(currentPose, delayedPos, delayedRot);
            updateModel(currentPose, targetModel);
        }
    }
}
\$\endgroup\$
  • \$\begingroup\$ This reads a lot like an XY problem \$\endgroup\$ – Elva Nov 2 '15 at 14:08
  • 1
    \$\begingroup\$ You were probably looking for BeginInvokeand EndInvoke - async calling, you could make the async thread sleep at the beginning effectively delaying its execution. I would not however recommend this approach(threads can cause a lot of overhead!) - see comment above. \$\endgroup\$ – wondra Nov 2 '15 at 23:23
  • \$\begingroup\$ @wondra Thank you. This was new to me. I looked at the pages, though I found it slightly advanced for my current programming skills! Do you think I could use this to to buffer the motion capture data and somehow feed it to the second character with a delay? \$\endgroup\$ – Joshua Nov 3 '15 at 9:27
  • 1
    \$\begingroup\$ Ofcourse you could, however as I said I do not suggest it - implementing some sort of buffer(accepted answer) sounds best to me. The corutines looks also promising, however they might not be suited as well (depends on thier implementation and how many delayed actions you need). \$\endgroup\$ – wondra Nov 3 '15 at 18:24
2
\$\begingroup\$

This seems like it would be straightforward to implement with some sort of event-queue-style thing.

I'm assuming that you have a method on your characters that gets called frequently by your data source, and that you immediately interpret that data into animation. This could be trivially flipped to work with polling, etc.

So, starting with something like...

public void setMoCap(MoCapObject newData){
  animateImmediately(newData);
}

It would be pretty easy to give each animated character a Queue<MoCapObject> moCapQueue to track that data, and a float animationDelay to determine how long they wait to respond.

public void setMoCap(MoCapObject newData){
  moCapQueue.Enqueue(newData);
}

Then, in your update method, respond if the delay has passed.

public void Update(){
  if(moCapQueue.Count > 0)
  {
    MoCapObject nextData = moCapQueue.Peek();

    if(nextData .timestamp <= (Time.time + animationDelay))
    {
      nextData = moCapQueue.Dequeue();
      animateImmediately(nextData );
    }
  }
}

This approach assumes that your data, however it comes in (MoCapObject, here) has a timestamp. If not, you'll have to do that manually (perhaps with two parallel Queues or a new wrapper object to enqueue).

It also doesn't take into account animation conflicts or any of the other myriad issues this kind of project has, but it shouldn't be any different from an existing solution on that front. Just delays response.

Also you may want to enable your "consume queued data" check to process more than one data point per frame.

\$\endgroup\$
  • \$\begingroup\$ Many thanks for your detailed and elaborate solution. My plug-in (mocap <-> unity) has several scripts that deal with data packets, creating pose for the character, animating the pose, updating, etc... I gathered so far that I should buffer the mocap data and then feed it to the second character with a delay, since a normal delay would only stop the 2nd character from moving but then it would pick up from the same data stream and basically get animated exactly the same... \$\endgroup\$ – Joshua Nov 3 '15 at 14:26
  • \$\begingroup\$ So, I will try your suggestion to place the data stream content (byte[]) into a queue and then this way delay the second character's movements. Here is the free plug-in that I am working in, if you would like to take a look at its scripts: link \$\endgroup\$ – Joshua Nov 3 '15 at 14:26
  • \$\begingroup\$ Thanks Chris. I adjusted your code suggestion to my scripts and solved the issue. Please see the edit at the bottom of the original post. \$\endgroup\$ – Joshua Nov 4 '15 at 14:26
1
\$\begingroup\$

Since Invoke() is mentioned, I'm assuming you're looking for a simple solution to just introduce some delay.The way to introduce delay in unity is Coroutines. Here's the Pseudocode :

IEnumerator AnimateCharacters()
{
    Animate first character.
    // Introduce delay using yield return
     yield return new waitforSeconds(number of seconds to delay);
   Animate second character

}

This will animate first character, delays executing next step by specified number of seconds, and then, executes the next step which is animation. You'd call this from a method you've set up to handle input like this:

void OnInput()
{
   startCoRoutine("AnimateCharacters");
}
\$\endgroup\$

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.