Spine player animation loop
I'm not sure on the background! I'll have to try it.
Where is the setViewport
in the document? and the play
method.
can't find on https://esotericsoftware.com/spine-api-reference
That API is the generic documentation applicable to all runtimes. This thread is about spine-player, so you'll want to read the Spine Web Player documentation about viewports:
https://esotericsoftware.com/spine-player#Viewports
It doesn't show setViewport
, but you can see it in the Player class:
EsotericSoftware/spine-runtimesblob/4.2/spine-ts/spine-player/src/Player.ts#L722
I dont know why but I end up coming to this thread every year.
ChatGPT didn’t provide the help I needed. I’ve gone through multiple example codes to learn more, but I’m still stuck on this simple issue and can’t seem to get past it
Please clarify for me: The code below works fine, except it does not play the animation, no errors it looks like it plays the first frame but does not update/apply every frame so animation is still, if I add player.play();
animation plays but I want to have more control over the state, and not just play and pause the player. eventually I want to play multiple animations on different tracks once, not in a loop
<script>
new spine.SpinePlayer("player-container", {
jsonUrl: "gifts.json",
atlasUrl: "gifts.atlas",
showControls: true,
skin: "bluff",
backgroundColor: "000000",
defaultMix: 0,
premultipliedAlpha: true,
success: function (player) {
player.setViewport("base");
player.animationState.setAnimation(0, "base", false);
}
});
</script>
if you visit here you will see the player is playing but animation is not.
See the player code here, where it calls success
and after:
EsotericSoftware/spine-runtimesblob/4.2/spine-ts/spine-player/src/Player.ts#L550-L567
if (!entry) {
will be false, because you set an entry. Next we go to } else if (!this.currentViewport) {
which is false because you set a viewport. The result is this code doesn't call play()
, which is what makes the player advance the skeleton's animation state.
It makes sense the player automatically plays if the animation
config property is set. Otherwise I'm not sure it makes sense to only play if a viewport hasn't been set. I'll bring it up with our dev team. Also, when this code doesn't call play()
, the play/pause button shows pause, which is strange.
In the meantime, try calling player.play();
.
- تم التحرير
Thanks for reporting this! That’s a regression.
You should be able to get your desired result by simply calling:
player.animationState.setAnimation(0, "base", false);
in your success callback.
Just wait until the end of the day. A fixed version (4.2.73) of the player will be available.
Released! Let us know if now it works as you expected.
My eyes are in tears—thank you so much! It works like a charm.
I have one more question: When I don't set an animation immediately on success and just add a keydown listener, the keys won't respond unless I click on the player, which then plays the animation. Is there a way to prevent the animation from playing or pausing when clicked? Also, why don’t the key presses work unless I first click on the player?
why don’t the key presses work unless I first click on the player?
You are probably adding the event listener to the html element containing the player. That event listener will listen events only when the element is focused.
You probably want to add the event listener to the document.
Is there a way to prevent the animation from playing or pausing when clicked?
Right now, the only way is setting showControls: false
. That has the side effect to hide the player UI.
You are probably adding the event listener to the html element containing the player
Not really, I add it to document, even tried on window, still it only catches after player is clicked
here is the code
<script>
var player = new spine.SpinePlayer("player-container", {
jsonUrl: "gifts.json",
atlasUrl: "gifts.atlas",
showControls: true,
skin: "bluff",
backgroundColor: "000000",
defaultMix: 0,
premultipliedAlpha: true,
success: function (player) {
player.setViewport("base");
},
});
document.addEventListener("keydown", function (event) {
if (!player) return; // Ensure player is loaded
let currentAnimation = player.animationState.getCurrent(0)?.animation?.name;
if (currentAnimation !== "base") {
player.animationState.setAnimation(0, "base", false);
} else {
let randomIndex = Math.floor(Math.random() * 6) + 1;
player.animationState.setAnimation(randomIndex, "meta_" + randomIndex, false);
}
});
</script>
Thanks for your help Davide you already saved my day, however I want the player to start without playing anything and only when I press a key it should play an animation once. but pressing key wont respond unless I click on player which starts animation which I dotn want.
if (!player) return;
This isn't doing anything. You set the player before even adding the listener, so it's always set.
As Davide said, you aren't setting an animation in success
, so the player isn't playing. Call play()
. Also don't indent your code inside a script like a psychopath.
<script>
var player = new spine.SpinePlayer("player-container", {
jsonUrl: "gifts.json",
atlasUrl: "gifts.atlas",
showControls: true,
skin: "bluff",
backgroundColor: "000000",
defaultMix: 0,
premultipliedAlpha: true,
success: function (player) {
player.setViewport("base");
player.play();
},
});
document.addEventListener("keydown", function (event) {
let currentAnimation = player.animationState.getCurrent(0)?.animation?.name;
if (currentAnimation !== "base") {
player.animationState.setAnimation(0, "base", false);
} else {
let randomIndex = Math.floor(Math.random() * 6) + 1;
player.animationState.setAnimation(randomIndex, "meta_" + randomIndex, false);
}
});
</script>
If I call play on success, the player continuously loops an animation. What I want is for the player to remain in a ready state without playing any animation, so when I press a key, it plays the corresponding animation
Also why does it play animation in a loop none of my setAnimation()
call animation in loop
Hmm, that's because setViewport
sets the animation:
EsotericSoftware/spine-runtimesblob/4.2/spine-ts/spine-player/src/Player.ts#L731
@Davide, why does it do that?
The problem is that if player.play()
is called when you don't pass an animation in the config, the player will select the first animation stored in the skeleton data and play it. We should probably change that.
I think we can trick the current behavior though. Do not call player.play()
on the success
callback, try to call it inside the keydown callback like this:
document.addEventListener("keydown", function (event) {
if (player.paused) {
player.play();
player.skeleton.setToSetupPose();
}
let currentAnimation = player.animationState.getCurrent(0)?.animation?.name;
if (currentAnimation !== "base") {
player.animationState.setAnimation(0, "base", false);
} else {
let randomIndex = Math.floor(Math.random() * 6) + 1;
player.animationState.setAnimation(randomIndex, "meta_" + randomIndex, false);
}
});
Still I wonder why setViewport
sets the animation.
It is a bit odd to want to "play" when there is no animation. To play means the animation is applied and updateWorldTransform is done. When there is no animation, it's not necessary. Maybe we should just check if there are any animations and if there are, we apply and updateWorldTransform. Then the concept of play/pause would exist only for the player to stop animations. It would be "playing" by default.
I've looked now into the logic of your page.
As far as I understood, the logic you want is to play:
- for the first key pressed, the base animation. This one will be execute only at the first key press.
- for the next key presses, a random meta_N animation
The problem now is that the base
animation is the first animation stored in the skeleton data, so the play()
function actually selects it as the animation for track 0.
Then, the if checks if the animation on track 0 is base
and with the current code that condition is always false. So we never call the player.animationState.setAnimation(0, "base", false);
.
So, just move the code introduced yesterday into the if like this:
document.addEventListener("keydown", function (event) {
let currentAnimation = player.animationState.getCurrent(0)?.animation?.name;
if (currentAnimation !== "base") {
if (player.paused) {
player.play();
player.skeleton.setToSetupPose();
}
player.animationState.setAnimation(0, "base", false);
} else {
let randomIndex = Math.floor(Math.random() * 6) + 1;
player.animationState.setAnimation(randomIndex, "meta_" + randomIndex, false);
}
});
Thanks! that worked, its not looping now.
Please also clarify this question: why at the first when animation is playing we see the content slides. the reason is viewport is not set? or mixing with setup pose?