Code review.

This commit is contained in:
Davide Tantillo 2026-01-30 15:10:12 +01:00
parent 2a01da00df
commit 3ff5a9e943
19 changed files with 115 additions and 182 deletions

View File

@ -68,7 +68,7 @@ export class AssetLoader {
public async loadAtlasEditor (sid: number, instance: SDK.IWorldInstance, renderer: SDK.Gfx.IWebGLRenderer) { public async loadAtlasEditor (sid: number, instance: SDK.IWorldInstance, renderer: SDK.Gfx.IWebGLRenderer) {
const projectFile = instance.GetProject().GetProjectFileBySID(sid); const projectFile = instance.GetProject().GetProjectFileBySID(sid);
if (!projectFile) throw new Error(`Atlas file not found wit the given SID: ${sid}`); if (!projectFile) throw new Error(`Atlas file not found with the given SID: ${sid}`);
const blob = projectFile.GetBlob(); const blob = projectFile.GetBlob();
const content = await blob.text(); const content = await blob.text();
@ -91,7 +91,7 @@ export class AssetLoader {
public async loadSpineTextureEditor (pageName: string, pma = false, instance: SDK.IWorldInstance) { public async loadSpineTextureEditor (pageName: string, pma = false, instance: SDK.IWorldInstance) {
const projectFile = instance.GetProject().GetProjectFileByExportPath(pageName); const projectFile = instance.GetProject().GetProjectFileByExportPath(pageName);
if (!projectFile) { if (!projectFile) {
throw new Error(`An error occured while loading the texture: ${pageName}`); throw new Error(`An error occurred while loading the texture: ${pageName}`);
} }
const content = projectFile.GetBlob(); const content = projectFile.GetBlob();
@ -207,9 +207,9 @@ export class AssetLoader {
} }
private async addToCache<T> (cache: ResourceCache<T>, cacheKey: string, promise: Promise<T>) { private async addToCache<T> (cache: ResourceCache<T>, cacheKey: string, promise: Promise<T>) {
const cachEntry: CacheEntry<T> = { promise, refCount: 1 }; const cacheEntry: CacheEntry<T> = { promise, refCount: 1 };
cache.set(cacheKey, cachEntry); cache.set(cacheKey, cacheEntry);
cachEntry.data = await promise; cacheEntry.data = await promise;
} }
private getFromCache<T> (cache: ResourceCache<T>, cacheKey: string) { private getFromCache<T> (cache: ResourceCache<T>, cacheKey: string) {
@ -220,7 +220,7 @@ export class AssetLoader {
return entry; return entry;
} }
static async createImageBitmapFromBlob (blob: Blob, pma: boolean): Promise<ImageBitmap | null> { static async createImageBitmapFromBlob (blob: Blob, pma: boolean): Promise<ImageBitmap> {
try { try {
return createImageBitmap(blob, { premultiplyAlpha: pma ? "none" : "premultiply" }); return createImageBitmap(blob, { premultiplyAlpha: pma ? "none" : "premultiply" });
} catch (e) { } catch (e) {
@ -230,5 +230,3 @@ export class AssetLoader {
} }
} }

View File

@ -47,7 +47,9 @@ export class C3Matrix {
private tempPoint = new Vector2(); private tempPoint = new Vector2();
public update (x: number, y: number, angle: number, scaleX = 1, scaleY = 1) { public update (x: number, y: number, angle: number, scaleX = 1, scaleY = 1) {
if (this.prevX === x && this.prevY === y && this.prevAngle === angle && this.prevScaleX === scaleX && this.prevScaleY === scaleY) return false; if (this.prevX === x && this.prevY === y &&
this.prevAngle === angle &&
this.prevScaleX === scaleX && this.prevScaleY === scaleY) return false;
this.prevX = x; this.prevX = x;
this.prevY = y; this.prevY = y;
this.prevAngle = angle; this.prevAngle = angle;

View File

@ -264,7 +264,7 @@ abstract class C3SkeletonRenderer<
} }
// clipping // clipping
this.setColor(0.8, 0, 0, 1) this.setColor(0.8, 0, 0, 1);
for (let i = 0, n = slots.length; i < n; i++) { for (let i = 0, n = slots.length; i < n; i++) {
const slot = slots[i]; const slot = slots[i];
if (!slot.bone.active) continue; if (!slot.bone.active) continue;
@ -388,8 +388,8 @@ export class C3RendererRuntime extends C3SkeletonRenderer<IRenderer, C3TextureRu
this.renderer.setColorFillMode(); this.renderer.setColorFillMode();
} }
protected setBlendMode (blenMode: BlendModeParameter = "normal"): void { protected setBlendMode (blendMode: BlendModeParameter = "normal"): void {
this.renderer.setBlendMode(blenMode); this.renderer.setBlendMode(blendMode);
} }
protected poly (points: number[]): void { protected poly (points: number[]): void {
@ -433,8 +433,8 @@ export class C3RendererEditor extends C3SkeletonRenderer<SDK.Gfx.IWebGLRenderer,
this.renderer.SetColorFillMode(); this.renderer.SetColorFillMode();
} }
protected setBlendMode (blenMode: BlendModeParameter = "normal"): void { protected setBlendMode (blendMode: BlendModeParameter = "normal"): void {
this.renderer.SetBlendMode(blenMode); this.renderer.SetBlendMode(blendMode);
} }
protected poly (points: number[]): void { protected poly (points: number[]): void {

View File

@ -41,7 +41,7 @@ export class C3TextureEditor extends Texture {
wrapY: toC3TextureWrap(page.vWrap), wrapY: toC3TextureWrap(page.vWrap),
defaultSampling: toC3Filter(page.minFilter), defaultSampling: toC3Filter(page.minFilter),
mipMap: toC3MipMap(page.minFilter), mipMap: toC3MipMap(page.minFilter),
} };
this.texture = renderer.CreateDynamicTexture(image.width, image.height, options); this.texture = renderer.CreateDynamicTexture(image.width, image.height, options);
this.renderer.UpdateTexture(image, this.texture, { premultiplyAlpha: !page.pma }); this.renderer.UpdateTexture(image, this.texture, { premultiplyAlpha: !page.pma });
} }
@ -72,12 +72,11 @@ export class C3TextureRuntime extends Texture {
wrapY: toC3TextureWrap(page.vWrap), wrapY: toC3TextureWrap(page.vWrap),
defaultSampling: toC3Filter(page.minFilter), defaultSampling: toC3Filter(page.minFilter),
mipMap: toC3MipMap(page.minFilter), mipMap: toC3MipMap(page.minFilter),
} };
this.texture = renderer.createDynamicTexture(image.width, image.height, options); this.texture = renderer.createDynamicTexture(image.width, image.height, options);
this.renderer.updateTexture(image, this.texture, { premultiplyAlpha: !page.pma }); this.renderer.updateTexture(image, this.texture, { premultiplyAlpha: !page.pma });
} }
setFilters () { setFilters () {
// cannot change filter after texture creation // cannot change filter after texture creation
} }

View File

@ -203,7 +203,6 @@ function setupModalHandlers (overlay: HTMLDivElement, onCancel: () => void, extr
interface ModalButton<T> { interface ModalButton<T> {
text: string; text: string;
color?: string;
value?: T; value?: T;
style?: 'primary' | 'secondary'; style?: 'primary' | 'secondary';
} }
@ -213,7 +212,6 @@ interface ModalOptions<T> {
title: string; title: string;
text: string; text: string;
buttons: ModalButton<T>[]; buttons: ModalButton<T>[];
maxWidth?: number;
} }
export function showModal<T> (options: ModalOptions<T>): Promise<T | undefined> { export function showModal<T> (options: ModalOptions<T>): Promise<T | undefined> {
@ -268,7 +266,6 @@ interface ListSelectionOptions {
darkMode: boolean; darkMode: boolean;
title: string; title: string;
items: string[]; items: string[];
maxWidth?: number;
maxHeight?: number; maxHeight?: number;
} }
@ -427,7 +424,6 @@ interface MultiListSelectionOptions {
title: string; title: string;
items: string[]; items: string[];
selectedItems?: string[]; selectedItems?: string[];
maxWidth?: number;
maxHeight?: number; maxHeight?: number;
} }

View File

@ -75,9 +75,7 @@ export class SetupPoseBoundsProvider implements SpineBoundsProvider {
skeleton.setupPose(); skeleton.setupPose();
skeleton.updateWorldTransform(Physics.update); skeleton.updateWorldTransform(Physics.update);
const bounds = skeleton.getBoundsRect(this.clipping ? new SkeletonClipping() : undefined); const bounds = skeleton.getBoundsRect(this.clipping ? new SkeletonClipping() : undefined);
return bounds.width === Number.NEGATIVE_INFINITY return validateBounds(bounds);
? { x: 0, y: 0, width: 0, height: 0 }
: bounds;
} }
} }
@ -114,7 +112,7 @@ export class SkinsAndAnimationBoundsProvider implements SpineBoundsProvider {
const customSkin = new Skin("custom-skin"); const customSkin = new Skin("custom-skin");
for (const skinName of this.skins) { for (const skinName of this.skins) {
const skin = data.findSkin(skinName); const skin = data.findSkin(skinName);
if (skin == null) continue; if (skin === null) continue;
customSkin.addSkin(skin); customSkin.addSkin(skin);
} }
skeleton.setSkin(customSkin); skeleton.setSkin(customSkin);
@ -126,9 +124,7 @@ export class SkinsAndAnimationBoundsProvider implements SpineBoundsProvider {
if (animation == null) { if (animation == null) {
skeleton.updateWorldTransform(Physics.update); skeleton.updateWorldTransform(Physics.update);
const bounds = skeleton.getBoundsRect(clipper); const bounds = skeleton.getBoundsRect(clipper);
return bounds.width === Number.NEGATIVE_INFINITY return validateBounds(bounds);
? { x: 0, y: 0, width: 0, height: 0 }
: bounds;
} else { } else {
let minX = Number.POSITIVE_INFINITY, let minX = Number.POSITIVE_INFINITY,
minY = Number.POSITIVE_INFINITY, minY = Number.POSITIVE_INFINITY,
@ -156,9 +152,13 @@ export class SkinsAndAnimationBoundsProvider implements SpineBoundsProvider {
width: maxX - minX, width: maxX - minX,
height: maxY - minY, height: maxY - minY,
}; };
return bounds.width === Number.NEGATIVE_INFINITY return validateBounds(bounds);
? { x: 0, y: 0, width: 0, height: 0 }
: bounds;
} }
} }
}
function validateBounds (bounds: Rectangle): Rectangle {
return bounds.width === Number.NEGATIVE_INFINITY
? { x: 0, y: 0, width: 0, height: 0 }
: bounds;
} }

View File

@ -111,8 +111,9 @@
"highlight": true, "highlight": true,
"params": [ "params": [
{ {
"id": "track", "id": "track-index",
"type": "number" "type": "number",
"initial-value": 0
}, },
{ {
"id": "animation", "id": "animation",
@ -130,8 +131,9 @@
"highlight": true, "highlight": true,
"params": [ "params": [
{ {
"id": "track", "id": "track-index",
"type": "number" "type": "number",
"initial-value": 0
}, },
{ {
"id": "animation", "id": "animation",
@ -163,12 +165,14 @@
"highlight": false, "highlight": false,
"params": [ "params": [
{ {
"id": "track", "id": "track-index",
"type": "number" "type": "number",
"initial-value": 0
}, },
{ {
"id": "mix-duration", "id": "mix-duration",
"type": "number" "type": "number",
"initial-value": 0.25
} }
] ]
}, },
@ -178,12 +182,14 @@
"highlight": false, "highlight": false,
"params": [ "params": [
{ {
"id": "track", "id": "track-index",
"type": "number" "type": "number",
"initial-value": 0
}, },
{ {
"id": "mix-duration", "id": "mix-duration",
"type": "number" "type": "number",
"initial-value": 0.25
}, },
{ {
"id": "delay", "id": "delay",
@ -260,17 +266,18 @@
"highlight": false, "highlight": false,
"params": [ "params": [
{ {
"id": "units", "id": "track-index",
"type": "combo", "type": "number",
"items": ["seconds", "ratio"] "initial-value": 0
}, },
{ {
"id": "time", "id": "time",
"type": "number" "type": "number"
}, },
{ {
"id": "track", "id": "units",
"type": "number" "type": "combo",
"items": ["seconds", "ratio"]
} }
] ]
}, },
@ -483,18 +490,6 @@
} }
], ],
"expressions": [ "expressions": [
{
"id": "double",
"expressionName": "Double",
"highlight": true,
"returnType": "number",
"params": [
{
"id": "number",
"type": "number"
}
]
},
{ {
"id": "slot-attachment", "id": "slot-attachment",
"expressionName": "SlotAttachment", "expressionName": "SlotAttachment",

View File

@ -3,11 +3,11 @@
"is-c3-addon": true, "is-c3-addon": true,
"sdk-version": 2, "sdk-version": 2,
"min-construct-version": "r459", "min-construct-version": "r470",
"type": "plugin", "type": "plugin",
"name": "Spine Construct3", "name": "Spine Construct3",
"id": "EsotericSoftware_SpineConstruct3", "id": "EsotericSoftware_SpineConstruct3",
"version": "1.0.0.1", "version": "4.3.0-beta",
"author": "Esoteric Software", "author": "Esoteric Software",
"website": "https://www.esotericsoftware.com", "website": "https://www.esotericsoftware.com",
"documentation": "https://www.esotericsoftware.com", "documentation": "https://www.esotericsoftware.com",

View File

@ -1,14 +1,9 @@
import type { SDKInstanceClass } from "./instance";
import type { SDKInstanceClass } from "./instance.ts";
const C3 = globalThis.C3; const C3 = globalThis.C3;
C3.Plugins.EsotericSoftware_SpineConstruct3.Acts = C3.Plugins.EsotericSoftware_SpineConstruct3.Acts =
{ {
Alert (this: SDKInstanceClass) {
alert(`Test`);
},
SetSkin (this: SDKInstanceClass, skinList: string) { SetSkin (this: SDKInstanceClass, skinList: string) {
this.setSkin(skinList.split(",")); this.setSkin(skinList.split(","));
}, },
@ -61,7 +56,7 @@ C3.Plugins.EsotericSoftware_SpineConstruct3.Acts =
this.setAnimationSpeed(speed); this.setAnimationSpeed(speed);
}, },
SetAnimationTime (this: SDKInstanceClass, units: 0 | 1, time: number, track: number) { SetAnimationTime (this: SDKInstanceClass, units: 0 | 1, track: number, time: number) {
this.setAnimationTime(units, time, track); this.setAnimationTime(units, time, track);
}, },

View File

@ -1,5 +1,4 @@
import type { SDKInstanceClass as SpineC3Instance } from "./instance";
import type { SDKInstanceClass as SpineC3Instance } from "./instance.ts";
const C3 = globalThis.C3; const C3 = globalThis.C3;

View File

@ -1,14 +1,9 @@
import type { SDKInstanceClass as SpineC3Instance } from "./instance";
import type { SDKInstanceClass as SpineC3Instance } from "./instance.ts";
const C3 = globalThis.C3; const C3 = globalThis.C3;
C3.Plugins.EsotericSoftware_SpineConstruct3.Exps = C3.Plugins.EsotericSoftware_SpineConstruct3.Exps =
{ {
Double (this: SpineC3Instance, num: number) {
return num * 2;
},
SlotAttachment (this: SpineC3Instance, slotName: string) { SlotAttachment (this: SpineC3Instance, slotName: string) {
if (!this.skeleton) return ""; if (!this.skeleton) return "";
const slot = this.skeleton.findSlot(slotName); const slot = this.skeleton.findSlot(slotName);
@ -45,16 +40,15 @@ C3.Plugins.EsotericSoftware_SpineConstruct3.Exps =
return this.getCurrentAnimation(trackIndex); return this.getCurrentAnimation(trackIndex);
}, },
GetEventData (this: SpineC3Instance, field: "float" | "int" | "string" | "balance" | "volume" | "audiopath" | "event" | "track" | "animation") { GetEventData (this: SpineC3Instance, field: "float" | "int" | "string" | "balance" | "volume" | "audiopath" | "event" | "track" | "animation") {
if (field === "float") return this.triggeredEventData?.floatValue; if (field === "float") return this.triggeredEventData?.floatValue ?? 0;
if (field === "int") return this.triggeredEventData?.intValue; if (field === "int") return this.triggeredEventData?.intValue ?? 0;
if (field === "string") return this.triggeredEventData?.stringValue; if (field === "string") return this.triggeredEventData?.stringValue ?? "";
if (field === "balance") return this.triggeredEventData?.balance; if (field === "balance") return this.triggeredEventData?.balance ?? 0;
if (field === "volume") return this.triggeredEventData?.volume; if (field === "volume") return this.triggeredEventData?.volume ?? 0;
if (field === "audiopath") return this.triggeredEventData?.data.audioPath; if (field === "audiopath") return this.triggeredEventData?.data.audioPath ?? "";
if (field === "event") return this.triggeredEventData?.data.name; if (field === "event") return this.triggeredEventData?.data.name ?? 0;
if (field === "animation") return this.triggeredEventData?.animation; if (field === "animation") return this.triggeredEventData?.animation ?? 0;
if (field === "track") return this.triggeredEventData?.track; if (field === "track") return this.triggeredEventData?.track ?? -1;
return ""; return "";
} }
}; };

View File

@ -1,4 +1,4 @@
import type { AnimationState, AssetLoader, Bone, C3Matrix, C3RendererRuntime, Event, NumberArrayLike, RegionAttachment, Skeleton, Skin, Slot, SpineBoundsProvider, SpineBoundsProviderType, TextureAtlas, } from "@esotericsoftware/spine-construct3-lib"; import type { AnimationState, AssetLoader, Bone, C3Matrix, C3RendererRuntime, Event, NumberArrayLike, Skeleton, Skin, Slot, SpineBoundsProvider, SpineBoundsProviderType, TextureAtlas, } from "@esotericsoftware/spine-construct3-lib";
const C3 = globalThis.C3; const C3 = globalThis.C3;
const spine = globalThis.spine; const spine = globalThis.spine;
@ -74,7 +74,6 @@ class SpineC3Instance extends globalThis.ISDKWorldInstanceBase {
const properties = this._getInitProperties(); const properties = this._getInitProperties();
if (properties) { if (properties) {
console.log(properties);
this.propAtlas = properties[0] as string; this.propAtlas = properties[0] as string;
this.propSkel = properties[1] as string; this.propSkel = properties[1] as string;
this.propLoaderScale = properties[2] as number; this.propLoaderScale = properties[2] as number;
@ -224,11 +223,11 @@ class SpineC3Instance extends globalThis.ISDKWorldInstanceBase {
if (!this.touchDown) return; if (!this.touchDown) return;
this.touchX = event.clientX; this.touchX = event.clientX;
this.touchY = event.clientY; this.touchY = event.clientY;
} };
private dragHandleUp = (event: ConstructPointerEvent) => { private dragHandleUp = (event: ConstructPointerEvent) => {
if (event.button === 0) this.touchDown = false; if (event.button === 0) this.touchDown = false;
} };
private dragHandleDispose () { private dragHandleDispose () {
this.runtime.removeEventListener("pointerdown", this.dragHandleDown); this.runtime.removeEventListener("pointerdown", this.dragHandleDown);
@ -317,8 +316,7 @@ class SpineC3Instance extends globalThis.ISDKWorldInstanceBase {
let hullLength = 8; let hullLength = 8;
if (attachment instanceof spine.RegionAttachment) { if (attachment instanceof spine.RegionAttachment) {
const regionAttachment = <RegionAttachment>attachment; attachment.computeWorldVertices(slot, vertices, 0, 2);
regionAttachment.computeWorldVertices(slot, vertices, 0, 2);
} else if (attachment instanceof spine.MeshAttachment) { } else if (attachment instanceof spine.MeshAttachment) {
attachment.computeWorldVertices(this.skeleton as Skeleton, slot, 0, attachment.worldVerticesLength, vertices, 0, 2); attachment.computeWorldVertices(this.skeleton as Skeleton, slot, 0, attachment.worldVerticesLength, vertices, 0, 2);
hullLength = attachment.hullLength; hullLength = attachment.hullLength;
@ -351,7 +349,7 @@ class SpineC3Instance extends globalThis.ISDKWorldInstanceBase {
private isPointInPolygon (vertices: NumberArrayLike, hullLength: number, px: number, py: number) { private isPointInPolygon (vertices: NumberArrayLike, hullLength: number, px: number, py: number) {
if (hullLength < 6) { if (hullLength < 6) {
throw new Error("A polygon must have at least 3 vertices (6 numbers in the array). "); throw new Error("A polygon must have at least 3 vertices (6 numbers in the array).");
} }
let isInside = false; let isInside = false;
@ -489,7 +487,6 @@ class SpineC3Instance extends globalThis.ISDKWorldInstanceBase {
if (!skeleton) return; if (!skeleton) return;
let boundsProvider: SpineBoundsProvider; let boundsProvider: SpineBoundsProvider;
console.log(this.propBoundsProvider);
if (this.propBoundsProvider === "animation-skin") { if (this.propBoundsProvider === "animation-skin") {
const { propSkin, propAnimation } = this; const { propSkin, propAnimation } = this;
if ((propSkin && propSkin.length > 0) || propAnimation) { if ((propSkin && propSkin.length > 0) || propAnimation) {
@ -549,7 +546,7 @@ class SpineC3Instance extends globalThis.ISDKWorldInstanceBase {
this.animationSpeed = speed; this.animationSpeed = speed;
} }
public setAnimationTime (units: 0 | 1, time: number, track: number) { public setAnimationTime (units: 0 | 1, track: number, time: number) {
if (!this.state) return; if (!this.state) return;
const trackEntry = this.state.tracks[track]; const trackEntry = this.state.tracks[track];
@ -672,7 +669,7 @@ class SpineC3Instance extends globalThis.ISDKWorldInstanceBase {
} else { } else {
const customSkin = new spine.Skin(skins.join(",")); const customSkin = new spine.Skin(skins.join(","));
for (const s of skins) { for (const s of skins) {
const skin = skeleton.data.findSkin(s) const skin = skeleton.data.findSkin(s);
if (!skin) throw new Error(`The given skin is not present in the skeleton data: ${s}`); if (!skin) throw new Error(`The given skin is not present in the skeleton data: ${s}`);
customSkin.addSkin(skin); customSkin.addSkin(skin);
} }

View File

@ -1,4 +1,3 @@
const C3 = globalThis.C3; const C3 = globalThis.C3;
C3.Plugins.EsotericSoftware_SpineConstruct3 = class SpineC3 extends globalThis.ISDKPluginBase { C3.Plugins.EsotericSoftware_SpineConstruct3 = class SpineC3 extends globalThis.ISDKPluginBase {

View File

@ -1,5 +1,4 @@
import type { SDKInstanceClass } from "./instance";
import type { SDKInstanceClass } from "./instance.ts";
const C3 = globalThis.C3; const C3 = globalThis.C3;

View File

@ -1,5 +1,3 @@
// / <reference types="editor/sdk" />
import type { AnimationState, AssetLoader, C3Matrix, C3RendererEditor, Skeleton, SpineBoundsProvider, SpineBoundsProviderType, TextureAtlas, } from "@esotericsoftware/spine-construct3-lib"; import type { AnimationState, AssetLoader, C3Matrix, C3RendererEditor, Skeleton, SpineBoundsProvider, SpineBoundsProviderType, TextureAtlas, } from "@esotericsoftware/spine-construct3-lib";
import type { SpineC3PluginType } from "./type"; import type { SpineC3PluginType } from "./type";
@ -45,16 +43,16 @@ class SpineC3PluginInstance extends SDK.IWorldInstanceBase {
* *
* In a Spine C3 GameObject: * In a Spine C3 GameObject:
* - the original size is equivalent to spineBounds that is set selecting the BoundsProvider * - the original size is equivalent to spineBounds that is set selecting the BoundsProvider
* - changing the C3 GameObject size from the editor will scale the skeleton by using skeleton.scaleX/Y * - changing the C3 GameObject size from the editor will scale the skeleton by using a C3Matrix,
* This information is stored into (PROP_SKELETON_OFFSET_SCALE_X and Y) and later passed to the runtime * not the skeleton.scaleX/Y.
* - the origin is position at the skeleton root * - the origin is position at the skeleton root
* *
* positioningBounds allows to offset the position and the size of the C3 GameObject * positioningBounds allows to offset the position and the size of the C3 GameObject
* with the one of the skeleton. When selected it allows to: * with the one of the skeleton. When selected it allows to:
* - move the C3 GameObjects position (visually the rectangle) keeping the skeleton still. * - move the C3 GameObjects position (visually the rectangle) keeping the skeleton still.
* This is obtained by adding an offset to the GameObject position. * This is obtained by adding an offset to the GameObject position.
* This information is stored into (PROP_SKELETON_OFFSET_SCALE_X and Y) and later passed to the runtime * This information is stored into (PROP_BOUNDS_OFFSET_X and Y) and later passed to the runtime
* - scale the C3 GameObjects keeping the skeleton.scaleX/Y as-is. * - scale the C3 GameObjects keeping the skeleton.scaleX/Y as-is, and storing the scale offset in (PROP_SKELETON_OFFSET_SCALE_X and Y).
*/ */
private spineBounds = { private spineBounds = {
x: 0, // determine the origin x (-x/width) x: 0, // determine the origin x (-x/width)
@ -161,7 +159,7 @@ class SpineC3PluginInstance extends SDK.IWorldInstanceBase {
} }
if (id === PLUGIN_CLASS.PROP_BOUNDS_PROVIDER_MOVE) { if (id === PLUGIN_CLASS.PROP_BOUNDS_PROVIDER_MOVE) {
value = value as boolean value = value as boolean;
if (value) { if (value) {
this.positionModePrevX = this._inst.GetX(); this.positionModePrevX = this._inst.GetX();
this.positionModePrevY = this._inst.GetY(); this.positionModePrevY = this._inst.GetY();
@ -292,9 +290,7 @@ class SpineC3PluginInstance extends SDK.IWorldInstanceBase {
message: this.lang('invalid-skins.message', [invalidSkins.join(", ")]) message: this.lang('invalid-skins.message', [invalidSkins.join(", ")])
}); });
const validSkins = split.filter(skinName => availableSkins.includes(skinName)); return split.filter(skinName => availableSkins.includes(skinName)).join(",");
console.log(validSkins);
return validSkins.join(",");
} }
return split.join(","); return split.join(",");
@ -337,7 +333,7 @@ class SpineC3PluginInstance extends SDK.IWorldInstanceBase {
} else { } else {
const customSkin = new spine.Skin(propValue); const customSkin = new spine.Skin(propValue);
for (const s of skins) { for (const s of skins) {
const skin = skeleton.data.findSkin(s) const skin = skeleton.data.findSkin(s);
if (!skin) return; if (!skin) return;
customSkin.addSkin(skin); customSkin.addSkin(skin);
} }
@ -436,7 +432,7 @@ class SpineC3PluginInstance extends SDK.IWorldInstanceBase {
if (this.getErrorsString()) { if (this.getErrorsString()) {
this.spineBoundsInit = false; this.spineBoundsInit = false;
return; return;
}; }
this.spineBoundsInit = true; this.spineBoundsInit = true;
@ -457,7 +453,6 @@ class SpineC3PluginInstance extends SDK.IWorldInstanceBase {
this.propOffsetAngle = 0; this.propOffsetAngle = 0;
this.propScaleX = 1; this.propScaleX = 1;
this.propScaleY = 1; this.propScaleY = 1;
return;
} }
private initBounds () { private initBounds () {
@ -499,13 +494,13 @@ class SpineC3PluginInstance extends SDK.IWorldInstanceBase {
if (boundsType === "animation-skin" && (skins.length === 0 && !animation)) if (boundsType === "animation-skin" && (skins.length === 0 && !animation))
errors.push("Animation/Skin bounds provider requires one between skin and animation to be set."); errors.push("Animation/Skin bounds provider requires one between skin and animation to be set.");
if (Boolean(!animation || skeleton?.data.findAnimation(animation)) === false) if (animation && !skeleton?.data.findAnimation(animation))
errors.push("Not existing animation"); errors.push("Not existing animation");
if (skins.length > 0) { if (skins.length > 0) {
const missingSkins = skins.filter(skin => !skeleton?.data.findSkin(skin)).join(", "); const missingSkins = skins.filter(skin => !skeleton?.data.findSkin(skin)).join(", ");
if (missingSkins) if (missingSkins)
errors.push("Not existing skin(s): ", missingSkins); errors.push(`Not existing skin(s): ${missingSkins}`);
} }
const { width, height } = spineBounds; const { width, height } = spineBounds;
@ -552,7 +547,7 @@ class SpineC3PluginInstance extends SDK.IWorldInstanceBase {
} }
private get propScaleX () { private get propScaleX () {
return this._inst.GetPropertyValue(PLUGIN_CLASS.PROP_SKELETON_OFFSET_SCALE_X) as number return this._inst.GetPropertyValue(PLUGIN_CLASS.PROP_SKELETON_OFFSET_SCALE_X) as number;
} }
private set propScaleX (value: number) { private set propScaleX (value: number) {
@ -560,7 +555,7 @@ class SpineC3PluginInstance extends SDK.IWorldInstanceBase {
} }
private get propScaleY () { private get propScaleY () {
return this._inst.GetPropertyValue(PLUGIN_CLASS.PROP_SKELETON_OFFSET_SCALE_Y) as number return this._inst.GetPropertyValue(PLUGIN_CLASS.PROP_SKELETON_OFFSET_SCALE_Y) as number;
} }
private set propScaleY (value: number) { private set propScaleY (value: number) {
@ -606,7 +601,6 @@ class SpineC3PluginInstance extends SDK.IWorldInstanceBase {
if (spriteType) { if (spriteType) {
const action = await spine.showModal({ const action = await spine.showModal({
darkMode: false, darkMode: false,
maxWidth: 500,
title: this.lang(`${type}.title`), title: this.lang(`${type}.title`),
text: this.lang(`${type}.message`, [collisionSpriteName]), text: this.lang(`${type}.message`, [collisionSpriteName]),
buttons: [ buttons: [
@ -616,7 +610,6 @@ class SpineC3PluginInstance extends SDK.IWorldInstanceBase {
}, },
{ {
text: this.lang(`${type}.buttons.${1}`, [collisionSpriteName]), text: this.lang(`${type}.buttons.${1}`, [collisionSpriteName]),
color: '#d9534f',
value: 'delete' value: 'delete'
} }
] ]
@ -702,7 +695,7 @@ class SpineC3PluginInstance extends SDK.IWorldInstanceBase {
let intlString = globalThis.lang(`${pluginContext}${stringKey}`); let intlString = globalThis.lang(`${pluginContext}${stringKey}`);
interpolate.forEach((toInterpolate, index) => { interpolate.forEach((toInterpolate, index) => {
intlString = intlString.replace(`{${index}}`, `${toInterpolate}`); intlString = intlString.replace(`{${index}}`, `${toInterpolate}`);
}) });
return intlString; return intlString;
} }

View File

@ -42,7 +42,7 @@
}, },
"spine-bounds-provider-group": { "spine-bounds-provider-group": {
"name": "Bounds provider", "name": "Bounds provider",
"desc": "Select the desired buound provider and fill the respective properties." "desc": "Select the desired bound provider and fill the respective properties."
}, },
"spine-bounds-provider": { "spine-bounds-provider": {
"name": "Bounds provider", "name": "Bounds provider",
@ -169,11 +169,6 @@
} }
}, },
"actions": { "actions": {
"do-alert": {
"list-name": "Do alert",
"display-text": "Do alert",
"description": "Do a dummy alert."
},
"set-skin": { "set-skin": {
"list-name": "Set skin", "list-name": "Set skin",
"display-text": "Set skin {0}", "display-text": "Set skin {0}",
@ -201,7 +196,7 @@
"display-text": "Set animation {1} on track {0} with loop {2}", "display-text": "Set animation {1} on track {0} with loop {2}",
"description": "Set animation", "description": "Set animation",
"params": { "params": {
"track": { "track-index": {
"name": "track", "name": "track",
"desc": "track" "desc": "track"
}, },
@ -220,7 +215,7 @@
"display-text": "Add animation {1} on track {0} with loop {2} and delay {3}", "display-text": "Add animation {1} on track {0} with loop {2} and delay {3}",
"description": "Add animation", "description": "Add animation",
"params": { "params": {
"track": { "track-index": {
"name": "track", "name": "track",
"desc": "track" "desc": "track"
}, },
@ -253,7 +248,7 @@
"display-text": "Set empty animation on track {0} with mix duration {1}", "display-text": "Set empty animation on track {0} with mix duration {1}",
"description": "Set an empty animation on a specific track", "description": "Set an empty animation on a specific track",
"params": { "params": {
"track": { "track-index": {
"name": "Track", "name": "Track",
"desc": "Track index" "desc": "Track index"
}, },
@ -268,7 +263,7 @@
"display-text": "Add empty animation on track {0} with mix duration {1} and delay {2}", "display-text": "Add empty animation on track {0} with mix duration {1} and delay {2}",
"description": "Add an empty animation to the animation queue on a specific track", "description": "Add an empty animation to the animation queue on a specific track",
"params": { "params": {
"track": { "track-index": {
"name": "Track", "name": "Track",
"desc": "Track index" "desc": "Track index"
}, },
@ -347,9 +342,17 @@
}, },
"set-animation-time": { "set-animation-time": {
"list-name": "Set animation time", "list-name": "Set animation time",
"display-text": "Set track {2} animation time to {1} ({0})", "display-text": "Set track {0} animation time to {1} ({2})",
"description": "Set the current time of an animation on a track", "description": "Set the current time of an animation on a track",
"params": { "params": {
"track-index": {
"name": "Track",
"desc": "Track index"
},
"time": {
"name": "Time",
"desc": "Time value (in seconds or as a ratio 0.0-1.0)"
},
"units": { "units": {
"name": "Units", "name": "Units",
"desc": "Time units", "desc": "Time units",
@ -357,14 +360,6 @@
"seconds": "Seconds (absolute time)", "seconds": "Seconds (absolute time)",
"ratio": "Ratio (0.0 to 1.0)" "ratio": "Ratio (0.0 to 1.0)"
} }
},
"time": {
"name": "Time",
"desc": "Time value (in seconds or as a ratio 0.0-1.0)"
},
"track": {
"name": "Track",
"desc": "Track index"
} }
} }
}, },
@ -593,16 +588,6 @@
} }
}, },
"expressions": { "expressions": {
"double": {
"description": "Double a number.",
"translated-name": "Double",
"params": {
"number": {
"name": "Number",
"desc": "The number to double."
}
}
},
"slot-attachment": { "slot-attachment": {
"description": "Get the name of the attachment on a slot. Returns empty string if no attachment.", "description": "Get the name of the attachment on a slot. Returns empty string if no attachment.",
"translated-name": "SlotAttachment", "translated-name": "SlotAttachment",

View File

@ -169,11 +169,6 @@
} }
}, },
"actions": { "actions": {
"do-alert": {
"list-name": "显示警告",
"display-text": "显示警告",
"description": "显示一个示例警告。"
},
"set-skin": { "set-skin": {
"list-name": "设置皮肤", "list-name": "设置皮肤",
"display-text": "设置皮肤 {0}", "display-text": "设置皮肤 {0}",
@ -201,7 +196,7 @@
"display-text": "在轨道{0}上设置动画{1},循环{2}", "display-text": "在轨道{0}上设置动画{1},循环{2}",
"description": "设置动画", "description": "设置动画",
"params": { "params": {
"track": { "track-index": {
"name": "轨道", "name": "轨道",
"desc": "轨道" "desc": "轨道"
}, },
@ -220,7 +215,7 @@
"display-text": "在轨道{0}上添加动画{1},循环{2},延迟{3}", "display-text": "在轨道{0}上添加动画{1},循环{2},延迟{3}",
"description": "添加动画", "description": "添加动画",
"params": { "params": {
"track": { "track-index": {
"name": "轨道", "name": "轨道",
"desc": "轨道" "desc": "轨道"
}, },
@ -253,7 +248,7 @@
"display-text": "在轨道{0}上设置空动画,混合时长{1}", "display-text": "在轨道{0}上设置空动画,混合时长{1}",
"description": "在特定轨道上设置空动画", "description": "在特定轨道上设置空动画",
"params": { "params": {
"track": { "track-index": {
"name": "轨道", "name": "轨道",
"desc": "轨道索引" "desc": "轨道索引"
}, },
@ -268,7 +263,7 @@
"display-text": "在轨道{0}上添加空动画,混合时长{1},延迟{2}", "display-text": "在轨道{0}上添加空动画,混合时长{1},延迟{2}",
"description": "将空动画添加到特定轨道的动画队列中", "description": "将空动画添加到特定轨道的动画队列中",
"params": { "params": {
"track": { "track-index": {
"name": "轨道", "name": "轨道",
"desc": "轨道索引" "desc": "轨道索引"
}, },
@ -347,9 +342,17 @@
}, },
"set-animation-time": { "set-animation-time": {
"list-name": "设置动画时间", "list-name": "设置动画时间",
"display-text": "将轨道{2}的动画时间设置为{1}{0}", "display-text": "将轨道{0}的动画时间设置为{1}{2}",
"description": "设置轨道上动画的当前时间", "description": "设置轨道上动画的当前时间",
"params": { "params": {
"track-index": {
"name": "轨道",
"desc": "轨道索引"
},
"time": {
"name": "时间",
"desc": "时间值秒或0.0-1.0的比例)"
},
"units": { "units": {
"name": "单位", "name": "单位",
"desc": "时间单位", "desc": "时间单位",
@ -357,14 +360,6 @@
"seconds": "秒(绝对时间)", "seconds": "秒(绝对时间)",
"ratio": "比例0.0到1.0" "ratio": "比例0.0到1.0"
} }
},
"time": {
"name": "时间",
"desc": "时间值秒或0.0-1.0的比例)"
},
"track": {
"name": "轨道",
"desc": "轨道索引"
} }
} }
}, },
@ -593,16 +588,6 @@
} }
}, },
"expressions": { "expressions": {
"double": {
"description": "将数字翻倍。",
"translated-name": "Double",
"params": {
"number": {
"name": "数字",
"desc": "要翻倍的数字。"
}
}
},
"slot-attachment": { "slot-attachment": {
"description": "获取插槽上的附件名称。如果没有附件则返回空字符串。", "description": "获取插槽上的附件名称。如果没有附件则返回空字符串。",
"translated-name": "SlotAttachment", "translated-name": "SlotAttachment",

View File

@ -1,6 +1,6 @@
import type { SpineBoundsProviderType } from "@esotericsoftware/spine-construct3-lib"; import type { SpineBoundsProviderType } from "@esotericsoftware/spine-construct3-lib";
import type { SDKEditorInstanceClass } from "./instance.ts"; import type { SDKEditorInstanceClass } from "./instance";
const SDK = globalThis.SDK; const SDK = globalThis.SDK;
@ -70,7 +70,7 @@ const PLUGIN_CLASS = class SpineC3Plugin extends SDK.IPluginBase {
this._info.AddFileDependency({ this._info.AddFileDependency({
filename: "c3runtime/spine-construct3-lib.js", filename: "c3runtime/spine-construct3-lib.js",
type: "external-runtime-script" type: "external-runtime-script"
}) });
SDK.Lang.PushContext(".properties"); SDK.Lang.PushContext(".properties");
@ -119,8 +119,6 @@ const PLUGIN_CLASS = class SpineC3Plugin extends SDK.IPluginBase {
}, },
callbackType: "for-each-instance" callbackType: "for-each-instance"
}), }),
]); ]);
SDK.Lang.PopContext(); // .properties SDK.Lang.PopContext(); // .properties
@ -132,4 +130,3 @@ const PLUGIN_CLASS = class SpineC3Plugin extends SDK.IPluginBase {
SDK.Plugins.EsotericSoftware_SpineConstruct3 = PLUGIN_CLASS; SDK.Plugins.EsotericSoftware_SpineConstruct3 = PLUGIN_CLASS;
PLUGIN_CLASS.Register(PLUGIN_ID, PLUGIN_CLASS); PLUGIN_CLASS.Register(PLUGIN_ID, PLUGIN_CLASS);

View File

@ -1,4 +1,3 @@
const SDK = globalThis.SDK; const SDK = globalThis.SDK;
const PLUGIN_CLASS = SDK.Plugins.EsotericSoftware_SpineConstruct3; const PLUGIN_CLASS = SDK.Plugins.EsotericSoftware_SpineConstruct3;
@ -21,7 +20,8 @@ export class SpineC3PluginType extends SDK.ITypeBase {
this.spineLogo = iRenderer.CreateDynamicTexture(image.width, image.height); this.spineLogo = iRenderer.CreateDynamicTexture(image.width, image.height);
iRenderer.UpdateTexture(image, this.spineLogo); iRenderer.UpdateTexture(image, this.spineLogo);
iLayoutView.Refresh(); iLayoutView.Refresh();
}) });
return undefined;
} }
}; };