From fdfc4642ad57cee748dcbcb25174dbaadd911e59 Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Wed, 13 Sep 2023 15:35:53 +0200 Subject: [PATCH 1/4] =?UTF-8?q?[haxe]=C2=A0minimal=20scene=20framework,=20?= =?UTF-8?q?fixes=20for=20Haxe=20C++,=20clean-up=20of=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- spine-haxe/example/src/BasicExample.hx | 29 ++++++++ spine-haxe/example/src/Main.hx | 54 +------------- spine-haxe/example/src/Scene.hx | 46 ++++++++++++ spine-haxe/spine-haxe/spine/SkeletonBinary.hx | 13 ++-- spine-haxe/spine-haxe/spine/SkeletonData.hx | 19 +++++ spine-haxe/spine-haxe/spine/SkeletonJson.hx | 27 +++---- .../spine/atlas/AssetsTextureLoader.hx | 30 ++++++++ .../spine-haxe/spine/atlas/TextureAtlas.hx | 14 +++- .../spine/starling/SkeletonAnimation.hx | 32 --------- .../spine/starling/SkeletonSprite.hx | 40 ++++++++--- .../spine/starling/StarlingTextureLoader.hx | 70 ------------------- 11 files changed, 182 insertions(+), 192 deletions(-) create mode 100644 spine-haxe/example/src/BasicExample.hx create mode 100644 spine-haxe/example/src/Scene.hx create mode 100644 spine-haxe/spine-haxe/spine/atlas/AssetsTextureLoader.hx delete mode 100644 spine-haxe/spine-haxe/spine/starling/SkeletonAnimation.hx delete mode 100644 spine-haxe/spine-haxe/spine/starling/StarlingTextureLoader.hx diff --git a/spine-haxe/example/src/BasicExample.hx b/spine-haxe/example/src/BasicExample.hx new file mode 100644 index 000000000..a332906df --- /dev/null +++ b/spine-haxe/example/src/BasicExample.hx @@ -0,0 +1,29 @@ +import openfl.utils.Assets; +import spine.SkeletonBinary; +import spine.SkeletonData; +import spine.SkeletonJson; +import spine.animation.AnimationStateData; +import spine.atlas.TextureAtlas; +import spine.attachments.AtlasAttachmentLoader; +import spine.starling.SkeletonSprite; +import starling.core.Starling; + +class BasicExample extends Scene { + var loadBinary = true; + + public function load():Void { + var atlas = TextureAtlas.fromAssets("assets/raptor.atlas"); + var skeletondata = SkeletonData.fromAssets("assets/raptor-pro" + (loadBinary ? ".skel" : ".json"), atlas); + var animationStateData = new AnimationStateData(skeletondata); + animationStateData.defaultMix = 0.25; + + var skeletonSprite = new SkeletonSprite(skeletondata, animationStateData); + skeletonSprite.x = Starling.current.stage.stageWidth / 2; + skeletonSprite.y = Starling.current.stage.stageHeight * 0.5; + + skeletonSprite.state.setAnimationByName(0, "walk", true); + + addChild(skeletonSprite); + juggler.add(skeletonSprite); + } +} diff --git a/spine-haxe/example/src/Main.hx b/spine-haxe/example/src/Main.hx index 5c854fb38..96995bfb2 100644 --- a/spine-haxe/example/src/Main.hx +++ b/spine-haxe/example/src/Main.hx @@ -1,29 +1,12 @@ package; -import starling.display.Image; -import haxe.io.Bytes; -import openfl.display.Bitmap; -import openfl.display.BitmapData; +import Scene.SceneManager; import openfl.display.Sprite; -import openfl.Assets; import openfl.geom.Rectangle; -import openfl.utils.ByteArray; -import openfl.utils.Endian; -import spine.animation.AnimationStateData; -import spine.atlas.TextureAtlas; -import spine.attachments.AtlasAttachmentLoader; -import spine.SkeletonBinary; -import spine.SkeletonData; -import spine.SkeletonJson; -import spine.starling.SkeletonAnimation; -import spine.starling.StarlingTextureLoader; import starling.core.Starling; import starling.events.Event; -import starling.textures.Texture; class Main extends Sprite { - private static inline var loadBinary:Bool = false; - private var starlingSingleton:Starling; public function new() { @@ -39,39 +22,6 @@ class Main extends Sprite { starlingSingleton.start(); Starling.current.stage.color = 0x000000; - loadSpineAnimation(); - } - - private function loadSpineAnimation():Void { - var textureAtlasBitmapData:BitmapData = Assets.getBitmapData("assets/raptor.png"); - var stAtlas = Assets.getText("assets/raptor.atlas"); - var binaryData = Assets.getBytes("assets/raptor-pro.skel"); - var jsonData = Assets.getText("assets/raptor-pro.json"); - - var textureAtlas = Texture.fromBitmapData(textureAtlasBitmapData); - var textureloader = new StarlingTextureLoader(textureAtlas); - var atlas = new TextureAtlas(stAtlas, textureloader); - - var skeletondata:SkeletonData; - if (loadBinary) { - var skeletonBinary:SkeletonBinary = new SkeletonBinary(new AtlasAttachmentLoader(atlas)); - var bytearray:ByteArray = ByteArray.fromBytes(binaryData); - bytearray.endian = Endian.BIG_ENDIAN; - skeletondata = skeletonBinary.readSkeletonData(bytearray); - } else { - var skeletonJson:SkeletonJson = new SkeletonJson(new AtlasAttachmentLoader(atlas)); - skeletondata = skeletonJson.readSkeletonData(jsonData); - } - - var stateData:AnimationStateData = new AnimationStateData(skeletondata); - stateData.defaultMix = 0.25; - - var skeletonanimation:SkeletonAnimation = new SkeletonAnimation(skeletondata, stateData); - skeletonanimation.x = Starling.current.stage.stageWidth / 2; - skeletonanimation.y = Starling.current.stage.stageHeight * 0.5; - - Starling.current.stage.addChild(skeletonanimation); - Starling.current.juggler.add(skeletonanimation); - skeletonanimation.state.setAnimationByName(0, "walk", true); + SceneManager.getInstance().switchScene(new BasicExample()); } } diff --git a/spine-haxe/example/src/Scene.hx b/spine-haxe/example/src/Scene.hx new file mode 100644 index 000000000..64b7cf4f0 --- /dev/null +++ b/spine-haxe/example/src/Scene.hx @@ -0,0 +1,46 @@ +import starling.core.Starling; +import starling.display.Sprite; + +class SceneManager { + private static var instance:SceneManager; + + private var currentScene:Sprite; + + private function new() { + // Singleton pattern to ensure only one instance of SceneManager + } + + public static function getInstance():SceneManager { + if (instance == null) { + instance = new SceneManager(); + } + return instance; + } + + public function switchScene(newScene:Scene):Void { + if (currentScene != null) { + currentScene.dispose(); + currentScene.removeFromParent(true); + } + currentScene = newScene; + starling.core.Starling.current.stage.addChild(currentScene); + newScene.load(); + } +} + +abstract class Scene extends Sprite { + var juggler = new starling.animation.Juggler(); + + public function new() { + super(); + Starling.current.juggler.add(juggler); + } + + abstract public function load():Void; + + public override function dispose():Void { + juggler.purge(); + Starling.current.juggler.remove(juggler); + super.dispose(); + } +} diff --git a/spine-haxe/spine-haxe/spine/SkeletonBinary.hx b/spine-haxe/spine-haxe/spine/SkeletonBinary.hx index 75ddd81b8..ef47b15a6 100644 --- a/spine-haxe/spine-haxe/spine/SkeletonBinary.hx +++ b/spine-haxe/spine-haxe/spine/SkeletonBinary.hx @@ -1,5 +1,7 @@ package spine; +import spine.attachments.AtlasAttachmentLoader; +import openfl.utils.Endian; import spine.animation.SequenceTimeline; import openfl.errors.ArgumentError; import openfl.errors.Error; @@ -81,20 +83,17 @@ class SkeletonBinary { private static inline var CURVE_STEPPED:Int = 1; private static inline var CURVE_BEZIER:Int = 2; - public function new(attachmentLoader:AttachmentLoader = null) { + public function new(attachmentLoader:AttachmentLoader) { this.attachmentLoader = attachmentLoader; } - public function readSkeletonData(object:ByteArray):SkeletonData { - if (object == null) - throw new ArgumentError("Object cannot be null"); - if (!Std.isOfType(object, ByteArrayData)) - throw new ArgumentError("Object must be ByteArrayData"); + public function readSkeletonData(bytes:ByteArray):SkeletonData { + bytes.endian = Endian.BIG_ENDIAN; var skeletonData:SkeletonData = new SkeletonData(); skeletonData.name = null; - var input:BinaryInput = new BinaryInput(object); + var input:BinaryInput = new BinaryInput(bytes); var lowHash:Int = input.readInt32(); var highHash:Int = input.readInt32(); diff --git a/spine-haxe/spine-haxe/spine/SkeletonData.hx b/spine-haxe/spine-haxe/spine/SkeletonData.hx index d6361d77a..c16c1336c 100644 --- a/spine-haxe/spine-haxe/spine/SkeletonData.hx +++ b/spine-haxe/spine-haxe/spine/SkeletonData.hx @@ -1,5 +1,8 @@ package spine; +import spine.attachments.AtlasAttachmentLoader; +import openfl.utils.Assets; +import spine.atlas.TextureAtlas; import openfl.errors.ArgumentError; import openfl.Vector; import spine.animation.Animation; @@ -27,6 +30,22 @@ class SkeletonData { public var imagesPath:String; public var audioPath:String; + public static function fromAssets(path:String, atlas:TextureAtlas, scale:Float = 1.0):SkeletonData { + if (StringTools.endsWith(path, ".skel")) { + var byteData = Assets.getBytes(path); + var loader = new SkeletonBinary(new AtlasAttachmentLoader(atlas)); + loader.scale = scale; + return loader.readSkeletonData(byteData); + } else if (StringTools.endsWith(path, ".json")) { + var jsonData = Assets.getText(path); + var loader = new SkeletonJson(new AtlasAttachmentLoader(atlas)); + loader.scale = scale; + return loader.readSkeletonData(jsonData); + } else { + throw new SpineException("Path of skeleton data file must end with .json or .skel"); + } + } + public function new() {} // --- Bones. diff --git a/spine-haxe/spine-haxe/spine/SkeletonJson.hx b/spine-haxe/spine-haxe/spine/SkeletonJson.hx index 9c7061386..873d72dbd 100644 --- a/spine-haxe/spine-haxe/spine/SkeletonJson.hx +++ b/spine-haxe/spine-haxe/spine/SkeletonJson.hx @@ -54,28 +54,17 @@ class SkeletonJson { private var linkedMeshes:Vector = new Vector(); - public function new(attachmentLoader:AttachmentLoader = null) { + public function new(attachmentLoader:AttachmentLoader) { this.attachmentLoader = attachmentLoader; } - /** @param object A String or ByteArray. */ - public function readSkeletonData(object:Object, name:String = null):SkeletonData { - if (object == null) + public function readSkeletonData(json:String):SkeletonData { + if (json == null) throw new ArgumentError("object cannot be null."); - var root:Object; - if (Std.isOfType(object, String)) { - root = Json.parse(cast(object, String)); - } else if (Std.isOfType(object, ByteArrayData)) { - root = Json.parse(cast(object, ByteArray).readUTFBytes(cast(object, ByteArray).length)); - } else if (Std.isOfType(object, Dynamic)) { - root = object; - } else { - throw new ArgumentError("object must be a String, ByteArray or Object."); - } + var root = Json.parse(json); var skeletonData:SkeletonData = new SkeletonData(); - skeletonData.name = name; // Skeleton. var skeletonMap:Object = getString(root, "skeleton", ""); @@ -1172,10 +1161,10 @@ class SkeletonJson { } var i:Int = value << 2; - var cx1:Float = curve[Std.string(i)]; - var cy1:Float = curve[Std.string(i + 1)] * scale; - var cx2:Float = curve[Std.string(i + 2)]; - var cy2:Float = curve[Std.string(i + 3)] * scale; + var cx1:Float = curve[i]; + var cy1:Float = curve[i + 1] * scale; + var cx2:Float = curve[i + 2]; + var cy2:Float = curve[i + 3] * scale; timeline.setBezier(bezier, frame, value, time1, value1, cx1, cy1, cx2, cy2, time2, value2); return bezier + 1; } diff --git a/spine-haxe/spine-haxe/spine/atlas/AssetsTextureLoader.hx b/spine-haxe/spine-haxe/spine/atlas/AssetsTextureLoader.hx new file mode 100644 index 000000000..443bc2817 --- /dev/null +++ b/spine-haxe/spine-haxe/spine/atlas/AssetsTextureLoader.hx @@ -0,0 +1,30 @@ +package spine.atlas; + +import starling.textures.Texture; +import spine.atlas.TextureAtlasRegion; +import spine.atlas.TextureAtlasPage; +import spine.atlas.TextureLoader; + +class AssetsTextureLoader implements TextureLoader { + private var basePath:String; + + public function new(basePath:String) { + this.basePath = basePath; + } + + public function loadPage(page:TextureAtlasPage, path:String) { + var bitmapData = openfl.utils.Assets.getBitmapData(basePath + "/" + path); + if (bitmapData == null) { + throw new SpineException("Could not load atlas page texture " + basePath + "/" + path); + } + page.texture = Texture.fromBitmapData(bitmapData); + } + + public function loadRegion(region:TextureAtlasRegion):Void { + region.texture = region.page.texture; + } + + public function unloadPage(page:TextureAtlasPage):Void { + cast(page.texture, Texture).dispose(); + } +} diff --git a/spine-haxe/spine-haxe/spine/atlas/TextureAtlas.hx b/spine-haxe/spine-haxe/spine/atlas/TextureAtlas.hx index 6bb23c5fd..b965a90de 100644 --- a/spine-haxe/spine-haxe/spine/atlas/TextureAtlas.hx +++ b/spine-haxe/spine-haxe/spine/atlas/TextureAtlas.hx @@ -1,5 +1,6 @@ package spine.atlas; +import openfl.utils.Assets; import openfl.errors.ArgumentError; import openfl.utils.ByteArray; import openfl.utils.Dictionary; @@ -10,6 +11,17 @@ class TextureAtlas { private var regions = new Vector(); private var textureLoader:TextureLoader; + public static function fromAssets(path:String) { + var basePath = ""; + var slashIndex = path.lastIndexOf("/"); + if (slashIndex != -1) { + basePath = path.substring(0, slashIndex); + } + + var textureLoader = new AssetsTextureLoader(basePath); + return new TextureAtlas(Assets.getText("assets/raptor.atlas"), textureLoader); + } + /** @param object A String or ByteArray. */ public function new(object:Dynamic, textureLoader:TextureLoader) { if (object == null) { @@ -132,7 +144,7 @@ class TextureAtlas { field(); } } - textureLoader.loadPage(page, line); + textureLoader.loadPage(page, page.name); pages.push(page); } else { region = new TextureAtlasRegion(page, line); diff --git a/spine-haxe/spine-haxe/spine/starling/SkeletonAnimation.hx b/spine-haxe/spine-haxe/spine/starling/SkeletonAnimation.hx deleted file mode 100644 index 99bce0d6f..000000000 --- a/spine-haxe/spine-haxe/spine/starling/SkeletonAnimation.hx +++ /dev/null @@ -1,32 +0,0 @@ -package spine.starling; - -import starling.core.Starling; -import spine.animation.AnimationState; -import spine.animation.AnimationStateData; -import spine.SkeletonData; -import starling.animation.IAnimatable; - -class SkeletonAnimation extends SkeletonSprite implements IAnimatable { - public var state:AnimationState; - - private var functionUpdate:Void->Void; - - public function new(skeletonData:SkeletonData, stateData:AnimationStateData = null) { - super(skeletonData); - state = new AnimationState(stateData != null ? stateData : new AnimationStateData(skeletonData)); - } - - public function advanceTime(time:Float):Void { - var stage = Starling.current.stage; - state.update(time); - state.apply(skeleton); - skeleton.updateWorldTransform(); - this.setRequiresRedraw(); - if (this.functionUpdate != null) - this.functionUpdate(); - } - - public function setFunctionAnimationUpdate(functionUpdate:Void->Void):Void { - this.functionUpdate = functionUpdate; - } -} diff --git a/spine-haxe/spine-haxe/spine/starling/SkeletonSprite.hx b/spine-haxe/spine-haxe/spine/starling/SkeletonSprite.hx index eaf407e1e..e95d595d6 100644 --- a/spine-haxe/spine-haxe/spine/starling/SkeletonSprite.hx +++ b/spine-haxe/spine-haxe/spine/starling/SkeletonSprite.hx @@ -1,37 +1,41 @@ package spine.starling; -import starling.textures.Texture; -import starling.utils.Max; +import starling.animation.IAnimatable; +import openfl.Vector; import openfl.geom.Matrix; import openfl.geom.Point; import openfl.geom.Rectangle; -import openfl.Vector; -import spine.atlas.TextureAtlasRegion; -import spine.attachments.Attachment; -import spine.attachments.ClippingAttachment; -import spine.attachments.MeshAttachment; -import spine.attachments.RegionAttachment; import spine.Bone; import spine.Skeleton; import spine.SkeletonClipping; import spine.SkeletonData; import spine.Slot; +import spine.animation.AnimationState; +import spine.animation.AnimationStateData; +import spine.attachments.Attachment; +import spine.attachments.ClippingAttachment; +import spine.attachments.MeshAttachment; +import spine.attachments.RegionAttachment; import starling.display.BlendMode; import starling.display.DisplayObject; -import starling.display.Image; import starling.rendering.IndexData; import starling.rendering.Painter; import starling.rendering.VertexData; +import starling.textures.Texture; import starling.utils.Color; import starling.utils.MatrixUtil; +import starling.utils.Max; -class SkeletonSprite extends DisplayObject { +class SkeletonSprite extends DisplayObject implements IAnimatable { static private var _tempPoint:Point = new Point(); static private var _tempMatrix:Matrix = new Matrix(); static private var _tempVertices:Vector = new Vector(); static private var blendModes:Vector = Vector.ofArray([BlendMode.NORMAL, BlendMode.ADD, BlendMode.MULTIPLY, BlendMode.SCREEN]); private var _skeleton:Skeleton; + + public var _state:AnimationState; + private var _smoothing:String = "bilinear"; private static var clipper:SkeletonClipping = new SkeletonClipping(); @@ -40,11 +44,12 @@ class SkeletonSprite extends DisplayObject { private var tempLight:spine.Color = new spine.Color(0, 0, 0); private var tempDark:spine.Color = new spine.Color(0, 0, 0); - public function new(skeletonData:SkeletonData) { + public function new(skeletonData:SkeletonData, animationStateData:AnimationStateData = null) { super(); Bone.yDown = true; _skeleton = new Skeleton(skeletonData); _skeleton.updateWorldTransform(); + _state = new AnimationState(animationStateData != null ? animationStateData : new AnimationStateData(skeletonData)); } override public function render(painter:Painter):Void { @@ -285,6 +290,12 @@ class SkeletonSprite extends DisplayObject { return _skeleton; } + public var state(get, never):AnimationState; + + private function get_state():AnimationState { + return _state; + } + public var smoothing(get, set):String; private function get_smoothing():String { @@ -295,4 +306,11 @@ class SkeletonSprite extends DisplayObject { _smoothing = smoothing; return _smoothing; } + + public function advanceTime(time:Float):Void { + _state.update(time); + _state.apply(skeleton); + skeleton.updateWorldTransform(); + this.setRequiresRedraw(); + } } diff --git a/spine-haxe/spine-haxe/spine/starling/StarlingTextureLoader.hx b/spine-haxe/spine-haxe/spine/starling/StarlingTextureLoader.hx deleted file mode 100644 index 0c11acc88..000000000 --- a/spine-haxe/spine-haxe/spine/starling/StarlingTextureLoader.hx +++ /dev/null @@ -1,70 +0,0 @@ -package spine.starling; - -import openfl.display.Bitmap; -import openfl.display.BitmapData; -import openfl.errors.ArgumentError; -import openfl.utils.Object; -import spine.atlas.TextureAtlasPage; -import spine.atlas.TextureAtlasRegion; -import spine.atlas.TextureLoader; -import starling.display.Image; -import starling.textures.Texture; - -class StarlingTextureLoader implements TextureLoader { - public var bitmapDatasOrTextures:Object = {}; - public var singleBitmapDataOrTexture:Dynamic; - - /** @param bitmaps A Bitmap or BitmapData or Texture for an atlas that has only one page, or for a multi page atlas an object where the - * key is the image path and the value is the Bitmap or BitmapData or Texture. */ - public function new(bitmapsOrTextures:Dynamic) { - if (Std.isOfType(bitmapsOrTextures, BitmapData)) { - singleBitmapDataOrTexture = cast(bitmapsOrTextures, BitmapData); - return; - } - if (Std.isOfType(bitmapsOrTextures, Bitmap)) { - singleBitmapDataOrTexture = cast(bitmapsOrTextures, Bitmap).bitmapData; - return; - } - if (Std.isOfType(bitmapsOrTextures, Texture)) { - singleBitmapDataOrTexture = cast(bitmapsOrTextures, Texture); - return; - } - - for (path in Reflect.fields(bitmapsOrTextures)) { - var object:Dynamic = Reflect.getProperty(bitmapsOrTextures, path); - var bitmapDataOrTexture:Dynamic; - if (Std.isOfType(object, BitmapData)) { - bitmapDataOrTexture = cast(object, BitmapData); - } else if (Std.isOfType(object, Bitmap)) { - bitmapDataOrTexture = cast(object, Bitmap).bitmapData; - } else if (Std.isOfType(object, Texture)) { - bitmapDataOrTexture = cast(object, Texture); - } else { - throw new ArgumentError("Object for path \"" + path + "\" must be a Bitmap, BitmapData or Texture: " + object); - } - bitmapDatasOrTextures[path] = bitmapDataOrTexture; - } - } - - public function loadPage(page:TextureAtlasPage, path:String):Void { - var bitmapDataOrTexture:Dynamic = singleBitmapDataOrTexture != null ? singleBitmapDataOrTexture : bitmapDatasOrTextures[path]; - if (bitmapDataOrTexture == null) { - throw new ArgumentError("BitmapData/Texture not found with name: " + path); - } - if (Std.isOfType(bitmapDataOrTexture, BitmapData)) { - var bitmapData:BitmapData = cast(bitmapDataOrTexture, BitmapData); - page.texture = Texture.fromBitmapData(bitmapData); - } else { - var texture:Texture = cast(bitmapDataOrTexture, Texture); - page.texture = texture; - } - } - - public function loadRegion(region:TextureAtlasRegion):Void { - region.texture = region.page.texture; - } - - public function unloadPage(page:TextureAtlasPage):Void { - cast(page.texture, Texture).dispose(); - } -} From 74615a5177af8d90a5246e6a54cdd9c439af8b9d Mon Sep 17 00:00:00 2001 From: Harald Csaszar Date: Wed, 13 Sep 2023 16:24:31 +0200 Subject: [PATCH 2/4] [unity] Timeline extension package: Added static `EditorEvent` callback for editor scripts to react to animation events outside play-mode. --- CHANGELOG.md | 1 + .../SpineAnimationStateMixerBehaviour.cs | 18 ++++++++++++++++-- .../package.json | 2 +- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7096502b9..51fa9e8a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -111,6 +111,7 @@ 3) Copy the original material, add *_Outline* to its name and set the shader to your outline-only shader like `Universal Render Pipeline/Spine/Outline/Skeleton-OutlineOnly` or `Spine/Outline/OutlineOnly-ZWrite`. 4) Assign this *_Outline* material at the new child GameObject's `MeshRenderer` component. If you are using `SkeletonRenderSeparator` and need to enable and disable the `SkeletonRenderSeparator` component at runtime, you can increase the `RenderCombinedMesh` `Reference Renderers` array by one and assign the `SkeletonRenderer` itself at the last entry after the parts renderers. Disabled `MeshRenderer` components will be skipped when combining the final mesh, so the combined mesh is automatically filled from the desired active renderers. + * Timeline extension package: Added static `EditorEvent` callback to allow editor scripts to react to animation events outside of play-mode. Register to the events via `Spine.Unity.Playables.SpineAnimationStateMixerBehaviour.EditorEvent += YourCallback;`. * **Breaking changes** * Made `SkeletonGraphic.unscaledTime` parameter protected, use the new property `UnscaledTime` instead. diff --git a/spine-unity/Modules/com.esotericsoftware.spine.timeline/Runtime/SpineAnimationState/SpineAnimationStateMixerBehaviour.cs b/spine-unity/Modules/com.esotericsoftware.spine.timeline/Runtime/SpineAnimationState/SpineAnimationStateMixerBehaviour.cs index 2a277ced3..553331415 100644 --- a/spine-unity/Modules/com.esotericsoftware.spine.timeline/Runtime/SpineAnimationState/SpineAnimationStateMixerBehaviour.cs +++ b/spine-unity/Modules/com.esotericsoftware.spine.timeline/Runtime/SpineAnimationState/SpineAnimationStateMixerBehaviour.cs @@ -31,7 +31,9 @@ #define SPEED_INCLUDED_IN_CLIP_TIME #endif +#if UNITY_EDITOR #define SPINE_EDITMODEPOSE +#endif using System; using UnityEngine; @@ -250,8 +252,11 @@ namespace Spine.Unity.Playables { } #if SPINE_EDITMODEPOSE + /// Animation event callback for editor scripts when outside of play-mode. + public static event AnimationState.TrackEntryEventDelegate EditorEvent; AnimationState dummyAnimationState; + ExposedList editorAnimationEvents = new ExposedList(); public void PreviewEditModePose (Playable playable, ISkeletonComponent skeletonComponent, IAnimationStateComponent animationStateComponent, @@ -259,6 +264,7 @@ namespace Spine.Unity.Playables { if (Application.isPlaying) return; if (animationStateComponent.IsNullOrDestroyed() || skeletonComponent == null) return; + editorAnimationEvents.Clear(false); int inputCount = playable.GetInputCount(); float rootSpeed = GetRootPlayableSpeed(playable); @@ -341,11 +347,19 @@ namespace Spine.Unity.Playables { } // Apply Pose + dummyAnimationState.Event += EditorEvent; dummyAnimationState.Update(0); dummyAnimationState.Apply(skeleton); + dummyAnimationState.Event -= EditorEvent; } else { - if (toAnimation != null) - toAnimation.Apply(skeleton, 0, toClipTime, clipData.loop, null, clipData.alpha, MixBlend.Setup, MixDirection.In); + if (toAnimation != null) { + toAnimation.Apply(skeleton, 0, toClipTime, clipData.loop, editorAnimationEvents, clipData.alpha, MixBlend.Setup, MixDirection.In); + if (EditorEvent != null) { + foreach (Spine.Event e in editorAnimationEvents) { + EditorEvent(null, e); + } + } + } } skeleton.UpdateWorldTransform(); diff --git a/spine-unity/Modules/com.esotericsoftware.spine.timeline/package.json b/spine-unity/Modules/com.esotericsoftware.spine.timeline/package.json index f7e25a8a3..5f194832e 100644 --- a/spine-unity/Modules/com.esotericsoftware.spine.timeline/package.json +++ b/spine-unity/Modules/com.esotericsoftware.spine.timeline/package.json @@ -2,7 +2,7 @@ "name": "com.esotericsoftware.spine.timeline", "displayName": "Spine Timeline Extensions", "description": "This plugin provides integration of spine-unity for the Unity Timeline.\n\nPrerequisites:\nIt requires a working installation of the spine-unity and spine-csharp runtimes as UPM packages (not as spine-unity unitypackage), version 4.1.\n(See http://esotericsoftware.com/git/spine-runtimes/spine-unity)", - "version": "4.1.12", + "version": "4.1.13", "unity": "2018.3", "author": { "name": "Esoteric Software", From 806ec03e0db66f6dafaa840b6e8e4f813d9ec3c0 Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Wed, 13 Sep 2023 17:36:45 +0200 Subject: [PATCH 3/4] [haxe] Fix binary asset loading, make Skeleton.getBounds() easier to use. --- spine-haxe/.vscode/launch.json | 7 ------ spine-haxe/example/src/BasicExample.hx | 9 ++++---- spine-haxe/project.xml | 1 + spine-haxe/spine-haxe/spine/BinaryInput.hx | 2 +- spine-haxe/spine-haxe/spine/Skeleton.hx | 27 +++++++++++----------- 5 files changed, 20 insertions(+), 26 deletions(-) diff --git a/spine-haxe/.vscode/launch.json b/spine-haxe/.vscode/launch.json index 6c29a8961..09a92a9da 100644 --- a/spine-haxe/.vscode/launch.json +++ b/spine-haxe/.vscode/launch.json @@ -4,13 +4,6 @@ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ - { - "name": "web", - "request": "launch", - "type": "chrome", - "url": "http://localhost:3000", - "webRoot": "${workspaceFolder}" - }, { "name": "lime", "type": "lime", diff --git a/spine-haxe/example/src/BasicExample.hx b/spine-haxe/example/src/BasicExample.hx index a332906df..6e29895fc 100644 --- a/spine-haxe/example/src/BasicExample.hx +++ b/spine-haxe/example/src/BasicExample.hx @@ -1,10 +1,7 @@ -import openfl.utils.Assets; -import spine.SkeletonBinary; +import openfl.geom.Rectangle; import spine.SkeletonData; -import spine.SkeletonJson; import spine.animation.AnimationStateData; import spine.atlas.TextureAtlas; -import spine.attachments.AtlasAttachmentLoader; import spine.starling.SkeletonSprite; import starling.core.Starling; @@ -18,8 +15,10 @@ class BasicExample extends Scene { animationStateData.defaultMix = 0.25; var skeletonSprite = new SkeletonSprite(skeletondata, animationStateData); + var bounds = skeletonSprite.skeleton.getBounds(); + skeletonSprite.scale = Starling.current.stage.stageWidth / bounds.width * 0.5; skeletonSprite.x = Starling.current.stage.stageWidth / 2; - skeletonSprite.y = Starling.current.stage.stageHeight * 0.5; + skeletonSprite.y = Starling.current.stage.stageHeight * 0.9; skeletonSprite.state.setAnimationByName(0, "walk", true); diff --git a/spine-haxe/project.xml b/spine-haxe/project.xml index 620685391..ceeafb6fb 100644 --- a/spine-haxe/project.xml +++ b/spine-haxe/project.xml @@ -11,5 +11,6 @@ + \ No newline at end of file diff --git a/spine-haxe/spine-haxe/spine/BinaryInput.hx b/spine-haxe/spine-haxe/spine/BinaryInput.hx index fc9f0be43..149b5c6e4 100644 --- a/spine-haxe/spine-haxe/spine/BinaryInput.hx +++ b/spine-haxe/spine-haxe/spine/BinaryInput.hx @@ -76,7 +76,7 @@ class BinaryInput { chars += String.fromCharCode(((b & 0x0F) << 12 | (readByte() & 0x3F) << 6 | readByte() & 0x3F)); i += 3; default: - chars += String.fromCharCode(b); + chars += String.fromCharCode(b & 0xff); i++; } } diff --git a/spine-haxe/spine-haxe/spine/Skeleton.hx b/spine-haxe/spine-haxe/spine/Skeleton.hx index 80f567293..cfb55991c 100644 --- a/spine-haxe/spine-haxe/spine/Skeleton.hx +++ b/spine-haxe/spine-haxe/spine/Skeleton.hx @@ -1,5 +1,6 @@ package spine; +import openfl.geom.Rectangle; import openfl.errors.ArgumentError; import openfl.utils.Dictionary; import openfl.Vector; @@ -561,11 +562,10 @@ class Skeleton { return _data.name != null ? _data.name : "Skeleton?"; } - public function getBounds(offset:Vector, size:Vector, temp:Vector):Void { - if (offset == null) - throw new ArgumentError("offset cannot be null."); - if (size == null) - throw new ArgumentError("size cannot be null."); + private var _tempVertices = new Vector(); + private var _bounds = new Rectangle(); + + public function getBounds():Rectangle { var minX:Float = Math.POSITIVE_INFINITY; var minY:Float = Math.POSITIVE_INFINITY; var maxX:Float = Math.NEGATIVE_INFINITY; @@ -576,14 +576,14 @@ class Skeleton { var attachment:Attachment = slot.attachment; if (Std.isOfType(attachment, RegionAttachment)) { verticesLength = 8; - temp.length = verticesLength; - vertices = temp; + _tempVertices.length = verticesLength; + vertices = _tempVertices; cast(attachment, RegionAttachment).computeWorldVertices(slot, vertices, 0, 2); } else if (Std.isOfType(attachment, MeshAttachment)) { var mesh:MeshAttachment = cast(attachment, MeshAttachment); verticesLength = mesh.worldVerticesLength; - temp.length = verticesLength; - vertices = temp; + _tempVertices.length = verticesLength; + vertices = _tempVertices; mesh.computeWorldVertices(slot, 0, verticesLength, vertices, 0, 2); } if (vertices != null) { @@ -599,9 +599,10 @@ class Skeleton { } } } - offset[0] = minX; - offset[1] = minY; - size[0] = maxX - minX; - size[1] = maxY - minY; + _bounds.x = minX; + _bounds.y = minY; + _bounds.width = maxX - minX; + _bounds.height = maxY - minY; + return _bounds; } } From ee502d854be9d17d66ff7d7b830ebc5ce92ade44 Mon Sep 17 00:00:00 2001 From: Harald Csaszar Date: Wed, 13 Sep 2023 17:43:14 +0200 Subject: [PATCH 4/4] [unity] Fixed `RenderCombinedMesh` incorrect index offsets. See commit ed75bff. --- .../Sample Components/RenderCombinedMesh.cs | 21 +++++++------------ .../Assets/Spine Examples/package.json | 2 +- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/spine-unity/Assets/Spine Examples/Scripts/Sample Components/RenderCombinedMesh.cs b/spine-unity/Assets/Spine Examples/Scripts/Sample Components/RenderCombinedMesh.cs index d1dfddcf3..2a5536cb4 100644 --- a/spine-unity/Assets/Spine Examples/Scripts/Sample Components/RenderCombinedMesh.cs +++ b/spine-unity/Assets/Spine Examples/Scripts/Sample Components/RenderCombinedMesh.cs @@ -178,12 +178,12 @@ namespace Spine.Unity.Examples { indexBuffer = new ExposedList(combinedIndexCount); } - if (positionBuffer.Count < combinedVertexCount) { + if (positionBuffer.Count != combinedVertexCount) { positionBuffer.Resize(combinedVertexCount); uvBuffer.Resize(combinedVertexCount); colorBuffer.Resize(combinedVertexCount); } - if (indexBuffer.Count < combinedIndexCount) { + if (indexBuffer.Count != combinedIndexCount) { indexBuffer.Resize(combinedIndexCount); } } @@ -223,32 +223,27 @@ namespace Spine.Unity.Examples { System.Array.Copy(positions, 0, this.positionBuffer.Items, combinedV, vertexCount); System.Array.Copy(uvs, 0, this.uvBuffer.Items, combinedV, vertexCount); System.Array.Copy(colors, 0, this.colorBuffer.Items, combinedV, vertexCount); - combinedV += vertexCount; for (int s = 0, submeshCount = mesh.subMeshCount; s < submeshCount; ++s) { int submeshIndexCount = (int)mesh.GetIndexCount(s); int[] submeshIndices = mesh.GetIndices(s); - System.Array.Copy(submeshIndices, 0, this.indexBuffer.Items, combinedI, submeshIndexCount); + int[] dstIndices = this.indexBuffer.Items; + for (int i = 0; i < submeshIndexCount; ++i) + dstIndices[i + combinedI] = submeshIndices[i] + combinedV; combinedI += submeshIndexCount; } + combinedV += vertexCount; } Mesh combinedMesh = doubleBufferedMesh.GetNext(); + combinedMesh.Clear(); #if SET_VERTICES_HAS_LENGTH_PARAMETER combinedMesh.SetVertices(this.positionBuffer.Items, 0, this.positionBuffer.Count); combinedMesh.SetUVs(0, this.uvBuffer.Items, 0, this.uvBuffer.Count); combinedMesh.SetColors(this.colorBuffer.Items, 0, this.colorBuffer.Count); combinedMesh.SetTriangles(this.indexBuffer.Items, 0, this.indexBuffer.Count, 0); #else - // fill excess with zero positions - { - int listCount = this.positionBuffer.Count; - Vector3[] positionArray = this.positionBuffer.Items; - int arrayLength = positionArray.Length; - Vector3 vector3zero = Vector3.zero; - for (int i = listCount; i < arrayLength; i++) - positionArray[i] = vector3zero; - } + // Note: excess already contains zero positions and indices after ExposedList.Resize(). combinedMesh.vertices = this.positionBuffer.Items; combinedMesh.uv = this.uvBuffer.Items; combinedMesh.colors32 = this.colorBuffer.Items; diff --git a/spine-unity/Assets/Spine Examples/package.json b/spine-unity/Assets/Spine Examples/package.json index fc07e4e58..b4f28beec 100644 --- a/spine-unity/Assets/Spine Examples/package.json +++ b/spine-unity/Assets/Spine Examples/package.json @@ -2,7 +2,7 @@ "name": "com.esotericsoftware.spine.spine-unity-examples", "displayName": "spine-unity Runtime Examples", "description": "This plugin provides example scenes and scripts for the spine-unity runtime.", - "version": "4.1.21", + "version": "4.1.22", "unity": "2018.3", "author": { "name": "Esoteric Software",