diff --git a/CHANGELOG.md b/CHANGELOG.md index 555d97031..ef990bed8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -171,7 +171,7 @@ * Tint Black: Added support for [Tint Black](http://en.esotericsoftware.com/spine-slots#Tint-black) functionality at all Spine URP shaders (2D and 3D shaders) and at all standard pipeline `Spine/Sprite` shaders. This feature can be enabled via the `Tint Black` material parameter in the Inspector. Note: The URP Sprite shaders provided in the Spine URP Shaders extension UPM package require the latest version of the spine-unity runtime (package version 4.1.12, 2023-05-31 or newer) to display the added material parameters in the Inspector GUI. * Added `SkeletonGraphic.MeshScale` property to allow access to calculated mesh scale. `MeshScale` is based on (1) Canvas pixels per unit, and (2) `RectTransform` bounds when using `Layout Scale Mode` other than `None` at `SkeletonGraphic` which scales the skeleton mesh to fit the parent `RectTransform` bounds accordingly. * Added `updateSeparatorPartScale` property to `SkeletonGraphic` to let render separator parts follow the scale (lossy scale) of the `SkeletonGraphic` GameObject. Defaults to `false` to maintain existing behaviour. - * Added experimental `EditorSkeletonPlayer` component to allow Editor playback of the initial animation set at `SkeletonAnimation` or `SkeletonGraphic` components. Add this component to your skeleton GameObject to enable the in-editor animation preview. Allows configurations for continuous playback when selected, deselected, and alternative single-frame preview by setting `Fixed Track Time` to any value other than 0. Limitations: At skeletons with variable material count the Inspector preview may be too unresponsive. It is then recommended to disable the `EditorSkeletonPlayer` component (at the top of the Inspector) to make it responsive again, then you can disable `Play When Selected` and re-enable the component to preview playback only when deselected. + * Added experimental `EditorSkeletonPlayer` component to allow Editor playback of the initial animation set at `SkeletonAnimation` or `SkeletonGraphic` components. Add this component to your skeleton GameObject to enable the in-editor animation preview. Allows configurations for continuous playback when selected, deselected, and alternative single-frame preview by setting `Fixed Track Time` to any value other than 0. Limitations: At skeletons with variable material count the Inspector preview may be too unresponsive. It is then recommended to disable the `EditorSkeletonPlayer` component (at the top of the Inspector) to make it responsive again, then you can disable `Play When Selected` and re-enable the component to preview playback only when deselected. * Added example component `RenderCombinedMesh` to render a combined mesh of multiple meshes or submeshes. This is required by `OutlineOnly` shaders to render a combined outline when using `SkeletonRenderSeparator` or multiple atlas pages which would normally lead to outlines around individual parts. To add a combined outline to your SkeletenRenderer: 1) Add a child GameObject and move it a bit back (e.g. position Z = 0.01). 2) Add a `RenderCombinedMesh` component, provided in the `Spine Examples/Scripts/Sample Components` directory. @@ -235,6 +235,7 @@ ### WebGL backend * `PolygonBatcher` can now disable culling automatically if the static variable `PolygonBatcher.disableCulling` is set to true. * Added `SpineCanvas`, a simpler way to render a scene via spine-webgl. See `spine-ts/spine-webgl/examples/barebones.html` and `spine-ts/spine-webgl/examples/mix-and-match.html`. + * Added `SpineCanavs.dispose()` to halt the updating and rendering of the canvas. ### Canvas backend * Improved example. diff --git a/spine-ts/spine-pixi/src/assets/atlasLoader.ts b/spine-ts/spine-pixi/src/assets/atlasLoader.ts index a05774563..cc008c449 100644 --- a/spine-ts/spine-pixi/src/assets/atlasLoader.ts +++ b/spine-ts/spine-pixi/src/assets/atlasLoader.ts @@ -29,7 +29,7 @@ import { TextureAtlas } from "@esotericsoftware/spine-core"; import { SpineTexture } from "../SpineTexture"; -import type { AssetExtension, LoadAsset, Loader } from "@pixi/assets"; +import type { AssetExtension, Loader } from "@pixi/assets"; import { LoaderParserPriority, checkExtension } from "@pixi/assets"; import type { Texture } from "@pixi/core"; import { ExtensionType, settings, utils, BaseTexture, extensions } from "@pixi/core"; @@ -58,7 +58,7 @@ const spineTextureAtlasLoader: AssetExtension { + testParse(asset: unknown, options: {src: string}): Promise { const isExtensionRight = checkExtension(options.src, ".atlas"); const isString = typeof asset === "string"; @@ -69,7 +69,7 @@ const spineTextureAtlasLoader: AssetExtension { + async parse(asset: RawAtlas, options: {src: string, data: ISpineAtlasMetadata}, loader: Loader): Promise { const metadata: ISpineAtlasMetadata = options.data || {}; let basePath = utils.path.dirname(options.src); diff --git a/spine-ts/spine-pixi/src/assets/skeletonLoader.ts b/spine-ts/spine-pixi/src/assets/skeletonLoader.ts index dc4c0e348..030b8de9f 100644 --- a/spine-ts/spine-pixi/src/assets/skeletonLoader.ts +++ b/spine-ts/spine-pixi/src/assets/skeletonLoader.ts @@ -27,7 +27,7 @@ * SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ -import type { AssetExtension, LoadAsset } from "@pixi/assets"; +import type { AssetExtension } from "@pixi/assets"; import { LoaderParserPriority, checkExtension } from "@pixi/assets"; import { ExtensionType, settings, extensions } from "@pixi/core"; @@ -62,7 +62,7 @@ const spineLoaderExtension: AssetExtension { + testParse(asset: unknown, options: {src: string}): Promise { const isJsonSpineModel = checkExtension(options.src, ".json") && isJson(asset); const isBinarySpineModel = checkExtension(options.src, ".skel") && isBuffer(asset); diff --git a/spine-ts/spine-webgl/src/SpineCanvas.ts b/spine-ts/spine-webgl/src/SpineCanvas.ts index d13a4d3e6..19371ae64 100644 --- a/spine-ts/spine-webgl/src/SpineCanvas.ts +++ b/spine-ts/spine-webgl/src/SpineCanvas.ts @@ -32,12 +32,12 @@ import { TimeKeeper, AssetManager, ManagedWebGLRenderingContext, SceneRenderer, /** An app running inside a {@link SpineCanvas}. The app life-cycle * is as follows: * - * 1. `loadAssets()` is called. The app can queue assets for loading via {@link SpineCanvas#assetManager}. + * 1. `loadAssets()` is called. The app can queue assets for loading via {@link SpineCanvas.assetManager}. * 2. `initialize()` is called when all assets are loaded. The app can setup anything it needs to enter the main application logic. * 3. `update()` is called periodically at screen refresh rate. The app can update its state. - * 4. `render()` is called periodically at screen refresh rate. The app can render its state via {@link SpineCanvas#renderer} or directly via the WebGL context in {@link SpineCanvas.gl}` + * 4. `render()` is called periodically at screen refresh rate. The app can render its state via {@link SpineCanvas.renderer} or directly via the WebGL context in {@link SpineCanvas.gl}. * - * The `error()` method is called in case the assets could not be loaded. + * The `error()` method is called in case the assets could not be loaded. The `dispose()` method is called in case the canvas has been disposed via {@link SpineCanvas.dispose}. */ export interface SpineCanvasApp { loadAssets?(canvas: SpineCanvas): void; @@ -45,6 +45,7 @@ export interface SpineCanvasApp { update?(canvas: SpineCanvas, delta: number): void; render?(canvas: SpineCanvas): void; error?(canvas: SpineCanvas, errors: StringMap): void; + dispose?(canvas: SpineCanvas): void; } /** Configuration passed to the {@link SpineCanvas} constructor */ @@ -75,8 +76,10 @@ export class SpineCanvas { /** The input processor used to listen to mouse, touch, and keyboard events. */ readonly input: Input; + private disposed = false; + /** Constructs a new spine canvas, rendering to the provided HTML canvas. */ - constructor (canvas: HTMLCanvasElement, config: SpineCanvasConfig) { + constructor (canvas: HTMLCanvasElement, private config: SpineCanvasConfig) { if (!config.pathPrefix) config.pathPrefix = ""; if (!config.app) config.app = { loadAssets: () => { }, @@ -84,6 +87,7 @@ export class SpineCanvas { update: () => { }, render: () => { }, error: () => { }, + dispose: () => { }, } if (!config.webglConfig) config.webglConfig = { alpha: true }; @@ -97,6 +101,7 @@ export class SpineCanvas { if (config.app.loadAssets) config.app.loadAssets(this); let loop = () => { + if (this.disposed) return; requestAnimationFrame(loop); this.time.update(); if (config.app.update) config.app.update(this, this.time.delta); @@ -104,6 +109,7 @@ export class SpineCanvas { } let waitForAssets = () => { + if (this.disposed) return; if (this.assetManager.isLoadingComplete()) { if (this.assetManager.hasErrors()) { if (config.app.error) config.app.error(this, this.assetManager.getErrors()); @@ -123,4 +129,10 @@ export class SpineCanvas { this.gl.clearColor(r, g, b, a); this.gl.clear(this.gl.COLOR_BUFFER_BIT); } + + /** Disposes the app, so the update() and render() functions are no longer called. Calls the dispose() callback.*/ + dispose() { + if (this.config.app.dispose) this.config.app.dispose(this); + this.disposed = true; + } }