diff --git a/spine-ts/spine-pixi-v7/src/Spine.ts b/spine-ts/spine-pixi-v7/src/Spine.ts index acb59c0e0..08e317a8c 100644 --- a/spine-ts/spine-pixi-v7/src/Spine.ts +++ b/spine-ts/spine-pixi-v7/src/Spine.ts @@ -102,6 +102,9 @@ export interface SpineFromOptions { /** The bounds provider to use. If undefined the bounds will be dynamic, calculated when requested and based on the current frame. */ boundsProvider?: SpineBoundsProvider, + + /** The ticker to use when {@link autoUpdate} is `true`. Defaults to {@link Ticker.shared}. */ + ticker?: Ticker, }; export interface SpineOptions { @@ -116,6 +119,9 @@ export interface SpineOptions { /** See {@link SpineFromOptions.boundsProvider}. */ boundsProvider?: SpineBoundsProvider, + + /** See {@link SpineFromOptions.ticker}. */ + ticker?: Ticker, } /** @@ -298,19 +304,37 @@ export class Spine extends Container { afterUpdateWorldTransforms: (object: Spine) => void = () => { }; private _autoUpdate: boolean = false; + private _ticker: Ticker = Ticker.shared; + public get autoUpdate (): boolean { return this._autoUpdate; } - /** When `true`, the Spine AnimationState and the Skeleton will be automatically updated using the {@link Ticker.shared} instance. */ + /** When `true`, the Spine AnimationState and the Skeleton will be automatically updated using the {@link ticker}. */ public set autoUpdate (value: boolean) { if (value && !this._autoUpdate) { - Ticker.shared.add(this.internalUpdate, this); + this._ticker.add(this.internalUpdate, this); } else if (!value && this._autoUpdate) { - Ticker.shared.remove(this.internalUpdate, this); + this._ticker.remove(this.internalUpdate, this); } this._autoUpdate = value; } + /** The ticker to use when {@link autoUpdate} is `true`. Defaults to {@link Ticker.shared}. */ + public get ticker (): Ticker { + return this._ticker; + } + /** Sets the ticker to use when {@link autoUpdate} is `true`. If `autoUpdate` is already `true`, the update callback will be moved from the old ticker to the new one. */ + public set ticker (value: Ticker) { + if (this._ticker === value) return; + + if (this._autoUpdate) { + this._ticker.remove(this.internalUpdate, this); + value.add(this.internalUpdate, this); + } + + this._ticker = value; + } + private meshesCache = new Map(); private static vectorAux: Vector2 = new Vector2(); @@ -374,6 +398,7 @@ export class Spine extends Container { this.initializeMeshFactory(oldOptions?.slotMeshFactory); } + if (options?.ticker) this._ticker = options.ticker; this.autoUpdate = options?.autoUpdate ?? true; this.boundsProvider = options.boundsProvider; @@ -408,7 +433,7 @@ export class Spine extends Container { this.hasNeverUpdated = false; // Because reasons, pixi uses deltaFrames at 60fps. We ignore the default deltaFrames and use the deltaSeconds from pixi ticker. - const delta = deltaSeconds ?? Ticker.shared.deltaMS / 1000; + const delta = deltaSeconds ?? this._ticker.deltaMS / 1000; this.state.update(delta); this.state.apply(this.skeleton); this.beforeUpdateWorldTransforms(this); @@ -966,7 +991,7 @@ export class Spine extends Container { return Spine.oldFrom(paramOne, atlasAssetName!, options); } - const { skeleton, atlas, scale = 1, darkTint, autoUpdate, boundsProvider } = paramOne; + const { skeleton, atlas, scale = 1, darkTint, autoUpdate, boundsProvider, ticker } = paramOne; const cacheKey = `${skeleton}-${atlas}-${scale}`; let skeletonData = Spine.skeletonCache[cacheKey]; if (!skeletonData) { @@ -978,7 +1003,7 @@ export class Spine extends Container { skeletonData = parser.readSkeletonData(skeletonAsset); Spine.skeletonCache[cacheKey] = skeletonData; } - return new Spine({ skeletonData, darkTint, autoUpdate, boundsProvider }); + return new Spine({ skeletonData, darkTint, autoUpdate, boundsProvider, ticker }); } diff --git a/spine-ts/spine-pixi-v8/src/Spine.ts b/spine-ts/spine-pixi-v8/src/Spine.ts index 5d21efbe4..a7084d77f 100644 --- a/spine-ts/spine-pixi-v8/src/Spine.ts +++ b/spine-ts/spine-pixi-v8/src/Spine.ts @@ -92,6 +92,9 @@ export interface SpineFromOptions { /** The bounds provider to use. If undefined the bounds will be dynamic, calculated when requested and based on the current frame. */ boundsProvider?: SpineBoundsProvider, + + /** The ticker to use when {@link autoUpdate} is `true`. Defaults to {@link Ticker.shared}. */ + ticker?: Ticker, }; const vectorAux = new Vector2(); @@ -244,6 +247,9 @@ export interface SpineOptions extends ContainerOptions { /** See {@link SpineFromOptions.boundsProvider}. */ boundsProvider?: SpineBoundsProvider, + + /** See {@link SpineFromOptions.ticker}. */ + ticker?: Ticker, } /** @@ -352,21 +358,38 @@ export class Spine extends ViewContainer { } private _autoUpdate = false; + private _ticker: Ticker = Ticker.shared; public get autoUpdate (): boolean { return this._autoUpdate; } - /** When `true`, the Spine AnimationState and the Skeleton will be automatically updated using the {@link Ticker.shared} instance. */ + /** When `true`, the Spine AnimationState and the Skeleton will be automatically updated using the {@link ticker}. */ public set autoUpdate (value: boolean) { if (value && !this._autoUpdate) { - Ticker.shared.add(this.internalUpdate, this); + this._ticker.add(this.internalUpdate, this); } else if (!value && this._autoUpdate) { - Ticker.shared.remove(this.internalUpdate, this); + this._ticker.remove(this.internalUpdate, this); } this._autoUpdate = value; } + /** The ticker to use when {@link autoUpdate} is `true`. Defaults to {@link Ticker.shared}. */ + public get ticker (): Ticker { + return this._ticker; + } + /** Sets the ticker to use when {@link autoUpdate} is `true`. If `autoUpdate` is already `true`, the update callback will be moved from the old ticker to the new one. */ + public set ticker (value: Ticker) { + if (this._ticker === value) return; + + if (this._autoUpdate) { + this._ticker.remove(this.internalUpdate, this); + value.add(this.internalUpdate, this); + } + + this._ticker = value; + } + private _boundsProvider?: SpineBoundsProvider; /** The bounds provider to use. If undefined the bounds will be dynamic, calculated when requested and based on the current frame. */ public get boundsProvider (): SpineBoundsProvider | undefined { @@ -396,6 +419,7 @@ export class Spine extends ViewContainer { this.skeleton = new Skeleton(skeletonData); this.state = new AnimationState(new AnimationStateData(skeletonData)); + if (options?.ticker) this._ticker = options.ticker; this.autoUpdate = options?.autoUpdate ?? true; // dark tint can be enabled by options, otherwise is enable if at least one slot has tint black @@ -418,9 +442,7 @@ export class Spine extends ViewContainer { } protected internalUpdate (_deltaFrame: any, deltaSeconds?: number): void { - // Because reasons, pixi uses deltaFrames at 60fps. - // We ignore the default deltaFrames and use the deltaSeconds from pixi ticker. - this._updateAndApplyState(deltaSeconds ?? Ticker.shared.deltaMS / 1000); + this._updateAndApplyState(deltaSeconds ?? this._ticker.deltaMS / 1000); } override get bounds () { @@ -1027,7 +1049,7 @@ export class Spine extends ViewContainer { public override destroy (options: DestroyOptions = false) { super.destroy(options); - Ticker.shared.remove(this.internalUpdate, this); + this._ticker.remove(this.internalUpdate, this); this.state.clearListeners(); this.debug = undefined; this.skeleton = null as any; @@ -1072,7 +1094,7 @@ export class Spine extends ViewContainer { * @param options - Options to configure the Spine game object. See {@link SpineFromOptions} * @returns {Spine} The Spine game object instantiated */ - static from ({ skeleton, atlas, scale = 1, darkTint, autoUpdate = true, boundsProvider }: SpineFromOptions) { + static from ({ skeleton, atlas, scale = 1, darkTint, autoUpdate = true, boundsProvider, ticker }: SpineFromOptions) { const cacheKey = `${skeleton}-${atlas}-${scale}`; if (Cache.has(cacheKey)) { @@ -1081,6 +1103,7 @@ export class Spine extends ViewContainer { darkTint, autoUpdate, boundsProvider, + ticker, }); } @@ -1102,6 +1125,7 @@ export class Spine extends ViewContainer { darkTint, autoUpdate, boundsProvider, + ticker, }); } }