hi Mario. I am working with warmanw on this project.
We were able to create a much smaller version of the project and reproduce the issue.
It's deployed here for your convenience.
http://cupid-demo.s3-website-us-west-2.amazonaws.com/
We are building a spine player of sorts and need to be able to jump to any frame in the animation sequence, i.e. fast forward to a given time mark.
At a high level, we stop the pixi app ticker and jump to a given time by manually updating the spine asset instance by small increments, until it reaches the desired time mark.
Here is the code. Pls let us know if you need anything else. Thank you for your input!
import 'pixi.js';
import 'pixi-spine';
function fastForwardSpineInstance({
pixiSpineInstance,
// fast forward amount in seconds
amount,
deltaStep = 0.01,
}) {
for (
let currFastForwardAmount = 0;
currFastForwardAmount < amount;
currFastForwardAmount += Math.min(amount - currFastForwardAmount, deltaStep)
) {
pixiSpineInstance.update(deltaStep);
}
}
const mixDuration = 0.2;
const animationEnd = 0.3;
const timeScale = 2;
const tracks = [
[{ anim: 'body/idle' }, { anim: 'body/shake' }],
[
{ anim: 'head/face/talk/graphemes/a', animationEnd, timeScale },
{ anim: 'head/face/talk/graphemes/b', animationEnd, timeScale },
{ anim: 'head/face/talk/graphemes/l', animationEnd, timeScale },
{ anim: 'head/face/talk/graphemes/u', animationEnd, timeScale },
{ anim: 'head/face/talk/graphemes/w', animationEnd, timeScale },
{ anim: 'head/face/talk/graphemes/a', animationEnd, timeScale },
{ anim: 'head/face/talk/graphemes/b', animationEnd, timeScale },
{ anim: 'head/face/talk/graphemes/l', animationEnd, timeScale },
{ anim: 'head/face/talk/graphemes/u', animationEnd, timeScale },
{ anim: 'head/face/talk/graphemes/w', animationEnd, timeScale },
{ anim: 'head/face/talk/graphemes/a', animationEnd, timeScale },
{ anim: 'head/face/talk/graphemes/b', animationEnd, timeScale },
{ anim: 'head/face/talk/graphemes/l', animationEnd, timeScale },
{ anim: 'head/face/talk/graphemes/u', animationEnd, timeScale },
{ anim: 'head/face/talk/graphemes/w', animationEnd, timeScale },
{ anim: 'head/face/talk/graphemes/a', animationEnd, timeScale },
{ anim: 'head/face/talk/graphemes/b', animationEnd, timeScale },
{ anim: 'head/face/talk/graphemes/l', animationEnd, timeScale },
{ anim: 'head/face/talk/graphemes/u', animationEnd, timeScale },
{ anim: 'head/face/talk/graphemes/w', animationEnd, timeScale },
],
];
function jumpToTime({ time, pixiSpineInstance, ticker }) {
pixiSpineInstance.state.setEmptyAnimations(0);
pixiSpineInstance.lastTime = Date.now();
ticker.update();
tracks.forEach((track, trackIndex) => {
pixiSpineInstance.state.setEmptyAnimation(trackIndex, 0);
track.forEach((clip) => {
const trackEntry = pixiSpineInstance.state.addAnimation(
trackIndex,
clip.anim,
false,
0,
);
trackEntry.mixDuration = mixDuration;
if (clip.animationEnd) {
trackEntry.animationEnd = clip.animationEnd;
}
if (clip.timeScale) {
trackEntry.timeScale = clip.timeScale;
}
});
});
fastForwardSpineInstance({
pixiSpineInstance,
amount: time,
});
ticker.update();
}
export default function loadPixiApp(app, isPlayBroken) {
function onAssetsLoaded(loader, resources) {
const pixiSpineInstance = new PIXI.spine.Spine(
resources.resource.spineData,
);
pixiSpineInstance.x = 400;
pixiSpineInstance.y = 575;
pixiSpineInstance.scale.set(0.4);
app.stage.addChild(pixiSpineInstance);
if (isPlayBroken) {
// We need to be able to jump to any point in our sequence.
// When we want to immitate a controlled playback, we stop the ticker
// then jump to the next frame one by one.
// That's when it breaks.
// Without the code below, it doesn't break.
let currentTime = 0;
const targetTime = 3;
const step = 1 / 30;
const waitTimeInMs = 10;
app.ticker.stop();
setTimeout(function scheduleNextStep() {
if (currentTime < targetTime) {
jumpToTime({
time: currentTime,
pixiSpineInstance,
ticker: app.ticker,
});
currentTime += step;
setTimeout(scheduleNextStep, waitTimeInMs);
}
}, waitTimeInMs);
} else {
jumpToTime({ time: 0, pixiSpineInstance, ticker: app.ticker });
}
}
app.loader.add('resource', 'cupid/Cupid.json').load(onAssetsLoaded);
}