diff --git a/examples/export/runtimes.sh b/examples/export/runtimes.sh index 02c9a1bf3..ebe0fc0ef 100755 --- a/examples/export/runtimes.sh +++ b/examples/export/runtimes.sh @@ -124,6 +124,10 @@ cp -f ../dragon/export/dragon.atlas "$ROOT/spine-flutter/example/assets/" cp -f ../dragon/export/dragon.png "$ROOT/spine-flutter/example/assets/" cp -f ../dragon/export/dragon_*.png "$ROOT/spine-flutter/example/assets/" +cp -f ../celestial-circus/export/celestial-circus-pro.skel "$ROOT/spine-flutter/example/assets/" +cp -f ../celestial-circus/export/celestial-circus.atlas "$ROOT/spine-flutter/example/assets/" +cp -f ../celestial-circus/export/celestial-circus.png "$ROOT/spine-flutter/example/assets/" + echo "spine-godot" rm -f "$ROOT"/spine-godot/example/assets/spineboy/*.atlas rm -f "$ROOT"/spine-godot/example/assets/spineboy/*.png diff --git a/spine-flutter/example/assets/celestial-circus-pro.skel b/spine-flutter/example/assets/celestial-circus-pro.skel new file mode 100644 index 000000000..7ab1a4e67 Binary files /dev/null and b/spine-flutter/example/assets/celestial-circus-pro.skel differ diff --git a/spine-flutter/example/assets/celestial-circus.atlas b/spine-flutter/example/assets/celestial-circus.atlas new file mode 100644 index 000000000..17f32b2a3 --- /dev/null +++ b/spine-flutter/example/assets/celestial-circus.atlas @@ -0,0 +1,173 @@ +celestial-circus.png + size: 1024, 1024 + filter: Linear, Linear + scale: 0.4 +arm-back-down + bounds: 324, 401, 38, 82 + rotate: 90 +arm-back-up + bounds: 290, 44, 83, 116 + rotate: 90 +arm-front-down + bounds: 706, 2, 36, 78 + rotate: 90 +arm-front-up + bounds: 860, 138, 77, 116 +bench + bounds: 725, 256, 189, 48 +body-bottom + bounds: 879, 868, 154, 124 + rotate: 90 +body-top + bounds: 725, 128, 126, 133 + rotate: 90 +chest + bounds: 408, 26, 104, 93 +cloud-back + bounds: 752, 378, 202, 165 +cloud-front + bounds: 2, 2, 325, 196 + rotate: 90 +collar + bounds: 786, 13, 47, 26 +ear + bounds: 1002, 643, 20, 28 +eye-back-shadow + bounds: 428, 395, 14, 10 +eye-front-shadow + bounds: 704, 529, 24, 14 +eye-reflex-back + bounds: 860, 128, 8, 7 + rotate: 90 +eye-reflex-front + bounds: 726, 386, 10, 7 +eye-white-back + bounds: 835, 23, 13, 16 +eye-white-front + bounds: 1005, 1000, 22, 17 + rotate: 90 +eyelashes-down-back + bounds: 232, 329, 11, 6 + rotate: 90 +eyelashes-down-front + bounds: 913, 851, 15, 6 + rotate: 90 +eyelashes-top-back + bounds: 408, 395, 18, 10 +eyelashes-top-front + bounds: 702, 179, 30, 16 + rotate: 90 +face + bounds: 514, 26, 93, 102 + rotate: 90 +feathers-back + bounds: 954, 625, 46, 46 +feathers-front + bounds: 706, 40, 72, 86 +fringe-middle-back + bounds: 200, 6, 33, 52 + rotate: 90 +fringe-middle-front + bounds: 878, 76, 60, 50 + rotate: 90 +fringe-side-back + bounds: 780, 41, 27, 94 + rotate: 90 +fringe-side-front + bounds: 939, 161, 26, 93 +glove-bottom-back + bounds: 954, 572, 51, 41 + rotate: 90 +glove-bottom-front + bounds: 916, 256, 47, 48 +hair-back-1 + bounds: 444, 395, 132, 306 + rotate: 90 +hair-back-2 + bounds: 438, 211, 80, 285 + rotate: 90 +hair-back-3 + bounds: 719, 306, 70, 268 + rotate: 90 +hair-back-4 + bounds: 438, 121, 88, 262 + rotate: 90 +hair-back-5 + bounds: 438, 293, 88, 279 + rotate: 90 +hair-back-6 + bounds: 200, 41, 88, 286 +hair-hat-shadow + bounds: 232, 398, 90, 41 +hand-back + bounds: 954, 673, 60, 47 + rotate: 90 +hand-front + bounds: 967, 172, 53, 60 +hat-back + bounds: 954, 802, 64, 45 + rotate: 90 +hat-front + bounds: 780, 70, 96, 56 +head-back + bounds: 618, 17, 102, 86 + rotate: 90 +jabot + bounds: 967, 234, 70, 55 + rotate: 90 +leg-back + bounds: 232, 441, 210, 333 +leg-front + bounds: 444, 529, 258, 320 +logo-brooch + bounds: 954, 545, 16, 25 +mouth + bounds: 408, 121, 22, 6 +neck + bounds: 232, 342, 39, 56 + rotate: 90 +nose + bounds: 742, 529, 6, 7 + rotate: 90 +nose-highlight + bounds: 719, 300, 4, 4 +nose-shadow + bounds: 869, 128, 7, 8 +pupil-back + bounds: 730, 529, 10, 14 +pupil-front + bounds: 254, 21, 12, 18 +rope-back + bounds: 232, 383, 10, 492 + rotate: 90 +rope-front + bounds: 232, 383, 10, 492 + rotate: 90 +rope-front-bottom + bounds: 954, 735, 42, 65 +skirt + bounds: 2, 776, 440, 246 +sock-bow + bounds: 408, 407, 33, 32 +spine-logo-body + bounds: 879, 853, 13, 32 + rotate: 90 +star-big + bounds: 939, 141, 18, 24 + rotate: 90 +star-medium + bounds: 742, 537, 6, 8 + rotate: 90 +star-small + bounds: 719, 378, 3, 4 + rotate: 90 +underskirt + bounds: 2, 329, 445, 228 + rotate: 90 +underskirt-back + bounds: 444, 851, 433, 171 +wing-back + bounds: 290, 129, 146, 252 +wing-front + bounds: 704, 545, 304, 248 + rotate: 90 diff --git a/spine-flutter/example/assets/celestial-circus.png b/spine-flutter/example/assets/celestial-circus.png new file mode 100644 index 000000000..a5c26ccf2 Binary files /dev/null and b/spine-flutter/example/assets/celestial-circus.png differ diff --git a/spine-flutter/example/lib/main.dart b/spine-flutter/example/lib/main.dart index 0c5b2bc2c..bd372f36c 100644 --- a/spine-flutter/example/lib/main.dart +++ b/spine-flutter/example/lib/main.dart @@ -36,6 +36,7 @@ import 'dress_up.dart'; import 'flame_example.dart'; import 'ik_following.dart'; import 'pause_play_animation.dart'; +import 'physics.dart'; import 'simple_animation.dart'; class ExampleSelector extends StatelessWidget { @@ -119,6 +120,18 @@ class ExampleSelector extends StatelessWidget { ); }, ), + spacer, + ElevatedButton( + child: const Text('Physics'), + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const PhysicsTest(), + ), + ); + }, + ), spacer, ElevatedButton( child: const Text('Flame: Simple Example'), diff --git a/spine-flutter/example/lib/physics.dart b/spine-flutter/example/lib/physics.dart new file mode 100644 index 000000000..9aa410c65 --- /dev/null +++ b/spine-flutter/example/lib/physics.dart @@ -0,0 +1,88 @@ +/// +/// Spine Runtimes License Agreement +/// Last updated July 28, 2023. Replaces all prior versions. +/// +/// Copyright (c) 2013-2023, Esoteric Software LLC +/// +/// Integration of the Spine Runtimes into software or otherwise creating +/// derivative works of the Spine Runtimes is permitted under the terms and +/// conditions of Section 2 of the Spine Editor License Agreement: +/// http://esotericsoftware.com/spine-editor-license +/// +/// Otherwise, it is permitted to integrate the Spine Runtimes into software or +/// otherwise create derivative works of the Spine Runtimes (collectively, +/// "Products"), provided that each user of the Products must obtain their own +/// Spine Editor license and redistribution of the Products in any form must +/// include this license and copyright notice. +/// +/// THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY +/// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +/// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +/// DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY +/// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +/// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, +/// BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND +/// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +/// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE +/// SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +/// + +import 'package:spine_flutter/spine_flutter.dart'; +import 'package:flutter/material.dart'; + +class PhysicsTest extends StatefulWidget { + const PhysicsTest({Key? key}) : super(key: key); + + @override + PhysicsState createState() => PhysicsState(); +} + +class PhysicsState extends State { + late SpineWidgetController controller; + Offset? mousePosition; + Offset? lastMousePosition; + Offset? delta; + + @override + void initState() { + super.initState(); + + controller = SpineWidgetController(onInitialized: (controller) { + controller.animationState.setAnimationByName(0, "eyeblink-long", true); + controller.animationState.setAnimationByName(1, "wings-and-feet", true); + }, onAfterUpdateWorldTransforms: (controller) { + if (lastMousePosition == null) { + lastMousePosition = mousePosition; + return; + } + if (mousePosition == null) { + return; + } + + final dx = mousePosition!.dx - lastMousePosition!.dx; + final dy = mousePosition!.dy - lastMousePosition!.dy; + final position = controller.skeleton.getPosition(); + position.x += dx; + position.y += dy; + controller.skeleton.setPosition(position.x, position.y); + lastMousePosition = mousePosition; + }); + } + + void _updateBonePosition(Offset position) { + mousePosition = controller.toSkeletonCoordinates(position); + } + + @override + Widget build(BuildContext context) { + reportLeaks(); + + return Scaffold( + appBar: AppBar(title: const Text('Physics (drag anywhere)')), + body: GestureDetector( + onPanDown: (drag) => _updateBonePosition(drag.localPosition), + onPanUpdate: (drag) => _updateBonePosition(drag.localPosition), + child: SpineWidget.fromAsset("assets/celestial-circus.atlas", "assets/celestial-circus-pro.skel", controller), + )); + } +} diff --git a/spine-flutter/lib/spine_flutter.dart b/spine-flutter/lib/spine_flutter.dart index 0cfa7eac3..02830ecc6 100644 --- a/spine-flutter/lib/spine_flutter.dart +++ b/spine-flutter/lib/spine_flutter.dart @@ -3008,6 +3008,12 @@ class Skeleton { _bindings.spine_skeleton_set_position(_skeleton, x, y); } + Vec2 getPosition() { + final x = _bindings.spine_skeleton_get_x(_skeleton); + final y = _bindings.spine_skeleton_get_y(_skeleton); + return Vec2(x, y); + } + /// Sets the skeleton X position, which is added to the root bone worldX position. /// /// Bones that do not inherit translation are still affected by this property.