Sfoglia il codice sorgente

[phaser] Mixins for SpineGameObject

Mario Zechner 2 anni fa
parent
commit
f3c4be2f90

+ 1 - 10
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": {

+ 2 - 2
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' });
 }

+ 1 - 2
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"
   }
 }

+ 29 - 0
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) {
+
+    }
+}

+ 82 - 18
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);
+        }
+    }
+
+    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();
+    }
+}
+

+ 3 - 5
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 };

+ 90 - 0
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<GameObjectComponent, GameObjectConstraint extends Phaser.GameObjects.GameObject> = <
+    GameObjectType extends Type<GameObjectConstraint>
+>(
+    BaseGameObject: GameObjectType
+) => GameObjectType & Type<GameObjectComponent>;
+
+export function createMixin<
+    GameObjectComponent,
+    GameObjectConstraint extends Phaser.GameObjects.GameObject = Phaser.GameObjects.GameObject
+>(
+    ...component: GameObjectComponent[]
+): Mixin<GameObjectComponent, GameObjectConstraint> {
+    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<Phaser.GameObjects.Components.Transform, Phaser.GameObjects.GameObject>;
+export const ComputedSizeMixin: ComputedSizeMixin = createMixin<Phaser.GameObjects.Components.ComputedSize>(ComputedSize);
+
+type DepthMixin = Mixin<Phaser.GameObjects.Components.Depth, Phaser.GameObjects.GameObject>;
+export const DepthMixin: DepthMixin = createMixin<Phaser.GameObjects.Components.Depth>(Depth);
+
+type FlipMixin = Mixin<Phaser.GameObjects.Components.Flip, Phaser.GameObjects.GameObject>;
+export const FlipMixin: FlipMixin = createMixin<Phaser.GameObjects.Components.Flip>(Depth);
+
+type ScrollFactorMixin = Mixin<Phaser.GameObjects.Components.ScrollFactor, Phaser.GameObjects.GameObject>;
+export const ScrollFactorMixin: ScrollFactorMixin = createMixin<Phaser.GameObjects.Components.ScrollFactor>(Depth);
+
+type TransformMixin = Mixin<Phaser.GameObjects.Components.Transform, Phaser.GameObjects.GameObject>;
+export const TransformMixin: TransformMixin = createMixin<Phaser.GameObjects.Components.Transform>(Transform);
+
+type VisibleMixin = Mixin<Phaser.GameObjects.Components.Visible, Phaser.GameObjects.GameObject>;
+export const VisibleMixin: VisibleMixin = createMixin<Phaser.GameObjects.Components.Visible>(Depth);
+