Browse Source

[phaser] Bounds providers.

Mario Zechner 2 years ago
parent
commit
90cd61e505

+ 1 - 1
spine-ts/spine-phaser/example/bounds-test.html

@@ -38,7 +38,7 @@ function preload () {
 }
 }
 
 
 function create () {
 function create () {
-    spineboy = this.add.spine(400, 300, 'spineboy-data', "spineboy-atlas");
+    spineboy = this.add.spine(400, 300, 'spineboy-data', "spineboy-atlas", new spine.SkinsAndAnimationBoundsProvider("run"));
     spineboy.scale = 0.4
     spineboy.scale = 0.4
     spineboy.setInteractive();
     spineboy.setInteractive();
     this.input.enableDebug(spineboy, 0xff00ff);
     this.input.enableDebug(spineboy, 0xff00ff);

+ 54 - 11
spine-ts/spine-phaser/src/SpineGameObject.ts

@@ -1,7 +1,7 @@
 import { SPINE_GAME_OBJECT_TYPE } from "./keys";
 import { SPINE_GAME_OBJECT_TYPE } from "./keys";
 import { SpinePlugin } from "./SpinePlugin";
 import { SpinePlugin } from "./SpinePlugin";
 import { ComputedSizeMixin, DepthMixin, FlipMixin, ScrollFactorMixin, TransformMixin, VisibleMixin } from "./mixins";
 import { ComputedSizeMixin, DepthMixin, FlipMixin, ScrollFactorMixin, TransformMixin, VisibleMixin } from "./mixins";
-import { AnimationState, AnimationStateData, MathUtils, Skeleton } from "@esotericsoftware/spine-core";
+import { AnimationState, AnimationStateData, MathUtils, Skeleton, Skin } from "@esotericsoftware/spine-core";
 
 
 class BaseSpineGameObject extends Phaser.GameObjects.GameObject {
 class BaseSpineGameObject extends Phaser.GameObjects.GameObject {
 	constructor(scene: Phaser.Scene, type: string) {
 	constructor(scene: Phaser.Scene, type: string) {
@@ -16,23 +16,66 @@ export interface SpineGameObjectBoundsProvider {
 export class SetupPoseBoundsProvider implements SpineGameObjectBoundsProvider {
 export class SetupPoseBoundsProvider implements SpineGameObjectBoundsProvider {
 	calculateBounds(gameObject: SpineGameObject) {
 	calculateBounds(gameObject: SpineGameObject) {
 		if (!gameObject.skeleton) return { x: 0, y: 0, width: 0, height: 0 };
 		if (!gameObject.skeleton) return { x: 0, y: 0, width: 0, height: 0 };
-		gameObject.skeleton.setToSetupPose();
-		gameObject.skeleton.updateWorldTransform();
-		return gameObject.skeleton.getBoundsRect();
+		// Make a copy of animation state and skeleton as this might be called while
+		// the skeleton in the GameObject has already been heavily modified. We can not
+		// reconstruct that state.
+		const skeleton = new Skeleton(gameObject.skeleton.data);
+		skeleton.setToSetupPose();
+		skeleton.updateWorldTransform();
+		return skeleton.getBoundsRect();
 	}
 	}
 }
 }
 
 
 export class SkinsAndAnimationBoundsProvider implements SpineGameObjectBoundsProvider {
 export class SkinsAndAnimationBoundsProvider implements SpineGameObjectBoundsProvider {
-	constructor(private animation: string, private skins: string[] = []) {
+	constructor(private animation: string, private skins: string[] = [], private timeStep: number = 0.05) {
 
 
 	}
 	}
 
 
 	calculateBounds(gameObject: SpineGameObject): { x: number; y: number; width: number; height: number; } {
 	calculateBounds(gameObject: SpineGameObject): { x: number; y: number; width: number; height: number; } {
-		if (!gameObject.skeleton) return { x: 0, y: 0, width: 0, height: 0 };
-		// FIXME
-		gameObject.skeleton.setToSetupPose();
-		gameObject.skeleton.updateWorldTransform();
-		return gameObject.skeleton.getBoundsRect();
+		if (!gameObject.skeleton || !gameObject.animationState) return { x: 0, y: 0, width: 0, height: 0 };
+		// Make a copy of animation state and skeleton as this might be called while
+		// the skeleton in the GameObject has already been heavily modified. We can not
+		// reconstruct that state.
+		const animationState = new AnimationState(gameObject.animationState.data);
+		const skeleton = new Skeleton(gameObject.skeleton.data);
+		const data = skeleton.data;
+		let customSkin = new Skin("custom-skin");
+		if (this.skins.length > 0) {
+			for (const skinName of this.skins) {
+				const skin = data.findSkin(skinName);
+				if (skin == null) continue;
+				customSkin.addSkin(skin);
+			}
+			skeleton.setSkin(customSkin);
+		}
+		skeleton.setToSetupPose();
+
+		const animation = this.animation != null ? data.findAnimation(this.animation!) : null;
+		let minX = Number.POSITIVE_INFINITY, minY = Number.POSITIVE_INFINITY, maxX = Number.NEGATIVE_INFINITY, maxY = Number.NEGATIVE_INFINITY;
+		if (animation == null) {
+			skeleton.updateWorldTransform();
+			const bounds = skeleton.getBoundsRect();
+			minX = bounds.x;
+			minY = bounds.y;
+			maxX = minX + bounds.width;
+			maxY = minY + bounds.height;
+		} else {
+			animationState.clearTracks();
+			animationState.setAnimationWith(0, animation, false);
+			const steps = Math.max(animation.duration / this.timeStep, 1.0);
+			for (let i = 0; i < steps; i++) {
+				animationState.update(i > 0 ? this.timeStep : 0);
+				animationState.apply(skeleton);
+				skeleton.updateWorldTransform();
+
+				const bounds = skeleton.getBoundsRect();
+				minX = Math.min(minX, bounds.x);
+				minY = Math.min(minY, bounds.y);
+				maxX = Math.max(maxX, minX + bounds.width);
+				maxY = Math.max(maxY, minY + bounds.height);
+			}
+		}
+		return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };
 	}
 	}
 }
 }
 
 
@@ -93,7 +136,7 @@ export class SpineGameObject extends ComputedSizeMixin(DepthMixin(FlipMixin(Scro
 	}
 	}
 
 
 	public get scaleY() {
 	public get scaleY() {
-		return this._scaleX;
+		return this._scaleY;
 	}
 	}
 
 
 	public set scaleY(value: number) {
 	public set scaleY(value: number) {

+ 3 - 3
spine-ts/spine-phaser/src/SpinePlugin.ts

@@ -30,7 +30,7 @@
 import Phaser from "phaser";
 import Phaser from "phaser";
 import { SPINE_ATLAS_CACHE_KEY, SPINE_CONTAINER_TYPE, SPINE_GAME_OBJECT_TYPE, SPINE_ATLAS_TEXTURE_CACHE_KEY, SPINE_SKELETON_DATA_FILE_TYPE, SPINE_ATLAS_FILE_TYPE, SPINE_SKELETON_FILE_CACHE_KEY as SPINE_SKELETON_DATA_CACHE_KEY } from "./keys";
 import { SPINE_ATLAS_CACHE_KEY, SPINE_CONTAINER_TYPE, SPINE_GAME_OBJECT_TYPE, SPINE_ATLAS_TEXTURE_CACHE_KEY, SPINE_SKELETON_DATA_FILE_TYPE, SPINE_ATLAS_FILE_TYPE, SPINE_SKELETON_FILE_CACHE_KEY as SPINE_SKELETON_DATA_CACHE_KEY } from "./keys";
 import { AtlasAttachmentLoader, Bone, GLTexture, SceneRenderer, Skeleton, SkeletonBinary, SkeletonData, SkeletonJson, TextureAtlas } from "@esotericsoftware/spine-webgl"
 import { AtlasAttachmentLoader, Bone, GLTexture, SceneRenderer, Skeleton, SkeletonBinary, SkeletonData, SkeletonJson, TextureAtlas } from "@esotericsoftware/spine-webgl"
-import { SpineGameObject } from "./SpineGameObject";
+import { SpineGameObject, SpineGameObjectBoundsProvider } from "./SpineGameObject";
 import { CanvasTexture, SkeletonRenderer } from "@esotericsoftware/spine-canvas";
 import { CanvasTexture, SkeletonRenderer } from "@esotericsoftware/spine-canvas";
 
 
 export class SpinePlugin extends Phaser.Plugins.ScenePlugin {
 export class SpinePlugin extends Phaser.Plugins.ScenePlugin {
@@ -98,8 +98,8 @@ export class SpinePlugin extends Phaser.Plugins.ScenePlugin {
 		pluginManager.registerFileType("spineAtlas", atlasFileCallback, scene);
 		pluginManager.registerFileType("spineAtlas", atlasFileCallback, scene);
 
 
 		let self = this;
 		let self = this;
-		let addSpineGameObject = function (this: Phaser.GameObjects.GameObjectFactory, x: number, y: number, dataKey: string, atlasKey: string) {
-			let gameObject = new SpineGameObject(scene, self, x, y, dataKey, atlasKey);
+		let addSpineGameObject = function (this: Phaser.GameObjects.GameObjectFactory, x: number, y: number, dataKey: string, atlasKey: string, boundsProvider: SpineGameObjectBoundsProvider) {
+			let gameObject = new SpineGameObject(scene, self, x, y, dataKey, atlasKey, boundsProvider);
 			this.displayList.add(gameObject);
 			this.displayList.add(gameObject);
 			this.updateList.add(gameObject);
 			this.updateList.add(gameObject);
 			return gameObject;
 			return gameObject;

+ 1 - 0
spine-ts/spine-phaser/src/index.ts

@@ -1,5 +1,6 @@
 export * from "./require-shim"
 export * from "./require-shim"
 export * from "./SpinePlugin"
 export * from "./SpinePlugin"
+export * from "./SpineGameObject"
 export * from "./mixins"
 export * from "./mixins"
 export * from "@esotericsoftware/spine-core";
 export * from "@esotericsoftware/spine-core";
 export * from "@esotericsoftware/spine-webgl";
 export * from "@esotericsoftware/spine-webgl";