About the use of Phaser3 and Tweens Animation.

We would like to get the coordinates and angles of multiple objects from a json file and create a playback of around 30,000~50,000 animations.
Is PhaserJS(3.60) suitable for this case?
If suitable, is it correct to use Tweens?

Is it possible to implement a seek bar function with Tweens animations?
I am currently implementing the following, but the animation starts late and the object coordinates and rotation angle are wrong.

Is it also possible to rewind the Tweens animation with the seek function?

The version of PhaserJs is 3.60.

// Seek part
const scenesIndex = Object.keys(tweensArray); // The form of tweensArray is shown below
const totalTime = Math.floor((scenesIndex.length * 100) * (startSceneNumber / 100)); // startSceneNumber contains the value of what percentage to skip.
this.tweens.getTweens().forEach((tween) =>{
    tween.pause();
    tween.seek(totalTime, 16.6, true);
    tween.resume();
    tween.targets[0].x = (tween.getValue() == 0) ? -1000 : tween.getValue();
    tween.targets[0].y = (tween.getValue() == 0) ? -1000 : tween.getValue();
  });


// The animation is created as follows.
// There are n animations and n + 22 tweens.
const tweensArray = {
  1: [],
  3: [],
  5: [Tween2],
  8: [Tween2],
  10: [Tween2, Tween2],
  14: [Tween2, Tween2, Tween2],
  15: [Tween2, Tween2, Tween2, Tween2],
  16: [Tween2, Tween2, Tween2, Tween2],
  17: [Tween2, Tween2, Tween2, Tween2],
  21: [Tween2, Tween2, Tween2],
  n: [Tween2 * n],
};


/*
  Methods for creating animation.

  targetsObj  : targetsObj is a sprite object.
          this.add.sprite(-1000, -1000, 'ImgObj').setAlpha(0).setDepth(500);
  imgPos : imgPos contains x and y coordinates.
  angle       : angle is the angle of rotation.
  counter     : counter is a counter for delay.
*/
createTweensAnimation(targetsObj, imgPos, angle, counter) {
    const tween = this.tweens.add({
      targets: TargetsObj,
      x: imgPos.x,
      y: imgPos.y,
      duration: 100,
      delay: counter * 100,
      persist: true,
      onStart: function() {
        TargetsObj.setAngle(angle);
        TargetsObj.setAlpha(1);
      },
      onPause: function() {
        console.log("onPause");
        TargetsObj.setAlpha(0);
      },
      onResume: function() {
        console.log("onResume");
        TargetsObj.setAlpha(0);
      }
    })
    return tween
}

Phaser can run a lot of tweens at once. But I think seeking all of those at once could be a problem.

It’s possible to make one tween for several targets with different end properties and delays (see the examples). That would be easily seekable.

You can seek a tween behind its current position, but you can’t play it backwards, AFAIK.

First, get a basic tween working the way you want it, before you try any seeking.

Thank you for your response.
Once I was able to play the basic Tweens animation without using seek.

From here I would like to add the seek function.

I have a few questions in response to your answers.

It’s possible to make one tween for several targets with different end properties and delays (see the examples 2). That would be easily seekable.

Q1. which example would that be?
Q2: I am creating an animation in which multiple objects are moving forward according to coordinates (like a car race or a relay on land).
Q3: To begin with, is it possible to implement seek in such a tweens animation?

You can seek a tween behind its current position, but you can’t play it backwards, AFAIK.

We obviously want it to seek before its current position, but we also want it to seek backwards from its current position.
Q1. is there an example somewhere of how to do this?

I guess not many :smiley: but see stagger delay and delay function.

Is there one set of destination coordinates (i.e., moving in a straight line) or several (moving along a path)?

You can seek in any tween. But if you use one tween controlling multiple targets, seeking that tween will affect all of them, naturally.

See seek to point in tween. Click and the tween will seek to 1s, while it’s running.

You can also restart or yoyo a tween, but that’s different from seeking.

Is there one set of destination coordinates (i.e., moving in a straight line) or several (moving along a path)?
You can seek in any tween. But if you use one tween controlling multiple targets, seeking that tween will affect all of them, naturally.

{
    1st animation:{},
    2nd animation:{},
    3rd animation:{},
    4th animation:{
        object1 : {"coordinates":[x, y], 'rotation angle': 0,0}
    },
    5th animation:{
        object1 : {"coordinates":[x, y], 'rotation angle': 0,0}
    },
    6th animation:{
        object1 : {"coordinates":[x, y], 'rotation angle': 0,0}
    },
    7th animation:{
        object1 : {"coordinates":[x, y], 'rotation angle': 0,0},
        object2 : {"coordinates":[x, y], 'rotation angle': 0,0}
    }
    8th animation:{
        object1 : {"coordinates":[x, y], 'rotation angle': 0,0},
        object2 : {"coordinates":[x, y], 'rotation angle': 0,0}
    },
    n animation:{
        objectn : {"coordinates":[x, y], 'rotation angle': 0,0},
        ......
    }
}

The Json file with animation:{object to be moved:{coordinates, rotation angle}} is read as above.
There are 30,000 to 50,000 such data, and based on this, we will create a chunk of tweens like the tweensArray I wrote at the beginning, and I wonder if it is difficult to seek them because of the amount of data.

See seek to point in tween. Click and the tween will seek to 1s, while it’s running.
You can also restart or yoyo a tween, but that’s different from seeking.

Thanks for the example.
What I want to achieve is different from yoyo.
If it is difficult to go back in seek, is it difficult to play back from a few seconds before when some button is pressed, etc.?

You may just have to try a prototype for this. I’m just guessing that seeking thousands of tweens at once might be slow. Do you definitely want to show 30–50k sprites at once?

I may have confused you on seek(), but I wasn’t sure what you meant by “rewind”. You can seek to any time position in a tween, ahead or behind, but it happens instantaneously.

I think I was completely wrong about this. All that matters is how many TweenData there are, not how many tweens.

I think the seek cost is

seekAmount * tween.data.length / seekDelta

Have a look at

This is 10,000 targets * 2 properties = 20,000 tween data seeking in 200ms steps (5fps). Seeking takes around 8–160ms for me, depending on how far.

You may just have to try a prototype for this. I’m just guessing that seeking thousands of tweens at once might be slow. Do you definitely want to show 30–50k sprites at once?

I don’t know how to explain it. it’s hard to say, it depends on the situation…
I know this is hard to understand, but I’ll try to explain.
If you have a coordinate Jsonfile like this.

{
  // animation of scene1
  1th animation:{
    object1 : {"coordinates":[x, y], 'rotation angle': 0,0}
  },
  // animation of scene2
  2th animation:{
    object1 : {"coordinates":[x, y], 'rotation angle': 0,0},
    object2 : {"coordinates":[x, y], 'rotation angle': 0,0}
  }
}

The contents of tweensArray would be as follows.

{
    1:[Tween2], // animation of scene1
    2:[Tween2, Tween2], // animation of scene2
}

In this case, object1 of the 1st animation and object1 of the 2nd animation are the same sprites.

In other words, “tween2 of 1st animation in tweensArray” and “tween2 of 1st animation in tweensArray 2” have the same number of places, so there are two places.

In other words, even if the length of this.tweens.getTweens(), Json file, or tweensArray is 30-50k, there will not be that many sprites.

You can seek to any time position in a tween, ahead or behind, but it happens instantaneously.

That’s great!
Is the seek forward or backward, or if it moves, is it seeked from the point where it is currently playing?

This is 10,000 targets * 2 properties = 20,000 tween data seeking in 200ms steps (5fps). Seeking takes around 8–160ms for me, depending on how far.

It works great.
In my case, I am repeatedly calling createTweensAnimation() in the following process.

const relayData = thie.cache.json.get('relayData');
const runners = {}
const runnerTweensArray = {}
let counter = 0
Object.entries(relayData).forEach(([sceneNumber, sceneData]) => {
  counter++;
  if (!runnerTweensArray[sceneNumber]) {
      runnerTweensArray[sceneNumber] = [];
  }
  Object.entries(sceneData).forEach(([runnerNumber, runData]) => {
    if (!runners[runnerNumber]) {
      runners[runnerNumber] = this.add.sprite(-1000, -1000, 'runner_obj').setAlpha(0).setDepth(500);
    }
    runnerTweensArray[sceneNumber].push(this.createTweensAnimation(runners[runnerNumber], runData, counter))
  });
});

I need to forEach to make it seek, is this also the cause of the slowdown?

const scenesIndex = Object.values(tweensArray).filter((item:any) => item.length > 0);
const totalTime = Math.floor((scenesIndex.length * 100) * (startSceneNumber / 100));
this.tweens.getTweens().forEach((tween) =>{
  tween.pause();
  tween.seek(totalTime, 16.6, true);
  tween.resume();
  tween.targets[0].x = (tween.getValue() == 0) ? -1000 : tween.getValue();
  tween.targets[0].y = (tween.getValue() == 0) ? -1000 : tween.getValue();
});

I will provied my source code and other information if there are any questions.

Seek happens by playing the tween from the start until it reaches the seek time position, all at once.

Generally, I would advise:

  • Make sure each sprite position is controlled by only one tween at once.
  • You don’t need to pause before seeking or resume after seeking.
  • Use the largest seek delta that’s acceptable (larger steps are faster).

Remove this. What’s this trying to do?

Seek happens by playing the tween from the start until it reaches the seek time position, all at once.

Thanks!
I understand.

Make sure each sprite is sprite position is controlled by only one tween at once.

I think my current form of the tween array is fine…

You don’t need to pause before seeking or resume after seeking.

I removed the pause and resume before and after seeking.

Use the largest seek delta that’s acceptable (larger steps are faster).

It’s hard to find an acceptable range.
I wonder how much is generally acceptable.

Remove this. What’s this trying to do?

I set this up because the rotation angle and coordinates didn’t fit well after seeking.
Remove this.

It’s basically a trade-off of computation speed vs. seek precision. If seeking is taking too long, try increasing the delta.

That’s odd. The x and y should work just like in the playing tween.

I noticed your tweens are very short (100ms). Are they supposed to run in a sequence?

It’s basically a trade-off of computation speed vs. seek precision. If seeking is taking too long, try increasing the delta.

I understand!
I’ll try to find just the right delta!
Too large a delta value isn’t good.

That’s odd. The x and y should work just like in the playing tween.

When I set the initial values of the sprites to -1000 for x and y and 0 for Alpha, and the animation starts, the initial values are updated by updating x and y, the rotation angle is updated by onStart, and Alpha is displayed at 1, but when I seek, the sprites are displayed in the upper left corner of the screen (0, 0). However, when I seek, the sprites are displayed in the upper left corner of the screen (0, 0) or moved from (0, 0).
If I seek continuously, it behaves more strangely.

I noticed your tweens are very short (100ms). Are they supposed to run in a sequence?

Yes, the animation is executed one tween at a time, not all at once.
Maybe it is due to the large amount of animations, but if the duration is large, it does not move as desired.

You might try instead using only one tween per target, with the full duration.

this.tweens.add({
  targets: sprite,
  x: [100, 200, 300 /* etc. */],
  y: [0, 100, 200],
  angle: [0, 90],
  interpolation: 'linear'
});

You might try instead using only one tween per target, with the full duration.

In one tween per target, all sprites start animating at the same time…
The last person running at the same time as the first runner also starts the animation…

You could still use a start delay like before.

I think you would need to reset each sprite to (-1000, -1000) before seeking any tween.

Also, seeking doesn’t call onStart etc. unless you pass true in the 3rd arg. So I don’t think angle will be updated without that.

If the tweens are 100ms then the seek interval needs to be smaller than that.

You could still use a start delay like before.

I’m sorry.
What do you mean by start delay?
Do you mean this one?
https://newdocs.phaser.io/docs/3.60.0/focus/Phaser.Tweens.Tween-startDelay

I think you would need to reset each sprite to (-1000, -1000) before seeking any tween.
Also, seeking doesn’t call onStart etc. unless you pass true in the 3rd arg. So I don’t think angle will be updated without that.

When I advance the animation to some extent and return with seek
It seems that the once-displayed sprite is displayed at the position (0, 0).

(1). Advance the animation by 70%.
(2). Seek to play from the 30% point.
Then the sprite displayed in (1) is seen stuck at (0, 0), although it does not appear at the 30% point.