Merge branch '4.1' into 4.2-beta

# Conflicts:
#	.gitignore
#	spine-cpp/spine-cpp/src/spine/SkeletonBinary.cpp
#	spine-ts/package-lock.json
#	spine-ts/package.json
This commit is contained in:
Mario Zechner 2022-12-27 11:48:02 +01:00
commit 68a9096049
52 changed files with 21388 additions and 52 deletions

1
.gitignore vendored
View File

@ -185,3 +185,4 @@ spine-flutter/src/spine-cpp
spine-godot/.clang-format
spine-ts/spine-phaser/dist

View File

@ -357,6 +357,29 @@ cp -f ../spineboy/export/spineboy-pro.skel "$ROOT/spine-ts/spine-player/example/
cp -f ../spineboy/export/spineboy-pma.atlas "$ROOT/spine-ts/spine-player/example/assets/"
cp -f ../spineboy/export/spineboy-pma.png "$ROOT/spine-ts/spine-player/example/assets/"
rm "$ROOT/spine-ts/spine-phaser/example/assets/"*
cp -f ../raptor/export/raptor-pro.json "$ROOT/spine-ts/spine-phaser/example/assets/"
cp -f ../raptor/export/raptor-pma.atlas "$ROOT/spine-ts/spine-phaser/example/assets/"
cp -f ../raptor/export/raptor-pma.png "$ROOT/spine-ts/spine-phaser/example/assets/"
cp -f ../spineboy/export/spineboy-pro.skel "$ROOT/spine-ts/spine-phaser/example/assets/"
cp -f ../spineboy/export/spineboy-pma.atlas "$ROOT/spine-ts/spine-phaser/example/assets/"
cp -f ../spineboy/export/spineboy-pma.png "$ROOT/spine-ts/spine-phaser/example/assets/"
cp -f ../coin/export/coin-pro.skel "$ROOT/spine-ts/spine-phaser/example/assets/"
cp -f ../coin/export/coin-pma.atlas "$ROOT/spine-ts/spine-phaser/example/assets/"
cp -f ../coin/export/coin-pma.png "$ROOT/spine-ts/spine-phaser/example/assets/"
cp -f ../stretchyman/export/stretchyman-pro.json "$ROOT/spine-ts/spine-phaser/example/assets/"
cp -f ../stretchyman/export/stretchyman-pro.skel "$ROOT/spine-ts/spine-phaser/example/assets/"
cp -f ../stretchyman/export/stretchyman-pma.atlas "$ROOT/spine-ts/spine-phaser/example/assets/"
cp -f ../stretchyman/export/stretchyman-pma.png "$ROOT/spine-ts/spine-phaser/example/assets/"
cp -f ../mix-and-match/export/mix-and-match-pro.json "$ROOT/spine-ts/spine-phaser/example/assets/"
cp -f ../mix-and-match/export/mix-and-match-pro.skel "$ROOT/spine-ts/spine-phaser/example/assets/"
cp -f ../mix-and-match/export/mix-and-match-pma.atlas "$ROOT/spine-ts/spine-phaser/example/assets/"
cp -f ../mix-and-match/export/mix-and-match-pma.png "$ROOT/spine-ts/spine-phaser/example/assets/"
echo "spine-monogame"
rm "$ROOT/spine-monogame/spine-monogame-example/data/"*
cp -f ../coin/export/coin-pro.json "$ROOT/spine-monogame/spine-monogame-example/data/"

View File

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

View File

@ -31,6 +31,14 @@
"name": "threejs-example",
"url": "http://localhost:8080/spine-threejs/example/index.html",
"webRoot": "${workspaceFolder}"
},
{
"type": "pwa-chrome",
"request": "launch",
"name": "phaser-example",
"url": "http://localhost:8080/spine-phaser/example/index.html",
"webRoot": "${workspaceFolder}"
}
]
}

View File

@ -16,6 +16,10 @@
<li><a href="/spine-canvas/example">Example</a></li>
<li><a href="/spine-canvas/example/mouse-click.html">Mouse click</a></li>
</ul>
<li>Phaser</li>
<ul>
<li><a href="/spine-phaser/example/index.html">Example</a></li>
</ul>
<li>Player</li>
<ul>
<li><a href="/spine-player/example/example.html">Example</a></li>

View File

@ -7,20 +7,22 @@
],
"scripts": {
"prepublish": "npm run clean && npm run build",
"clean": "npx rimraf spine-core/dist spine-canvas/dist spine-webgl/dist spine-player/dist spine-threejs/dist",
"build": "npm run clean && npm run build:modules && concurrently \"npm run build:core\" \"npm run build:canvas\" \"npm run build:webgl\" \"npm run build:player\" \"npm run build:threejs\"",
"clean": "npx rimraf spine-core/dist spine-canvas/dist spine-webgl/dist spine-phaser/dist spine-player/dist spine-threejs/dist",
"build": "npm run clean && npm run build:modules && concurrently \"npm run build:core\" \"npm run build:canvas\" \"npm run build:webgl\" \"npm run build:phaser\" \"npm run build:player\" \"npm run build:threejs\"",
"postbuild": "npm run minify",
"build:modules": "npx tsc -b -clean && npx tsc -b",
"build:core": "npx esbuild --bundle spine-core/src/index.ts --tsconfig=spine-core/tsconfig.json --sourcemap --outfile=spine-core/dist/iife/spine-core.js --format=iife --global-name=spine",
"build:canvas": "npx esbuild --bundle spine-canvas/src/index.ts --tsconfig=spine-canvas/tsconfig.json --sourcemap --outfile=spine-canvas/dist/iife/spine-canvas.js --format=iife --global-name=spine",
"build:webgl": "npx esbuild --bundle spine-webgl/src/index.ts --tsconfig=spine-webgl/tsconfig.json --sourcemap --outfile=spine-webgl/dist/iife/spine-webgl.js --format=iife --global-name=spine",
"build:player": "npx copyfiles -f spine-player/css/spine-player.css spine-player/dist/ && npx npx esbuild --bundle spine-player/src/index.ts --tsconfig=spine-player/tsconfig.json --sourcemap --outfile=spine-player/dist/iife/spine-player.js --format=iife --global-name=spine",
"build:phaser": "npx esbuild --bundle spine-phaser/src/index.ts --tsconfig=spine-phaser/tsconfig.json --sourcemap --outfile=spine-phaser/dist/iife/spine-phaser.js --external:Phaser --alias:phaser=Phaser --format=iife --global-name=spine",
"build:threejs": "npx esbuild --bundle spine-threejs/src/index.ts --tsconfig=spine-threejs/tsconfig.json --sourcemap --outfile=spine-threejs/dist/iife/spine-threejs.js --external:three --format=iife --global-name=spine",
"minify": "npx esbuild --minify spine-core/dist/iife/spine-core.js --outfile=spine-core/dist/iife/spine-core.min.js && npx esbuild --minify spine-canvas/dist/iife/spine-canvas.js --outfile=spine-canvas/dist/iife/spine-canvas.min.js && npx esbuild --minify spine-player/dist/iife/spine-player.js --outfile=spine-player/dist/iife/spine-player.min.js && npx esbuild --minify spine-webgl/dist/iife/spine-webgl.js --outfile=spine-webgl/dist/iife/spine-webgl.min.js && npx esbuild --minify spine-threejs/dist/iife/spine-threejs.js --outfile=spine-threejs/dist/iife/spine-threejs.min.js",
"dev": "concurrently \"npx live-server --no-browser\" \"npm run dev:modules\" \"npm run dev:canvas\" \"npm run dev:webgl\" \"npm run dev:player\" \"npm run dev:threejs\"",
"minify": "npx esbuild --minify spine-core/dist/iife/spine-core.js --outfile=spine-core/dist/iife/spine-core.min.js && npx esbuild --minify spine-canvas/dist/iife/spine-canvas.js --outfile=spine-canvas/dist/iife/spine-canvas.min.js && npx esbuild --minify spine-player/dist/iife/spine-player.js --outfile=spine-player/dist/iife/spine-player.min.js && npx esbuild --minify spine-phaser/dist/iife/spine-phaser.js --outfile=spine-phaser/dist/iife/spine-phaser.min.js && npx esbuild --minify spine-webgl/dist/iife/spine-webgl.js --outfile=spine-webgl/dist/iife/spine-webgl.min.js && npx esbuild --minify spine-threejs/dist/iife/spine-threejs.js --outfile=spine-threejs/dist/iife/spine-threejs.min.js",
"dev": "concurrently \"npx live-server --no-browser\" \"npm run dev:canvas\" \"npm run dev:webgl\" \"npm run dev:phaser\" \"npm run dev:player\" \"npm run dev:threejs\"",
"dev:modules": "npm run build:modules -- --watch",
"dev:canvas": "npm run build:canvas -- --watch",
"dev:webgl": "npm run build:webgl -- --watch",
"dev:phaser": "npm run build:phaser -- --watch",
"dev:player": "npm run build:player -- --watch",
"dev:threejs": "npm run build:threejs -- --watch"
},
@ -46,18 +48,18 @@
"workspaces": [
"spine-core",
"spine-canvas",
"spine-phaser",
"spine-player",
"spine-threejs",
"spine-webgl"
],
"devDependencies": {
"concurrently": "^6.2.1",
"@types/offscreencanvas": "^2019.6.4",
"concurrently": "^7.6.0",
"copyfiles": "^2.4.1",
"esbuild": "^0.12.22",
"live-server": "^1.2.1",
"npx": "^10.2.2",
"esbuild": "^0.16.4",
"live-server": "^1.2.2",
"rimraf": "^3.0.2",
"typescript": "^4.9.3",
"@types/offscreencanvas": "^2019.6.4"
"typescript": "^4.9.4"
}
}

View File

@ -3,7 +3,7 @@ set -e
if [ ! "$#" -eq 2 ]; then
echo "Usage: ./publish.sh <last-version> <new-version>"
exit
exit
else
lastVersion=${1%/}
newVersion=${2%/}
@ -14,6 +14,7 @@ fi
sed -i '' "s/$lastVersion/$newVersion/" package.json
sed -i '' "s/$lastVersion/$newVersion/" spine-canvas/package.json
sed -i '' "s/$lastVersion/$newVersion/" spine-core/package.json
sed -i '' "s/$lastVersion/$newVersion/" spine-phaser/package.json
sed -i '' "s/$lastVersion/$newVersion/" spine-player/package.json
sed -i '' "s/$lastVersion/$newVersion/" spine-threejs/package.json
sed -i '' "s/$lastVersion/$newVersion/" spine-webgl/package.json

View File

@ -45,6 +45,8 @@ import { Color, Utils, MathUtils, Vector2, NumberArrayLike } from "./Utils";
*
* See [Instance objects](http://esotericsoftware.com/spine-runtime-architecture#Instance-objects) in the Spine Runtimes Guide. */
export class Skeleton {
static yDown = false;;
/** The skeleton's setup pose data. */
data: SkeletonData;
@ -81,7 +83,15 @@ export class Skeleton {
/** Scales the entire skeleton on the Y axis. This affects all bones, even if the bone's transform mode disallows scale
* inheritance. */
scaleY = 1;
private _scaleY = 1;
public get scaleY() {
return Skeleton.yDown ? -this._scaleY : this._scaleY;
}
public set scaleY(scaleY: number) {
this._scaleY = scaleY;
}
/** Sets the skeleton X position, which is added to the root bone worldX position. */
x = 0;

View File

@ -0,0 +1,26 @@
Spine Runtimes License Agreement
Last updated May 1, 2019. Replaces all prior versions.
Copyright (c) 2013-2019, Esoteric Software LLC
Integration of the Spine Runtimes into software or otherwise creating
derivative works of the Spine Runtimes is permitted under the terms and
conditions of Section 2 of the Spine Editor License Agreement:
http://esotericsoftware.com/spine-editor-license
Otherwise, it is permitted to integrate the Spine Runtimes into software
or otherwise create derivative works of the Spine Runtimes (collectively,
"Products"), provided that each user of the Products must obtain their own
Spine Editor license and redistribution of the Products in any form must
include this license and copyright notice.
THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS
INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -0,0 +1,3 @@
# spine-ts Phaser
Please see https://github.com/EsotericSoftware/spine-runtimes/blob/4.0/spine-ts/README.md for more information.

View File

@ -0,0 +1,59 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="//cdn.jsdelivr.net/npm/phaser@3.55.2/dist/phaser.js"></script>
<script src="../dist/iife/spine-phaser.js"></script>
<title>Spine Phaser Example</title>
</head>
<body>
<h1>Arcade Physics example</h1>
</body>
<script>
var config = {
type: Phaser.AUTO,
width: 800,
height: 600,
type: Phaser.WEBGL,
physics: {
default: 'arcade',
arcade: {
debug: true,
gravity: { y: 200 }
}
},
scene: {
preload: preload,
create: create,
},
plugins: {
scene: [
{ key: "spine.SpinePlugin", plugin: spine.SpinePlugin, mapping: "spine" }
]
}
};
let game = new Phaser.Game(config);
function preload () {
this.load.spineBinary("coin-data", "assets/coin-pro.skel");
this.load.spineAtlas("coin-atlas", "assets/coin-pma.atlas");
}
function create () {
let coin = this.add.spine(400, 200, 'coin-data', "coin-atlas");
coin.animationState.setAnimation(0, "animation", true);
coin.setScale(0.3);
coin.setSize(280, 280);
this.physics.add.existing(coin);
coin.body.setOffset(0, 50);
coin.body.setVelocity(100, 200);
coin.body.setBounce(1, 1);
coin.body.setCollideWorldBounds(true);
}
</script>
</html>

View File

@ -0,0 +1,19 @@
coin-pma.png
size: 1024, 1024
filter: Linear, Linear
pma: true
coin-front-logo
bounds: 2, 570, 305, 302
coin-front-shine-logo
bounds: 2, 286, 282, 282
coin-front-shine-spineboy
bounds: 305, 283, 282, 282
coin-front-spineboy
bounds: 309, 567, 305, 302
rotate: 90
coin-side-round
bounds: 2, 2, 144, 282
coin-side-straight
bounds: 286, 286, 17, 282
shine
bounds: 148, 39, 72, 245

Binary file not shown.

After

Width:  |  Height:  |  Size: 222 KiB

Binary file not shown.

View File

@ -0,0 +1,363 @@
mix-and-match-pma.png
size: 1024, 512
filter: Linear, Linear
pma: true
scale: 0.5
base-head
bounds: 587, 2, 95, 73
boy/arm-front
bounds: 558, 271, 36, 115
boy/backpack
bounds: 235, 109, 119, 153
boy/backpack-pocket
bounds: 328, 73, 34, 62
rotate: 90
boy/backpack-strap-front
bounds: 665, 79, 38, 88
boy/backpack-up
bounds: 395, 364, 21, 70
rotate: 90
boy/body
bounds: 251, 264, 97, 132
rotate: 90
boy/boot-ribbon-front
bounds: 648, 131, 9, 11
boy/collar
bounds: 744, 4, 73, 29
rotate: 90
boy/ear
bounds: 383, 109, 19, 23
rotate: 90
boy/eye-back-low-eyelid
bounds: 739, 284, 17, 6
rotate: 90
boy/eye-back-pupil
bounds: 832, 443, 8, 9
rotate: 90
boy/eye-back-up-eyelid
bounds: 558, 264, 23, 5
boy/eye-back-up-eyelid-back
bounds: 802, 491, 19, 10
rotate: 90
boy/eye-front-low-eyelid
bounds: 386, 363, 22, 7
rotate: 90
boy/eye-front-pupil
bounds: 816, 389, 9, 9
boy/eye-front-up-eyelid
bounds: 160, 71, 31, 6
rotate: 90
boy/eye-front-up-eyelid-back
bounds: 801, 434, 26, 9
rotate: 90
boy/eye-iris-back
bounds: 618, 264, 17, 17
boy/eye-iris-front
bounds: 727, 264, 18, 18
boy/eye-white-back
bounds: 580, 131, 20, 12
boy/eye-white-front
bounds: 510, 130, 27, 13
boy/eyebrow-back
bounds: 751, 88, 20, 11
rotate: 90
boy/eyebrow-front
bounds: 483, 130, 25, 11
boy/hair-back
bounds: 494, 388, 122, 81
rotate: 90
boy/hair-bangs
bounds: 667, 284, 70, 37
boy/hair-side
bounds: 789, 374, 25, 43
boy/hand-backfingers
bounds: 467, 364, 19, 21
boy/hand-front-fingers
bounds: 488, 364, 19, 21
boy/hat
bounds: 615, 417, 93, 56
rotate: 90
boy/leg-front
bounds: 138, 104, 31, 158
boy/mouth-close
bounds: 551, 365, 21, 5
rotate: 90
girl-blue-cape/mouth-close
bounds: 551, 365, 21, 5
rotate: 90
girl-spring-dress/mouth-close
bounds: 551, 365, 21, 5
rotate: 90
girl/mouth-close
bounds: 551, 365, 21, 5
rotate: 90
boy/mouth-smile
bounds: 705, 79, 29, 7
boy/nose
bounds: 836, 473, 17, 10
rotate: 90
boy/pompom
bounds: 747, 273, 48, 43
rotate: 90
boy/zip
bounds: 648, 144, 14, 23
girl-blue-cape/back-eyebrow
bounds: 602, 131, 18, 12
girl-blue-cape/body-dress
bounds: 2, 264, 109, 246
girl-blue-cape/body-ribbon
bounds: 615, 283, 50, 38
girl-blue-cape/cape-back
bounds: 2, 69, 134, 193
girl-blue-cape/cape-back-up
bounds: 386, 387, 123, 106
rotate: 90
girl-blue-cape/cape-ribbon
bounds: 675, 264, 50, 18
girl-blue-cape/cape-shoulder-back
bounds: 751, 110, 49, 59
girl-blue-cape/cape-shoulder-front
bounds: 113, 264, 62, 76
rotate: 90
girl-blue-cape/cape-up-front
bounds: 399, 264, 98, 117
rotate: 90
girl-blue-cape/ear
bounds: 775, 2, 19, 23
girl-spring-dress/ear
bounds: 775, 2, 19, 23
girl/ear
bounds: 775, 2, 19, 23
girl-blue-cape/eye-back-low-eyelid
bounds: 802, 463, 17, 6
girl-spring-dress/eye-back-low-eyelid
bounds: 802, 463, 17, 6
girl/eye-back-low-eyelid
bounds: 802, 463, 17, 6
girl-blue-cape/eye-back-pupil
bounds: 816, 367, 8, 9
girl-spring-dress/eye-back-pupil
bounds: 816, 367, 8, 9
girl/eye-back-pupil
bounds: 816, 367, 8, 9
girl-blue-cape/eye-back-up-eyelid
bounds: 554, 131, 24, 12
girl-spring-dress/eye-back-up-eyelid
bounds: 554, 131, 24, 12
girl/eye-back-up-eyelid
bounds: 554, 131, 24, 12
girl-blue-cape/eye-back-up-eyelid-back
bounds: 832, 453, 17, 11
rotate: 90
girl-spring-dress/eye-back-up-eyelid-back
bounds: 832, 453, 17, 11
rotate: 90
girl/eye-back-up-eyelid-back
bounds: 832, 453, 17, 11
rotate: 90
girl-blue-cape/eye-front-low-eyelid
bounds: 739, 303, 18, 6
rotate: 90
girl-spring-dress/eye-front-low-eyelid
bounds: 739, 303, 18, 6
rotate: 90
girl/eye-front-low-eyelid
bounds: 739, 303, 18, 6
rotate: 90
girl-blue-cape/eye-front-pupil
bounds: 816, 378, 9, 9
girl-spring-dress/eye-front-pupil
bounds: 816, 378, 9, 9
girl/eye-front-pupil
bounds: 816, 378, 9, 9
girl-blue-cape/eye-front-up-eyelid
bounds: 392, 77, 30, 14
rotate: 90
girl-spring-dress/eye-front-up-eyelid
bounds: 392, 77, 30, 14
rotate: 90
girl/eye-front-up-eyelid
bounds: 392, 77, 30, 14
rotate: 90
girl-blue-cape/eye-front-up-eyelid-back
bounds: 455, 130, 26, 11
girl-spring-dress/eye-front-up-eyelid-back
bounds: 455, 130, 26, 11
girl/eye-front-up-eyelid-back
bounds: 455, 130, 26, 11
girl-blue-cape/eye-iris-back
bounds: 637, 264, 17, 17
girl-blue-cape/eye-iris-front
bounds: 802, 471, 18, 18
girl-blue-cape/eye-white-back
bounds: 596, 264, 20, 16
girl-spring-dress/eye-white-back
bounds: 596, 264, 20, 16
girl-blue-cape/eye-white-front
bounds: 796, 5, 20, 16
rotate: 90
girl-spring-dress/eye-white-front
bounds: 796, 5, 20, 16
rotate: 90
girl/eye-white-front
bounds: 796, 5, 20, 16
rotate: 90
girl-blue-cape/front-eyebrow
bounds: 608, 149, 18, 12
rotate: 90
girl-blue-cape/hair-back
bounds: 508, 145, 117, 98
rotate: 90
girl-blue-cape/hair-bangs
bounds: 673, 419, 91, 40
rotate: 90
girl-blue-cape/hair-head-side-back
bounds: 196, 331, 30, 52
rotate: 90
girl-blue-cape/hair-head-side-front
bounds: 738, 323, 41, 42
girl-blue-cape/hair-side
bounds: 473, 3, 36, 71
girl-blue-cape/hand-front-fingers
bounds: 509, 365, 19, 21
girl-spring-dress/hand-front-fingers
bounds: 509, 365, 19, 21
girl-blue-cape/leg-front
bounds: 168, 72, 30, 158
rotate: 90
girl-blue-cape/mouth-smile
bounds: 736, 79, 29, 7
girl-spring-dress/mouth-smile
bounds: 736, 79, 29, 7
girl/mouth-smile
bounds: 736, 79, 29, 7
girl-blue-cape/nose
bounds: 747, 264, 11, 7
girl-spring-dress/nose
bounds: 747, 264, 11, 7
girl/nose
bounds: 747, 264, 11, 7
girl-blue-cape/sleeve-back
bounds: 767, 79, 42, 29
girl-blue-cape/sleeve-front
bounds: 408, 76, 52, 119
rotate: 90
girl-spring-dress/arm-front
bounds: 596, 282, 17, 111
girl-spring-dress/back-eyebrow
bounds: 801, 420, 18, 12
girl-spring-dress/body-up
bounds: 179, 4, 64, 66
girl-spring-dress/cloak-down
bounds: 775, 27, 50, 50
girl-spring-dress/cloak-up
bounds: 360, 7, 64, 58
rotate: 90
girl-spring-dress/eye-iris-back
bounds: 656, 264, 17, 17
girl-spring-dress/eye-iris-front
bounds: 814, 492, 18, 18
girl-spring-dress/front-eyebrow
bounds: 822, 472, 18, 12
rotate: 90
girl-spring-dress/hair-back
bounds: 196, 363, 147, 93
rotate: 90
girl-spring-dress/hair-bangs
bounds: 696, 326, 91, 40
rotate: 90
girl-spring-dress/hair-head-side-back
bounds: 529, 76, 30, 52
girl-spring-dress/hair-head-side-front
bounds: 781, 323, 41, 42
girl-spring-dress/hair-side
bounds: 511, 3, 36, 71
girl-spring-dress/leg-front
bounds: 171, 104, 30, 158
girl-spring-dress/neck
bounds: 138, 70, 20, 32
girl-spring-dress/shoulder-ribbon
bounds: 622, 131, 36, 24
rotate: 90
girl-spring-dress/skirt
bounds: 113, 328, 182, 81
rotate: 90
girl-spring-dress/underskirt
bounds: 2, 2, 175, 65
girl/arm-front
bounds: 577, 395, 36, 115
girl/back-eyebrow
bounds: 834, 492, 18, 12
rotate: 90
girl/bag-base
bounds: 191, 264, 62, 58
rotate: 90
girl/bag-strap-front
bounds: 385, 265, 12, 96
girl/bag-top
bounds: 738, 367, 49, 50
girl/body
bounds: 356, 130, 97, 132
girl/boot-ribbon-front
bounds: 539, 130, 13, 13
girl/eye-iris-back
bounds: 821, 424, 17, 17
girl/eye-iris-front
bounds: 812, 443, 18, 18
girl/eye-white-back
bounds: 814, 5, 20, 16
rotate: 90
girl/front-eyebrow
bounds: 816, 400, 18, 12
rotate: 90
girl/hair-back
bounds: 291, 363, 147, 93
rotate: 90
girl/hair-bangs
bounds: 715, 419, 91, 40
rotate: 90
girl/hair-flap-down-front
bounds: 288, 5, 70, 65
girl/hair-head-side-back
bounds: 561, 77, 30, 52
girl/hair-head-side-front
bounds: 757, 419, 41, 42
rotate: 90
girl/hair-patch
bounds: 245, 4, 66, 41
rotate: 90
girl/hair-side
bounds: 549, 3, 36, 71
girl/hair-strand-back-1
bounds: 684, 3, 58, 74
girl/hair-strand-back-2
bounds: 692, 171, 91, 58
rotate: 90
girl/hair-strand-back-3
bounds: 615, 323, 92, 79
rotate: 90
girl/hair-strand-front-1
bounds: 518, 269, 38, 94
girl/hair-strand-front-2
bounds: 593, 79, 70, 50
girl/hair-strand-front-3
bounds: 705, 88, 44, 81
girl/hand-front-fingers
bounds: 530, 365, 19, 21
girl/hat
bounds: 608, 169, 93, 82
rotate: 90
girl/leg-front
bounds: 203, 104, 30, 158
girl/pompom
bounds: 757, 462, 48, 43
rotate: 90
girl/scarf
bounds: 455, 143, 119, 51
rotate: 90
girl/scarf-back
bounds: 420, 2, 72, 51
rotate: 90
girl/zip
bounds: 356, 109, 19, 25
rotate: 90

Binary file not shown.

After

Width:  |  Height:  |  Size: 369 KiB

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,101 @@
raptor-pma.png
size: 1024, 512
filter: Linear, Linear
pma: true
scale: 0.5
back-arm
bounds: 829, 88, 46, 25
rotate: 90
back-bracer
bounds: 195, 238, 39, 28
rotate: 90
back-hand
bounds: 724, 140, 36, 34
rotate: 90
back-knee
bounds: 760, 131, 49, 67
rotate: 90
back-thigh
bounds: 225, 238, 39, 24
rotate: 90
eyes-open
bounds: 975, 204, 47, 45
front-arm
bounds: 969, 112, 48, 26
front-bracer
bounds: 724, 97, 41, 29
rotate: 90
front-hand
bounds: 251, 239, 41, 38
front-open-hand
bounds: 856, 76, 43, 44
rotate: 90
front-thigh
bounds: 729, 178, 57, 29
rotate: 90
gun
bounds: 894, 251, 107, 103
gun-nohand
bounds: 764, 241, 105, 102
head
bounds: 756, 345, 136, 149
lower-leg
bounds: 475, 237, 73, 98
rotate: 90
mouth-grind
bounds: 975, 172, 47, 30
mouth-smile
bounds: 975, 140, 47, 30
neck
bounds: 366, 282, 18, 21
raptor-back-arm
bounds: 636, 97, 82, 86
rotate: 90
raptor-body
bounds: 2, 2, 632, 233
raptor-front-arm
bounds: 871, 168, 81, 102
rotate: 90
raptor-front-leg
bounds: 2, 237, 191, 257
raptor-hindleg-back
bounds: 195, 279, 169, 215
raptor-horn
bounds: 431, 312, 182, 80
rotate: 90
raptor-horn-back
bounds: 513, 318, 176, 77
rotate: 90
raptor-jaw
bounds: 894, 356, 126, 138
raptor-jaw-tooth
bounds: 294, 240, 37, 48
rotate: 90
raptor-mouth-inside
bounds: 344, 241, 36, 41
rotate: 90
raptor-saddle-strap-back
bounds: 575, 242, 54, 74
raptor-saddle-strap-front
bounds: 764, 182, 57, 95
rotate: 90
raptor-saddle-w-shadow
bounds: 592, 323, 162, 171
raptor-tail-shadow
bounds: 366, 305, 189, 63
rotate: 90
raptor-tongue
bounds: 387, 239, 86, 64
stirrup-back
bounds: 829, 136, 44, 35
rotate: 90
stirrup-front
bounds: 866, 121, 45, 50
rotate: 90
stirrup-strap
bounds: 918, 120, 49, 46
torso
bounds: 636, 181, 54, 91
rotate: 90
visor
bounds: 631, 237, 131, 84

Binary file not shown.

After

Width:  |  Height:  |  Size: 409 KiB

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,102 @@
spineboy-pma.png
size: 1024, 256
filter: Linear, Linear
pma: true
scale: 0.5
crosshair
bounds: 813, 160, 45, 45
eye-indifferent
bounds: 569, 2, 47, 45
eye-surprised
bounds: 643, 7, 47, 45
rotate: 90
front-bracer
bounds: 811, 51, 29, 40
front-fist-closed
bounds: 807, 93, 38, 41
front-fist-open
bounds: 815, 210, 43, 44
front-foot
bounds: 706, 64, 63, 35
rotate: 90
front-shin
bounds: 80, 11, 41, 92
front-thigh
bounds: 754, 12, 23, 56
front-upper-arm
bounds: 618, 5, 23, 49
goggles
bounds: 214, 20, 131, 83
gun
bounds: 347, 14, 105, 102
rotate: 90
head
bounds: 80, 105, 136, 149
hoverboard-board
bounds: 2, 8, 246, 76
rotate: 90
hoverboard-thruster
bounds: 478, 2, 30, 32
hoverglow-small
bounds: 218, 117, 137, 38
rotate: 90
mouth-grind
bounds: 775, 80, 47, 30
rotate: 90
mouth-oooo
bounds: 779, 31, 47, 30
rotate: 90
mouth-smile
bounds: 783, 207, 47, 30
rotate: 90
muzzle-glow
bounds: 779, 4, 25, 25
muzzle-ring
bounds: 451, 14, 25, 105
muzzle01
bounds: 664, 60, 67, 40
rotate: 90
muzzle02
bounds: 580, 56, 68, 42
rotate: 90
muzzle03
bounds: 478, 36, 83, 53
rotate: 90
muzzle04
bounds: 533, 49, 75, 45
rotate: 90
muzzle05
bounds: 624, 56, 68, 38
rotate: 90
neck
bounds: 806, 8, 18, 21
portal-bg
bounds: 258, 121, 133, 133
portal-flare1
bounds: 690, 2, 56, 30
rotate: 90
portal-flare2
bounds: 510, 3, 57, 31
portal-flare3
bounds: 722, 4, 58, 30
rotate: 90
portal-shade
bounds: 393, 121, 133, 133
portal-streaks1
bounds: 528, 126, 126, 128
portal-streaks2
bounds: 656, 129, 125, 125
rear-bracer
bounds: 826, 13, 28, 36
rear-foot
bounds: 743, 70, 57, 30
rotate: 90
rear-shin
bounds: 174, 14, 38, 89
rear-thigh
bounds: 783, 158, 28, 47
rear-upper-arm
bounds: 783, 136, 20, 44
rotate: 90
torso
bounds: 123, 13, 49, 90

Binary file not shown.

After

Width:  |  Height:  |  Size: 239 KiB

Binary file not shown.

View File

@ -0,0 +1,16 @@
stretchyman-pma.png
size: 1024, 256
filter: Linear, Linear
pma: true
back-arm
bounds: 149, 45, 72, 202
back-leg
bounds: 312, 4, 100, 318
rotate: 90
body
bounds: 223, 106, 141, 452
rotate: 90
front-arm
bounds: 2, 26, 145, 221
head
bounds: 223, 2, 87, 102

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,44 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="//cdn.jsdelivr.net/npm/phaser@3.55.2/dist/phaser.js"></script>
<script src="../dist/iife/spine-phaser.js"></script>
<title>Spine Phaser Example</title>
</head>
<body>
<h1>Basic example</h1>
</body>
<script>
var config = {
type: Phaser.AUTO,
width: 800,
height: 600,
type: Phaser.WEBGL,
scene: {
preload: preload,
create: create,
},
plugins: {
scene: [
{ key: "spine.SpinePlugin", plugin: spine.SpinePlugin, mapping: "spine" }
]
}
};
let game = new Phaser.Game(config);
function preload () {
this.load.spineBinary("spineboy-data", "assets/spineboy-pro.skel");
this.load.spineAtlas("spineboy-atlas", "assets/spineboy-pma.atlas");
}
function create () {
let spineboy = this.add.spine(400, 300, 'spineboy-data', "spineboy-atlas");
spineboy.scale = 0.5;
spineboy.animationState.setAnimation(0, "walk", true);
}
</script>
</html>

View File

@ -0,0 +1,63 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="//cdn.jsdelivr.net/npm/phaser@3.55.2/dist/phaser.js"></script>
<script src="../dist/iife/spine-phaser.js"></script>
<title>Spine Phaser Example</title>
</head>
<body>
<h1>Batching test</h1>
</body>
<script>
var config = {
type: Phaser.AUTO,
width: 800,
height: 600,
type: Phaser.WEBGL,
scene: {
preload: preload,
create: create,
update: update,
},
plugins: {
scene: [
{ key: "spine.SpinePlugin", plugin: spine.SpinePlugin, mapping: "spine" }
]
}
};
let game = new Phaser.Game(config);
let debug;
function preload () {
this.load.spineJson("raptor-data", "assets/raptor-pro.json");
this.load.spineAtlas("raptor-atlas", "assets/raptor-pma.atlas");
this.load.spineBinary("spineboy-data", "assets/spineboy-pro.skel");
this.load.spineAtlas("spineboy-atlas", "assets/spineboy-pma.atlas");
}
function create () {
let plugin = this.spine;
let x = 25;
let y = 60;
for (let j = 0; j < 10; j++, y+= 600 / 10) {
for (let i = 0; i < 20; i++, x += 800 / 20) {
let obj = Math.random() > 0.5
? this.add.spine(x, y, 'spineboy-data', "spineboy-atlas")
: this.add.spine(x, y, 'raptor-data', "raptor-atlas");
obj.animationState.setAnimation(0, "walk", true);
obj.scale = 0.1;
}
x = 25;
}
debug = this.add.text(0, 600 - 40, "FPS: ");
}
function update () {
debug.setText("draw calls: " + spine.PolygonBatcher.getAndResetGlobalDrawCalls() + "\ndelta: " + game.loop.delta);
}
</script>
</html>

View File

@ -0,0 +1,70 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="//cdn.jsdelivr.net/npm/phaser@3.55.2/dist/phaser.js"></script>
<script src="../dist/iife/spine-phaser.js"></script>
<title>Spine Phaser Example</title>
</head>
<body>
<h1>Blend test</h1>
</body>
<script>
var config = {
type: Phaser.AUTO,
width: 800,
height: 600,
type: Phaser.WEBGL,
backgroundColor: '#cdcdcd',
scene: {
preload: preload,
create: create,
update: update
},
plugins: {
scene: [
{ key: "spine.SpinePlugin", plugin: spine.SpinePlugin, mapping: "spine" }
]
}
};
let controls;
let game = new Phaser.Game(config);
function preload () {
this.load.spineBinary("spineboy-data", "assets/spineboy-pro.skel");
this.load.spineAtlas("spineboy-atlas", "assets/spineboy-pma.atlas");
}
function create () {
for (var i = 0; i < 4; i++) {
let obj = this.add.spine(i * 200, 600, 'spineboy-data', 'spineboy-atlas').setScale(0.25);
obj.setScale(0.25);
obj.animationState.setAnimation(0, "idle", true);
obj.animationState.setAnimation(1, "shoot", true);
}
var cursors = this.input.keyboard.createCursorKeys();
var controlConfig = {
camera: this.cameras.main,
left: cursors.left,
right: cursors.right,
up: cursors.up,
down: cursors.down,
zoomIn: this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.Q),
zoomOut: this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.E),
acceleration: 0.35,
drag: 0.01,
maxSpeed: 1.2
};
controls = new Phaser.Cameras.Controls.SmoothedKeyControl(controlConfig);
}
function update (time, delta) {
controls.update(delta);
}
</script>
</html>

View File

@ -0,0 +1,56 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="//cdn.jsdelivr.net/npm/phaser@3.55.2/dist/phaser.js"></script>
<script src="../dist/iife/spine-phaser.js"></script>
<title>Spine Phaser Example</title>
</head>
<body>
<h1>Bounds test</h1>
</body>
<script>
var config = {
type: Phaser.AUTO,
width: 800,
height: 600,
type: Phaser.WEBGL,
scene: {
preload: preload,
create: create,
update: update,
},
plugins: {
scene: [
{ key: "spine.SpinePlugin", plugin: spine.SpinePlugin, mapping: "spine" }
]
}
};
let game = new Phaser.Game(config);
let spineboy;
function preload () {
this.load.spineBinary("spineboy-data", "assets/spineboy-pro.skel");
this.load.spineAtlas("spineboy-atlas", "assets/spineboy-pma.atlas");
}
function create () {
spineboy = this.add.spine(400, 300, 'spineboy-data', "spineboy-atlas", new spine.SkinsAndAnimationBoundsProvider("run"));
spineboy.scale = 0.4
spineboy.setInteractive();
this.input.enableDebug(spineboy, 0xff00ff);
spineboy.on("pointerdown", () => spineboy.animationState.setAnimation(0, "run", true));
}
let time = 0;
function update (t, delta) {
time += delta / 1000;
let scale = 0.4 + Math.cos(time) * 0.2;
spineboy.scale = scale;
spineboy.angle++;
}
</script>
</html>

View File

@ -0,0 +1,113 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="//cdn.jsdelivr.net/npm/phaser@3.55.2/dist/phaser.js"></script>
<script src="../dist/iife/spine-phaser.js"></script>
<title>Spine Phaser Example</title>
</head>
<body>
<h1>Camera pipeline test</h1>
<script>
var config = {
type: Phaser.AUTO,
width: 800,
height: 600,
type: Phaser.WEBGL,
backgroundColor: '#cdcdcd',
scene: {
preload: preload,
create: create,
},
plugins: {
scene: [
{ key: "spine.SpinePlugin", plugin: spine.SpinePlugin, mapping: "spine" }
]
}
};
const fragShader = `
#define SHADER_NAME PLASMA_FS
precision mediump float;
uniform sampler2D uMainSampler;
uniform float uTime;
uniform vec2 uResolution;
varying vec2 outTexCoord;
void main()
{
vec2 p = -1.0 + 2.0 * gl_FragCoord.xy / uResolution.xy;
float x = p.x;
float y = p.y;
float mov0 = x+y+cos(sin(uTime)*2.0)*100.+sin(x/100.)*1000.;
float mov1 = y / 0.9 + uTime;
float mov2 = x / 0.2;
float c1 = abs(sin(mov1+uTime)/2.+mov2/2.-mov1-mov2+uTime);
float c2 = abs(sin(c1+sin(mov0/1000.+uTime)+sin(y/40.+uTime)+sin((x+y)/100.)*3.));
float c3 = abs(sin(c2+cos(mov1+mov2+c2)+cos(mov2)+sin(x/1000.)));
vec4 pixel = texture2D(uMainSampler, outTexCoord);
gl_FragColor = pixel * vec4(c1, c2, c3, 1);
}
`;
class PlasmaPostFX extends Phaser.Renderer.WebGL.Pipelines.PostFXPipeline {
constructor(game) {
super({
game,
name: 'PlasmaPostFX',
fragShader,
uniforms: [
'uMainSampler',
'uTime',
'uResolution'
]
});
}
onPreRender() {
this.set1f('uTime', this.game.loop.time / 1000);
}
onDraw(renderTarget) {
this.set2f('uResolution', renderTarget.width, renderTarget.height);
this.bindAndDraw(renderTarget);
}
}
let game = new Phaser.Game(config);
function preload() {
this.load.spineBinary("spineboy-data", "assets/spineboy-pro.skel");
this.load.spineAtlas("spineboy-atlas", "assets/spineboy-pma.atlas");
this.load.image("img", "assets/raptor-pma.png")
}
function create() {
this.renderer.pipelines.addPostPipeline('PlasmaPostFX', PlasmaPostFX);
// FIXME: Need a dummy sprite so the MultiPipeline sets up state
// so rendering the Spine sprite actually works. Unsure what state
// is needed.
var s = this.add.sprite(0, 0, 'img');
let spineboy = this.add.spine(400, 300, 'spineboy-data', "spineboy-atlas");
spineboy.scale = 0.5;
spineboy.animationState.setAnimation(0, "walk", true);
this.cameras.main.setPostPipeline("PlasmaPostFX");
}
</script>
</body>
</html>

View File

@ -0,0 +1,71 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="//cdn.jsdelivr.net/npm/phaser@3.55.2/dist/phaser.js"></script>
<script src="../dist/iife/spine-phaser.js"></script>
<title>Spine Phaser Example</title>
</head>
<body>
<h1>Control bones</h1>
<script>
var config = {
type: Phaser.AUTO,
width: 800,
height: 600,
type: Phaser.WEBGL,
scene: {
preload: preload,
create: create,
},
plugins: {
scene: [
{ key: "spine.SpinePlugin", plugin: spine.SpinePlugin, mapping: "spine" }
]
}
};
let game = new Phaser.Game(config);
function preload () {
this.load.spineBinary("stretchyman-data", "assets/stretchyman-pro.skel");
this.load.spineAtlas("stretchyman-atlas", "assets/stretchyman-pma.atlas");
}
function create () {
let stretchyman = this.add.spine(400, 550, 'stretchyman-data', "stretchyman-atlas");
stretchyman.scale = 0.8;
stretchyman.skeleton.updateWorldTransform();
var controlBones = ["back-arm-ik-target", "back-leg-ik-target", "front-arm-ik-target", "front-leg-ik-target"];
for (var i = 0; i < controlBones.length; i++)
{
var bone = stretchyman.skeleton.findBone(controlBones[i]);
let point = {x: bone.worldX, y: bone.worldY};
stretchyman.skeletonToPhaserWorldCoordinates(point);
var control = this.add.circle(point.x, point.y, 4, 0xff00ff).setData('bone', bone);
control.setInteractive();
this.input.setDraggable(control);
this.input.on('drag', function (pointer, gameObject, dragX, dragY) {
gameObject.x = dragX;
gameObject.y = dragY;
var bone = gameObject.getData('bone');
let point = { x: dragX, y: dragY };
stretchyman.phaserWorldCoordinatesToBone(point, bone);
bone.x = point.x;
bone.y = point.y;
bone.update();
}, this);
}
}
</script>
</body>
</html>

View File

@ -0,0 +1,24 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="//cdn.jsdelivr.net/npm/phaser@3.55.2/dist/phaser.js"></script>
<title>Spine Phaser Example</title>
</head>
<body>
<h1>Spine Phaser</h1>
<ul>
<li><a href="./basic-example.html">Basic example</a></li>
<li><a href="./batching-test.html">Batching test</a></li>
<li><a href="./multi-scene-test.html">Multi-scene test</a></li>
<li><a href="./bounds-test.html">Bounds test</a></li>
<li><a href="./visibility-test.html">Visibility test</a></li>
<li><a href="./arcade-physics-test.html">Arcade physics example</a></li>
<li><a href="./blend-test.html">Blend test</a></li>
<li><a href="./camera-pipeline-test.html">Camera pipeline test</a></li>
<li><a href="./control-bones-test.html">Control bones</a></li>
</ul>
</body>
</html>

View File

@ -0,0 +1,65 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="//cdn.jsdelivr.net/npm/phaser@3.55.2/dist/phaser.js"></script>
<script src="../dist/iife/spine-phaser.js"></script>
<title>Spine Phaser Example</title>
</head>
<body>
<h1>Multi-scene test</h1>
</body>
<script>
class Scene1 extends Phaser.Scene {
constructor () {
super({key: "Scene1"})
}
preload() {
this.load.spineBinary("spineboy-data", "assets/spineboy-pro.skel");
this.load.spineAtlas("spineboy-atlas", "assets/spineboy-pma.atlas");
}
create() {
let spineboy = this.add.spine(400, 500, 'spineboy-data', "spineboy-atlas");
spineboy.scale = 0.5;
spineboy.animationState.setAnimation(0, "walk", true);
this.input.once('pointerdown', () => this.scene.start('Scene2'));
}
}
class Scene2 extends Phaser.Scene {
constructor () {
super({key: "Scene2"})
}
preload() {
this.load.spineJson("raptor-data", "assets/raptor-pro.json");
this.load.spineAtlas("raptor-atlas", "assets/raptor-pma.atlas");
}
create() {
let raptor = this.add.spine(300, 600, 'raptor-data', "raptor-atlas");
raptor.scale = 0.5;
raptor.animationState.setAnimation(0, "walk", true);
this.input.once('pointerdown', () => this.scene.start('Scene1'));
}
}
var config = {
type: Phaser.AUTO,
width: 800,
height: 600,
type: Phaser.WEBGL,
scene: [ Scene1, Scene2 ],
plugins: {
scene: [
{ key: "spine.SpinePlugin", plugin: spine.SpinePlugin, mapping: "spine" }
]
}
};
let game = new Phaser.Game(config);
</script>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 563 B

View File

@ -0,0 +1,50 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="//cdn.jsdelivr.net/npm/phaser@3.55.2/dist/phaser.js"></script>
<script src="../dist/iife/spine-phaser.js"></script>
<title>Spine Phaser Example</title>
</head>
<body>
<h1>Basic example</h1>
</body>
<script>
var config = {
type: Phaser.AUTO,
width: 800,
height: 600,
type: Phaser.WEBGL,
scene: {
preload: preload,
create: create,
},
plugins: {
scene: [
{ key: "spine.SpinePlugin", plugin: spine.SpinePlugin, mapping: "spine" }
]
}
};
let game = new Phaser.Game(config);
function preload () {
this.load.spineBinary("spineboy-data", "assets/spineboy-pro.skel");
this.load.spineAtlas("spineboy-atlas", "assets/spineboy-pma.atlas");
}
function create () {
let spineboy = this.add.spine(250, 500, 'spineboy-data', "spineboy-atlas");
spineboy.scale = 0.5;
spineboy.animationState.setAnimation(0, "walk", true);
let spineboy2 = this.add.spine(550, 500, 'spineboy-data', "spineboy-atlas");
spineboy2.scale = 0.5;
spineboy2.animationState.setAnimation(0, "run", true);
this.input.on('pointerdown', () => spineboy2.visible = !spineboy2.visible);
}
</script>
</html>

View File

@ -0,0 +1,38 @@
{
"name": "@esotericsoftware/spine-phaser",
"version": "4.2.10",
"description": "The official Spine Runtimes for the Phaser.",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"files": [
"dist/**/*",
"README.md",
"LICENSE"
],
"scripts": {},
"repository": {
"type": "git",
"url": "git+https://github.com/esotericsoftware/spine-runtimes.git"
},
"keywords": [
"gamedev",
"animations",
"2d",
"spine",
"game-dev",
"runtimes",
"skeletal"
],
"author": "Esoteric Software LLC",
"license": "LicenseRef-LICENSE",
"bugs": {
"url": "https://github.com/esotericsoftware/spine-runtimes/issues"
},
"homepage": "https://github.com/esotericsoftware/spine-runtimes#readme",
"dependencies": {
"@esotericsoftware/spine-core": "^4.2.10",
"@esotericsoftware/spine-webgl": "^4.2.10",
"@esotericsoftware/spine-canvas": "^4.2.10",
"phaser": "^3.55.2"
}
}

View File

@ -0,0 +1,255 @@
import { SPINE_GAME_OBJECT_TYPE } from "./keys";
import { SpinePlugin } from "./SpinePlugin";
import { ComputedSizeMixin, DepthMixin, FlipMixin, ScrollFactorMixin, TransformMixin, VisibleMixin } from "./mixins";
import { AnimationState, AnimationStateData, Bone, MathUtils, Skeleton, Skin, Vector2 } from "@esotericsoftware/spine-core";
import { Vector3 } from "@esotericsoftware/spine-webgl";
class BaseSpineGameObject extends Phaser.GameObjects.GameObject {
constructor(scene: Phaser.Scene, type: string) {
super(scene, type);
}
}
export interface SpineGameObjectBoundsProvider {
calculateBounds(gameObject: SpineGameObject): { x: number, y: number, width: number, height: number };
}
export class SetupPoseBoundsProvider implements SpineGameObjectBoundsProvider {
calculateBounds(gameObject: SpineGameObject) {
if (!gameObject.skeleton) 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 skeleton = new Skeleton(gameObject.skeleton.data);
skeleton.setToSetupPose();
skeleton.updateWorldTransform();
return skeleton.getBoundsRect();
}
}
export class SkinsAndAnimationBoundsProvider implements SpineGameObjectBoundsProvider {
constructor(private animation: string, private skins: string[] = [], private timeStep: number = 0.05) {
}
calculateBounds(gameObject: SpineGameObject): { x: number; y: number; width: number; height: number; } {
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;
if (this.skins.length > 0) {
let customSkin = new Skin("custom-skin");
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;
if (animation == null) {
skeleton.updateWorldTransform();
return skeleton.getBoundsRect();
} else {
let minX = Number.POSITIVE_INFINITY, minY = Number.POSITIVE_INFINITY, maxX = Number.NEGATIVE_INFINITY, maxY = Number.NEGATIVE_INFINITY;
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 };
}
}
}
export class SpineGameObject extends ComputedSizeMixin(DepthMixin(FlipMixin(ScrollFactorMixin(TransformMixin(VisibleMixin(BaseSpineGameObject)))))) {
blendMode = -1;
skeleton: Skeleton | null = null;
animationStateData: AnimationStateData | null = null;
animationState: AnimationState | null = null;
private premultipliedAlpha = false;
private _displayOriginX = 0;
private _displayOriginY = 0;
private _scaleX = 1;
private _scaleY = 1;
constructor(scene: Phaser.Scene, private plugin: SpinePlugin, x: number, y: number, dataKey: string, atlasKey: string, public boundsProvider: SpineGameObjectBoundsProvider = new SetupPoseBoundsProvider()) {
super(scene, SPINE_GAME_OBJECT_TYPE);
this.setPosition(x, y); x
this.setSkeleton(dataKey, atlasKey);
}
setSkeleton(dataKey: string, atlasKey: string) {
if (dataKey && atlasKey) {
this.premultipliedAlpha = this.plugin.isAtlasPremultiplied(atlasKey);
this.skeleton = this.plugin.createSkeleton(dataKey, atlasKey);
this.animationStateData = new AnimationStateData(this.skeleton.data);
this.animationState = new AnimationState(this.animationStateData);
this.updateSize();
} else {
this.skeleton = null;
this.animationStateData = null;
this.animationState = null;
}
}
public get displayOriginX() {
return this._displayOriginX;
}
public set displayOriginX(value: number) {
this._displayOriginX = value;
}
public get displayOriginY() {
return this._displayOriginY;
}
public set displayOriginY(value: number) {
this._displayOriginY = value;
}
public get scaleX() {
return this._scaleX;
}
public set scaleX(value: number) {
this._scaleX = value;
this.updateSize();
}
public get scaleY() {
return this._scaleY;
}
public set scaleY(value: number) {
this._scaleY = value;
this.updateSize();
}
updateSize() {
if (!this.skeleton) return;
let bounds = this.boundsProvider.calculateBounds(this);
// For some reason the TS compiler and the ComputedSize mixin don't work well together...
let self = this as any;
self.width = bounds.width;
self.height = bounds.height;
this.displayOriginX = -bounds.x;
this.displayOriginY = -bounds.y;
}
skeletonToPhaserWorldCoordinates(point: {x: number, y: number}) {
let transform = this.getWorldTransformMatrix();
let a = transform.a, b = transform.b, c = transform.c, d = transform.d, tx = transform.tx, ty = transform.ty;
let x = point.x
let y = point.y
point.x = x * a + y * c + tx;
point.y = x * b + y * d + ty;
}
phaserWorldCoordinatesToSkeleton(point: {x: number, y: number}) {
let transform = this.getWorldTransformMatrix();
transform = transform.invert();
let a = transform.a, b = transform.b, c = transform.c, d = transform.d, tx = transform.tx, ty = transform.ty;
let x = point.x
let y = point.y
point.x = x * a + y * c + tx;
point.y = x * b + y * d + ty;
}
phaserWorldCoordinatesToBone(point: {x: number, y: number}, bone: Bone) {
this.phaserWorldCoordinatesToSkeleton(point);
if (bone.parent) {
bone.parent.worldToLocal(point as Vector2);
} else {
bone.worldToLocal(point as Vector2);
}
}
preUpdate(time: number, delta: number) {
if (!this.skeleton || !this.animationState) return;
this.animationState.update(delta / 1000);
this.animationState.apply(this.skeleton);
this.skeleton.updateWorldTransform();
}
preDestroy() {
this.skeleton = null;
this.animationState = null;
// FIXME tear down any event emitters
}
willRender(camera: Phaser.Cameras.Scene2D.Camera) {
if (!this.visible) return false;
var GameObjectRenderMask = 0xf;
var result = (!this.skeleton || !(GameObjectRenderMask !== this.renderFlags || (this.cameraFilter !== 0 && (this.cameraFilter & camera.id))));
return result;
}
renderWebGL(renderer: Phaser.Renderer.WebGL.WebGLRenderer, src: SpineGameObject, camera: Phaser.Cameras.Scene2D.Camera, parentMatrix: Phaser.GameObjects.Components.TransformMatrix) {
if (!this.skeleton || !this.animationState || !this.plugin.webGLRenderer) return;
let sceneRenderer = this.plugin.webGLRenderer;
if (renderer.newType) {
renderer.pipelines.clear();
sceneRenderer.begin();
}
camera.addToRenderList(src);
let transform = Phaser.GameObjects.GetCalcMatrix(src, camera, parentMatrix).calc;
let a = transform.a, b = transform.b, c = transform.c, d = transform.d, tx = transform.tx, ty = transform.ty;
sceneRenderer.drawSkeleton(this.skeleton, this.premultipliedAlpha, -1, -1, (vertices, numVertices, stride) => {
for (let i = 0; i < numVertices; i += stride) {
let vx = vertices[i];
let vy = vertices[i + 1];
vertices[i] = vx * a + vy * c + tx;
vertices[i + 1] = vx * b + vy * d + ty;
}
});
if (!renderer.nextTypeMatch) {
sceneRenderer.end();
renderer.pipelines.rebind();
}
}
renderCanvas(renderer: Phaser.Renderer.Canvas.CanvasRenderer, src: SpineGameObject, camera: Phaser.Cameras.Scene2D.Camera, parentMatrix: Phaser.GameObjects.Components.TransformMatrix) {
if (!this.skeleton || !this.animationState || !this.plugin.canvasRenderer) return;
let context = renderer.currentContext;
let skeletonRenderer = this.plugin.canvasRenderer;
(skeletonRenderer as any).ctx = context;
camera.addToRenderList(src);
let transform = Phaser.GameObjects.GetCalcMatrix(src, camera, parentMatrix).calc;
let skeleton = this.skeleton;
skeleton.x = transform.tx;
skeleton.y = transform.ty;
skeleton.scaleX = transform.scaleX;
skeleton.scaleY = transform.scaleY;
let root = skeleton.getRootBone()!;
root.rotation = -MathUtils.radiansToDegrees * transform.rotationNormalized;
this.skeleton.updateWorldTransform();
context.save();
skeletonRenderer.draw(skeleton);
context.restore();
}
}

View File

@ -0,0 +1,315 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated September 24, 2021. Replaces all prior versions.
*
* Copyright (c) 2013-2021, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
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 { AtlasAttachmentLoader, Bone, GLTexture, SceneRenderer, Skeleton, SkeletonBinary, SkeletonData, SkeletonJson, TextureAtlas } from "@esotericsoftware/spine-webgl"
import { SpineGameObject, SpineGameObjectBoundsProvider } from "./SpineGameObject";
import { CanvasTexture, SkeletonRenderer } from "@esotericsoftware/spine-canvas";
export class SpinePlugin extends Phaser.Plugins.ScenePlugin {
game: Phaser.Game;
isWebGL: boolean;
gl: WebGLRenderingContext | null;
textureManager: Phaser.Textures.TextureManager;
phaserRenderer: Phaser.Renderer.Canvas.CanvasRenderer | Phaser.Renderer.WebGL.WebGLRenderer | null;
webGLRenderer: SceneRenderer | null;
canvasRenderer: SkeletonRenderer | null;
skeletonDataCache: Phaser.Cache.BaseCache;
atlasCache: Phaser.Cache.BaseCache;
constructor (scene: Phaser.Scene, pluginManager: Phaser.Plugins.PluginManager, pluginKey: string) {
super(scene, pluginManager, pluginKey);
var game = this.game = pluginManager.game;
this.isWebGL = this.game.config.renderType === 2;
this.gl = this.isWebGL ? (this.game.renderer as Phaser.Renderer.WebGL.WebGLRenderer).gl : null;
this.textureManager = this.game.textures;
this.phaserRenderer = this.game.renderer;
this.webGLRenderer = null;
this.canvasRenderer = null;
this.skeletonDataCache = this.game.cache.addCustom(SPINE_SKELETON_DATA_CACHE_KEY);
this.atlasCache = this.game.cache.addCustom(SPINE_ATLAS_CACHE_KEY);
if (!this.phaserRenderer) {
this.phaserRenderer = {
width: game.scale.width,
height: game.scale.height,
preRender: () => { },
postRender: () => { },
render: () => { },
destroy: () => { }
} as unknown as Phaser.Renderer.Canvas.CanvasRenderer;
}
let skeletonJsonFileCallback = function (this: any, key: string,
url: string,
xhrSettings: Phaser.Types.Loader.XHRSettingsObject) {
let file = new SpineSkeletonDataFile(this as any, key, url, SpineSkeletonDataFileType.json, xhrSettings);
this.addFile(file.files);
return this;
};
pluginManager.registerFileType("spineJson", skeletonJsonFileCallback, scene);
let skeletonBinaryFileCallback = function (this: any, key: string,
url: string,
xhrSettings: Phaser.Types.Loader.XHRSettingsObject) {
let file = new SpineSkeletonDataFile(this as any, key, url, SpineSkeletonDataFileType.binary, xhrSettings);
this.addFile(file.files);
return this;
};
pluginManager.registerFileType("spineBinary", skeletonBinaryFileCallback, scene);
let atlasFileCallback = function (this: any, key: string,
url: string,
premultipliedAlpha: boolean,
xhrSettings: Phaser.Types.Loader.XHRSettingsObject) {
let file = new SpineAtlasFile(this as any, key, url, premultipliedAlpha, xhrSettings);
this.addFile(file.files);
return this;
};
pluginManager.registerFileType("spineAtlas", atlasFileCallback, scene);
let self = this;
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.updateList.add(gameObject);
return gameObject;
};
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);
}
boot () {
Skeleton.yDown = true;
if (this.isWebGL) {
if (!this.webGLRenderer) {
this.webGLRenderer = new SceneRenderer((this.phaserRenderer! as Phaser.Renderer.WebGL.WebGLRenderer).canvas, this.gl!, true);
}
this.game.scale.on(Phaser.Scale.Events.RESIZE, this.onResize, this);
} else {
if (!this.canvasRenderer) {
this.canvasRenderer = new SkeletonRenderer(this.scene.sys.context);
}
}
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);
}
onResize () {
var phaserRenderer = this.phaserRenderer;
var sceneRenderer = this.webGLRenderer;
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.up.y = -1;
sceneRenderer.camera.direction.z = 1;
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.webGLRenderer) this.webGLRenderer.dispose();
}
isAtlasPremultiplied(atlasKey: string) {
let atlasFile = this.game.cache.text.get(atlasKey);
if (!atlasFile) return false;
return atlasFile.premultipliedAlpha;
}
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 { data: string, premultipliedAlpha: boolean };
atlas = new TextureAtlas(atlasFile.data);
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 SkeletonBinary(new AtlasAttachmentLoader(atlas));
skeletonData = binary.readSkeletonData(new Uint8Array(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 = true, 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.data = {
data: file.data,
premultipliedAlpha: this.premultipliedAlpha || file.data.indexOf("pma: true") >= 0
};
file.addToCache();
}
}
}
}
}

View File

@ -0,0 +1,8 @@
export * from "./require-shim"
export * from "./SpinePlugin"
export * from "./SpineGameObject"
export * from "./mixins"
export * from "@esotericsoftware/spine-core";
export * from "@esotericsoftware/spine-webgl";
import { SpinePlugin } from "./SpinePlugin";
(window as any).spine = { SpinePlugin: SpinePlugin };

View File

@ -0,0 +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_TEXTURE_CACHE_KEY = "esotericsoftware.spine.atlas.texture.cache";
export const SPINE_LOADER_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_CONTAINER_TYPE = "spineContainer";

View File

@ -0,0 +1,77 @@
/*
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.Depth;
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) => {
(Phaser as any).Class.mixin(BaseGameObject, component);
return BaseGameObject as any;
};
}
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>(Flip);
type ScrollFactorMixin = Mixin<Phaser.GameObjects.Components.ScrollFactor, Phaser.GameObjects.GameObject>;
export const ScrollFactorMixin: ScrollFactorMixin = createMixin<Phaser.GameObjects.Components.ScrollFactor>(ScrollFactor);
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>(Visible);

View File

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

View File

@ -0,0 +1,36 @@
{
"extends": "../tsconfig.base.json",
"compilerOptions": {
"baseUrl": ".",
"rootDir": "./src",
"outDir": "./dist",
"paths": {
"@esotericsoftware/spine-core": [
"../spine-core/src"
],
"@esotericsoftware/spine-canvas": [
"../spine-canvas/src"
],
"@esotericsoftware/spine-webgl": [
"../spine-webgl/src"
]
}
},
"include": [
"**/*.ts",
],
"exclude": [
"dist/**/*.d.ts"
],
"references": [
{
"path": "../spine-core"
},
{
"path": "../spine-canvas"
},
{
"path": "../spine-webgl"
}
]
}

View File

@ -1,3 +1,3 @@
export * from './Player';
export * from "@esotericsoftware/spine-core";
export * from "@esotericsoftware/spine-webgl";
export * from "@esotericsoftware/spine-webgl";

View File

@ -36,6 +36,7 @@ import { ManagedWebGLRenderingContext } from "./WebGL";
export class PolygonBatcher implements Disposable {
private context: ManagedWebGLRenderingContext;
private drawCalls = 0;
private static globalDrawCalls = 0;
private isDrawing = false;
private mesh: Mesh;
private shader: Shader | null = null;
@ -120,6 +121,7 @@ export class PolygonBatcher implements Disposable {
this.mesh.setVerticesLength(0);
this.mesh.setIndicesLength(0);
this.drawCalls++;
PolygonBatcher.globalDrawCalls++;
}
end () {
@ -138,6 +140,12 @@ export class PolygonBatcher implements Disposable {
return this.drawCalls;
}
static getAndResetGlobalDrawCalls () {
let result = PolygonBatcher.globalDrawCalls;
PolygonBatcher.globalDrawCalls = 0;
return result;
}
dispose () {
this.mesh.dispose();
}

View File

@ -34,7 +34,7 @@ import { PolygonBatcher } from "./PolygonBatcher";
import { Shader } from "./Shader";
import { ShapeRenderer } from "./ShapeRenderer";
import { SkeletonDebugRenderer } from "./SkeletonDebugRenderer";
import { SkeletonRenderer } from "./SkeletonRenderer";
import { SkeletonRenderer, VertexTransformer } from "./SkeletonRenderer";
import { ManagedWebGLRenderingContext } from "./WebGL";
;
@ -86,10 +86,10 @@ export class SceneRenderer implements Disposable {
this.enableRenderer(this.batcher);
}
drawSkeleton (skeleton: Skeleton, premultipliedAlpha = false, slotRangeStart = -1, slotRangeEnd = -1) {
drawSkeleton (skeleton: Skeleton, premultipliedAlpha = false, slotRangeStart = -1, slotRangeEnd = -1, transform: VertexTransformer | null = null) {
this.enableRenderer(this.batcher);
this.skeletonRenderer.premultipliedAlpha = premultipliedAlpha;
this.skeletonRenderer.draw(this.batcher, skeleton, slotRangeStart, slotRangeEnd);
this.skeletonRenderer.draw(this.batcher, skeleton, slotRangeStart, slotRangeEnd, transform);
}
drawSkeletonDebug (skeleton: Skeleton, premultipliedAlpha = false, ignoredBones?: Array<string>) {

View File

@ -37,6 +37,8 @@ class Renderable {
constructor (public vertices: NumberArrayLike, public numVertices: number, public numFloats: number) { }
};
export type VertexTransformer = (vertices: NumberArrayLike, numVertices: number, stride: number) => void;
export class SkeletonRenderer {
static QUAD_TRIANGLES = [0, 1, 2, 2, 3, 0];
@ -60,7 +62,7 @@ export class SkeletonRenderer {
this.vertices = Utils.newFloatArray(this.vertexSize * 1024);
}
draw (batcher: PolygonBatcher, skeleton: Skeleton, slotRangeStart: number = -1, slotRangeEnd: number = -1) {
draw (batcher: PolygonBatcher, skeleton: Skeleton, slotRangeStart: number = -1, slotRangeEnd: number = -1, transformer: VertexTransformer | null = null) {
let clipper = this.clipper;
let premultipliedAlpha = this.premultipliedAlpha;
let twoColorTint = this.twoColorTint;
@ -174,6 +176,7 @@ export class SkeletonRenderer {
clipper.clipTriangles(renderable.vertices, renderable.numFloats, triangles, triangles.length, uvs, finalColor, darkColor, twoColorTint);
let clippedVertices = new Float32Array(clipper.clippedVertices);
let clippedTriangles = clipper.clippedTriangles;
if (transformer) transformer(renderable.vertices, renderable.numFloats, vertexSize);
batcher.draw(texture, clippedVertices, clippedTriangles);
} else {
let verts = renderable.vertices;
@ -201,6 +204,7 @@ export class SkeletonRenderer {
}
}
let view = (renderable.vertices as Float32Array).subarray(0, renderable.numFloats);
if (transformer) transformer(renderable.vertices, renderable.numFloats, vertexSize);
batcher.draw(texture, view, triangles);
}
}

View File

@ -8,7 +8,8 @@
"esModuleInterop": true,
"lib": [
"DOM",
"ES2015"
"ES2015",
"ScriptHost"
],
"declaration": true,
"composite": true,

View File

@ -10,6 +10,9 @@
{
"path": "./spine-webgl"
},
{
"path": "./spine-phaser"
},
{
"path": "./spine-player"
},