[phaser] Loading and caching of skeleton data, atlas, textures, creation of skeletons, animation state in game object.

# Conflicts:
#	spine-cpp/spine-cpp/src/spine/SkeletonBinary.cpp
#	spine-flutter/src/spine_flutter.cpp
This commit is contained in:
Mario Zechner 2022-12-12 15:24:02 +01:00
parent e805b6c8bc
commit 9ece84dc85
12 changed files with 368 additions and 211 deletions

View File

@ -1256,6 +1256,7 @@ Animation *SkeletonBinary::readAnimation(const String &name, DataInput *input, S
switch (timelineType) { switch (timelineType) {
case ATTACHMENT_DEFORM: { case ATTACHMENT_DEFORM: {
VertexAttachment *attachment = static_cast<VertexAttachment *>(baseAttachment);
bool weighted = attachment->_bones.size() > 0; bool weighted = attachment->_bones.size() > 0;
Vector<float> &vertices = attachment->_vertices; Vector<float> &vertices = attachment->_vertices;
int deformLength = weighted ? (int) vertices.size() / 3 * 2 : (int) vertices.size(); int deformLength = weighted ? (int) vertices.size() / 3 * 2 : (int) vertices.size();

View File

@ -34,42 +34,42 @@
using namespace spine; using namespace spine;
int main(void) { int main(void) {
String atlasFile("data/spineboy-pma.atlas"); String atlasFile("data/spineboy-pma.atlas");
String skeletonFile("data/spineboy-pro.skel"); String skeletonFile("data/spineboy-pro.skel");
float scale = 0.6f; float scale = 0.6f;
SFMLTextureLoader textureLoader; SFMLTextureLoader textureLoader;
Atlas *atlas = new Atlas(atlasFile, &textureLoader); Atlas *atlas = new Atlas(atlasFile, &textureLoader);
SkeletonData *skeletonData = nullptr; SkeletonData *skeletonData = nullptr;
if (strncmp(skeletonFile.buffer(), ".skel", skeletonFile.length()) > 0) { if (strncmp(skeletonFile.buffer(), ".skel", skeletonFile.length()) > 0) {
SkeletonBinary binary(atlas); SkeletonBinary binary(atlas);
binary.setScale(scale); binary.setScale(scale);
skeletonData = binary.readSkeletonDataFile(skeletonFile); skeletonData = binary.readSkeletonDataFile(skeletonFile);
} else { } else {
SkeletonJson json(atlas); SkeletonJson json(atlas);
json.setScale(scale); json.setScale(scale);
skeletonData = json.readSkeletonDataFile(skeletonFile); skeletonData = json.readSkeletonDataFile(skeletonFile);
} }
AnimationStateData stateData(skeletonData); AnimationStateData stateData(skeletonData);
SkeletonDrawable drawable(skeletonData, &stateData); SkeletonDrawable drawable(skeletonData, &stateData);
drawable.skeleton->setPosition(320, 590); drawable.skeleton->setPosition(320, 590);
drawable.state->setAnimation(0, "walk", true); drawable.state->setAnimation(0, "walk", true);
sf::RenderWindow window(sf::VideoMode(640, 640), "Spine SFML - testbed"); sf::RenderWindow window(sf::VideoMode(640, 640), "Spine SFML - testbed");
window.setFramerateLimit(60); window.setFramerateLimit(60);
sf::Event event; sf::Event event;
sf::Clock deltaClock; sf::Clock deltaClock;
while (window.isOpen()) { while (window.isOpen()) {
while (window.pollEvent(event)) while (window.pollEvent(event))
if (event.type == sf::Event::Closed) window.close(); if (event.type == sf::Event::Closed) window.close();
float delta = deltaClock.getElapsedTime().asSeconds(); float delta = deltaClock.getElapsedTime().asSeconds();
deltaClock.restart(); deltaClock.restart();
drawable.update(delta); drawable.update(delta);
window.clear(); window.clear();
window.draw(drawable); window.draw(drawable);
window.display(); window.display();
} }
return 0; return 0;
} }

View File

@ -8419,7 +8419,9 @@
"version": "4.2.10", "version": "4.2.10",
"license": "LicenseRef-LICENSE", "license": "LicenseRef-LICENSE",
"dependencies": { "dependencies": {
"@esotericsoftware/spine-canvas": "^4.2.10",
"@esotericsoftware/spine-core": "^4.2.10", "@esotericsoftware/spine-core": "^4.2.10",
"@esotericsoftware/spine-webgl": "^4.2.10",
"phaser": "^3.55.2" "phaser": "^3.55.2"
} }
}, },

View File

@ -8,7 +8,7 @@ var config = {
type: Phaser.AUTO, type: Phaser.AUTO,
width: 800, width: 800,
height: 600, height: 600,
// type: Phaser.CANVAS, type: Phaser.CANVAS,
scene: { scene: {
preload: preload, preload: preload,
create: create, create: create,
@ -23,11 +23,12 @@ var config = {
var game = new Phaser.Game(config); var game = new Phaser.Game(config);
function preload () { function preload () {
this.load.spine("raptor", "assets/raptor-pro.json", "assets/raptor.atlas", true); this.load.spineJson("raptor-data", "assets/raptor-pro.json");
this.load.spineAtlas("raptor-atlas", "assets/raptor.atlas");
} }
function create () { function create () {
let plugin = this.spine; let plugin = this.spine;
var boy = this.add.spine(400, 600, 'raptor'); var boy = this.add.spine(400, 600, 'raptor-data', "raptor-atlas");
this.add.text(10, 10, "Spine", { font: '16px Courier', fill: '#00ff00' }); this.add.text(10, 10, "Spine", { font: '16px Courier', fill: '#00ff00' });
} }

View File

@ -31,6 +31,8 @@
"homepage": "https://github.com/esotericsoftware/spine-runtimes#readme", "homepage": "https://github.com/esotericsoftware/spine-runtimes#readme",
"dependencies": { "dependencies": {
"@esotericsoftware/spine-core": "^4.2.10", "@esotericsoftware/spine-core": "^4.2.10",
"@esotericsoftware/spine-webgl": "^4.2.10",
"@esotericsoftware/spine-canvas": "^4.2.10",
"phaser": "^3.55.2" "phaser": "^3.55.2"
} }
} }

View File

@ -1,13 +0,0 @@
import { SPINE_ATLAS_CACHE_KEY, SPINE_FILE_TYPE, SPINE_LOADER_TYPE } from "./keys";
export class SpineFile extends Phaser.Loader.MultiFile {
constructor(loader: Phaser.Loader.LoaderPlugin, key: string, jsonURL: string, atlasURL: string, premultipliedAlpha: boolean = false, jsonXhrSettings: Phaser.Types.Loader.XHRSettingsObject, atlasXhrSettings: Phaser.Types.Loader.XHRSettingsObject) {
let json = new Phaser.Loader.FileTypes.JSONFile(loader, key, jsonURL, jsonXhrSettings);
let atlas = new Phaser.Loader.FileTypes.TextFile(loader, key, atlasURL, atlasXhrSettings);
atlas.cache = loader.cacheManager.custom[SPINE_ATLAS_CACHE_KEY];
super(loader, SPINE_FILE_TYPE, key, [json, atlas]);
}
addToCache() {
}
}

View File

@ -1,11 +1,12 @@
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, Skeleton } 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) {
super(scene, type); super(scene, type);
} }
} }
interface SpineContainer { interface SpineContainer {
@ -13,21 +14,32 @@ interface SpineContainer {
} }
export class SpineGameObject extends ComputedSizeMixin(DepthMixin(FlipMixin(ScrollFactorMixin(TransformMixin(VisibleMixin(BaseSpineGameObject)))))) { export class SpineGameObject extends ComputedSizeMixin(DepthMixin(FlipMixin(ScrollFactorMixin(TransformMixin(VisibleMixin(BaseSpineGameObject)))))) {
blendMode = -1; blendMode = -1;
skeleton: Skeleton | null = null;
animationState: AnimationState | null = null;
constructor(scene: Phaser.Scene, plugin: SpinePlugin, x: number, y: number, key: string) { constructor (scene: Phaser.Scene, private plugin: SpinePlugin, x: number, y: number, dataKey: string, atlasKey: string) {
super(scene, SPINE_GAME_OBJECT_TYPE); super(scene, SPINE_GAME_OBJECT_TYPE);
this.setPosition(x, y); this.setPosition(x, y);
} }
preUpdate(time: number, delta: number) { setSkeleton (dataKey: string, atlasKey: string) {
} if (dataKey && atlasKey) {
this.skeleton = this.plugin.createSkeleton(dataKey, atlasKey);
this.animationState = new AnimationState(new AnimationStateData(this.skeleton.data));
} else {
this.skeleton = null;
}
}
renderWebGL(renderer: Phaser.Renderer.WebGL.WebGLRenderer, src: SpineGameObject, camera: Phaser.Cameras.Scene2D.Camera, parentMatrix: Phaser.GameObjects.Components.TransformMatrix, container: SpineContainer) { 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) {
renderCanvas(renderer: Phaser.Renderer.Canvas.CanvasRenderer, src: SpineGameObject, camera: Phaser.Cameras.Scene2D.Camera, parentMatrix: Phaser.GameObjects.Components.TransformMatrix) { }
} renderCanvas (renderer: Phaser.Renderer.Canvas.CanvasRenderer, src: SpineGameObject, camera: Phaser.Cameras.Scene2D.Camera, parentMatrix: Phaser.GameObjects.Components.TransformMatrix) {
}
} }

View File

@ -28,135 +28,286 @@
*****************************************************************************/ *****************************************************************************/
import Phaser from "phaser"; import Phaser from "phaser";
import { SPINE_ATLAS_CACHE_KEY, SPINE_CONTAINER_TYPE, SPINE_FILE_TYPE, SPINE_GAME_OBJECT_TYPE, SPINE_TEXTURE_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 { SceneRenderer, SkeletonDebugRenderer, SkeletonRenderer } from "@esotericsoftware/spine-webgl" import { AtlasAttachmentLoader, GLTexture, SceneRenderer, Skeleton, SkeletonData, SkeletonDebugRenderer, SkeletonJson, SkeletonRenderer, TextureAtlas } from "@esotericsoftware/spine-webgl"
import { SpineFile } from "./SpineFile";
import { SpineGameObject } from "./SpineGameObject"; import { SpineGameObject } from "./SpineGameObject";
import { CanvasTexture } from "@esotericsoftware/spine-canvas";
export class SpinePlugin extends Phaser.Plugins.ScenePlugin { export class SpinePlugin extends Phaser.Plugins.ScenePlugin {
game: Phaser.Game; game: Phaser.Game;
isWebGL: boolean; isWebGL: boolean;
atlasCache: Phaser.Cache.BaseCache; gl: WebGLRenderingContext | null;
spineTextureCache: Phaser.Cache.BaseCache; textureManager: Phaser.Textures.TextureManager;
jsonCache: Phaser.Cache.BaseCache; phaserRenderer: Phaser.Renderer.Canvas.CanvasRenderer | Phaser.Renderer.WebGL.WebGLRenderer | null;
textures: Phaser.Textures.TextureManager; sceneRenderer: SceneRenderer | null;
gl: WebGLRenderingContext | null; skeletonDataCache: Phaser.Cache.BaseCache;
phaserRenderer: Phaser.Renderer.Canvas.CanvasRenderer | Phaser.Renderer.WebGL.WebGLRenderer | null; atlasCache: Phaser.Cache.BaseCache;
sceneRenderer: SceneRenderer | null;
constructor(scene: Phaser.Scene, pluginManager: Phaser.Plugins.PluginManager, pluginKey: string) { constructor (scene: Phaser.Scene, pluginManager: Phaser.Plugins.PluginManager, pluginKey: string) {
super(scene, pluginManager, pluginKey); super(scene, pluginManager, pluginKey);
var game = this.game = pluginManager.game; var game = this.game = pluginManager.game;
this.isWebGL = this.game.config.renderType === 2; this.isWebGL = this.game.config.renderType === 2;
this.atlasCache = this.game.cache.addCustom(SPINE_ATLAS_CACHE_KEY); this.gl = this.isWebGL ? (this.game.renderer as Phaser.Renderer.WebGL.WebGLRenderer).gl : null;
this.spineTextureCache = this.game.cache.addCustom(SPINE_TEXTURE_CACHE_KEY); this.textureManager = this.game.textures;
this.jsonCache = this.game.cache.json; this.phaserRenderer = this.game.renderer;
this.textures = this.game.textures; this.sceneRenderer = null;
this.gl = this.isWebGL ? (this.game.renderer as Phaser.Renderer.WebGL.WebGLRenderer).gl : null; this.skeletonDataCache = this.game.cache.addCustom(SPINE_SKELETON_DATA_CACHE_KEY);
this.phaserRenderer = this.game.renderer; this.atlasCache = this.game.cache.addCustom(SPINE_ATLAS_CACHE_KEY);
this.sceneRenderer = null;
if (!this.phaserRenderer) { if (!this.phaserRenderer) {
this.phaserRenderer = { this.phaserRenderer = {
width: game.scale.width, width: game.scale.width,
height: game.scale.height, height: game.scale.height,
preRender: () => { }, preRender: () => { },
postRender: () => { }, postRender: () => { },
render: () => { }, render: () => { },
destroy: () => { } destroy: () => { }
} as unknown as Phaser.Renderer.Canvas.CanvasRenderer; } as unknown as Phaser.Renderer.Canvas.CanvasRenderer;
} }
let fileCallback = function (this: any, key: string, let skeletonJsonFileCallback = function (this: any, key: string,
jsonURL: string, url: string,
atlasURL: string, xhrSettings: Phaser.Types.Loader.XHRSettingsObject) {
premultipliedAlpha: boolean, let file = new SpineSkeletonDataFile(this as any, key, url, SpineSkeletonDataFileType.json, xhrSettings);
jsonXhrSettings: Phaser.Types.Loader.XHRSettingsObject, this.addFile(file.files);
atlasXhrSettings: Phaser.Types.Loader.XHRSettingsObject) { return this;
let file = new SpineFile(this as any, key, jsonURL, atlasURL, premultipliedAlpha, jsonXhrSettings, atlasXhrSettings); };
this.addFile(file.files); pluginManager.registerFileType("spineJson", skeletonJsonFileCallback, scene);
return this;
};
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 skeletonBinaryFileCallback = function (this: any, key: string,
let key = config.key ? config.key : null; url: string,
let gameObject = new SpineGameObject(this.scene, self, 0, 0, key); xhrSettings: Phaser.Types.Loader.XHRSettingsObject) {
if (addToScene !== undefined) { let file = new SpineSkeletonDataFile(this as any, key, url, SpineSkeletonDataFileType.binary, xhrSettings);
config.add = addToScene; this.addFile(file.files);
} return this;
Phaser.GameObjects.BuildGameObject(this.scene, gameObject, config); };
} pluginManager.registerFileType("spineBinary", skeletonBinaryFileCallback, scene);
pluginManager.registerFileType(SPINE_FILE_TYPE, fileCallback, scene);
pluginManager.registerGameObject(SPINE_GAME_OBJECT_TYPE, addSpineGameObject, makeSpineGameObject);
}
boot() { let atlasFileCallback = function (this: any, key: string,
if (this.isWebGL) { url: string,
// Monkeypatch the Spine setBlendMode functions, or batching is destroyed! premultipliedAlpha: boolean,
let setBlendMode = function (this: any, srcBlend: any, dstBlend: any) { xhrSettings: Phaser.Types.Loader.XHRSettingsObject) {
if (srcBlend !== this.srcBlend || dstBlend !== this.dstBlend) { let file = new SpineAtlasFile(this as any, key, url, premultipliedAlpha, xhrSettings);
let gl = this.context.gl; this.addFile(file.files);
this.srcBlend = srcBlend; return this;
this.dstBlend = dstBlend; };
if (this.isDrawing) { pluginManager.registerFileType("spineAtlas", atlasFileCallback, scene);
this.flush();
gl.blendFunc(this.srcBlend, this.dstBlend);
}
}
};
var sceneRenderer = this.sceneRenderer; let self = this;
if (!sceneRenderer) { let addSpineGameObject = function (this: Phaser.GameObjects.GameObjectFactory, x: number, y: number, dataKey: string, atlasKey: string) {
sceneRenderer = new SceneRenderer((this.phaserRenderer! as Phaser.Renderer.WebGL.WebGLRenderer).canvas, this.gl!, true); let gameObject = new SpineGameObject(scene, self, x, y, dataKey, atlasKey);
sceneRenderer.batcher.setBlendMode = setBlendMode; this.displayList.add(gameObject);
(sceneRenderer as any).shapes.setBlendMode = setBlendMode; this.updateList.add(gameObject);
} return gameObject;
};
this.sceneRenderer = sceneRenderer; let makeSpineGameObject = function (this: Phaser.GameObjects.GameObjectFactory, config: any, addToScene: boolean) {
} let dataKey = config.dataKey ? config.dataKey : null;
let atlasKey = config.atlasKey ? config.atlasKey : null;
let gameObject = new SpineGameObject(this.scene, self, 0, 0, dataKey, atlasKey);
if (addToScene !== undefined) {
config.add = addToScene;
}
Phaser.GameObjects.BuildGameObject(this.scene, gameObject, config);
}
pluginManager.registerGameObject(SPINE_GAME_OBJECT_TYPE, addSpineGameObject, makeSpineGameObject);
}
var eventEmitter = this.systems.events; boot () {
eventEmitter.once('shutdown', this.shutdown, this); if (this.isWebGL) {
eventEmitter.once('destroy', this.destroy, this); // Monkeypatch the Spine setBlendMode functions, or batching is destroyed!
this.game.events.once('destroy', this.gameDestroy, this); 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);
}
}
};
onResize() { var sceneRenderer = this.sceneRenderer;
var phaserRenderer = this.phaserRenderer; if (!sceneRenderer) {
var sceneRenderer = this.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;
}
if (phaserRenderer && sceneRenderer) { this.sceneRenderer = 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() { var eventEmitter = this.systems.events;
this.systems.events.off("shutdown", this.shutdown, this); eventEmitter.once('shutdown', this.shutdown, this);
if (this.isWebGL) { eventEmitter.once('destroy', this.destroy, this);
this.game.scale.off(Phaser.Scale.Events.RESIZE, this.onResize, this); this.game.events.once('destroy', this.gameDestroy, this);
} }
}
destroy() { onResize () {
this.shutdown() var phaserRenderer = this.phaserRenderer;
} var sceneRenderer = this.sceneRenderer;
gameDestroy() { if (phaserRenderer && sceneRenderer) {
this.pluginManager.removeGameObject(SPINE_GAME_OBJECT_TYPE, true, true); var viewportWidth = phaserRenderer.width;
this.pluginManager.removeGameObject(SPINE_CONTAINER_TYPE, true, true); var viewportHeight = phaserRenderer.height;
if (this.sceneRenderer) this.sceneRenderer.dispose(); 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();
}
createSkeleton (dataKey: string, atlasKey: string) {
let atlas: TextureAtlas;
if (this.atlasCache.exists(atlasKey)) {
atlas = this.atlasCache.get(atlasKey);
} else {
let atlasFile = this.game.cache.text.get(atlasKey) as string;
atlas = new TextureAtlas(atlasFile);
if (this.isWebGL) {
let gl = this.gl!;
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
for (let atlasPage of atlas.pages) {
atlasPage.setTexture(new GLTexture(gl, this.textureManager.get(atlasKey + "!" + atlasPage.name).getSourceImage() as HTMLImageElement | ImageBitmap, false));
}
} else {
for (let atlasPage of atlas.pages) {
atlasPage.setTexture(new CanvasTexture(this.textureManager.get(atlasKey + "!" + atlasPage.name).getSourceImage() as HTMLImageElement | ImageBitmap));
}
}
this.atlasCache.add(atlasKey, atlas);
}
let skeletonData: SkeletonData;
if (this.skeletonDataCache.exists(dataKey)) {
skeletonData = this.skeletonDataCache.get(dataKey);
} else {
if (this.game.cache.json.exists(dataKey)) {
let jsonFile = this.game.cache.json.get(dataKey) as any;
let json = new SkeletonJson(new AtlasAttachmentLoader(atlas));
skeletonData = json.readSkeletonData(jsonFile);
} else {
let binaryFile = this.game.cache.binary.get(dataKey) as ArrayBuffer;
let binary = new SkeletonJson(new AtlasAttachmentLoader(atlas));
skeletonData = binary.readSkeletonData(binaryFile);
}
this.skeletonDataCache.add(dataKey, skeletonData);
}
return new Skeleton(skeletonData);
}
} }
export enum SpineSkeletonDataFileType {
json,
binary
}
export class SpineSkeletonDataFile extends Phaser.Loader.MultiFile {
constructor (loader: Phaser.Loader.LoaderPlugin, key: string, url: string, public fileType: SpineSkeletonDataFileType, xhrSettings: Phaser.Types.Loader.XHRSettingsObject) {
let file = null;
let isJson = fileType == SpineSkeletonDataFileType.json;
if (isJson) {
file = new Phaser.Loader.FileTypes.JSONFile(loader, {
key: key,
url: url,
extension: "json",
xhrSettings: xhrSettings,
} as Phaser.Types.Loader.FileTypes.JSONFileConfig);
} else {
file = new Phaser.Loader.FileTypes.BinaryFile(loader, {
key: key,
url: url,
extension: "skel",
xhrSettings: xhrSettings,
} as Phaser.Types.Loader.FileTypes.BinaryFileConfig);
}
super(loader, SPINE_SKELETON_DATA_FILE_TYPE, key, [file]);
}
onFileComplete (file: Phaser.Loader.File) {
this.pending--;
}
addToCache () {
if (this.isReadyToProcess()) this.files[0].addToCache();
}
}
export class SpineAtlasFile extends Phaser.Loader.MultiFile {
constructor (loader: Phaser.Loader.LoaderPlugin, key: string, url: string, public premultipliedAlpha: boolean, xhrSettings: Phaser.Types.Loader.XHRSettingsObject) {
super(loader, SPINE_ATLAS_FILE_TYPE, key, [
new Phaser.Loader.FileTypes.TextFile(loader, {
key: key,
url: url,
xhrSettings: xhrSettings,
extension: "atlas"
})
]);
}
onFileComplete (file: Phaser.Loader.File) {
if (this.files.indexOf(file) != -1) {
this.pending--;
if (file.type == "text") {
var lines = file.data.split('\n');
let textures = [];
textures.push(lines[0]);
for (var t = 1; t < lines.length; t++) {
var line = lines[t];
if (line.trim() === '' && t < lines.length - 1) {
line = lines[t + 1];
textures.push(line);
}
}
let basePath = file.src.match(/^.*\//);
for (var i = 0; i < textures.length; i++) {
var url = basePath + textures[i];
var key = file.key + "!" + textures[i];
var image = new Phaser.Loader.FileTypes.ImageFile(this.loader, key, url);
if (!this.loader.keyExists(image)) {
this.addToMultiFile(image);
this.loader.addFile(image);
}
}
}
}
}
addToCache () {
if (this.isReadyToProcess()) {
let textureManager = this.loader.textureManager;
for (let file of this.files) {
if (file.type == "image") {
if (!textureManager.exists(file.key)) {
textureManager.addImage(file.key, file.data);
}
} else {
file.addToCache();
}
}
}
}
}

View File

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

View File

@ -1,6 +1,8 @@
export const SPINE_SKELETON_FILE_CACHE_KEY = "esotericsoftware.spine.skeletonFile.cache";
export const SPINE_ATLAS_CACHE_KEY = "esotericsoftware.spine.atlas.cache"; export const SPINE_ATLAS_CACHE_KEY = "esotericsoftware.spine.atlas.cache";
export const SPINE_TEXTURE_CACHE_KEY = "esotericsoftware.spine.texture.cache"; export const SPINE_ATLAS_TEXTURE_CACHE_KEY = "esotericsoftware.spine.atlas.texture.cache";
export const SPINE_LOADER_TYPE = "spine"; export const SPINE_LOADER_TYPE = "spine";
export const SPINE_FILE_TYPE = "spine"; export const SPINE_SKELETON_DATA_FILE_TYPE = "spineSkeletonData";
export const SPINE_ATLAS_FILE_TYPE = "spineAtlasData";
export const SPINE_GAME_OBJECT_TYPE = "spine"; export const SPINE_GAME_OBJECT_TYPE = "spine";
export const SPINE_CONTAINER_TYPE = "spineContainer"; export const SPINE_CONTAINER_TYPE = "spineContainer";

View File

@ -33,41 +33,41 @@ export const Transform = components.Transform;
export const Visible = components.Visible; export const Visible = components.Visible;
export interface Type< export interface Type<
T, T,
P extends any[] = any[] P extends any[] = any[]
> extends Function { > extends Function {
new(...args: P): T; new(...args: P): T;
} }
export type Mixin<GameObjectComponent, GameObjectConstraint extends Phaser.GameObjects.GameObject> = < export type Mixin<GameObjectComponent, GameObjectConstraint extends Phaser.GameObjects.GameObject> = <
GameObjectType extends Type<GameObjectConstraint> GameObjectType extends Type<GameObjectConstraint>
>( >(
BaseGameObject: GameObjectType BaseGameObject: GameObjectType
) => GameObjectType & Type<GameObjectComponent>; ) => GameObjectType & Type<GameObjectComponent>;
export function createMixin< export function createMixin<
GameObjectComponent, GameObjectComponent,
GameObjectConstraint extends Phaser.GameObjects.GameObject = Phaser.GameObjects.GameObject GameObjectConstraint extends Phaser.GameObjects.GameObject = Phaser.GameObjects.GameObject
>( > (
...component: GameObjectComponent[] ...component: GameObjectComponent[]
): Mixin<GameObjectComponent, GameObjectConstraint> { ): Mixin<GameObjectComponent, GameObjectConstraint> {
return (BaseGameObject) => { return (BaseGameObject) => {
applyMixins(BaseGameObject, component); applyMixins(BaseGameObject, component);
return BaseGameObject as any; return BaseGameObject as any;
}; };
} }
function applyMixins(derivedCtor: any, constructors: any[]) { function applyMixins (derivedCtor: any, constructors: any[]) {
constructors.forEach((baseCtor) => { constructors.forEach((baseCtor) => {
Object.getOwnPropertyNames(baseCtor.prototype || baseCtor).forEach((name) => { Object.getOwnPropertyNames(baseCtor.prototype || baseCtor).forEach((name) => {
Object.defineProperty( Object.defineProperty(
derivedCtor.prototype, derivedCtor.prototype,
name, name,
Object.getOwnPropertyDescriptor(baseCtor.prototype || baseCtor, name) || Object.getOwnPropertyDescriptor(baseCtor.prototype || baseCtor, name) ||
Object.create(null) Object.create(null)
); );
}); });
}); });
} }
type ComputedSizeMixin = Mixin<Phaser.GameObjects.Components.Transform, Phaser.GameObjects.GameObject>; type ComputedSizeMixin = Mixin<Phaser.GameObjects.Components.Transform, Phaser.GameObjects.GameObject>;

View File

@ -2,10 +2,10 @@ declare global {
var require: any; var require: any;
} }
if (window.Phaser) { if (window.Phaser) {
let prevRequire = window.require; let prevRequire = window.require;
window.require = (x: string) => { window.require = (x: string) => {
if (prevRequire) return prevRequire(x); if (prevRequire) return prevRequire(x);
else if (x === "Phaser") return window.Phaser; else if (x === "Phaser") return window.Phaser;
} }
} }
export {} export { }