mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-03-26 22:49:01 +08:00
[ts] Update main dependencies, add formatting script, add Biome for linting, temporary commit
This commit is contained in:
parent
b544dd99ed
commit
8eb9ba957b
1
.gitignore
vendored
1
.gitignore
vendored
@ -251,3 +251,4 @@ spine-libgdx/.factorypath
|
|||||||
spine-libgdx/.project
|
spine-libgdx/.project
|
||||||
.clang-format
|
.clang-format
|
||||||
spine-c/codegen/spine-cpp-types.json
|
spine-c/codegen/spine-cpp-types.json
|
||||||
|
spine-flutter/example/devtools_options.yaml
|
||||||
|
|||||||
11
spine-ts/biome.json
Normal file
11
spine-ts/biome.json
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"formatter": {
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
"linter": {
|
||||||
|
"enabled": true,
|
||||||
|
"rules": {
|
||||||
|
"recommended": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1013
spine-ts/package-lock.json
generated
1013
spine-ts/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -10,6 +10,8 @@
|
|||||||
"prepublish": "npm run clean && npm run build",
|
"prepublish": "npm run clean && npm run build",
|
||||||
"clean": "npx rimraf spine-core/dist spine-canvas/dist spine-canvaskit/dist spine-webgl/dist spine-phaser-v3/dist spine-phaser-v4/dist spine-player/dist spine-threejs/dist spine-pixi-v7/dist spine-pixi-v8/dist spine-webcomponents/dist",
|
"clean": "npx rimraf spine-core/dist spine-canvas/dist spine-canvaskit/dist spine-webgl/dist spine-phaser-v3/dist spine-phaser-v4/dist spine-player/dist spine-threejs/dist spine-pixi-v7/dist spine-pixi-v8/dist spine-webcomponents/dist",
|
||||||
"build": "npm run clean && npm run build:modules && concurrently 'npm run build:core:iife' 'npm run build:core:esm' 'npm run build:canvas:iife' 'npm run build:canvas:esm' 'npm run build:canvaskit:iife' 'npm run build:canvaskit:esm' 'npm run build:webgl:iife' 'npm run build:webgl:esm' 'npm run build:phaser-v3:iife' 'npm run build:phaser-v4:iife' 'npm run build:phaser-v3:esm' 'npm run build:phaser-v4:esm' 'npm run build:player:iife' 'npm run build:player:esm' 'npm run build:player:css' 'npm run build:threejs:iife' 'npm run build:threejs:esm' 'npm run build:pixi-v7:iife' 'npm run build:pixi-v7:esm' 'npm run build:pixi-v8:iife' 'npm run build:pixi-v8:esm' 'npm run build:webcomponents:iife' 'npm run build:webcomponents:esm'",
|
"build": "npm run clean && npm run build:modules && concurrently 'npm run build:core:iife' 'npm run build:core:esm' 'npm run build:canvas:iife' 'npm run build:canvas:esm' 'npm run build:canvaskit:iife' 'npm run build:canvaskit:esm' 'npm run build:webgl:iife' 'npm run build:webgl:esm' 'npm run build:phaser-v3:iife' 'npm run build:phaser-v4:iife' 'npm run build:phaser-v3:esm' 'npm run build:phaser-v4:esm' 'npm run build:player:iife' 'npm run build:player:esm' 'npm run build:player:css' 'npm run build:threejs:iife' 'npm run build:threejs:esm' 'npm run build:pixi-v7:iife' 'npm run build:pixi-v7:esm' 'npm run build:pixi-v8:iife' 'npm run build:pixi-v8:esm' 'npm run build:webcomponents:iife' 'npm run build:webcomponents:esm'",
|
||||||
|
"format": "npx tsx scripts/format.ts",
|
||||||
|
"lint": "npx biome lint .",
|
||||||
"postbuild": "npm run minify",
|
"postbuild": "npm run minify",
|
||||||
"build:modules": "npx tsc -b -clean && npx tsc -b",
|
"build:modules": "npx tsc -b -clean && npx tsc -b",
|
||||||
"build:core:iife": "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:core:iife": "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",
|
||||||
@ -82,12 +84,15 @@
|
|||||||
"spine-webcomponents"
|
"spine-webcomponents"
|
||||||
],
|
],
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/offscreencanvas": "^2019.6.4",
|
"@types/offscreencanvas": "^2019.7.3",
|
||||||
"concurrently": "^7.6.0",
|
"concurrently": "^9.2.0",
|
||||||
"copyfiles": "^2.4.1",
|
"copyfiles": "^2.4.1",
|
||||||
"esbuild": "^0.25.4",
|
"esbuild": "^0.25.6",
|
||||||
"alive-server": "^1.3.0",
|
"alive-server": "^1.3.0",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^6.0.1",
|
||||||
"typescript": "5.6.2"
|
"typescript": "^5.8.3",
|
||||||
|
"typescript-formatter": "^7.2.2",
|
||||||
|
"@biomejs/biome": "^2.1.1",
|
||||||
|
"tsx": "^4.19.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
38
spine-ts/scripts/format.ts
Normal file
38
spine-ts/scripts/format.ts
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import { execSync } from 'node:child_process';
|
||||||
|
import * as fs from 'node:fs';
|
||||||
|
import * as path from 'node:path';
|
||||||
|
|
||||||
|
function findTypeScriptFiles(dir: string, files: string[] = []): string[] {
|
||||||
|
if (!fs.existsSync(dir)) return files;
|
||||||
|
|
||||||
|
fs.readdirSync(dir).forEach(name => {
|
||||||
|
const filePath = path.join(dir, name);
|
||||||
|
const stat = fs.statSync(filePath);
|
||||||
|
|
||||||
|
if (stat.isDirectory()) {
|
||||||
|
// Skip node_modules and dist directories
|
||||||
|
if (name !== 'node_modules' && name !== 'dist') {
|
||||||
|
findTypeScriptFiles(filePath, files);
|
||||||
|
}
|
||||||
|
} else if (name.endsWith('.ts') && !name.endsWith('.d.ts')) {
|
||||||
|
files.push(filePath);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find all TypeScript files in spine-* directories
|
||||||
|
const allFiles: string[] = [];
|
||||||
|
fs.readdirSync('.').forEach(name => {
|
||||||
|
if (name.startsWith('spine-') && fs.statSync(name).isDirectory()) {
|
||||||
|
findTypeScriptFiles(name, allFiles);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (allFiles.length > 0) {
|
||||||
|
console.log(`Formatting ${allFiles.length} TypeScript files...`);
|
||||||
|
execSync(`npx tsfmt -r ${allFiles.join(' ')}`, { stdio: 'inherit' });
|
||||||
|
} else {
|
||||||
|
console.log('No TypeScript files found to format.');
|
||||||
|
}
|
||||||
12
spine-ts/scripts/tsconfig.json
Normal file
12
spine-ts/scripts/tsconfig.json
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2020",
|
||||||
|
"module": "commonjs",
|
||||||
|
"lib": ["ES2020"],
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"strict": true
|
||||||
|
},
|
||||||
|
"include": ["*.ts"]
|
||||||
|
}
|
||||||
@ -32,178 +32,178 @@
|
|||||||
import { promises as fs } from 'fs';
|
import { promises as fs } from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import {
|
import {
|
||||||
AnimationState,
|
AnimationState,
|
||||||
AnimationStateData,
|
AnimationStateData,
|
||||||
AtlasAttachmentLoader,
|
AtlasAttachmentLoader,
|
||||||
Physics,
|
Physics,
|
||||||
Skeleton,
|
Skeleton,
|
||||||
SkeletonBinary,
|
SkeletonBinary,
|
||||||
SkeletonData,
|
SkeletonData,
|
||||||
SkeletonJson,
|
SkeletonJson,
|
||||||
TextureAtlas
|
TextureAtlas
|
||||||
} from '../src/index.js';
|
} from '../src/index.js';
|
||||||
|
|
||||||
// Printer class for hierarchical output
|
// Printer class for hierarchical output
|
||||||
class Printer {
|
class Printer {
|
||||||
private indentLevel = 0;
|
private indentLevel = 0;
|
||||||
private readonly INDENT = " ";
|
private readonly INDENT = " ";
|
||||||
|
|
||||||
print(text: string): void {
|
print (text: string): void {
|
||||||
const indent = this.INDENT.repeat(this.indentLevel);
|
const indent = this.INDENT.repeat(this.indentLevel);
|
||||||
console.log(indent + text);
|
console.log(indent + text);
|
||||||
}
|
}
|
||||||
|
|
||||||
indent(): void {
|
indent (): void {
|
||||||
this.indentLevel++;
|
this.indentLevel++;
|
||||||
}
|
}
|
||||||
|
|
||||||
unindent(): void {
|
unindent (): void {
|
||||||
this.indentLevel--;
|
this.indentLevel--;
|
||||||
}
|
}
|
||||||
|
|
||||||
printSkeletonData(data: SkeletonData): void {
|
printSkeletonData (data: SkeletonData): void {
|
||||||
this.print("SkeletonData {");
|
this.print("SkeletonData {");
|
||||||
this.indent();
|
this.indent();
|
||||||
|
|
||||||
this.print(`name: "${data.name || ""}"`);
|
this.print(`name: "${data.name || ""}"`);
|
||||||
this.print(`version: ${data.version ? `"${data.version}"` : "null"}`);
|
this.print(`version: ${data.version ? `"${data.version}"` : "null"}`);
|
||||||
this.print(`hash: ${data.hash ? `"${data.hash}"` : "null"}`);
|
this.print(`hash: ${data.hash ? `"${data.hash}"` : "null"}`);
|
||||||
this.print(`x: ${this.formatFloat(data.x)}`);
|
this.print(`x: ${this.formatFloat(data.x)}`);
|
||||||
this.print(`y: ${this.formatFloat(data.y)}`);
|
this.print(`y: ${this.formatFloat(data.y)}`);
|
||||||
this.print(`width: ${this.formatFloat(data.width)}`);
|
this.print(`width: ${this.formatFloat(data.width)}`);
|
||||||
this.print(`height: ${this.formatFloat(data.height)}`);
|
this.print(`height: ${this.formatFloat(data.height)}`);
|
||||||
this.print(`referenceScale: ${this.formatFloat(data.referenceScale)}`);
|
this.print(`referenceScale: ${this.formatFloat(data.referenceScale)}`);
|
||||||
this.print(`fps: ${this.formatFloat(data.fps || 0)}`);
|
this.print(`fps: ${this.formatFloat(data.fps || 0)}`);
|
||||||
this.print(`imagesPath: ${data.imagesPath ? `"${data.imagesPath}"` : "null"}`);
|
this.print(`imagesPath: ${data.imagesPath ? `"${data.imagesPath}"` : "null"}`);
|
||||||
this.print(`audioPath: ${data.audioPath ? `"${data.audioPath}"` : "null"}`);
|
this.print(`audioPath: ${data.audioPath ? `"${data.audioPath}"` : "null"}`);
|
||||||
|
|
||||||
// TODO: Add bones, slots, skins, animations, etc. in future expansion
|
// TODO: Add bones, slots, skins, animations, etc. in future expansion
|
||||||
|
|
||||||
this.unindent();
|
this.unindent();
|
||||||
this.print("}");
|
this.print("}");
|
||||||
}
|
}
|
||||||
|
|
||||||
printSkeleton(skeleton: Skeleton): void {
|
printSkeleton (skeleton: Skeleton): void {
|
||||||
this.print("Skeleton {");
|
this.print("Skeleton {");
|
||||||
this.indent();
|
this.indent();
|
||||||
|
|
||||||
this.print(`x: ${this.formatFloat(skeleton.x)}`);
|
this.print(`x: ${this.formatFloat(skeleton.x)}`);
|
||||||
this.print(`y: ${this.formatFloat(skeleton.y)}`);
|
this.print(`y: ${this.formatFloat(skeleton.y)}`);
|
||||||
this.print(`scaleX: ${this.formatFloat(skeleton.scaleX)}`);
|
this.print(`scaleX: ${this.formatFloat(skeleton.scaleX)}`);
|
||||||
this.print(`scaleY: ${this.formatFloat(skeleton.scaleY)}`);
|
this.print(`scaleY: ${this.formatFloat(skeleton.scaleY)}`);
|
||||||
this.print(`time: ${this.formatFloat(skeleton.time)}`);
|
this.print(`time: ${this.formatFloat(skeleton.time)}`);
|
||||||
|
|
||||||
// TODO: Add runtime state (bones, slots, etc.) in future expansion
|
// TODO: Add runtime state (bones, slots, etc.) in future expansion
|
||||||
|
|
||||||
this.unindent();
|
this.unindent();
|
||||||
this.print("}");
|
this.print("}");
|
||||||
}
|
}
|
||||||
|
|
||||||
private formatFloat(value: number): string {
|
private formatFloat (value: number): string {
|
||||||
// Format to 6 decimal places, matching Java/C++ output
|
// Format to 6 decimal places, matching Java/C++ output
|
||||||
return value.toFixed(6).replace(',', '.');
|
return value.toFixed(6).replace(',', '.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Main DebugPrinter class
|
// Main DebugPrinter class
|
||||||
class DebugPrinter {
|
class DebugPrinter {
|
||||||
static async main(args: string[]): Promise<void> {
|
static async main (args: string[]): Promise<void> {
|
||||||
if (args.length < 2) {
|
if (args.length < 2) {
|
||||||
console.error("Usage: DebugPrinter <skeleton-path> <atlas-path> [animation-name]");
|
console.error("Usage: DebugPrinter <skeleton-path> <atlas-path> [animation-name]");
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
const skeletonPath = args[0];
|
const skeletonPath = args[0];
|
||||||
const atlasPath = args[1];
|
const atlasPath = args[1];
|
||||||
const animationName = args.length >= 3 ? args[2] : null;
|
const animationName = args.length >= 3 ? args[2] : null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Load atlas
|
// Load atlas
|
||||||
const atlasData = await fs.readFile(atlasPath, 'utf8');
|
const atlasData = await fs.readFile(atlasPath, 'utf8');
|
||||||
const atlas = new TextureAtlas(atlasData);
|
const atlas = new TextureAtlas(atlasData);
|
||||||
|
|
||||||
// Load skeleton data
|
// Load skeleton data
|
||||||
const skeletonData = await this.loadSkeletonData(skeletonPath, atlas);
|
const skeletonData = await this.loadSkeletonData(skeletonPath, atlas);
|
||||||
|
|
||||||
// Print skeleton data
|
// Print skeleton data
|
||||||
const printer = new Printer();
|
const printer = new Printer();
|
||||||
console.log("=== SKELETON DATA ===");
|
console.log("=== SKELETON DATA ===");
|
||||||
printer.printSkeletonData(skeletonData);
|
printer.printSkeletonData(skeletonData);
|
||||||
|
|
||||||
// Create skeleton and animation state
|
// Create skeleton and animation state
|
||||||
const skeleton = new Skeleton(skeletonData);
|
const skeleton = new Skeleton(skeletonData);
|
||||||
const stateData = new AnimationStateData(skeletonData);
|
const stateData = new AnimationStateData(skeletonData);
|
||||||
const state = new AnimationState(stateData);
|
const state = new AnimationState(stateData);
|
||||||
|
|
||||||
skeleton.setupPose();
|
skeleton.setupPose();
|
||||||
|
|
||||||
// Set animation or setup pose
|
// Set animation or setup pose
|
||||||
if (animationName) {
|
if (animationName) {
|
||||||
// Find and set animation
|
// Find and set animation
|
||||||
const animation = skeletonData.findAnimation(animationName);
|
const animation = skeletonData.findAnimation(animationName);
|
||||||
if (!animation) {
|
if (!animation) {
|
||||||
console.error(`Animation not found: ${animationName}`);
|
console.error(`Animation not found: ${animationName}`);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
state.setAnimation(0, animationName, true);
|
state.setAnimation(0, animationName, true);
|
||||||
// Update and apply
|
// Update and apply
|
||||||
state.update(0.016);
|
state.update(0.016);
|
||||||
state.apply(skeleton);
|
state.apply(skeleton);
|
||||||
}
|
}
|
||||||
|
|
||||||
skeleton.updateWorldTransform(Physics.update);
|
skeleton.updateWorldTransform(Physics.update);
|
||||||
|
|
||||||
// Print skeleton state
|
// Print skeleton state
|
||||||
console.log("\n=== SKELETON STATE ===");
|
console.log("\n=== SKELETON STATE ===");
|
||||||
printer.printSkeleton(skeleton);
|
printer.printSkeleton(skeleton);
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error:", error);
|
console.error("Error:", error);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async loadSkeletonData(skeletonPath: string, atlas: TextureAtlas): Promise<SkeletonData> {
|
private static async loadSkeletonData (skeletonPath: string, atlas: TextureAtlas): Promise<SkeletonData> {
|
||||||
const attachmentLoader = new AtlasAttachmentLoader(atlas);
|
const attachmentLoader = new AtlasAttachmentLoader(atlas);
|
||||||
const ext = path.extname(skeletonPath).toLowerCase();
|
const ext = path.extname(skeletonPath).toLowerCase();
|
||||||
|
|
||||||
if (ext === '.json') {
|
if (ext === '.json') {
|
||||||
const jsonData = await fs.readFile(skeletonPath, 'utf8');
|
const jsonData = await fs.readFile(skeletonPath, 'utf8');
|
||||||
const json = new SkeletonJson(attachmentLoader);
|
const json = new SkeletonJson(attachmentLoader);
|
||||||
json.scale = 1;
|
json.scale = 1;
|
||||||
const skeletonData = json.readSkeletonData(jsonData);
|
const skeletonData = json.readSkeletonData(jsonData);
|
||||||
|
|
||||||
// Set name from filename if not already set
|
// Set name from filename if not already set
|
||||||
if (!skeletonData.name) {
|
if (!skeletonData.name) {
|
||||||
const basename = path.basename(skeletonPath);
|
const basename = path.basename(skeletonPath);
|
||||||
const nameWithoutExt = basename.substring(0, basename.lastIndexOf('.')) || basename;
|
const nameWithoutExt = basename.substring(0, basename.lastIndexOf('.')) || basename;
|
||||||
skeletonData.name = nameWithoutExt;
|
skeletonData.name = nameWithoutExt;
|
||||||
}
|
}
|
||||||
|
|
||||||
return skeletonData;
|
return skeletonData;
|
||||||
} else if (ext === '.skel') {
|
} else if (ext === '.skel') {
|
||||||
const binaryData = await fs.readFile(skeletonPath);
|
const binaryData = await fs.readFile(skeletonPath);
|
||||||
const binary = new SkeletonBinary(attachmentLoader);
|
const binary = new SkeletonBinary(attachmentLoader);
|
||||||
binary.scale = 1;
|
binary.scale = 1;
|
||||||
const skeletonData = binary.readSkeletonData(new Uint8Array(binaryData));
|
const skeletonData = binary.readSkeletonData(new Uint8Array(binaryData));
|
||||||
|
|
||||||
// Set name from filename if not already set
|
// Set name from filename if not already set
|
||||||
if (!skeletonData.name) {
|
if (!skeletonData.name) {
|
||||||
const basename = path.basename(skeletonPath);
|
const basename = path.basename(skeletonPath);
|
||||||
const nameWithoutExt = basename.substring(0, basename.lastIndexOf('.')) || basename;
|
const nameWithoutExt = basename.substring(0, basename.lastIndexOf('.')) || basename;
|
||||||
skeletonData.name = nameWithoutExt;
|
skeletonData.name = nameWithoutExt;
|
||||||
}
|
}
|
||||||
|
|
||||||
return skeletonData;
|
return skeletonData;
|
||||||
} else {
|
} else {
|
||||||
throw new Error(`Unsupported skeleton file format: ${ext}`);
|
throw new Error(`Unsupported skeleton file format: ${ext}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run if called directly
|
// Run if called directly
|
||||||
if (import.meta.url === `file://${process.argv[1]}`) {
|
if (import.meta.url === `file://${process.argv[1]}`) {
|
||||||
DebugPrinter.main(process.argv.slice(2));
|
DebugPrinter.main(process.argv.slice(2));
|
||||||
}
|
}
|
||||||
|
|
||||||
export default DebugPrinter;
|
export default DebugPrinter;
|
||||||
@ -2,28 +2,28 @@ import * as Phaser from "phaser"
|
|||||||
import * as spine from "@esotericsoftware/spine-phaser-v3"
|
import * as spine from "@esotericsoftware/spine-phaser-v3"
|
||||||
|
|
||||||
class SpineDemo extends Phaser.Scene {
|
class SpineDemo extends Phaser.Scene {
|
||||||
preload() {
|
preload () {
|
||||||
this.load.spineBinary("spineboy-data", "/assets/spineboy-pro.skel");
|
this.load.spineBinary("spineboy-data", "/assets/spineboy-pro.skel");
|
||||||
this.load.spineAtlas("spineboy-atlas", "/assets/spineboy-pma.atlas");
|
this.load.spineAtlas("spineboy-atlas", "/assets/spineboy-pma.atlas");
|
||||||
}
|
}
|
||||||
|
|
||||||
create() {
|
create () {
|
||||||
const spineboy = this.add.spine(400, 500, 'spineboy-data', "spineboy-atlas");
|
const spineboy = this.add.spine(400, 500, 'spineboy-data', "spineboy-atlas");
|
||||||
spineboy.scale = 0.5;
|
spineboy.scale = 0.5;
|
||||||
spineboy.animationState.setAnimation(0, "walk", true);
|
spineboy.animationState.setAnimation(0, "walk", true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
width: 800,
|
width: 800,
|
||||||
height: 600,
|
height: 600,
|
||||||
type: Phaser.WEBGL,
|
type: Phaser.WEBGL,
|
||||||
scene: [SpineDemo],
|
scene: [SpineDemo],
|
||||||
plugins: {
|
plugins: {
|
||||||
scene: [
|
scene: [
|
||||||
{ key: "spine.SpinePlugin", plugin: spine.SpinePlugin, mapping: "spine" }
|
{ key: "spine.SpinePlugin", plugin: spine.SpinePlugin, mapping: "spine" }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
new Phaser.Game(config);
|
new Phaser.Game(config);
|
||||||
@ -37,13 +37,13 @@ export const Alpha = components.Alpha;
|
|||||||
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>;
|
||||||
|
|
||||||
|
|||||||
@ -2,28 +2,28 @@ import * as Phaser from "phaser"
|
|||||||
import * as spine from "@esotericsoftware/spine-phaser-v4"
|
import * as spine from "@esotericsoftware/spine-phaser-v4"
|
||||||
|
|
||||||
class SpineDemo extends Phaser.Scene {
|
class SpineDemo extends Phaser.Scene {
|
||||||
preload() {
|
preload () {
|
||||||
this.load.spineBinary("spineboy-data", "/assets/spineboy-pro.skel");
|
this.load.spineBinary("spineboy-data", "/assets/spineboy-pro.skel");
|
||||||
this.load.spineAtlas("spineboy-atlas", "/assets/spineboy-pma.atlas");
|
this.load.spineAtlas("spineboy-atlas", "/assets/spineboy-pma.atlas");
|
||||||
}
|
}
|
||||||
|
|
||||||
create() {
|
create () {
|
||||||
const spineboy = this.add.spine(400, 500, 'spineboy-data', "spineboy-atlas");
|
const spineboy = this.add.spine(400, 500, 'spineboy-data', "spineboy-atlas");
|
||||||
spineboy.scale = 0.5;
|
spineboy.scale = 0.5;
|
||||||
spineboy.animationState.setAnimation(0, "walk", true);
|
spineboy.animationState.setAnimation(0, "walk", true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
width: 800,
|
width: 800,
|
||||||
height: 600,
|
height: 600,
|
||||||
type: Phaser.WEBGL,
|
type: Phaser.WEBGL,
|
||||||
scene: [SpineDemo],
|
scene: [SpineDemo],
|
||||||
plugins: {
|
plugins: {
|
||||||
scene: [
|
scene: [
|
||||||
{ key: "spine.SpinePlugin", plugin: spine.SpinePlugin, mapping: "spine" }
|
{ key: "spine.SpinePlugin", plugin: spine.SpinePlugin, mapping: "spine" }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
new Phaser.Game(config);
|
new Phaser.Game(config);
|
||||||
@ -37,13 +37,13 @@ export const Alpha = components.Alpha;
|
|||||||
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>;
|
||||||
|
|
||||||
|
|||||||
@ -3,43 +3,43 @@ import { Spine } from '@esotericsoftware/spine-pixi-v7';
|
|||||||
|
|
||||||
/** The PixiJS app Application instance, shared across the project */
|
/** The PixiJS app Application instance, shared across the project */
|
||||||
export const app = new Application<HTMLCanvasElement>({
|
export const app = new Application<HTMLCanvasElement>({
|
||||||
width: window.innerWidth,
|
width: window.innerWidth,
|
||||||
height: window.innerHeight,
|
height: window.innerHeight,
|
||||||
resolution: window.devicePixelRatio || 1,
|
resolution: window.devicePixelRatio || 1,
|
||||||
autoDensity: true,
|
autoDensity: true,
|
||||||
resizeTo: window,
|
resizeTo: window,
|
||||||
backgroundColor: 0x2c3e50,
|
backgroundColor: 0x2c3e50,
|
||||||
hello: true,
|
hello: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
/** Setup app and initialise assets */
|
/** Setup app and initialise assets */
|
||||||
async function init() {
|
async function init () {
|
||||||
// Add pixi canvas element (app.view) to the document's body
|
// Add pixi canvas element (app.view) to the document's body
|
||||||
document.body.appendChild(app.view);
|
document.body.appendChild(app.view);
|
||||||
|
|
||||||
// Pre-load the skeleton data and atlas. You can also load .json skeleton data.
|
// Pre-load the skeleton data and atlas. You can also load .json skeleton data.
|
||||||
Assets.add({ alias: "spineboyData", src: "assets/spineboy-pro.skel" });
|
Assets.add({ alias: "spineboyData", src: "assets/spineboy-pro.skel" });
|
||||||
Assets.add({ alias: "spineboyAtlas", src: "assets/spineboy-pma.atlas" });
|
Assets.add({ alias: "spineboyAtlas", src: "assets/spineboy-pma.atlas" });
|
||||||
await Assets.load(["spineboyData", "spineboyAtlas"]);
|
await Assets.load(["spineboyData", "spineboyAtlas"]);
|
||||||
|
|
||||||
// Create the spine display object
|
// Create the spine display object
|
||||||
const spineboy = Spine.from("spineboyData", "spineboyAtlas", {
|
const spineboy = Spine.from("spineboyData", "spineboyAtlas", {
|
||||||
scale: 0.5,
|
scale: 0.5,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Set the default mix time to use when transitioning
|
// Set the default mix time to use when transitioning
|
||||||
// from one animation to the next.
|
// from one animation to the next.
|
||||||
spineboy.state.data.defaultMix = 0.2;
|
spineboy.state.data.defaultMix = 0.2;
|
||||||
|
|
||||||
// Center the spine object on screen.
|
// Center the spine object on screen.
|
||||||
spineboy.x = window.innerWidth / 2;
|
spineboy.x = window.innerWidth / 2;
|
||||||
spineboy.y = window.innerHeight / 2 + spineboy.getBounds().height / 2;
|
spineboy.y = window.innerHeight / 2 + spineboy.getBounds().height / 2;
|
||||||
|
|
||||||
// Set animation "cape-follow-example" on track 0, looped.
|
// Set animation "cape-follow-example" on track 0, looped.
|
||||||
spineboy.state.setAnimation(0, "run", true);
|
spineboy.state.setAnimation(0, "run", true);
|
||||||
|
|
||||||
// Add the display object to the stage.
|
// Add the display object to the stage.
|
||||||
app.stage.addChild(spineboy);
|
app.stage.addChild(spineboy);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init everything
|
// Init everything
|
||||||
|
|||||||
@ -62,11 +62,11 @@ const spineTextureAtlasLoader: AssetExtension<RawAtlas | TextureAtlas, ISpineAtl
|
|||||||
name: loaderName,
|
name: loaderName,
|
||||||
},
|
},
|
||||||
|
|
||||||
test(url: string): boolean {
|
test (url: string): boolean {
|
||||||
return checkExtension(url, ".atlas");
|
return checkExtension(url, ".atlas");
|
||||||
},
|
},
|
||||||
|
|
||||||
async load(url: string): Promise<RawAtlas> {
|
async load (url: string): Promise<RawAtlas> {
|
||||||
const response = await settings.ADAPTER.fetch(url);
|
const response = await settings.ADAPTER.fetch(url);
|
||||||
|
|
||||||
const txt = await response.text();
|
const txt = await response.text();
|
||||||
@ -74,7 +74,7 @@ const spineTextureAtlasLoader: AssetExtension<RawAtlas | TextureAtlas, ISpineAtl
|
|||||||
return txt;
|
return txt;
|
||||||
},
|
},
|
||||||
|
|
||||||
testParse(asset: unknown, options: ResolvedAsset): Promise<boolean> {
|
testParse (asset: unknown, options: ResolvedAsset): Promise<boolean> {
|
||||||
const isExtensionRight = checkExtension(options.src!, ".atlas");
|
const isExtensionRight = checkExtension(options.src!, ".atlas");
|
||||||
const isString = typeof asset === "string";
|
const isString = typeof asset === "string";
|
||||||
const isExplicitLoadParserSet = options.loadParser === loaderName;
|
const isExplicitLoadParserSet = options.loadParser === loaderName;
|
||||||
@ -82,11 +82,11 @@ const spineTextureAtlasLoader: AssetExtension<RawAtlas | TextureAtlas, ISpineAtl
|
|||||||
return Promise.resolve((isExtensionRight || isExplicitLoadParserSet) && isString);
|
return Promise.resolve((isExtensionRight || isExplicitLoadParserSet) && isString);
|
||||||
},
|
},
|
||||||
|
|
||||||
unload(atlas: TextureAtlas) {
|
unload (atlas: TextureAtlas) {
|
||||||
atlas.dispose();
|
atlas.dispose();
|
||||||
},
|
},
|
||||||
|
|
||||||
async parse(asset: RawAtlas, options: {src: string, data: ISpineAtlasMetadata}, loader: Loader): Promise<TextureAtlas> {
|
async parse (asset: RawAtlas, options: { src: string, data: ISpineAtlasMetadata }, loader: Loader): Promise<TextureAtlas> {
|
||||||
const metadata: ISpineAtlasMetadata = options.data || {};
|
const metadata: ISpineAtlasMetadata = options.data || {};
|
||||||
let basePath = utils.path.dirname(options.src);
|
let basePath = utils.path.dirname(options.src);
|
||||||
|
|
||||||
@ -36,11 +36,11 @@ type SkeletonBinaryAsset = Uint8Array;
|
|||||||
|
|
||||||
const loaderName = "spineSkeletonLoader";
|
const loaderName = "spineSkeletonLoader";
|
||||||
|
|
||||||
function isJson(resource: any): resource is SkeletonJsonAsset {
|
function isJson (resource: any): resource is SkeletonJsonAsset {
|
||||||
return resource.hasOwnProperty("bones");
|
return resource.hasOwnProperty("bones");
|
||||||
}
|
}
|
||||||
|
|
||||||
function isBuffer(resource: any): resource is SkeletonBinaryAsset {
|
function isBuffer (resource: any): resource is SkeletonBinaryAsset {
|
||||||
return resource instanceof Uint8Array;
|
return resource instanceof Uint8Array;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,18 +55,18 @@ const spineLoaderExtension: AssetExtension<SkeletonJsonAsset | SkeletonBinaryAss
|
|||||||
name: loaderName,
|
name: loaderName,
|
||||||
},
|
},
|
||||||
|
|
||||||
test(url) {
|
test (url) {
|
||||||
return checkExtension(url, ".skel");
|
return checkExtension(url, ".skel");
|
||||||
},
|
},
|
||||||
|
|
||||||
async load(url: string): Promise<SkeletonBinaryAsset> {
|
async load (url: string): Promise<SkeletonBinaryAsset> {
|
||||||
const response = await settings.ADAPTER.fetch(url);
|
const response = await settings.ADAPTER.fetch(url);
|
||||||
|
|
||||||
const buffer = new Uint8Array(await response.arrayBuffer());
|
const buffer = new Uint8Array(await response.arrayBuffer());
|
||||||
|
|
||||||
return buffer;
|
return buffer;
|
||||||
},
|
},
|
||||||
testParse(asset: unknown, options: ResolvedAsset): Promise<boolean> {
|
testParse (asset: unknown, options: ResolvedAsset): Promise<boolean> {
|
||||||
const isJsonSpineModel = checkExtension(options.src!, ".json") && isJson(asset);
|
const isJsonSpineModel = checkExtension(options.src!, ".json") && isJson(asset);
|
||||||
const isBinarySpineModel = checkExtension(options.src!, ".skel") && isBuffer(asset);
|
const isBinarySpineModel = checkExtension(options.src!, ".skel") && isBuffer(asset);
|
||||||
const isExplicitLoadParserSet = options.loadParser === loaderName;
|
const isExplicitLoadParserSet = options.loadParser === loaderName;
|
||||||
@ -44,7 +44,7 @@ export class DarkTintBatchGeometry extends Geometry {
|
|||||||
* @param {boolean} [_static=false] - Optimization flag, where `false`
|
* @param {boolean} [_static=false] - Optimization flag, where `false`
|
||||||
* is updated every frame, `true` doesn't change frame-to-frame.
|
* is updated every frame, `true` doesn't change frame-to-frame.
|
||||||
*/
|
*/
|
||||||
constructor(_static = false) {
|
constructor (_static = false) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this._buffer = new Buffer(undefined, _static, false);
|
this._buffer = new Buffer(undefined, _static, false);
|
||||||
|
|||||||
@ -38,7 +38,7 @@ export class DarkTintGeometry extends Geometry {
|
|||||||
* @param {boolean} [_static=false] - Optimization flag, where `false`
|
* @param {boolean} [_static=false] - Optimization flag, where `false`
|
||||||
* is updated every frame, `true` doesn't change frame-to-frame.
|
* is updated every frame, `true` doesn't change frame-to-frame.
|
||||||
*/
|
*/
|
||||||
constructor(_static = false) {
|
constructor (_static = false) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
const verticesBuffer = new Buffer(undefined);
|
const verticesBuffer = new Buffer(undefined);
|
||||||
|
|||||||
@ -94,7 +94,7 @@ export class DarkTintMaterial extends Shader {
|
|||||||
private _tintColor: Color;
|
private _tintColor: Color;
|
||||||
private _darkTintColor: Color;
|
private _darkTintColor: Color;
|
||||||
|
|
||||||
constructor(texture?: Texture) {
|
constructor (texture?: Texture) {
|
||||||
const uniforms = {
|
const uniforms = {
|
||||||
uSampler: texture ?? Texture.EMPTY,
|
uSampler: texture ?? Texture.EMPTY,
|
||||||
alpha: 1,
|
alpha: 1,
|
||||||
@ -127,10 +127,10 @@ export class DarkTintMaterial extends Shader {
|
|||||||
this._colorDirty = true;
|
this._colorDirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get texture(): Texture {
|
public get texture (): Texture {
|
||||||
return this.uniforms.uSampler;
|
return this.uniforms.uSampler;
|
||||||
}
|
}
|
||||||
public set texture(value: Texture) {
|
public set texture (value: Texture) {
|
||||||
if (this.uniforms.uSampler !== value) {
|
if (this.uniforms.uSampler !== value) {
|
||||||
if (!this.uniforms.uSampler.baseTexture.alphaMode !== !value.baseTexture.alphaMode) {
|
if (!this.uniforms.uSampler.baseTexture.alphaMode !== !value.baseTexture.alphaMode) {
|
||||||
this._colorDirty = true;
|
this._colorDirty = true;
|
||||||
@ -141,7 +141,7 @@ export class DarkTintMaterial extends Shader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public set alpha(value: number) {
|
public set alpha (value: number) {
|
||||||
if (value === this._alpha) {
|
if (value === this._alpha) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -149,11 +149,11 @@ export class DarkTintMaterial extends Shader {
|
|||||||
this._alpha = value;
|
this._alpha = value;
|
||||||
this._colorDirty = true;
|
this._colorDirty = true;
|
||||||
}
|
}
|
||||||
public get alpha(): number {
|
public get alpha (): number {
|
||||||
return this._alpha;
|
return this._alpha;
|
||||||
}
|
}
|
||||||
|
|
||||||
public set tint(value: ColorSource) {
|
public set tint (value: ColorSource) {
|
||||||
if (value === this.tint) {
|
if (value === this.tint) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -162,11 +162,11 @@ export class DarkTintMaterial extends Shader {
|
|||||||
this._tintRGB = this._tintColor.toLittleEndianNumber();
|
this._tintRGB = this._tintColor.toLittleEndianNumber();
|
||||||
this._colorDirty = true;
|
this._colorDirty = true;
|
||||||
}
|
}
|
||||||
public get tint(): ColorSource {
|
public get tint (): ColorSource {
|
||||||
return this._tintColor.value!;
|
return this._tintColor.value!;
|
||||||
}
|
}
|
||||||
|
|
||||||
public set darkTint(value: ColorSource) {
|
public set darkTint (value: ColorSource) {
|
||||||
if (value === this.darkTint) {
|
if (value === this.darkTint) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -175,20 +175,20 @@ export class DarkTintMaterial extends Shader {
|
|||||||
this._darkTintRGB = this._darkTintColor.toLittleEndianNumber();
|
this._darkTintRGB = this._darkTintColor.toLittleEndianNumber();
|
||||||
this._colorDirty = true;
|
this._colorDirty = true;
|
||||||
}
|
}
|
||||||
public get darkTint(): ColorSource {
|
public get darkTint (): ColorSource {
|
||||||
return this._darkTintColor.value!;
|
return this._darkTintColor.value!;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get tintValue(): number {
|
public get tintValue (): number {
|
||||||
return this._tintColor.toNumber();
|
return this._tintColor.toNumber();
|
||||||
}
|
}
|
||||||
|
|
||||||
public get darkTintValue(): number {
|
public get darkTintValue (): number {
|
||||||
return this._darkTintColor.toNumber();
|
return this._darkTintColor.toNumber();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Gets called automatically by the Mesh. Intended to be overridden for custom {@link PIXI.MeshMaterial} objects. */
|
/** Gets called automatically by the Mesh. Intended to be overridden for custom {@link PIXI.MeshMaterial} objects. */
|
||||||
public update(): void {
|
public update (): void {
|
||||||
if (this._colorDirty) {
|
if (this._colorDirty) {
|
||||||
this._colorDirty = false;
|
this._colorDirty = false;
|
||||||
Color.shared.setValue(this._tintColor).premultiply(this._alpha, true).toArray(this.uniforms.uColor);
|
Color.shared.setValue(this._tintColor).premultiply(this._alpha, true).toArray(this.uniforms.uColor);
|
||||||
|
|||||||
@ -51,24 +51,24 @@ export class DarkTintMesh extends Mesh<DarkTintMaterial> {
|
|||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
public _darkTintRGB: number = 0;
|
public _darkTintRGB: number = 0;
|
||||||
|
|
||||||
constructor(texture?: Texture) {
|
constructor (texture?: Texture) {
|
||||||
super(new DarkTintGeometry(), new DarkTintMaterial(texture), undefined, undefined);
|
super(new DarkTintGeometry(), new DarkTintMaterial(texture), undefined, undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
public get darkTint(): ColorSource | null {
|
public get darkTint (): ColorSource | null {
|
||||||
return "darkTint" in this.shader ? (this.shader as unknown as DarkTintMaterial).darkTint : null;
|
return "darkTint" in this.shader ? (this.shader as unknown as DarkTintMaterial).darkTint : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public set darkTint(value: ColorSource | null) {
|
public set darkTint (value: ColorSource | null) {
|
||||||
(this.shader as unknown as DarkTintMaterial).darkTint = value!;
|
(this.shader as unknown as DarkTintMaterial).darkTint = value!;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get darkTintValue(): number {
|
public get darkTintValue (): number {
|
||||||
return (this.shader as unknown as DarkTintMaterial).darkTintValue;
|
return (this.shader as unknown as DarkTintMaterial).darkTintValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
protected override _renderToBatch(renderer: Renderer): void {
|
protected override _renderToBatch (renderer: Renderer): void {
|
||||||
const geometry = this.geometry;
|
const geometry = this.geometry;
|
||||||
const shader = this.shader;
|
const shader = this.shader;
|
||||||
|
|
||||||
|
|||||||
@ -83,7 +83,7 @@ export class DarkTintRenderer extends BatchRenderer {
|
|||||||
type: ExtensionType.RendererPlugin,
|
type: ExtensionType.RendererPlugin,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(renderer: Renderer) {
|
constructor (renderer: Renderer) {
|
||||||
super(renderer);
|
super(renderer);
|
||||||
this.shaderGenerator = new BatchShaderGenerator(vertex, fragment);
|
this.shaderGenerator = new BatchShaderGenerator(vertex, fragment);
|
||||||
this.geometryClass = DarkTintBatchGeometry;
|
this.geometryClass = DarkTintBatchGeometry;
|
||||||
@ -91,7 +91,7 @@ export class DarkTintRenderer extends BatchRenderer {
|
|||||||
this.vertexSize = 7;
|
this.vertexSize = 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override packInterleavedGeometry(element: IDarkTintElement, attributeBuffer: ViewableBuffer, indexBuffer: Uint16Array, aIndex: number, iIndex: number): void {
|
public override packInterleavedGeometry (element: IDarkTintElement, attributeBuffer: ViewableBuffer, indexBuffer: Uint16Array, aIndex: number, iIndex: number): void {
|
||||||
const { uint32View, float32View } = attributeBuffer;
|
const { uint32View, float32View } = attributeBuffer;
|
||||||
const packedVertices = aIndex / this.vertexSize;
|
const packedVertices = aIndex / this.vertexSize;
|
||||||
const uvs = element.uvs;
|
const uvs = element.uvs;
|
||||||
|
|||||||
@ -4,8 +4,8 @@ export * from './SpineDebugRenderer.js';
|
|||||||
export * from './SpineTexture.js';
|
export * from './SpineTexture.js';
|
||||||
export * from './SlotMesh.js';
|
export * from './SlotMesh.js';
|
||||||
export * from './DarkSlotMesh.js';
|
export * from './DarkSlotMesh.js';
|
||||||
export * from './assets/atlasLoader.js';
|
export * from './assets/Atlas-Loader.js';
|
||||||
export * from './assets/skeletonLoader.js';
|
export * from './assets/Skeleton-Loader.js';
|
||||||
export * from './darkTintMesh/DarkTintBatchGeom.js';
|
export * from './darkTintMesh/DarkTintBatchGeom.js';
|
||||||
export * from './darkTintMesh/DarkTintGeom.js';
|
export * from './darkTintMesh/DarkTintGeom.js';
|
||||||
export * from './darkTintMesh/DarkTintMaterial.js';
|
export * from './darkTintMesh/DarkTintMaterial.js';
|
||||||
@ -14,5 +14,5 @@ export * from './darkTintMesh/DarkTintRenderer.js';
|
|||||||
export * from "@esotericsoftware/spine-core";
|
export * from "@esotericsoftware/spine-core";
|
||||||
|
|
||||||
|
|
||||||
import './assets/atlasLoader.js'; // Side effects install the loaders into pixi
|
import './assets/Atlas-Loader.js'; // Side effects install the loaders into pixi
|
||||||
import './assets/skeletonLoader.js'; // Side effects install the loaders into pixi
|
import './assets/Skeleton-Loader.js'; // Side effects install the loaders into pixi
|
||||||
|
|||||||
@ -5,45 +5,45 @@ import { Spine } from '@esotericsoftware/spine-pixi-v8';
|
|||||||
export const app = new Application();
|
export const app = new Application();
|
||||||
|
|
||||||
/** Setup app and initialise assets */
|
/** Setup app and initialise assets */
|
||||||
async function init() {
|
async function init () {
|
||||||
await app.init({
|
await app.init({
|
||||||
width: window.innerWidth,
|
width: window.innerWidth,
|
||||||
height: window.innerHeight,
|
height: window.innerHeight,
|
||||||
resolution: window.devicePixelRatio || 1,
|
resolution: window.devicePixelRatio || 1,
|
||||||
autoDensity: true,
|
autoDensity: true,
|
||||||
resizeTo: window,
|
resizeTo: window,
|
||||||
backgroundColor: 0x2c3e50,
|
backgroundColor: 0x2c3e50,
|
||||||
hello: true,
|
hello: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
// Add pixi canvas element (app.view) to the document's body
|
// Add pixi canvas element (app.view) to the document's body
|
||||||
document.body.appendChild(app.canvas);
|
document.body.appendChild(app.canvas);
|
||||||
|
|
||||||
// Pre-load the skeleton data and atlas. You can also load .json skeleton data.
|
// Pre-load the skeleton data and atlas. You can also load .json skeleton data.
|
||||||
Assets.add({ alias: "spineboyData", src: "assets/spineboy-pro.skel" });
|
Assets.add({ alias: "spineboyData", src: "assets/spineboy-pro.skel" });
|
||||||
Assets.add({ alias: "spineboyAtlas", src: "assets/spineboy-pma.atlas" });
|
Assets.add({ alias: "spineboyAtlas", src: "assets/spineboy-pma.atlas" });
|
||||||
await Assets.load(["spineboyData", "spineboyAtlas"]);
|
await Assets.load(["spineboyData", "spineboyAtlas"]);
|
||||||
|
|
||||||
// Create the spine display object
|
// Create the spine display object
|
||||||
const spineboy = Spine.from({
|
const spineboy = Spine.from({
|
||||||
atlas: "spineboyAtlas",
|
atlas: "spineboyAtlas",
|
||||||
skeleton: "spineboyData",
|
skeleton: "spineboyData",
|
||||||
scale: 0.5,
|
scale: 0.5,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Set the default mix time to use when transitioning
|
// Set the default mix time to use when transitioning
|
||||||
// from one animation to the next.
|
// from one animation to the next.
|
||||||
spineboy.state.data.defaultMix = 0.2;
|
spineboy.state.data.defaultMix = 0.2;
|
||||||
|
|
||||||
// Center the spine object on screen.
|
// Center the spine object on screen.
|
||||||
spineboy.x = window.innerWidth / 2;
|
spineboy.x = window.innerWidth / 2;
|
||||||
spineboy.y = window.innerHeight / 2 + spineboy.getBounds().height / 2;
|
spineboy.y = window.innerHeight / 2 + spineboy.getBounds().height / 2;
|
||||||
|
|
||||||
// Set animation "cape-follow-example" on track 0, looped.
|
// Set animation "cape-follow-example" on track 0, looped.
|
||||||
spineboy.state.setAnimation(0, "run", true);
|
spineboy.state.setAnimation(0, "run", true);
|
||||||
|
|
||||||
// Add the display object to the stage.
|
// Add the display object to the stage.
|
||||||
app.stage.addChild(spineboy);
|
app.stage.addChild(spineboy);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init everything
|
// Init everything
|
||||||
|
|||||||
@ -28,13 +28,13 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
import './require-shim.js'; // Side effects add require pixi.js to global scope
|
import './require-shim.js'; // Side effects add require pixi.js to global scope
|
||||||
import './assets/atlasLoader.js'; // Side effects install the loaders into pixi
|
import './assets/Atlas-Loader.js'; // Side effects install the loaders into pixi
|
||||||
import './assets/skeletonLoader.js'; // Side effects install the loaders into pixi
|
import './assets/Skeleton-Loader.js'; // Side effects install the loaders into pixi
|
||||||
import './darktint/DarkTintBatcher.js'; // Side effects install the batcher into pixi
|
import './darktint/DarkTintBatcher.js'; // Side effects install the batcher into pixi
|
||||||
import './SpinePipe.js';
|
import './SpinePipe.js';
|
||||||
|
|
||||||
export * from './assets/atlasLoader.js';
|
export * from './assets/Atlas-Loader.js';
|
||||||
export * from './assets/skeletonLoader.js';
|
export * from './assets/Skeleton-Loader.js';
|
||||||
export * from './require-shim.js';
|
export * from './require-shim.js';
|
||||||
export * from './Spine.js';
|
export * from './Spine.js';
|
||||||
export * from './SpineDebugRenderer.js';
|
export * from './SpineDebugRenderer.js';
|
||||||
|
|||||||
@ -16,120 +16,120 @@ let lastFrameTime = Date.now() / 1000;
|
|||||||
let baseUrl = "/assets/";
|
let baseUrl = "/assets/";
|
||||||
let skeletonFile = "raptor-pro.json";
|
let skeletonFile = "raptor-pro.json";
|
||||||
let atlasFile = skeletonFile
|
let atlasFile = skeletonFile
|
||||||
.replace("-pro", "")
|
.replace("-pro", "")
|
||||||
.replace("-ess", "")
|
.replace("-ess", "")
|
||||||
.replace(".json", ".atlas");
|
.replace(".json", ".atlas");
|
||||||
let animation = "walk";
|
let animation = "walk";
|
||||||
|
|
||||||
function init() {
|
function init () {
|
||||||
// create the THREE.JS camera, scene and renderer (WebGL)
|
// create the THREE.JS camera, scene and renderer (WebGL)
|
||||||
let width = window.innerWidth,
|
let width = window.innerWidth,
|
||||||
height = window.innerHeight;
|
height = window.innerHeight;
|
||||||
camera = new THREE.PerspectiveCamera(75, width / height, 1, 3000);
|
camera = new THREE.PerspectiveCamera(75, width / height, 1, 3000);
|
||||||
camera.position.y = 100;
|
camera.position.y = 100;
|
||||||
camera.position.z = 400;
|
camera.position.z = 400;
|
||||||
scene = new THREE.Scene();
|
scene = new THREE.Scene();
|
||||||
renderer = new THREE.WebGLRenderer();
|
renderer = new THREE.WebGLRenderer();
|
||||||
renderer.setSize(width, height);
|
renderer.setSize(width, height);
|
||||||
document.body.appendChild(renderer.domElement);
|
document.body.appendChild(renderer.domElement);
|
||||||
canvas = renderer.domElement;
|
canvas = renderer.domElement;
|
||||||
controls = new OrbitControls(camera, renderer.domElement);
|
controls = new OrbitControls(camera, renderer.domElement);
|
||||||
|
|
||||||
// LIGHTS - Ambient
|
// LIGHTS - Ambient
|
||||||
const ambientLight = new THREE.AmbientLight(0xffffff, 7.0)
|
const ambientLight = new THREE.AmbientLight(0xffffff, 7.0)
|
||||||
scene.add(ambientLight)
|
scene.add(ambientLight)
|
||||||
|
|
||||||
// LIGHTS - spotLight
|
// LIGHTS - spotLight
|
||||||
const spotLight = new THREE.SpotLight(0xffffff, 5, 1200, Math.PI / 4, 0, 0)
|
const spotLight = new THREE.SpotLight(0xffffff, 5, 1200, Math.PI / 4, 0, 0)
|
||||||
spotLight.position.set(0, 1000, 0)
|
spotLight.position.set(0, 1000, 0)
|
||||||
spotLight.castShadow = true
|
spotLight.castShadow = true
|
||||||
spotLight.shadow.mapSize.set(8192, 8192)
|
spotLight.shadow.mapSize.set(8192, 8192)
|
||||||
spotLight.shadow.bias = -0.00001;
|
spotLight.shadow.bias = -0.00001;
|
||||||
scene.add(spotLight)
|
scene.add(spotLight)
|
||||||
|
|
||||||
// load the assets required to display the Raptor model
|
// load the assets required to display the Raptor model
|
||||||
assetManager = new spine.AssetManager(baseUrl);
|
assetManager = new spine.AssetManager(baseUrl);
|
||||||
assetManager.loadText(skeletonFile);
|
assetManager.loadText(skeletonFile);
|
||||||
assetManager.loadTextureAtlas(atlasFile);
|
assetManager.loadTextureAtlas(atlasFile);
|
||||||
|
|
||||||
requestAnimationFrame(load);
|
requestAnimationFrame(load);
|
||||||
}
|
}
|
||||||
|
|
||||||
function load() {
|
function load () {
|
||||||
if (assetManager.isLoadingComplete()) {
|
if (assetManager.isLoadingComplete()) {
|
||||||
// Add a box to the scene to which we attach the skeleton mesh
|
// Add a box to the scene to which we attach the skeleton mesh
|
||||||
geometry = new THREE.BoxGeometry(200, 200, 200);
|
geometry = new THREE.BoxGeometry(200, 200, 200);
|
||||||
material = new THREE.MeshBasicMaterial({
|
material = new THREE.MeshBasicMaterial({
|
||||||
color: 0xff0000,
|
color: 0xff0000,
|
||||||
wireframe: true,
|
wireframe: true,
|
||||||
});
|
});
|
||||||
mesh = new THREE.Mesh(geometry, material);
|
mesh = new THREE.Mesh(geometry, material);
|
||||||
scene.add(mesh);
|
scene.add(mesh);
|
||||||
|
|
||||||
// Load the texture atlas using name.atlas and name.png from the AssetManager.
|
// Load the texture atlas using name.atlas and name.png from the AssetManager.
|
||||||
// The function passed to TextureAtlas is used to resolve relative paths.
|
// The function passed to TextureAtlas is used to resolve relative paths.
|
||||||
atlas = assetManager.require(atlasFile);
|
atlas = assetManager.require(atlasFile);
|
||||||
|
|
||||||
// Create a AtlasAttachmentLoader that resolves region, mesh, boundingbox and path attachments
|
// Create a AtlasAttachmentLoader that resolves region, mesh, boundingbox and path attachments
|
||||||
atlasLoader = new spine.AtlasAttachmentLoader(atlas);
|
atlasLoader = new spine.AtlasAttachmentLoader(atlas);
|
||||||
|
|
||||||
// Create a SkeletonJson instance for parsing the .json file.
|
// Create a SkeletonJson instance for parsing the .json file.
|
||||||
let skeletonJson = new spine.SkeletonJson(atlasLoader);
|
let skeletonJson = new spine.SkeletonJson(atlasLoader);
|
||||||
|
|
||||||
// Set the scale to apply during parsing, parse the file, and create a new skeleton.
|
// Set the scale to apply during parsing, parse the file, and create a new skeleton.
|
||||||
skeletonJson.scale = 0.4;
|
skeletonJson.scale = 0.4;
|
||||||
let skeletonData = skeletonJson.readSkeletonData(
|
let skeletonData = skeletonJson.readSkeletonData(
|
||||||
assetManager.require(skeletonFile)
|
assetManager.require(skeletonFile)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Create a SkeletonMesh from the data and attach it to the scene
|
// Create a SkeletonMesh from the data and attach it to the scene
|
||||||
skeletonMesh = new spine.SkeletonMesh({
|
skeletonMesh = new spine.SkeletonMesh({
|
||||||
skeletonData,
|
skeletonData,
|
||||||
materialFactory(parameters) {
|
materialFactory (parameters) {
|
||||||
return new THREE.MeshStandardMaterial({ ...parameters, metalness: .5 });
|
return new THREE.MeshStandardMaterial({ ...parameters, metalness: .5 });
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
skeletonMesh.state.setAnimation(0, animation, true);
|
skeletonMesh.state.setAnimation(0, animation, true);
|
||||||
mesh.add(skeletonMesh);
|
mesh.add(skeletonMesh);
|
||||||
|
|
||||||
requestAnimationFrame(render);
|
requestAnimationFrame(render);
|
||||||
} else requestAnimationFrame(load);
|
} else requestAnimationFrame(load);
|
||||||
}
|
}
|
||||||
|
|
||||||
let lastTime = Date.now();
|
let lastTime = Date.now();
|
||||||
function render() {
|
function render () {
|
||||||
// calculate delta time for animation purposes
|
// calculate delta time for animation purposes
|
||||||
let now = Date.now() / 1000;
|
let now = Date.now() / 1000;
|
||||||
let delta = now - lastFrameTime;
|
let delta = now - lastFrameTime;
|
||||||
lastFrameTime = now;
|
lastFrameTime = now;
|
||||||
|
|
||||||
// resize canvas to use full page, adjust camera/renderer
|
// resize canvas to use full page, adjust camera/renderer
|
||||||
resize();
|
resize();
|
||||||
|
|
||||||
// Update orbital controls
|
// Update orbital controls
|
||||||
controls.update();
|
controls.update();
|
||||||
|
|
||||||
// update the animation
|
// update the animation
|
||||||
skeletonMesh.update(delta);
|
skeletonMesh.update(delta);
|
||||||
|
|
||||||
// render the scene
|
// render the scene
|
||||||
renderer.render(scene, camera);
|
renderer.render(scene, camera);
|
||||||
|
|
||||||
requestAnimationFrame(render);
|
requestAnimationFrame(render);
|
||||||
}
|
}
|
||||||
|
|
||||||
function resize() {
|
function resize () {
|
||||||
let w = window.innerWidth;
|
let w = window.innerWidth;
|
||||||
let h = window.innerHeight;
|
let h = window.innerHeight;
|
||||||
if (canvas.width != w || canvas.height != h) {
|
if (canvas.width != w || canvas.height != h) {
|
||||||
canvas.width = w;
|
canvas.width = w;
|
||||||
canvas.height = h;
|
canvas.height = h;
|
||||||
}
|
}
|
||||||
|
|
||||||
camera.aspect = w / h;
|
camera.aspect = w / h;
|
||||||
camera.updateProjectionMatrix();
|
camera.updateProjectionMatrix();
|
||||||
|
|
||||||
renderer.setSize(w, h);
|
renderer.setSize(w, h);
|
||||||
}
|
}
|
||||||
|
|
||||||
init();
|
init();
|
||||||
11
tests/biome.json
Normal file
11
tests/biome.json
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"formatter": {
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
"linter": {
|
||||||
|
"enabled": true,
|
||||||
|
"rules": {
|
||||||
|
"recommended": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -3,11 +3,15 @@
|
|||||||
"type": "module",
|
"type": "module",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"compare": "tsx compare-with-reference-impl.ts"
|
"compare": "tsx compare-with-reference-impl.ts",
|
||||||
|
"format": "npx tsfmt -r ./**/*.ts",
|
||||||
|
"lint": "npx biome lint ."
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^20.0.0",
|
"@types/node": "^20.0.0",
|
||||||
"tsx": "^4.0.0"
|
"tsx": "^4.0.0",
|
||||||
|
"typescript-formatter": "^7.2.2",
|
||||||
|
"@biomejs/biome": "^2.1.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mariozechner/lsp-cli": "^0.1.3"
|
"@mariozechner/lsp-cli": "^0.1.3"
|
||||||
|
|||||||
24
tests/tsfmt.json
Normal file
24
tests/tsfmt.json
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"baseIndentSize": 0,
|
||||||
|
"indentSize": 4,
|
||||||
|
"tabSize": 4,
|
||||||
|
"indentStyle": 2,
|
||||||
|
"newLineCharacter": "\n",
|
||||||
|
"convertTabsToSpaces": false,
|
||||||
|
"insertSpaceAfterCommaDelimiter": true,
|
||||||
|
"insertSpaceAfterSemicolonInForStatements": true,
|
||||||
|
"insertSpaceBeforeAndAfterBinaryOperators": true,
|
||||||
|
"insertSpaceAfterConstructor": true,
|
||||||
|
"insertSpaceAfterKeywordsInControlFlowStatements": true,
|
||||||
|
"insertSpaceAfterFunctionKeywordForAnonymousFunctions": true,
|
||||||
|
"insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis": false,
|
||||||
|
"insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets": false,
|
||||||
|
"insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces": true,
|
||||||
|
"insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces": false,
|
||||||
|
"insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces": false,
|
||||||
|
"insertSpaceAfterTypeAssertion": false,
|
||||||
|
"insertSpaceBeforeFunctionParenthesis": true,
|
||||||
|
"insertSpaceBeforeTypeAnnotation": false,
|
||||||
|
"placeOpenBraceOnNewLineForFunctions": false,
|
||||||
|
"placeOpenBraceOnNewLineForControlBlocks": false
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user