Merge branch '4.2' into 4.3-beta

This commit is contained in:
Nathan Sweet 2025-03-23 12:23:19 -04:00
commit 073f4d3ec4
10 changed files with 218 additions and 39 deletions

View File

@ -204,18 +204,16 @@ namespace Spine {
from.animationLast = from.nextAnimationLast;
from.trackLast = from.nextTrackLast;
if (to.nextTrackLast != -1) { // The from entry was applied at least once.
bool discard = to.mixTime == 0 && from.mixTime == 0; // Discard the from entry when neither have advanced yet.
if (to.mixTime >= to.mixDuration || discard) {
// Require totalAlpha == 0 to ensure mixing is complete or the transition is a single frame or discarded.
if (from.totalAlpha == 0 || to.mixDuration == 0 || discard) {
to.mixingFrom = from.mixingFrom;
if (from.mixingFrom != null) from.mixingFrom.mixingTo = to;
to.interruptAlpha = from.interruptAlpha;
queue.End(from);
}
return finished;
// The from entry was applied at least once and the mix is complete.
if (to.nextTrackLast != -1 && to.mixTime >= to.mixDuration) {
// Mixing is complete for all entries before the from entry or the mix is instantaneous.
if (from.totalAlpha == 0 || to.mixDuration == 0) {
to.mixingFrom = from.mixingFrom;
if (from.mixingFrom != null) from.mixingFrom.mixingTo = to;
to.interruptAlpha = from.interruptAlpha;
queue.End(from);
}
return finished;
}
from.trackTime += delta * from.timeScale;

View File

@ -1287,7 +1287,7 @@ namespace Spine {
}
}
}
return optimizePositive ? result : ((result >> 1) ^ -(result & 1));
return optimizePositive ? result : ((int)((uint)result >> 1) ^ -(result & 1));
}
public string ReadString () {

View File

@ -2,7 +2,7 @@
"name": "com.esotericsoftware.spine.spine-csharp",
"displayName": "spine-csharp Runtime",
"description": "This plugin provides the spine-csharp core runtime.",
"version": "4.2.34",
"version": "4.2.36",
"unity": "2018.3",
"author": {
"name": "Esoteric Software",

View File

@ -17,8 +17,8 @@
"cpp"
],
"description": "The official Spine Runtime for Haxe",
"version": "4.2.5",
"releasenote": "Update to 4.2.5",
"version": "4.2.6",
"releasenote": "Update to 4.2.6",
"contributors": [
"esotericsoftware"
],

View File

@ -148,18 +148,16 @@ class AnimationState {
from.animationLast = from.nextAnimationLast;
from.trackLast = from.nextTrackLast;
if (to.nextTrackLast != -1) { // The from entry was applied at least once.
var discard:Bool = to.mixTime == 0 && from.mixTime == 0; // Discard the from entry when neither have advanced yet.
if (to.mixTime >= to.mixDuration || discard) {
// Require totalAlpha == 0 to ensure mixing is complete or the transition is a single frame or discarded.
if (from.totalAlpha == 0 || to.mixDuration == 0 || discard) {
to.mixingFrom = from.mixingFrom;
if (from.mixingFrom != null) from.mixingFrom.mixingTo = to;
to.interruptAlpha = from.interruptAlpha;
queue.end(from);
}
return finished;
// The from entry was applied at least once and the mix is complete.
if (to.nextTrackLast != -1 && to.mixTime >= to.mixDuration) {
// Mixing is complete for all entries before the from entry or the mix is instantaneous.
if (from.totalAlpha == 0 || to.mixDuration == 0) {
to.mixingFrom = from.mixingFrom;
if (from.mixingFrom != null) from.mixingFrom.mixingTo = to;
to.interruptAlpha = from.interruptAlpha;
queue.end(from);
}
return finished;
}
from.trackTime += delta * from.timeScale;

View File

@ -116,9 +116,9 @@ class SkeletonSprite extends FlxObject
override public function destroy():Void
{
skeleton = null;
state.clearListeners();
state = null;
stateData = null;
skeleton = null;
_tempVertices = null;
_quadTriangles = null;

View File

@ -416,4 +416,13 @@ class SkeletonSprite extends DisplayObject implements IAnimatable {
bone.worldToLocal(point);
}
}
override public function dispose():Void {
if (_state != null) {
_state.clearListeners();
_state = null;
}
if (_skeleton != null) _skeleton = null;
super.dispose();
}
}

View File

@ -151,6 +151,9 @@
<li>
<a href="/spine-phaser/example/physics4.html">Physics IV</a>
</li>
<li>
<a href="/spine-phaser/example/inline-loading.html">Inline loading</a>
</li>
</ul>
<li>Player</li>
<ul>

View File

@ -138,18 +138,16 @@ export class AnimationState {
from.animationLast = from.nextAnimationLast;
from.trackLast = from.nextTrackLast;
if (to.nextTrackLast != -1) { // The from entry was applied at least once.
const discard = to.mixTime == 0 && from.mixTime == 0; // Discard the from entry when neither have advanced yet.
if (to.mixTime >= to.mixDuration || discard) {
// Require totalAlpha == 0 to ensure mixing is complete or the transition is a single frame or discarded.
if (from.totalAlpha == 0 || to.mixDuration == 0 || discard) {
to.mixingFrom = from.mixingFrom;
if (from.mixingFrom != null) from.mixingFrom.mixingTo = to;
to.interruptAlpha = from.interruptAlpha;
this.queue.end(from);
}
return finished;
// The from entry was applied at least once and the mix is complete.
if (to.nextTrackLast != -1 && to.mixTime >= to.mixDuration) {
// Mixing is complete for all entries before the from entry or the mix is instantaneous.
if (from.totalAlpha == 0 || to.mixDuration == 0) {
to.mixingFrom = from.mixingFrom;
if (from.mixingFrom != null) from.mixingFrom.mixingTo = to;
to.interruptAlpha = from.interruptAlpha;
this.queue.end(from);
}
return finished;
}
from.trackTime += delta * from.timeScale;

View File

@ -0,0 +1,173 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script src="//cdn.jsdelivr.net/npm/phaser@3.60.0/dist/phaser.js"></script>
<script src="../dist/iife/spine-phaser.js"></script>
<link rel="stylesheet" href="../../index.css" />
<title>Spine Phaser Example</title>
</head>
<body class="p-4 flex flex-col items-center">
<h1>Inline loading example</h1>
</body>
<script>
const png0 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAF0lEQVR4XmNgGAWDGPwHAnSxUTAKaAYAuNkD/UsHnp0AAAAASUVORK5CYII=";
const png1 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGklEQVR4XmNgGAWDGPyHAnRxogHFBoyCEQUAsVwP8T2BZi4AAAAASUVORK5CYII=";
const atlasString =
`spine.png
size:16,16
filter:Linear,Linear
pma:true
pixel1
bounds:6,6,1,1
spine_2.png
size:16,16
filter:Linear,Linear
pma:true
pixel2
bounds:6,6,2,2
`
const skeletonJson = {
"skeleton": {
"hash": "Xgvriu12nCE",
"spine": "0.0.00",
"x": -150,
"y": -50,
"width": 300,
"height": 100,
"images": "./images/",
"audio": "./audio"
},
"bones": [
{ "name": "root", "scaleX": 50, "scaleY": 50 }
],
"slots": [
{ "name": "1", "bone": "root", "attachment": "pixel1" },
{ "name": "2", "bone": "root", "attachment": "pixel2" }
],
"skins": [
{
"name": "default",
"attachments": {
"1": {
"pixel1": { "x": 1, "width": 1, "height": 1 }
},
"2": {
"pixel2": { "x": -1, "width": 2, "height": 2 }
}
}
}
],
"animations": {
"animation": {
"slots": {
"1": {
"rgba": [
{ "color": "3f00ffff" },
{ "time": 0.3333, "color": "ffffffff" },
{ "time": 0.6667, "color": "3f00ffff" }
]
},
"2": {
"rgba": [
{ "color": "ff0000ff" },
{ "time": 0.3333, "color": "ffffffff" },
{ "time": 0.6667, "color": "ff0000ff" }
]
}
}
}
}
}
class BasicExample extends Phaser.Scene {
atlasKey = "spineboy-atlas";
async preload() {
// manually add the text atlas to the game cache
this.game.cache.text.add(this.atlasKey, { data: atlasString, premultipliedAlpha: true });
// manually add the json skeleton to the game cache
this.game.cache.json.add("spineboy-data", skeletonJson);
}
async create() {
// associate the base64 encoded pngs to their name in the text atlas
const texturesMap = {
"spine.png": png0,
"spine_2.png": png1,
}
const loadedTexturePromises = [];
const textureCallbackList = [];
const textureCallback = (resolve, combinedKey) => key => {
if (combinedKey === key) resolve()
}
// loop over the pngs to load
Object.entries(texturesMap).forEach(([keyTexture, value]) => {
// the cache key that spine plugin will search
const combinedKey = `${this.atlasKey}!${keyTexture}`;
// addBase64 is async and we should wait for the ADD event before starting the game
this.textures.addBase64(combinedKey, value);
const promise = new Promise((resolve) => {
const cb = textureCallback(resolve, combinedKey);
textureCallbackList.push(cb);
this.textures.on(Phaser.Textures.Events.ADD, cb);
});
// collecting all promises waiting for the ADD event
loadedTexturePromises.push(promise);
})
// wait for all pngs to be decoded and loaded
await Promise.all(loadedTexturePromises);
// unregister the listener to the textures since we're done at listenting at texture events
textureCallbackList.forEach(cb => this.textures.off(Phaser.Textures.Events.ADD, cb));
// now all assets are loaded, create the game object as usual
const spineboy = this.add.spine(
400,
300,
"spineboy-data",
"spineboy-atlas"
);
spineboy.setInteractive();
spineboy.displayWidth = 200;
spineboy.displayHeight = (spineboy.height / spineboy.width) * 200;
this.input.enableDebug(spineboy, 0xff00ff);
spineboy.animationState.setAnimation(0, "animation", true);
}
}
new Phaser.Game({
type: Phaser.AUTO,
width: 800,
height: 600,
type: Phaser.WEBGL,
scene: [BasicExample],
plugins: {
scene: [
{
key: "spine.SpinePlugin",
plugin: spine.SpinePlugin,
mapping: "spine",
},
],
},
});
</script>
</html>