diff --git a/spine-flutter/example/lib/main.dart b/spine-flutter/example/lib/main.dart index b1cfa990f..04bab4b79 100644 --- a/spine-flutter/example/lib/main.dart +++ b/spine-flutter/example/lib/main.dart @@ -12,12 +12,24 @@ class ExampleSelector extends StatelessWidget { mainAxisSize: MainAxisSize.min, children: [ ElevatedButton( - child: const Text('Spineboy'), + child: const Text('Simple Animation'), onPressed: () { Navigator.push( context, MaterialPageRoute( - builder: (context) => const Spineboy(), + builder: (context) => const SimpleAnimation(), + ), + ); + }, + ), + spacer, + ElevatedButton( + child: const Text('Animation State Listener'), + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const AnimationStateEvents(), ), ); }, @@ -30,8 +42,8 @@ class ExampleSelector extends StatelessWidget { } } -class Spineboy extends StatelessWidget { - const Spineboy({Key? key}) : super(key: key); +class SimpleAnimation extends StatelessWidget { + const SimpleAnimation({Key? key}) : super(key: key); @override Widget build(BuildContext context) { @@ -52,8 +64,13 @@ class AnimationStateEvents extends StatelessWidget { @override Widget build(BuildContext context) { final controller = SpineWidgetController((controller) { + for (final bone in controller.skeleton!.getBones()) { + print(bone); + } + controller.skeleton?.setScaleX(0.5); + controller.skeleton?.setScaleY(0.5); + controller.skeleton?.setColor(Color(1, 1, 0, 1)); final trackEntry = controller.animationState?.setAnimation(0, "walk", true); - controller.skeleton.setColor(1, 0, 0, 1); }); return Scaffold( diff --git a/spine-flutter/lib/spine_flutter.dart b/spine-flutter/lib/spine_flutter.dart index 0895b63b1..1b36a8571 100644 --- a/spine-flutter/lib/spine_flutter.dart +++ b/spine-flutter/lib/spine_flutter.dart @@ -80,41 +80,41 @@ class Atlas { } class SkeletonData { - final Pointer _skeletonData; + final spine_skeleton_data _skeletonData; bool _disposed; SkeletonData(this._skeletonData): _disposed = false; static SkeletonData fromJson(Atlas atlas, String json) { final jsonNative = json.toNativeUtf8(); - final skeletonData = _bindings.spine_skeleton_data_load_json(atlas._atlas, jsonNative.cast()); - if (skeletonData.ref.error.address != nullptr.address) { - final Pointer error = skeletonData.ref.error.cast(); + final result = _bindings.spine_skeleton_data_load_json(atlas._atlas, jsonNative.cast()); + if (result.error.address != nullptr.address) { + final Pointer error = result.error.cast(); final message = error.toDartString(); - _bindings.spine_skeleton_data_result_dispose(skeletonData); + calloc.free(error); throw Exception("Couldn't load skeleton data: $message"); } - return SkeletonData(skeletonData); + return SkeletonData(result.skeletonData); } static SkeletonData fromBinary(Atlas atlas, Uint8List binary) { final Pointer binaryNative = malloc.allocate(binary.lengthInBytes); binaryNative.asTypedList(binary.lengthInBytes).setAll(0, binary); - final skeletonData = _bindings.spine_skeleton_data_load_binary(atlas._atlas, binaryNative.cast(), binary.lengthInBytes); + final result = _bindings.spine_skeleton_data_load_binary(atlas._atlas, binaryNative.cast(), binary.lengthInBytes); malloc.free(binaryNative); - if (skeletonData.ref.error.address != nullptr.address) { - final Pointer error = skeletonData.ref.error.cast(); + if (result.error.address != nullptr.address) { + final Pointer error = result.error.cast(); final message = error.toDartString(); - _bindings.spine_skeleton_data_result_dispose(skeletonData); + calloc.free(error); throw Exception("Couldn't load skeleton data: $message"); } - return SkeletonData(skeletonData); + return SkeletonData(result.skeletonData); } void dispose() { if (_disposed) return; _disposed = true; - _bindings.spine_skeleton_data_result_dispose(_skeletonData); + _bindings.spine_skeleton_data_dispose(_skeletonData); } } @@ -136,6 +136,21 @@ class Attachment { Attachment(this._attachment); } +class Skin { + final spine_skin _skin; + + Skin(this._skin); +} + +class Color { + double r; + double g; + double b; + double a; + + Color(this.r, this.g, this.b, this.a); +} + class IkConstraint { final spine_ik_constraint _constraint; @@ -154,6 +169,15 @@ class PathConstraint { PathConstraint(this._constraint); } +class Bounds { + double x; + double y; + double width; + double height; + + Bounds(this.x, this.y, this.width, this.height); +} + class Skeleton { final spine_skeleton _skeleton; @@ -266,6 +290,140 @@ class Skeleton { if (constraint.address == nullptr.address) return null; return PathConstraint(constraint); } + + /// Returns the axis aligned bounding box (AABB) of the region and mesh attachments for the current pose. + /// @param outX The horizontal distance between the skeleton origin and the left side of the AABB. + /// @param outY The vertical distance between the skeleton origin and the bottom side of the AABB. + /// @param outWidth The width of the AABB + /// @param outHeight The height of the AABB. + /// @param outVertexBuffer Reference to hold a Vector of floats. This method will assign it with new floats as needed. + Bounds getBounds() { + final nativeBounds = _bindings.spine_skeleton_get_bounds(_skeleton); + return Bounds(nativeBounds.x, nativeBounds.y, nativeBounds.width, nativeBounds.height); + } + + Bone? getRootBone() { + final bone = _bindings.spine_skeleton_get_root_bone(_skeleton); + if (bone.address == nullptr.address) return null; + return Bone(bone); + } + + SkeletonData? getData() { + final data = _bindings.spine_skeleton_get_data(_skeleton); + if (data.address == nullptr.address) return null; + return SkeletonData(data); + } + + List getBones() { + final List bones = []; + final numBones = _bindings.spine_skeleton_get_num_bones(_skeleton); + final nativeBones = _bindings.spine_skeleton_get_bones(_skeleton); + for (int i = 0; i < numBones; i++) { + bones.add(Bone(nativeBones[i])); + } + return bones; + } + + List getSlots() { + final List slots = []; + final numSlots = _bindings.spine_skeleton_get_num_slots(_skeleton); + final nativeSlots = _bindings.spine_skeleton_get_slots(_skeleton); + for (int i = 0; i < numSlots; i++) { + slots.add(Slot(nativeSlots[i])); + } + return slots; + } + + List getDrawOrder() { + final List slots = []; + final numSlots = _bindings.spine_skeleton_get_num_draw_order(_skeleton); + final nativeDrawOrder = _bindings.spine_skeleton_get_draw_order(_skeleton); + for (int i = 0; i < numSlots; i++) { + slots.add(Slot(nativeDrawOrder[i])); + } + return slots; + } + + List getIkConstraints() { + final List constraints = []; + final numConstraints = _bindings.spine_skeleton_get_num_ik_constraints(_skeleton); + final nativeConstraints = _bindings.spine_skeleton_get_ik_constraints(_skeleton); + for (int i = 0; i < numConstraints; i++) { + constraints.add(IkConstraint(nativeConstraints[i])); + } + return constraints; + } + + List getPathConstraints() { + final List constraints = []; + final numConstraints = _bindings.spine_skeleton_get_num_path_constraints(_skeleton); + final nativeConstraints = _bindings.spine_skeleton_get_path_constraints(_skeleton); + for (int i = 0; i < numConstraints; i++) { + constraints.add(PathConstraint(nativeConstraints[i])); + } + return constraints; + } + + List getTransformConstraints() { + final List constraints = []; + final numConstraints = _bindings.spine_skeleton_get_num_transform_constraints(_skeleton); + final nativeConstraints = _bindings.spine_skeleton_get_transform_constraints(_skeleton); + for (int i = 0; i < numConstraints; i++) { + constraints.add(TransformConstraint(nativeConstraints[i])); + } + return constraints; + } + + Skin? getSkin() { + final skin = _bindings.spine_skeleton_get_skin(_skeleton); + if (skin.address == nullptr.address) return null; + return Skin(skin); + } + + Color getColor() { + final color = _bindings.spine_skeleton_get_color(_skeleton); + return Color(color.r, color.g, color.b, color.a); + } + + void setColor(Color color) { + _bindings.spine_skeleton_set_color(_skeleton, color.r, color.g, color.b, color.a); + } + + void setPosition(double x, double y) { + _bindings.spine_skeleton_set_position(_skeleton, x, y); + } + + double getX() { + return _bindings.spine_skeleton_get_x(_skeleton); + } + + void setX(double x) { + _bindings.spine_skeleton_set_x(_skeleton, x); + } + + double getY() { + return _bindings.spine_skeleton_get_x(_skeleton); + } + + void setY(double y) { + _bindings.spine_skeleton_set_y(_skeleton, y); + } + + double getScaleX() { + return _bindings.spine_skeleton_get_scale_x(_skeleton); + } + + void setScaleX(double scaleX) { + _bindings.spine_skeleton_set_scale_x(_skeleton, scaleX); + } + + double getScaleY() { + return _bindings.spine_skeleton_get_scale_x(_skeleton); + } + + void setScaleY(double scaleY) { + _bindings.spine_skeleton_set_scale_y(_skeleton, scaleY); + } } class Animation { @@ -659,7 +817,7 @@ class SkeletonDrawable { bool _disposed; SkeletonDrawable(this.atlas, this.skeletonData, this._ownsData): _disposed = false { - _drawable = _bindings.spine_skeleton_drawable_create(skeletonData._skeletonData.ref.skeletonData.cast()); + _drawable = _bindings.spine_skeleton_drawable_create(skeletonData._skeletonData); skeleton = Skeleton(_drawable.ref.skeleton); animationState = AnimationState(_drawable.ref.animationState); } diff --git a/spine-flutter/lib/spine_flutter_bindings_generated.dart b/spine-flutter/lib/spine_flutter_bindings_generated.dart index 1b2f83659..4e36801b5 100644 --- a/spine-flutter/lib/spine_flutter_bindings_generated.dart +++ b/spine-flutter/lib/spine_flutter_bindings_generated.dart @@ -82,7 +82,7 @@ class SpineFlutterBindings { late final _spine_atlas_dispose = _spine_atlas_disposePtr .asFunction)>(); - ffi.Pointer spine_skeleton_data_load_json( + spine_skeleton_data_result spine_skeleton_data_load_json( ffi.Pointer atlas, ffi.Pointer skeletonData, ) { @@ -94,15 +94,14 @@ class SpineFlutterBindings { late final _spine_skeleton_data_load_jsonPtr = _lookup< ffi.NativeFunction< - ffi.Pointer Function( - ffi.Pointer, + spine_skeleton_data_result Function(ffi.Pointer, ffi.Pointer)>>('spine_skeleton_data_load_json'); late final _spine_skeleton_data_load_json = _spine_skeleton_data_load_jsonPtr.asFunction< - ffi.Pointer Function( + spine_skeleton_data_result Function( ffi.Pointer, ffi.Pointer)>(); - ffi.Pointer spine_skeleton_data_load_binary( + spine_skeleton_data_result spine_skeleton_data_load_binary( ffi.Pointer atlas, ffi.Pointer skeletonData, int length, @@ -116,33 +115,31 @@ class SpineFlutterBindings { late final _spine_skeleton_data_load_binaryPtr = _lookup< ffi.NativeFunction< - ffi.Pointer Function( + spine_skeleton_data_result Function( ffi.Pointer, ffi.Pointer, ffi.Int32)>>('spine_skeleton_data_load_binary'); late final _spine_skeleton_data_load_binary = _spine_skeleton_data_load_binaryPtr.asFunction< - ffi.Pointer Function( + spine_skeleton_data_result Function( ffi.Pointer, ffi.Pointer, int)>(); - void spine_skeleton_data_result_dispose( - ffi.Pointer skeletonData, + void spine_skeleton_data_dispose( + spine_skeleton_data skeletonData, ) { - return _spine_skeleton_data_result_dispose( + return _spine_skeleton_data_dispose( skeletonData, ); } - late final _spine_skeleton_data_result_disposePtr = _lookup< - ffi.NativeFunction< - ffi.Void Function(ffi.Pointer)>>( - 'spine_skeleton_data_result_dispose'); - late final _spine_skeleton_data_result_dispose = - _spine_skeleton_data_result_disposePtr - .asFunction)>(); + late final _spine_skeleton_data_disposePtr = + _lookup>( + 'spine_skeleton_data_dispose'); + late final _spine_skeleton_data_dispose = _spine_skeleton_data_disposePtr + .asFunction(); ffi.Pointer spine_skeleton_drawable_create( - ffi.Pointer skeletonData, + spine_skeleton_data skeletonData, ) { return _spine_skeleton_drawable_create( skeletonData, @@ -150,14 +147,12 @@ class SpineFlutterBindings { } late final _spine_skeleton_drawable_createPtr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Pointer)>>( - 'spine_skeleton_drawable_create'); + ffi.NativeFunction< + ffi.Pointer Function( + spine_skeleton_data)>>('spine_skeleton_drawable_create'); late final _spine_skeleton_drawable_create = _spine_skeleton_drawable_createPtr.asFunction< - ffi.Pointer Function( - ffi.Pointer)>(); + ffi.Pointer Function(spine_skeleton_data)>(); ffi.Pointer spine_skeleton_drawable_render( ffi.Pointer drawable, diff --git a/spine-flutter/src/spine_flutter.cpp b/spine-flutter/src/spine_flutter.cpp index 7e0c8cbfe..848e703bf 100644 --- a/spine-flutter/src/spine_flutter.cpp +++ b/spine-flutter/src/spine_flutter.cpp @@ -61,42 +61,40 @@ FFI_PLUGIN_EXPORT void spine_atlas_dispose(spine_atlas *atlas) { SpineExtension::free(atlas, __FILE__, __LINE__); } -FFI_PLUGIN_EXPORT spine_skeleton_data_result *spine_skeleton_data_load_json(spine_atlas *atlas, const char *skeletonData) { +FFI_PLUGIN_EXPORT spine_skeleton_data_result spine_skeleton_data_load_json(spine_atlas *atlas, const char *skeletonData) { + spine_skeleton_data_result result = { nullptr, nullptr }; Bone::setYDown(true); - if (!atlas) return nullptr; - if (!atlas->atlas) return nullptr; - if (!skeletonData) return nullptr; + if (!atlas) return result; + if (!atlas->atlas) return result; + if (!skeletonData) return result; SkeletonJson json((Atlas*)atlas->atlas); SkeletonData *data = json.readSkeletonData(skeletonData); - spine_skeleton_data_result *result = SpineExtension::calloc(1, __FILE__, __LINE__); - result->skeletonData = data; + result.skeletonData = data; if (!json.getError().isEmpty()) { - result->error = strdup(json.getError().buffer()); + result.error = strdup(json.getError().buffer()); } return result; } -FFI_PLUGIN_EXPORT spine_skeleton_data_result* spine_skeleton_data_load_binary(spine_atlas *atlas, const unsigned char *skeletonData, int32_t length) { +FFI_PLUGIN_EXPORT spine_skeleton_data_result spine_skeleton_data_load_binary(spine_atlas *atlas, const unsigned char *skeletonData, int32_t length) { + spine_skeleton_data_result result = { nullptr, nullptr }; Bone::setYDown(true); - if (!atlas) return nullptr; - if (!atlas->atlas) return nullptr; - if (!skeletonData) return nullptr; - if (length <= 0) return nullptr; + if (!atlas) return result; + if (!atlas->atlas) return result; + if (!skeletonData) return result; + if (length <= 0) return result; SkeletonBinary binary((Atlas*)atlas->atlas); SkeletonData *data = binary.readSkeletonData(skeletonData, length); - spine_skeleton_data_result *result = SpineExtension::calloc(1, __FILE__, __LINE__); - result->skeletonData = data; + result.skeletonData = data; if (!binary.getError().isEmpty()) { - result->error = strdup(binary.getError().buffer()); + result.error = strdup(binary.getError().buffer()); } return result; } -FFI_PLUGIN_EXPORT void spine_skeleton_data_result_dispose(spine_skeleton_data_result *skeletonData) { +FFI_PLUGIN_EXPORT void spine_skeleton_data_dispose(spine_skeleton_data skeletonData) { if (!skeletonData) return; - if (skeletonData->skeletonData) delete (SkeletonData*)skeletonData->skeletonData; - if (skeletonData->error) free(skeletonData->error); - SpineExtension::free(skeletonData, __FILE__, __LINE__); + delete (SkeletonData*)skeletonData; } spine_render_command *spine_render_command_create(int32_t numVertices, int32_t numIndices, spine_blend_mode blendMode, int pageIndex) { @@ -122,7 +120,7 @@ void spine_render_command_dispose(spine_render_command *cmd) { SpineExtension::free(cmd, __FILE__, __LINE__); } -FFI_PLUGIN_EXPORT spine_skeleton_drawable *spine_skeleton_drawable_create(spine_skeleton_data *skeletonData) { +FFI_PLUGIN_EXPORT spine_skeleton_drawable *spine_skeleton_drawable_create(spine_skeleton_data skeletonData) { spine_skeleton_drawable *drawable = SpineExtension::calloc(1, __FILE__, __LINE__); drawable->skeleton = new Skeleton((SkeletonData*)skeletonData); AnimationState *state = new AnimationState(new AnimationStateData((SkeletonData*)skeletonData)); diff --git a/spine-flutter/src/spine_flutter.h b/spine-flutter/src/spine_flutter.h index 6a06388ac..dcac2cd11 100644 --- a/spine-flutter/src/spine_flutter.h +++ b/spine-flutter/src/spine_flutter.h @@ -108,11 +108,11 @@ FFI_PLUGIN_EXPORT void spine_report_leaks(); FFI_PLUGIN_EXPORT spine_atlas* spine_atlas_load(const char *atlasData); FFI_PLUGIN_EXPORT void spine_atlas_dispose(spine_atlas *atlas); -FFI_PLUGIN_EXPORT spine_skeleton_data_result* spine_skeleton_data_load_json(spine_atlas *atlas, const char *skeletonData); -FFI_PLUGIN_EXPORT spine_skeleton_data_result* spine_skeleton_data_load_binary(spine_atlas *atlas, const unsigned char *skeletonData, int32_t length); -FFI_PLUGIN_EXPORT void spine_skeleton_data_result_dispose(spine_skeleton_data_result *skeletonData); +FFI_PLUGIN_EXPORT spine_skeleton_data_result spine_skeleton_data_load_json(spine_atlas *atlas, const char *skeletonData); +FFI_PLUGIN_EXPORT spine_skeleton_data_result spine_skeleton_data_load_binary(spine_atlas *atlas, const unsigned char *skeletonData, int32_t length); +FFI_PLUGIN_EXPORT void spine_skeleton_data_dispose(spine_skeleton_data skeletonData); -FFI_PLUGIN_EXPORT spine_skeleton_drawable *spine_skeleton_drawable_create(spine_skeleton_data *skeletonData); +FFI_PLUGIN_EXPORT spine_skeleton_drawable *spine_skeleton_drawable_create(spine_skeleton_data skeletonData); FFI_PLUGIN_EXPORT spine_render_command *spine_skeleton_drawable_render(spine_skeleton_drawable *drawable); FFI_PLUGIN_EXPORT void spine_skeleton_drawable_dispose(spine_skeleton_drawable *drawable);