diff --git a/spine-ts/package-lock.json b/spine-ts/package-lock.json index 4b5bf2617..c61880364 100644 --- a/spine-ts/package-lock.json +++ b/spine-ts/package-lock.json @@ -7301,14 +7301,6 @@ "path": "^0.12.7" } }, - "node_modules/phaser3-project-template": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/phaser3-project-template/-/phaser3-project-template-1.0.9.tgz", - "integrity": "sha512-HUfQHE7Eg2kymO58ojGyIZGWlL7FY145Hdp1j0NAVkYrMJhZ2A3Ha40oe74OIwXx09pLYMRZJA/AepgmlPJcdA==", - "dependencies": { - "phaser": "^3.3.0" - } - }, "node_modules/posix-character-classes": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", @@ -8428,8 +8420,7 @@ "license": "LicenseRef-LICENSE", "dependencies": { "@esotericsoftware/spine-core": "^4.2.10", - "phaser": "^3.55.2", - "phaser3-project-template": "^1.0.9" + "phaser": "^3.55.2" } }, "spine-player": { diff --git a/spine-ts/spine-phaser/example/index.js b/spine-ts/spine-phaser/example/index.js index ea1013d32..637de5c38 100644 --- a/spine-ts/spine-phaser/example/index.js +++ b/spine-ts/spine-phaser/example/index.js @@ -27,6 +27,6 @@ function preload () { function create () { let plugin = this.spine; - let numbers = plugin.getNumbers(10); - this.add.text(10, 10, numbers, { font: '16px Courier', fill: '#00ff00' }); + var boy = this.add.spine(400, 600, 'raptor'); + this.add.text(10, 10, "Spine", { font: '16px Courier', fill: '#00ff00' }); } \ No newline at end of file diff --git a/spine-ts/spine-phaser/package.json b/spine-ts/spine-phaser/package.json index 16ac17992..43c74ca5c 100644 --- a/spine-ts/spine-phaser/package.json +++ b/spine-ts/spine-phaser/package.json @@ -31,7 +31,6 @@ "homepage": "https://github.com/esotericsoftware/spine-runtimes#readme", "dependencies": { "@esotericsoftware/spine-core": "^4.2.10", - "phaser": "^3.55.2", - "phaser3-project-template": "^1.0.9" + "phaser": "^3.55.2" } } diff --git a/spine-ts/spine-phaser/src/SpineGameObject.ts b/spine-ts/spine-phaser/src/SpineGameObject.ts index e69de29bb..457deaf20 100644 --- a/spine-ts/spine-phaser/src/SpineGameObject.ts +++ b/spine-ts/spine-phaser/src/SpineGameObject.ts @@ -0,0 +1,29 @@ +import { SPINE_GAME_OBJECT_TYPE } from "./keys"; +import { SpinePlugin } from "./SpinePlugin"; +import { ComputedSizeMixin, DepthMixin, FlipMixin, ScrollFactorMixin, TransformMixin, VisibleMixin } from "./mixins"; + +class BaseSpineGameObject extends Phaser.GameObjects.GameObject { + constructor(scene: Phaser.Scene, type: string) { + super(scene, type); + } +} + +interface SpineContainer { + +} + +export class SpineGameObject extends ComputedSizeMixin(DepthMixin(FlipMixin(ScrollFactorMixin(TransformMixin(VisibleMixin(BaseSpineGameObject)))))) { + blendMode = -1; + + constructor(scene: Phaser.Scene, plugin: SpinePlugin, x: number, y: number, key: string) { + super(scene, SPINE_GAME_OBJECT_TYPE); + this.setPosition(x, y); + } + + preUpdate(time: number, delta: number) { + } + + renderWebGL(renderer: Phaser.Renderer.WebGL.WebGLRenderer, src: SpineGameObject, camera: Phaser.Cameras.Scene2D.Camera, parentMatrix: Phaser.GameObjects.Components.TransformMatrix, container: SpineContainer) { + + } +} \ No newline at end of file diff --git a/spine-ts/spine-phaser/src/SpinePlugin.ts b/spine-ts/spine-phaser/src/SpinePlugin.ts index 012ae63e0..d9f8e8ef1 100644 --- a/spine-ts/spine-phaser/src/SpinePlugin.ts +++ b/spine-ts/spine-phaser/src/SpinePlugin.ts @@ -28,9 +28,10 @@ *****************************************************************************/ import Phaser from "phaser"; -import { SPINE_ATLAS_CACHE_KEY, SPINE_FILE_TYPE, SPINE_TEXTURE_CACHE_KEY } from "./keys"; +import { SPINE_ATLAS_CACHE_KEY, SPINE_CONTAINER_TYPE, SPINE_FILE_TYPE, SPINE_GAME_OBJECT_TYPE, SPINE_TEXTURE_CACHE_KEY } from "./keys"; import { SceneRenderer, SkeletonDebugRenderer, SkeletonRenderer } from "@esotericsoftware/spine-webgl" import { SpineFile } from "./SpineFile"; +import { SpineGameObject } from "./SpineGameObject"; export class SpinePlugin extends Phaser.Plugins.ScenePlugin { game: Phaser.Game; @@ -42,8 +43,6 @@ export class SpinePlugin extends Phaser.Plugins.ScenePlugin { gl: WebGLRenderingContext | null; phaserRenderer: Phaser.Renderer.Canvas.CanvasRenderer | Phaser.Renderer.WebGL.WebGLRenderer | null; sceneRenderer: SceneRenderer | null; - skeletonRenderer: SkeletonRenderer | null; - skeletonDebugRenderer: SkeletonDebugRenderer | null; constructor(scene: Phaser.Scene, pluginManager: Phaser.Plugins.PluginManager, pluginKey: string) { super(scene, pluginManager, pluginKey); @@ -56,8 +55,6 @@ export class SpinePlugin extends Phaser.Plugins.ScenePlugin { this.gl = this.isWebGL ? (this.game.renderer as Phaser.Renderer.WebGL.WebGLRenderer).gl : null; this.phaserRenderer = this.game.renderer; this.sceneRenderer = null; - this.skeletonRenderer = null; - this.skeletonDebugRenderer = null; if (!this.phaserRenderer) { this.phaserRenderer = { @@ -70,29 +67,96 @@ export class SpinePlugin extends Phaser.Plugins.ScenePlugin { } as unknown as Phaser.Renderer.Canvas.CanvasRenderer; } - let fileCallback = function (this: any, key: string | Phaser.Types.Loader.FileTypes.JSONFileConfig | Phaser.Types.Loader.FileTypes.JSONFileConfig[], + let fileCallback = function (this: any, key: string, jsonURL: string, - atlasURL: string | string[], + atlasURL: string, premultipliedAlpha: boolean, jsonXhrSettings: Phaser.Types.Loader.XHRSettingsObject, atlasXhrSettings: Phaser.Types.Loader.XHRSettingsObject) { - let file = new SpineFile(this as any, key, jsonURL, atlasURL, premultipliedAlpha, jsonXhrSettings, atlasXhrSettings); - this.addFile(file.files); - return this; + let file = new SpineFile(this as any, key, jsonURL, atlasURL, premultipliedAlpha, jsonXhrSettings, atlasXhrSettings); + this.addFile(file.files); return this; }; - pluginManager.registerFileType(SPINE_FILE_TYPE, fileCallback, scene); + let self = this; + let addSpineGameObject = function (this: Phaser.GameObjects.GameObjectFactory, x: number, y: number, key: string) { + let gameObject = new SpineGameObject(scene, self, x, y, key); + this.displayList.add(gameObject); + this.updateList.add(gameObject); + }; + + let makeSpineGameObject = function (this: Phaser.GameObjects.GameObjectFactory, config: any, addToScene: boolean) { + let key = config.key ? config.key : null; + let gameObject = new SpineGameObject(this.scene, self, 0, 0, key); + if (addToScene !== undefined) { + config.add = addToScene; + } + Phaser.GameObjects.BuildGameObject(this.scene, gameObject, config); + } + + pluginManager.registerFileType(SPINE_FILE_TYPE, fileCallback, scene); + pluginManager.registerGameObject(SPINE_GAME_OBJECT_TYPE, addSpineGameObject, makeSpineGameObject); } boot() { - // FIXME + if (this.isWebGL) { + // Monkeypatch the Spine setBlendMode functions, or batching is destroyed! + let setBlendMode = function (this: any, srcBlend: any, dstBlend: any) { + if (srcBlend !== this.srcBlend || dstBlend !== this.dstBlend) { + let gl = this.context.gl; + this.srcBlend = srcBlend; + this.dstBlend = dstBlend; + if (this.isDrawing) { + this.flush(); + gl.blendFunc(this.srcBlend, this.dstBlend); + } + } + }; + + var sceneRenderer = this.sceneRenderer; + if (!sceneRenderer) { + sceneRenderer = new SceneRenderer((this.phaserRenderer! as Phaser.Renderer.WebGL.WebGLRenderer).canvas, this.gl!, true); + sceneRenderer.batcher.setBlendMode = setBlendMode; + (sceneRenderer as any).shapes.setBlendMode = setBlendMode; + } + + this.sceneRenderer = sceneRenderer; + } + + var eventEmitter = this.systems.events; + eventEmitter.once('shutdown', this.shutdown, this); + eventEmitter.once('destroy', this.destroy, this); + this.game.events.once('destroy', this.gameDestroy, this); } - getNumbers(count: number) { - let numbers = []; - for (let i = 0; i < count; i++) - numbers.push(i); - return numbers; + onResize() { + var phaserRenderer = this.phaserRenderer; + var sceneRenderer = this.sceneRenderer; + + if (phaserRenderer && sceneRenderer) { + var viewportWidth = phaserRenderer.width; + var viewportHeight = phaserRenderer.height; + sceneRenderer.camera.position.x = viewportWidth / 2; + sceneRenderer.camera.position.y = viewportHeight / 2; + sceneRenderer.camera.setViewport(viewportWidth, viewportHeight); + } } -} \ No newline at end of file + + shutdown() { + this.systems.events.off("shutdown", this.shutdown, this); + if (this.isWebGL) { + this.game.scale.off(Phaser.Scale.Events.RESIZE, this.onResize, this); + } + } + + destroy() { + this.shutdown() + } + + gameDestroy() { + this.pluginManager.removeGameObject(SPINE_GAME_OBJECT_TYPE, true, true); + this.pluginManager.removeGameObject(SPINE_CONTAINER_TYPE, true, true); + if (this.sceneRenderer) this.sceneRenderer.dispose(); + } +} + diff --git a/spine-ts/spine-phaser/src/index.ts b/spine-ts/spine-phaser/src/index.ts index 470e10f59..c86582c84 100644 --- a/spine-ts/spine-phaser/src/index.ts +++ b/spine-ts/spine-phaser/src/index.ts @@ -1,10 +1,8 @@ export * from "./require-shim" -import { SpinePlugin } from "./SpinePlugin"; -{ - let w = window as any; - w["spine.SpinePlugin"] = SpinePlugin; -} export * from "./SpinePlugin" export * from "./SpineFile" +export * from "./mixins" export * from "@esotericsoftware/spine-core"; export * from "@esotericsoftware/spine-canvas"; +import { SpinePlugin } from "./SpinePlugin"; +(window as any).spine = { SpinePlugin: SpinePlugin }; diff --git a/spine-ts/spine-phaser/src/mixins.ts b/spine-ts/spine-phaser/src/mixins.ts new file mode 100644 index 000000000..58fd7d137 --- /dev/null +++ b/spine-ts/spine-phaser/src/mixins.ts @@ -0,0 +1,90 @@ +/* +The MIT License (MIT) + +Copyright (c) 2021-present AgogPixel + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +// Adapted from https://github.com/agogpixel/phaser3-ts-utils/tree/main + +let components = (Phaser.GameObjects.Components as any); +export const ComputedSize = components.ComputedSize; +export const Depth = components.ComputedSize; +export const Flip = components.Flip; +export const ScrollFactor = components.ScrollFactor; +export const Transform = components.Transform; +export const Visible = components.Visible; + +export interface Type< + T, + P extends any[] = any[] +> extends Function { + new(...args: P): T; +} + +export type Mixin = < + GameObjectType extends Type +>( + BaseGameObject: GameObjectType +) => GameObjectType & Type; + +export function createMixin< + GameObjectComponent, + GameObjectConstraint extends Phaser.GameObjects.GameObject = Phaser.GameObjects.GameObject +>( + ...component: GameObjectComponent[] +): Mixin { + return (BaseGameObject) => { + applyMixins(BaseGameObject, component); + return BaseGameObject as any; + }; +} + +function applyMixins(derivedCtor: any, constructors: any[]) { + constructors.forEach((baseCtor) => { + Object.getOwnPropertyNames(baseCtor.prototype || baseCtor).forEach((name) => { + Object.defineProperty( + derivedCtor.prototype, + name, + Object.getOwnPropertyDescriptor(baseCtor.prototype || baseCtor, name) || + Object.create(null) + ); + }); + }); +} + +type ComputedSizeMixin = Mixin; +export const ComputedSizeMixin: ComputedSizeMixin = createMixin(ComputedSize); + +type DepthMixin = Mixin; +export const DepthMixin: DepthMixin = createMixin(Depth); + +type FlipMixin = Mixin; +export const FlipMixin: FlipMixin = createMixin(Depth); + +type ScrollFactorMixin = Mixin; +export const ScrollFactorMixin: ScrollFactorMixin = createMixin(Depth); + +type TransformMixin = Mixin; +export const TransformMixin: TransformMixin = createMixin(Transform); + +type VisibleMixin = Mixin; +export const VisibleMixin: VisibleMixin = createMixin(Depth); +