diff --git a/cocos/spine/skeleton.ts b/cocos/spine/skeleton.ts index dabf158481a..e4879c40abc 100644 --- a/cocos/spine/skeleton.ts +++ b/cocos/spine/skeleton.ts @@ -298,8 +298,6 @@ export class Skeleton extends UIRenderer { * @engineInternal */ public _curFrame: AnimationFrame | null = null; - // Is need update skeltonData - protected _needUpdateSkeltonData = true; protected _listener: TrackEntryListeners | null = null; /** @@ -328,6 +326,8 @@ export class Skeleton extends UIRenderer { private _eventListenerID: number = -1; private _slotTextures: Map | null = null; + private _isRenderable: boolean = false; + constructor () { super(); this._useVertexOpacity = true; @@ -695,11 +695,9 @@ export class Skeleton extends UIRenderer { */ public onEnable (): void { super.onEnable(); - if (this._instance) { - this._instance.enable = true; - } this._flushAssembler(); SkeletonSystem.getInstance().add(this); + this._isRenderable = true; } /** * @en Be called when component state becomes disabled. @@ -707,10 +705,8 @@ export class Skeleton extends UIRenderer { */ public onDisable (): void { super.onDisable(); - if (this._instance) { - this._instance.enable = false; - } SkeletonSystem.getInstance().remove(this); + this._isRenderable = false; } public onDestroy (): void { @@ -771,12 +767,14 @@ export class Skeleton extends UIRenderer { this._skeleton = null!; this._textures = []; this._refreshInspector(); + if (this._isRenderable) { + SkeletonSystem.getInstance().remove(this); + } return; } if (this._instance) { this._instance.dtRate = this._timeScale * timeScale; } - this._needUpdateSkeltonData = false; //const data = this.skeletonData?.getRuntimeData(); //if (!data) return; //this.setSkeletonData(data); @@ -858,6 +856,9 @@ export class Skeleton extends UIRenderer { this._state = this._instance!.getAnimationState(); this._instance!.setPremultipliedAlpha(this._premultipliedAlpha); } + if (this._isRenderable) { + SkeletonSystem.getInstance().add(this); + } // Recreate render data and mark dirty this._flushAssembler(); } diff --git a/native/cocos/editor-support/spine-creator-support/SkeletonAnimation.cpp b/native/cocos/editor-support/spine-creator-support/SkeletonAnimation.cpp index d3b60c17bac..da8da068ea8 100644 --- a/native/cocos/editor-support/spine-creator-support/SkeletonAnimation.cpp +++ b/native/cocos/editor-support/spine-creator-support/SkeletonAnimation.cpp @@ -46,17 +46,11 @@ struct TrackEntryListeners { }; void animationCallback(AnimationState *state, EventType type, TrackEntry *entry, Event *event) { - (static_cast(state->getRendererObject()))->onAnimationStateEvent(entry, type, event); + (static_cast(state->getRendererObject()))->cacheAnimationEvent(entry, type, event); } void trackEntryCallback(AnimationState *state, EventType type, TrackEntry *entry, Event *event) { - (static_cast(state->getRendererObject()))->onTrackEntryEvent(entry, type, event); - if (type == EventType_Dispose) { - if (entry->getRendererObject()) { - delete static_cast(entry->getRendererObject()); - entry->setRendererObject(nullptr); - } - } + (static_cast(state->getRendererObject()))->cacheTrackEvent(entry, type, event); } static TrackEntryListeners *getListeners(TrackEntry *entry) { @@ -72,6 +66,24 @@ void SkeletonAnimation::setGlobalTimeScale(float timeScale) { GlobalTimeScale = timeScale; } +void SkeletonAnimation::cacheAnimationEvent(spine::TrackEntry *entry, spine::EventType type, spine::Event *event) { + _vecAnimationEvents.push_back({type, entry, event}); +} + +void SkeletonAnimation::cacheTrackEvent(spine::TrackEntry *entry, spine::EventType type, spine::Event *event) { + _vecTrackEvents.push_back({type, entry, event}); +} + +void SkeletonAnimation::dispatchEvents() { + auto animationEvents = std::move(_vecAnimationEvents); + auto trackEvents = std::move(_vecTrackEvents); + for (const auto &info : animationEvents) { + onAnimationStateEvent(info.entry, info.type, info.event); + } + for (const auto &info : trackEvents) { + onTrackEntryEvent(info.entry, info.type, info.event); + } +} SkeletonAnimation *SkeletonAnimation::create() { auto *skeleton = new SkeletonAnimation(); return skeleton; @@ -104,7 +116,10 @@ void SkeletonAnimation::initialize() { _state->setListener(animationCallback); } -SkeletonAnimation::SkeletonAnimation() = default; +SkeletonAnimation::SkeletonAnimation() { + _vecAnimationEvents.reserve(EventType::EventType_Event + 1); + _vecTrackEvents.reserve(EventType::EventType_Event + 1); +} SkeletonAnimation::~SkeletonAnimation() { _startListener = nullptr; @@ -133,6 +148,7 @@ void SkeletonAnimation::update(float deltaTime) { _skeleton->updateWorldTransform(Physics::Physics_Update); #endif } + dispatchEvents(); } void SkeletonAnimation::setAnimationStateData(AnimationStateData *stateData) { @@ -265,6 +281,8 @@ void SkeletonAnimation::onTrackEntryEvent(TrackEntry *entry, EventType type, Eve break; case EventType_Dispose: if (listeners->disposeListener) listeners->disposeListener(entry); + delete static_cast(entry->getRendererObject()); + entry->setRendererObject(nullptr); break; case EventType_Complete: if (listeners->completeListener) listeners->completeListener(entry); diff --git a/native/cocos/editor-support/spine-creator-support/SkeletonAnimation.h b/native/cocos/editor-support/spine-creator-support/SkeletonAnimation.h index af53afaf603..b06969da8de 100644 --- a/native/cocos/editor-support/spine-creator-support/SkeletonAnimation.h +++ b/native/cocos/editor-support/spine-creator-support/SkeletonAnimation.h @@ -30,9 +30,15 @@ #pragma once #include "spine-creator-support/SkeletonRenderer.h" #include "spine/spine.h" +#include namespace cc { +struct CacheEventInfo { + spine::EventType type{spine::EventType::EventType_Start}; + spine::TrackEntry *entry{nullptr}; + spine::Event *event{nullptr}; +}; typedef std::function StartListener; typedef std::function InterruptListener; typedef std::function EndListener; @@ -82,6 +88,10 @@ class SkeletonAnimation : public cc::SkeletonRenderer { virtual void onAnimationStateEvent(spine::TrackEntry *entry, spine::EventType type, spine::Event *event); virtual void onTrackEntryEvent(spine::TrackEntry *entry, spine::EventType type, spine::Event *event); + void cacheAnimationEvent(spine::TrackEntry *entry, spine::EventType type, spine::Event *event); + void cacheTrackEvent(spine::TrackEntry *entry, spine::EventType type, spine::Event *event); + void dispatchEvents(); + spine::AnimationState *getState() const; SkeletonAnimation(); @@ -103,6 +113,8 @@ class SkeletonAnimation : public cc::SkeletonRenderer { private: typedef cc::SkeletonRenderer super; + std::vector _vecAnimationEvents; + std::vector _vecTrackEvents; }; } // namespace cc diff --git a/native/cocos/editor-support/spine-wasm/3.8/spine-type-export.cpp b/native/cocos/editor-support/spine-wasm/3.8/spine-type-export.cpp index 1b60e13cfb5..0492c5cf032 100644 --- a/native/cocos/editor-support/spine-wasm/3.8/spine-type-export.cpp +++ b/native/cocos/editor-support/spine-wasm/3.8/spine-type-export.cpp @@ -1354,7 +1354,6 @@ EMSCRIPTEN_BINDINGS(spine) { .constructor<>() .property("isCache", &SpineSkeletonInstance::isCache) .property("dtRate", &SpineSkeletonInstance::dtRate) - .property("enable", &SpineSkeletonInstance::enable) .function("initSkeleton", &SpineSkeletonInstance::initSkeleton, allow_raw_pointers()) .function("setAnimation", &SpineSkeletonInstance::setAnimation, allow_raw_pointers()) .function("setSkin", &SpineSkeletonInstance::setSkin) diff --git a/native/cocos/editor-support/spine-wasm/4.2/spine-type-export.cpp b/native/cocos/editor-support/spine-wasm/4.2/spine-type-export.cpp index 1745ab8c9ba..97d15c80e82 100644 --- a/native/cocos/editor-support/spine-wasm/4.2/spine-type-export.cpp +++ b/native/cocos/editor-support/spine-wasm/4.2/spine-type-export.cpp @@ -1359,7 +1359,6 @@ EMSCRIPTEN_BINDINGS(spine) { .constructor<>() .property("isCache", &SpineSkeletonInstance::isCache) .property("dtRate", &SpineSkeletonInstance::dtRate) - .property("enable", &SpineSkeletonInstance::enable) .function("initSkeleton", &SpineSkeletonInstance::initSkeleton, allow_raw_pointers()) .function("setAnimation", &SpineSkeletonInstance::setAnimation, allow_raw_pointers()) .function("setSkin", &SpineSkeletonInstance::setSkin) diff --git a/native/cocos/editor-support/spine-wasm/spine-skeleton-instance.cpp b/native/cocos/editor-support/spine-wasm/spine-skeleton-instance.cpp index a6a683728c3..5328c6263fe 100644 --- a/native/cocos/editor-support/spine-wasm/spine-skeleton-instance.cpp +++ b/native/cocos/editor-support/spine-wasm/spine-skeleton-instance.cpp @@ -14,13 +14,24 @@ using namespace spine; static void animationCallback(AnimationState *state, EventType type, TrackEntry *entry, Event *event) { SpineSkeletonInstance *instance = (static_cast(state->getRendererObject())); - instance->onAnimationStateEvent(entry, type, event); + if (instance) { + SpineEventInfo info; + info.entry = entry; + info.eventType = type; + info.event = event; + instance->animationEvents.add(info); + } } static void trackEntryCallback(AnimationState *state, EventType type, TrackEntry *entry, Event *event) { - void* renderObj = state->getRendererObject(); - if (renderObj) { - (static_cast(renderObj))->onTrackEntryEvent(entry, type, event); + SpineSkeletonInstance *instance = (static_cast(state->getRendererObject())); + if (instance) { + SpineEventInfo info; + info.entry = entry; + info.eventType = type; + info.event = event; + instance->trackEvents.add(info); + if (type == EventType_Dispose) { if (entry->getRendererObject()) { entry->setRendererObject(nullptr); @@ -31,10 +42,14 @@ static void trackEntryCallback(AnimationState *state, EventType type, TrackEntry SpineSkeletonInstance::SpineSkeletonInstance() { _model = new SpineModel(); + animationEvents.ensureCapacity(spine::EventType::EventType_Event + 1); + trackEvents.ensureCapacity(spine::EventType::EventType_Event + 1); } SpineSkeletonInstance::~SpineSkeletonInstance() { _trackListenerSet.clear(); + animationEvents.clear(); + trackEvents.clear(); _skeletonData = nullptr; if (_clipper) delete _clipper; if (_animState) delete _animState; @@ -48,6 +63,9 @@ void SpineSkeletonInstance::destroy() { } Skeleton *SpineSkeletonInstance::initSkeleton(SkeletonData *data) { + if (data == _skeletonData) { + return _skeleton; + } if (_clipper) delete _clipper; if (_animState) delete _animState; if (_animStateData) delete _animStateData; @@ -99,6 +117,20 @@ void SpineSkeletonInstance::updateAnimation(float dltTime) { _skeleton->update(dltTime); _animState->update(dltTime); _animState->apply(*_skeleton); + //Cache animation events then call back to JS. + auto vecAnimationEvents = animationEvents; + animationEvents.clear(); + //Cache track events then call back to JS. + auto vecTrackEvents = trackEvents; + trackEvents.clear(); + for (int i = 0; i < vecAnimationEvents.size(); i++) { + auto& info = vecAnimationEvents[i]; + onAnimationStateEvent(info.entry, info.eventType, info.event); + } + for (int i = 0; i < vecTrackEvents.size(); i++) { + auto& info = vecTrackEvents[i]; + onTrackEntryEvent(info.entry, info.eventType, info.event); + } } SpineModel *SpineSkeletonInstance::updateRenderData() { diff --git a/native/cocos/editor-support/spine-wasm/spine-skeleton-instance.h b/native/cocos/editor-support/spine-wasm/spine-skeleton-instance.h index e101fa8810e..75974869361 100644 --- a/native/cocos/editor-support/spine-wasm/spine-skeleton-instance.h +++ b/native/cocos/editor-support/spine-wasm/spine-skeleton-instance.h @@ -7,6 +7,13 @@ #include "mesh-type-define.h" #include "spine-model.h" +namespace { + struct SpineEventInfo { + spine::TrackEntry* entry{nullptr}; + spine::EventType eventType{spine::EventType::EventType_Start}; + spine::Event *event{nullptr}; + }; +} enum DEBUG_SHAPE_TYPE { DEBUG_REGION = 0, DEBUG_MESH = 1 @@ -59,8 +66,11 @@ class SpineSkeletonInstance { void setSlotTexture(const spine::String &slotName, const spine::String& textureUuid); void destroy(); bool isCache{false}; - bool enable{true}; float dtRate{1.0F}; + + // Used internal for cache event + spine::Vector animationEvents; + spine::Vector trackEvents; private: void collectMeshData(); diff --git a/native/external-config.json b/native/external-config.json index 7810b7fd6e3..74b9c685b8a 100644 --- a/native/external-config.json +++ b/native/external-config.json @@ -3,6 +3,6 @@ "type": "github", "owner": "cocos", "name": "cocos-engine-external", - "checkout": "v3.8.7-4" + "checkout": "v3.8.7-5" } } \ No newline at end of file diff --git a/native/tools/swig-config/spine_3_8.i b/native/tools/swig-config/spine_3_8.i index f0fc3c6ade5..20d0a3b4ae8 100644 --- a/native/tools/swig-config/spine_3_8.i +++ b/native/tools/swig-config/spine_3_8.i @@ -64,6 +64,9 @@ using namespace spine; %ignore cc::SkeletonAnimation::createWithData; %ignore cc::SkeletonAnimation::onTrackEntryEvent; %ignore cc::SkeletonAnimation::onAnimationStateEvent; +%ignore cc::SkeletonAnimation::cacheAnimationEvent; +%ignore cc::SkeletonAnimation::cacheTrackEvent; +%ignore cc::SkeletonAnimation::dispatchEvents; %ignore spine::TrackEntry::setListener; %ignore spine::AnimationState::setListener; %ignore spine::Attachment::getRTTI; diff --git a/native/tools/swig-config/spine_4_2.i b/native/tools/swig-config/spine_4_2.i index 961674ba511..b98aa0466cc 100644 --- a/native/tools/swig-config/spine_4_2.i +++ b/native/tools/swig-config/spine_4_2.i @@ -63,6 +63,9 @@ using namespace spine; %ignore cc::SkeletonAnimation::createWithData; %ignore cc::SkeletonAnimation::onTrackEntryEvent; %ignore cc::SkeletonAnimation::onAnimationStateEvent; +%ignore cc::SkeletonAnimation::cacheAnimationEvent; +%ignore cc::SkeletonAnimation::cacheTrackEvent; +%ignore cc::SkeletonAnimation::dispatchEvents; %ignore spine::TrackEntry::setListener; %ignore spine::AnimationState::setListener; %ignore spine::Attachment::getRTTI; diff --git a/platforms/native/engine/jsb-spine-skeleton.js b/platforms/native/engine/jsb-spine-skeleton.js index 7bcce7ec10b..4404610023b 100644 --- a/platforms/native/engine/jsb-spine-skeleton.js +++ b/platforms/native/engine/jsb-spine-skeleton.js @@ -721,7 +721,6 @@ const cacheManager = require('./jsb-cache-manager'); this._nativeSkeleton._comp = null; this._nativeSkeleton = null; } - this._needUpdateSkeltonData = false; }; const _onDestroy = skeleton.onDestroy;