@@ -47,18 +47,32 @@ import {
47
47
import type { SpineTexture } from "./SpineTexture.js" ;
48
48
import { SlotMesh } from "./SlotMesh.js" ;
49
49
import { DarkSlotMesh } from "./DarkSlotMesh.js" ;
50
- import type { ISpineDebugRenderer } from "./SpineDebugRenderer.js" ;
50
+ import type { ISpineDebugRenderer , SpineDebugRenderer } from "./SpineDebugRenderer.js" ;
51
51
import { Assets } from "@pixi/assets" ;
52
52
import type { IPointData } from "@pixi/core" ;
53
- import { Ticker , utils } from "@pixi/core" ;
53
+ import { Ticker } from "@pixi/core" ;
54
54
import type { IDestroyOptions , DisplayObject } from "@pixi/display" ;
55
55
import { Container } from "@pixi/display" ;
56
56
57
+ /**
58
+ * Options to configure a {@link Spine} game object.
59
+ */
57
60
export interface ISpineOptions {
61
+ /** Set the {@link Spine.autoUpdate} value. If omitted, it is set to `true`. */
58
62
autoUpdate ?: boolean ;
63
+ /** The value passed to the skeleton reader. If omitted, 1 is passed. See {@link SkeletonBinary.scale} for details. */
64
+ scale ?: number ;
65
+ /**
66
+ * A factory to override the default ones to render Spine meshes ({@link DarkSlotMesh} or {@link SlotMesh}).
67
+ * If omitted, a factory returning a ({@link DarkSlotMesh} or {@link SlotMesh}) will be used depending on the presence of
68
+ * a dark tint mesh in the skeleton.
69
+ * */
59
70
slotMeshFactory ?: ( ) => ISlotMesh ;
60
71
}
61
72
73
+ /**
74
+ * AnimationStateListener {@link https://en.esotericsoftware.com/spine-api-reference#AnimationStateListener events} exposed for Pixi.
75
+ */
62
76
export interface SpineEvents {
63
77
complete : [ trackEntry : TrackEntry ] ;
64
78
dispose : [ trackEntry : TrackEntry ] ;
@@ -68,14 +82,23 @@ export interface SpineEvents {
68
82
start : [ trackEntry : TrackEntry ] ;
69
83
}
70
84
85
+ /**
86
+ * The class to instantiate a {@link Spine} game object in Pixi.
87
+ * The static method {@link Spine.from} should be used to instantiate a Spine game object.
88
+ */
71
89
export class Spine extends Container {
90
+ /** The skeleton for this Spine game object. */
72
91
public skeleton : Skeleton ;
92
+ /** The animation state for this Spine game object. */
73
93
public state : AnimationState ;
74
94
75
95
private _debug ?: ISpineDebugRenderer | undefined = undefined ;
76
96
public get debug ( ) : ISpineDebugRenderer | undefined {
77
97
return this . _debug ;
78
98
}
99
+ /** Pass a {@link SpineDebugRenderer} or create your own {@link ISpineDebugRenderer} to render bones, meshes, ...
100
+ * @example spineGO.debug = new SpineDebugRenderer();
101
+ */
79
102
public set debug ( value : ISpineDebugRenderer | undefined ) {
80
103
if ( this . _debug ) {
81
104
this . _debug . unregisterSpine ( this ) ;
@@ -86,13 +109,14 @@ export class Spine extends Container {
86
109
this . _debug = value ;
87
110
}
88
111
89
- protected slotMeshFactory : ( ) => ISlotMesh = ( ( ) : ISlotMesh => new SlotMesh ( ) ) ;
112
+ protected slotMeshFactory : ( ) => ISlotMesh = ( ) => new SlotMesh ( ) ;
90
113
91
114
private autoUpdateWarned : boolean = false ;
92
115
private _autoUpdate : boolean = true ;
93
116
public get autoUpdate ( ) : boolean {
94
117
return this . _autoUpdate ;
95
118
}
119
+ /** When `true`, the Spine AnimationState and the Skeleton will be automatically updated using the {@link Ticker.shared} instance. */
96
120
public set autoUpdate ( value : boolean ) {
97
121
if ( value ) {
98
122
Ticker . shared . add ( this . internalUpdate , this ) ;
@@ -115,7 +139,6 @@ export class Spine extends Container {
115
139
private lightColor = new Color ( ) ;
116
140
private darkColor = new Color ( ) ;
117
141
118
-
119
142
constructor ( skeletonData : SkeletonData , options ?: ISpineOptions ) {
120
143
super ( ) ;
121
144
@@ -134,13 +157,14 @@ export class Spine extends Container {
134
157
} else {
135
158
for ( let i = 0 ; i < this . skeleton . slots . length ; i ++ ) {
136
159
if ( this . skeleton . slots [ i ] . data . darkColor ) {
137
- this . slotMeshFactory = options ?. slotMeshFactory ?? ( ( ) : ISlotMesh => new DarkSlotMesh ( ) ) ;
160
+ this . slotMeshFactory = options ?. slotMeshFactory ?? ( ( ) => new DarkSlotMesh ( ) ) ;
138
161
break ;
139
162
}
140
163
}
141
164
}
142
165
}
143
166
167
+ /** If {@link Spine.autoUpdate} is `false`, this method allows to update the AnimationState and the Skeleton with the given delta. */
144
168
public update ( deltaSeconds : number ) : void {
145
169
if ( this . autoUpdate && ! this . autoUpdateWarned ) {
146
170
console . warn ( "You are calling update on a Spine instance that has autoUpdate set to true. This is probably not what you want." ) ;
@@ -156,6 +180,7 @@ export class Spine extends Container {
156
180
this . skeleton . update ( delta ) ;
157
181
}
158
182
183
+ /** Before rendering, apply the state change to the Spine AnimationState and update the skeleton transform, then call the {@link Container.updateTransform}. */
159
184
public override updateTransform ( ) : void {
160
185
this . updateSpineTransform ( ) ;
161
186
this . debug ?. renderDebug ( this ) ;
@@ -171,6 +196,7 @@ export class Spine extends Container {
171
196
this . sortChildren ( ) ;
172
197
}
173
198
199
+ /** Destroy Spine game object elements, then call the {@link Container.destroy} with the given options */
174
200
public override destroy ( options ?: boolean | IDestroyOptions | undefined ) : void {
175
201
for ( const [ , mesh ] of this . meshesCache ) {
176
202
mesh ?. destroy ( ) ;
@@ -320,13 +346,19 @@ export class Spine extends Container {
320
346
Spine . clipper . clipEnd ( ) ;
321
347
}
322
348
349
+ /**
350
+ * Set the position of the bone given in input through a {@link IPointData}.
351
+ * @param bone: the bone name or the bone instance to set the position
352
+ * @param outPos: the new position of the bone.
353
+ * @throws {Error }: if the given bone is not found in the skeleton, an error is thrown
354
+ */
323
355
public setBonePosition ( bone : string | Bone , position : IPointData ) : void {
324
356
const boneAux = bone ;
325
357
if ( typeof bone === "string" ) {
326
358
bone = this . skeleton . findBone ( bone ) ! ;
327
359
}
328
360
329
- if ( ! bone ) throw Error ( `Cant set bone position, bone ${ String ( boneAux ) } not found` ) ;
361
+ if ( ! bone ) throw Error ( `Cannot set bone position, bone ${ String ( boneAux ) } not found` ) ;
330
362
Spine . vectorAux . set ( position . x , position . y ) ;
331
363
332
364
if ( bone . parent ) {
@@ -340,14 +372,20 @@ export class Spine extends Container {
340
372
}
341
373
}
342
374
375
+ /**
376
+ * Return the position of the bone given in input into an {@link IPointData}.
377
+ * @param bone: the bone name or the bone instance to get the position from
378
+ * @param outPos: an optional {@link IPointData} to use to return the bone position, rathern than instantiating a new object.
379
+ * @returns {IPointData | undefined }: the position of the bone, or undefined if no matching bone is found in the skeleton
380
+ */
343
381
public getBonePosition ( bone : string | Bone , outPos ?: IPointData ) : IPointData | undefined {
344
382
const boneAux = bone ;
345
383
if ( typeof bone === "string" ) {
346
384
bone = this . skeleton . findBone ( bone ) ! ;
347
385
}
348
386
349
387
if ( ! bone ) {
350
- console . error ( `Cant set bone position! Bone ${ String ( boneAux ) } not found` ) ;
388
+ console . error ( `Cannot get bone position! Bone ${ String ( boneAux ) } not found` ) ;
351
389
return outPos ;
352
390
}
353
391
@@ -360,9 +398,26 @@ export class Spine extends Container {
360
398
return outPos ;
361
399
}
362
400
401
+ /** A cache containing skeleton data and atlases already loaded by {@link Spine.from}. */
363
402
public static readonly skeletonCache : Record < string , SkeletonData > = Object . create ( null ) ;
364
403
365
- public static from ( skeletonAssetName : string , atlasAssetName : string , options ?: ISpineOptions & { scale ?: number } ) : Spine {
404
+ /**
405
+ * Use this method to instantiate a Spine game object.
406
+ * Before instantiating a Spine game object, the skeleton (`.skel` or `.json`) and the atlas text files must be loaded into the Assets. For example:
407
+ * ```
408
+ * PIXI.Assets.add("sackData", "./assets/sack-pro.skel");
409
+ * PIXI.Assets.add("sackAtlas", "./assets/sack-pma.atlas");
410
+ * await PIXI.Assets.load(["sackData", "sackAtlas"]);
411
+ * ```
412
+ * Once a Spine game object is created, its skeleton data is cached into {@link Spine.skeletonCache} using the key:
413
+ * `${skeletonAssetName}-${atlasAssetName}-${options?.scale ?? 1}`
414
+ *
415
+ * @param skeletonAssetName - the asset name for the skeleton `.skel` or `.json` file previously loaded into the Assets
416
+ * @param atlasAssetName - the asset name for the atlas file previously loaded into the Assets
417
+ * @param options - Options to configure the Spine game object
418
+ * @returns {Spine } The Spine game object instantiated
419
+ */
420
+ public static from ( skeletonAssetName : string , atlasAssetName : string , options ?: ISpineOptions ) : Spine {
366
421
const cacheKey = `${ skeletonAssetName } -${ atlasAssetName } -${ options ?. scale ?? 1 } ` ;
367
422
let skeletonData = Spine . skeletonCache [ cacheKey ] ;
368
423
if ( skeletonData ) {
@@ -381,6 +436,9 @@ export class Spine extends Container {
381
436
382
437
Skeleton . yDown = true ;
383
438
439
+ /**
440
+ * Represents the mesh type used in a Spine objects. Available implementations are {@link DarkSlotMesh} and {@link SlotMesh}.
441
+ */
384
442
export interface ISlotMesh extends DisplayObject {
385
443
name : string ;
386
444
updateFromSpineData (
0 commit comments