mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-03-26 22:49:01 +08:00
[flutter] handle implicit enum values in codegen, extension for animation state listeners + test
This commit is contained in:
parent
71eb86c15e
commit
0e5de94529
@ -212,9 +212,10 @@ export class DartWriter {
|
|||||||
private transformEnum (cEnum: CEnum): DartEnum {
|
private transformEnum (cEnum: CEnum): DartEnum {
|
||||||
return {
|
return {
|
||||||
name: this.toDartTypeName(cEnum.name),
|
name: this.toDartTypeName(cEnum.name),
|
||||||
values: cEnum.values.map((value) => ({
|
values: cEnum.values.map((value, index) => ({
|
||||||
name: this.toDartEnumValueName(value.name, cEnum.name),
|
name: this.toDartEnumValueName(value.name, cEnum.name),
|
||||||
value: Number.parseInt(value.value ?? "0")
|
// C enums without explicit values are implicitly numbered 0, 1, 2, etc.
|
||||||
|
value: value.value !== undefined ? Number.parseInt(value.value) : index
|
||||||
}))
|
}))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -32,12 +32,12 @@
|
|||||||
/// AttachmentType enum
|
/// AttachmentType enum
|
||||||
enum AttachmentType {
|
enum AttachmentType {
|
||||||
region(0),
|
region(0),
|
||||||
boundingbox(0),
|
boundingbox(1),
|
||||||
mesh(0),
|
mesh(2),
|
||||||
linkedmesh(0),
|
linkedmesh(3),
|
||||||
path(0),
|
path(4),
|
||||||
point(0),
|
point(5),
|
||||||
clipping(0);
|
clipping(6);
|
||||||
|
|
||||||
const AttachmentType(this.value);
|
const AttachmentType(this.value);
|
||||||
final int value;
|
final int value;
|
||||||
|
|||||||
@ -32,9 +32,9 @@
|
|||||||
/// BlendMode enum
|
/// BlendMode enum
|
||||||
enum BlendMode {
|
enum BlendMode {
|
||||||
normal(0),
|
normal(0),
|
||||||
additive(0),
|
additive(1),
|
||||||
multiply(0),
|
multiply(2),
|
||||||
screen(0);
|
screen(3);
|
||||||
|
|
||||||
const BlendMode(this.value);
|
const BlendMode(this.value);
|
||||||
final int value;
|
final int value;
|
||||||
|
|||||||
@ -32,11 +32,11 @@
|
|||||||
/// EventType enum
|
/// EventType enum
|
||||||
enum EventType {
|
enum EventType {
|
||||||
start(0),
|
start(0),
|
||||||
interrupt(0),
|
interrupt(1),
|
||||||
end(0),
|
end(2),
|
||||||
dispose(0),
|
dispose(3),
|
||||||
complete(0),
|
complete(4),
|
||||||
event(0);
|
event(5);
|
||||||
|
|
||||||
const EventType(this.value);
|
const EventType(this.value);
|
||||||
final int value;
|
final int value;
|
||||||
|
|||||||
@ -32,12 +32,12 @@
|
|||||||
/// Format enum
|
/// Format enum
|
||||||
enum Format {
|
enum Format {
|
||||||
alpha(0),
|
alpha(0),
|
||||||
intensity(0),
|
intensity(1),
|
||||||
luminanceAlpha(0),
|
luminanceAlpha(2),
|
||||||
rgb565(0),
|
rgb565(3),
|
||||||
rgba4444(0),
|
rgba4444(4),
|
||||||
rgb888(0),
|
rgb888(5),
|
||||||
rgba8888(0);
|
rgba8888(6);
|
||||||
|
|
||||||
const Format(this.value);
|
const Format(this.value);
|
||||||
final int value;
|
final int value;
|
||||||
|
|||||||
@ -32,10 +32,10 @@
|
|||||||
/// Inherit enum
|
/// Inherit enum
|
||||||
enum Inherit {
|
enum Inherit {
|
||||||
normal(0),
|
normal(0),
|
||||||
onlyTranslation(0),
|
onlyTranslation(1),
|
||||||
noRotationOrReflection(0),
|
noRotationOrReflection(2),
|
||||||
noScale(0),
|
noScale(3),
|
||||||
noScaleOrReflection(0);
|
noScaleOrReflection(4);
|
||||||
|
|
||||||
const Inherit(this.value);
|
const Inherit(this.value);
|
||||||
final int value;
|
final int value;
|
||||||
|
|||||||
@ -32,9 +32,9 @@
|
|||||||
/// MixBlend enum
|
/// MixBlend enum
|
||||||
enum MixBlend {
|
enum MixBlend {
|
||||||
setup(0),
|
setup(0),
|
||||||
first(0),
|
first(1),
|
||||||
replace(0),
|
replace(2),
|
||||||
add(0);
|
add(3);
|
||||||
|
|
||||||
const MixBlend(this.value);
|
const MixBlend(this.value);
|
||||||
final int value;
|
final int value;
|
||||||
|
|||||||
@ -32,7 +32,7 @@
|
|||||||
/// MixDirection enum
|
/// MixDirection enum
|
||||||
enum MixDirection {
|
enum MixDirection {
|
||||||
directionIn(0),
|
directionIn(0),
|
||||||
directionOut(0);
|
directionOut(1);
|
||||||
|
|
||||||
const MixDirection(this.value);
|
const MixDirection(this.value);
|
||||||
final int value;
|
final int value;
|
||||||
|
|||||||
@ -32,9 +32,9 @@
|
|||||||
/// Physics enum
|
/// Physics enum
|
||||||
enum Physics {
|
enum Physics {
|
||||||
none(0),
|
none(0),
|
||||||
reset(0),
|
reset(1),
|
||||||
update(0),
|
update(2),
|
||||||
pose(0);
|
pose(3);
|
||||||
|
|
||||||
const Physics(this.value);
|
const Physics(this.value);
|
||||||
final int value;
|
final int value;
|
||||||
|
|||||||
@ -32,7 +32,7 @@
|
|||||||
/// PositionMode enum
|
/// PositionMode enum
|
||||||
enum PositionMode {
|
enum PositionMode {
|
||||||
fixed(0),
|
fixed(0),
|
||||||
percent(0);
|
percent(1);
|
||||||
|
|
||||||
const PositionMode(this.value);
|
const PositionMode(this.value);
|
||||||
final int value;
|
final int value;
|
||||||
|
|||||||
@ -32,8 +32,8 @@
|
|||||||
/// RotateMode enum
|
/// RotateMode enum
|
||||||
enum RotateMode {
|
enum RotateMode {
|
||||||
tangent(0),
|
tangent(0),
|
||||||
chain(0),
|
chain(1),
|
||||||
chainScale(0);
|
chainScale(2);
|
||||||
|
|
||||||
const RotateMode(this.value);
|
const RotateMode(this.value);
|
||||||
final int value;
|
final int value;
|
||||||
|
|||||||
@ -32,9 +32,9 @@
|
|||||||
/// SpacingMode enum
|
/// SpacingMode enum
|
||||||
enum SpacingMode {
|
enum SpacingMode {
|
||||||
length(0),
|
length(0),
|
||||||
fixed(0),
|
fixed(1),
|
||||||
percent(0),
|
percent(2),
|
||||||
proportional(0);
|
proportional(3);
|
||||||
|
|
||||||
const SpacingMode(this.value);
|
const SpacingMode(this.value);
|
||||||
final int value;
|
final int value;
|
||||||
|
|||||||
@ -6749,6 +6749,83 @@ class SpineDartBindings {
|
|||||||
_spine_skeleton_drawable_get_animation_state_eventsPtr
|
_spine_skeleton_drawable_get_animation_state_eventsPtr
|
||||||
.asFunction<spine_animation_state_events Function(spine_skeleton_drawable)>();
|
.asFunction<spine_animation_state_events Function(spine_skeleton_drawable)>();
|
||||||
|
|
||||||
|
/// Animation state events functions
|
||||||
|
int spine_animation_state_events_get_num_events(
|
||||||
|
spine_animation_state_events events,
|
||||||
|
) {
|
||||||
|
return _spine_animation_state_events_get_num_events(
|
||||||
|
events,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
late final _spine_animation_state_events_get_num_eventsPtr =
|
||||||
|
_lookup<ffi.NativeFunction<ffi.Int32 Function(spine_animation_state_events)>>(
|
||||||
|
'spine_animation_state_events_get_num_events');
|
||||||
|
late final _spine_animation_state_events_get_num_events =
|
||||||
|
_spine_animation_state_events_get_num_eventsPtr.asFunction<int Function(spine_animation_state_events)>();
|
||||||
|
|
||||||
|
int spine_animation_state_events_get_event_type(
|
||||||
|
spine_animation_state_events events,
|
||||||
|
int index,
|
||||||
|
) {
|
||||||
|
return _spine_animation_state_events_get_event_type(
|
||||||
|
events,
|
||||||
|
index,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
late final _spine_animation_state_events_get_event_typePtr =
|
||||||
|
_lookup<ffi.NativeFunction<ffi.Int32 Function(spine_animation_state_events, ffi.Int32)>>(
|
||||||
|
'spine_animation_state_events_get_event_type');
|
||||||
|
late final _spine_animation_state_events_get_event_type =
|
||||||
|
_spine_animation_state_events_get_event_typePtr.asFunction<int Function(spine_animation_state_events, int)>();
|
||||||
|
|
||||||
|
spine_track_entry spine_animation_state_events_get_track_entry(
|
||||||
|
spine_animation_state_events events,
|
||||||
|
int index,
|
||||||
|
) {
|
||||||
|
return _spine_animation_state_events_get_track_entry(
|
||||||
|
events,
|
||||||
|
index,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
late final _spine_animation_state_events_get_track_entryPtr =
|
||||||
|
_lookup<ffi.NativeFunction<spine_track_entry Function(spine_animation_state_events, ffi.Int32)>>(
|
||||||
|
'spine_animation_state_events_get_track_entry');
|
||||||
|
late final _spine_animation_state_events_get_track_entry = _spine_animation_state_events_get_track_entryPtr
|
||||||
|
.asFunction<spine_track_entry Function(spine_animation_state_events, int)>();
|
||||||
|
|
||||||
|
spine_event spine_animation_state_events_get_event(
|
||||||
|
spine_animation_state_events events,
|
||||||
|
int index,
|
||||||
|
) {
|
||||||
|
return _spine_animation_state_events_get_event(
|
||||||
|
events,
|
||||||
|
index,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
late final _spine_animation_state_events_get_eventPtr =
|
||||||
|
_lookup<ffi.NativeFunction<spine_event Function(spine_animation_state_events, ffi.Int32)>>(
|
||||||
|
'spine_animation_state_events_get_event');
|
||||||
|
late final _spine_animation_state_events_get_event =
|
||||||
|
_spine_animation_state_events_get_eventPtr.asFunction<spine_event Function(spine_animation_state_events, int)>();
|
||||||
|
|
||||||
|
void spine_animation_state_events_reset(
|
||||||
|
spine_animation_state_events events,
|
||||||
|
) {
|
||||||
|
return _spine_animation_state_events_reset(
|
||||||
|
events,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
late final _spine_animation_state_events_resetPtr =
|
||||||
|
_lookup<ffi.NativeFunction<ffi.Void Function(spine_animation_state_events)>>(
|
||||||
|
'spine_animation_state_events_reset');
|
||||||
|
late final _spine_animation_state_events_reset =
|
||||||
|
_spine_animation_state_events_resetPtr.asFunction<void Function(spine_animation_state_events)>();
|
||||||
|
|
||||||
/// Skin functions
|
/// Skin functions
|
||||||
spine_skin_entries spine_skin_get_entries(
|
spine_skin_entries spine_skin_get_entries(
|
||||||
spine_skin skin,
|
spine_skin skin,
|
||||||
|
|||||||
@ -32,13 +32,13 @@
|
|||||||
/// TextureFilter enum
|
/// TextureFilter enum
|
||||||
enum TextureFilter {
|
enum TextureFilter {
|
||||||
unknown(0),
|
unknown(0),
|
||||||
nearest(0),
|
nearest(1),
|
||||||
linear(0),
|
linear(2),
|
||||||
mipMap(0),
|
mipMap(3),
|
||||||
mipMapNearestNearest(0),
|
mipMapNearestNearest(4),
|
||||||
mipMapLinearNearest(0),
|
mipMapLinearNearest(5),
|
||||||
mipMapNearestLinear(0),
|
mipMapNearestLinear(6),
|
||||||
mipMapLinearLinear(0);
|
mipMapLinearLinear(7);
|
||||||
|
|
||||||
const TextureFilter(this.value);
|
const TextureFilter(this.value);
|
||||||
final int value;
|
final int value;
|
||||||
|
|||||||
@ -32,8 +32,8 @@
|
|||||||
/// TextureWrap enum
|
/// TextureWrap enum
|
||||||
enum TextureWrap {
|
enum TextureWrap {
|
||||||
mirroredRepeat(0),
|
mirroredRepeat(0),
|
||||||
clampToEdge(0),
|
clampToEdge(1),
|
||||||
repeat(0);
|
repeat(2);
|
||||||
|
|
||||||
const TextureWrap(this.value);
|
const TextureWrap(this.value);
|
||||||
final int value;
|
final int value;
|
||||||
|
|||||||
@ -43,6 +43,12 @@ import 'generated/clipping_attachment.dart';
|
|||||||
import 'generated/path_attachment.dart';
|
import 'generated/path_attachment.dart';
|
||||||
import 'generated/point_attachment.dart';
|
import 'generated/point_attachment.dart';
|
||||||
import 'generated/rtti.dart';
|
import 'generated/rtti.dart';
|
||||||
|
import 'generated/skeleton.dart';
|
||||||
|
import 'generated/animation_state.dart';
|
||||||
|
import 'generated/animation_state_data.dart';
|
||||||
|
import 'generated/track_entry.dart';
|
||||||
|
import 'generated/event.dart';
|
||||||
|
import 'generated/event_type.dart';
|
||||||
|
|
||||||
// Export generated classes
|
// Export generated classes
|
||||||
export 'generated/api.dart';
|
export 'generated/api.dart';
|
||||||
@ -213,3 +219,108 @@ extension SkinExtensions on Skin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Event listener callback for animation state events
|
||||||
|
typedef AnimationStateListener = void Function(EventType type, TrackEntry entry, Event? event);
|
||||||
|
|
||||||
|
/// Convenient drawable that combines skeleton, animation state, and rendering
|
||||||
|
class SkeletonDrawable {
|
||||||
|
final Pointer<spine_skeleton_drawable_wrapper> _drawable;
|
||||||
|
final Map<TrackEntry, AnimationStateListener> _trackEntryListeners = {};
|
||||||
|
AnimationStateListener? _stateListener;
|
||||||
|
|
||||||
|
late final Skeleton skeleton;
|
||||||
|
late final AnimationState animationState;
|
||||||
|
late final AnimationStateData animationStateData;
|
||||||
|
|
||||||
|
SkeletonDrawable(SkeletonData skeletonData)
|
||||||
|
: _drawable = SpineBindings.bindings.spine_skeleton_drawable_create(skeletonData.nativePtr.cast()) {
|
||||||
|
if (_drawable == nullptr) {
|
||||||
|
throw Exception("Failed to create skeleton drawable");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get references to the skeleton and animation state
|
||||||
|
final skeletonPtr = SpineBindings.bindings.spine_skeleton_drawable_get_skeleton(_drawable.cast());
|
||||||
|
skeleton = Skeleton.fromPointer(skeletonPtr);
|
||||||
|
|
||||||
|
final animationStatePtr = SpineBindings.bindings.spine_skeleton_drawable_get_animation_state(_drawable.cast());
|
||||||
|
animationState = AnimationState.fromPointer(animationStatePtr);
|
||||||
|
|
||||||
|
final animationStateDataPtr =
|
||||||
|
SpineBindings.bindings.spine_skeleton_drawable_get_animation_state_data(_drawable.cast());
|
||||||
|
animationStateData = AnimationStateData.fromPointer(animationStateDataPtr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update the animation state and process events
|
||||||
|
void update(double delta) {
|
||||||
|
// Update animation state
|
||||||
|
animationState.update(delta);
|
||||||
|
|
||||||
|
// Process events
|
||||||
|
final eventsPtr = SpineBindings.bindings.spine_skeleton_drawable_get_animation_state_events(_drawable.cast());
|
||||||
|
if (eventsPtr != nullptr) {
|
||||||
|
final numEvents = SpineBindings.bindings.spine_animation_state_events_get_num_events(eventsPtr.cast());
|
||||||
|
|
||||||
|
for (int i = 0; i < numEvents; i++) {
|
||||||
|
// Get event type
|
||||||
|
final eventTypeValue = SpineBindings.bindings.spine_animation_state_events_get_event_type(eventsPtr.cast(), i);
|
||||||
|
final type = EventType.fromValue(eventTypeValue);
|
||||||
|
|
||||||
|
// Get track entry
|
||||||
|
final trackEntryPtr = SpineBindings.bindings.spine_animation_state_events_get_track_entry(eventsPtr.cast(), i);
|
||||||
|
final trackEntry = TrackEntry.fromPointer(trackEntryPtr);
|
||||||
|
|
||||||
|
// Get event (may be null)
|
||||||
|
final eventPtr = SpineBindings.bindings.spine_animation_state_events_get_event(eventsPtr.cast(), i);
|
||||||
|
final event = eventPtr.address == 0 ? null : Event.fromPointer(eventPtr);
|
||||||
|
|
||||||
|
// Call track entry listener if registered
|
||||||
|
if (_trackEntryListeners.containsKey(trackEntry)) {
|
||||||
|
_trackEntryListeners[trackEntry]?.call(type, trackEntry, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call global state listener
|
||||||
|
_stateListener?.call(type, trackEntry, event);
|
||||||
|
|
||||||
|
// Remove listener if track entry is being disposed
|
||||||
|
if (type == EventType.dispose) {
|
||||||
|
_trackEntryListeners.remove(trackEntry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset events for next frame
|
||||||
|
SpineBindings.bindings.spine_animation_state_events_reset(eventsPtr.cast());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply animation state to skeleton
|
||||||
|
animationState.apply(skeleton);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set a listener for all animation state events
|
||||||
|
void setListener(AnimationStateListener? listener) {
|
||||||
|
_stateListener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Internal method to set a listener for a specific track entry
|
||||||
|
void _setTrackEntryListener(TrackEntry entry, AnimationStateListener? listener) {
|
||||||
|
if (listener == null) {
|
||||||
|
_trackEntryListeners.remove(entry);
|
||||||
|
} else {
|
||||||
|
_trackEntryListeners[entry] = listener;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void dispose() {
|
||||||
|
_trackEntryListeners.clear();
|
||||||
|
_stateListener = null;
|
||||||
|
SpineBindings.bindings.spine_skeleton_drawable_dispose(_drawable.cast());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extension to add setListener to TrackEntry
|
||||||
|
extension TrackEntryExtensions on TrackEntry {
|
||||||
|
/// Set a listener for events from this track entry
|
||||||
|
void setListener(SkeletonDrawable drawable, AnimationStateListener? listener) {
|
||||||
|
drawable._setTrackEntryListener(this, listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
74
spine-flutter/test/skeleton_drawable_test.dart
Normal file
74
spine-flutter/test/skeleton_drawable_test.dart
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
import 'package:spine_flutter/spine_dart.dart';
|
||||||
|
|
||||||
|
void main() async {
|
||||||
|
print('Testing SkeletonDrawable and event listeners...');
|
||||||
|
|
||||||
|
// Initialize with debug extension enabled
|
||||||
|
await initSpineDart(enableMemoryDebugging: true);
|
||||||
|
|
||||||
|
// Load atlas and skeleton data
|
||||||
|
final atlasData = File('../example/assets/spineboy.atlas').readAsStringSync();
|
||||||
|
final atlas = loadAtlas(atlasData);
|
||||||
|
final skeletonJson = File('../example/assets/spineboy-pro.json').readAsStringSync();
|
||||||
|
final skeletonData = loadSkeletonDataJson(atlas, skeletonJson);
|
||||||
|
|
||||||
|
// Create skeleton drawable
|
||||||
|
final drawable = SkeletonDrawable(skeletonData);
|
||||||
|
print('SkeletonDrawable created successfully');
|
||||||
|
|
||||||
|
// Track events
|
||||||
|
final events = <String>[];
|
||||||
|
|
||||||
|
// Set global animation state listener
|
||||||
|
drawable.setListener((type, entry, event) {
|
||||||
|
final eventName = event?.data.name ?? 'null';
|
||||||
|
events.add('State: $type, event: $eventName');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set an animation
|
||||||
|
final trackEntry = drawable.animationState.setAnimation(0, 'walk', true);
|
||||||
|
print('Set animation: walk');
|
||||||
|
|
||||||
|
// Set track entry specific listener
|
||||||
|
trackEntry.setListener(drawable, (type, entry, event) {
|
||||||
|
final eventName = event?.data.name ?? 'null';
|
||||||
|
events.add('TrackEntry: $type, event: $eventName');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update several times to trigger events
|
||||||
|
print('\nUpdating animation state...');
|
||||||
|
for (int i = 0; i < 5; i++) {
|
||||||
|
drawable.update(0.016); // ~60fps
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print collected events
|
||||||
|
print('\nCollected events:');
|
||||||
|
for (final event in events) {
|
||||||
|
print(' $event');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test switching animations
|
||||||
|
print('\nSwitching to run animation...');
|
||||||
|
events.clear();
|
||||||
|
drawable.animationState.setAnimation(0, 'run', true);
|
||||||
|
|
||||||
|
// Update a few more times
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
drawable.update(0.016);
|
||||||
|
}
|
||||||
|
|
||||||
|
print('\nEvents after switching:');
|
||||||
|
for (final event in events) {
|
||||||
|
print(' $event');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
drawable.dispose();
|
||||||
|
skeletonData.dispose();
|
||||||
|
atlas.dispose();
|
||||||
|
|
||||||
|
// Report memory leaks
|
||||||
|
reportLeaks();
|
||||||
|
print('\nTest complete');
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user