mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-02-04 14:24:53 +08:00
612 lines
19 KiB
TypeScript
612 lines
19 KiB
TypeScript
// / <reference types="editor/sdk" />
|
|
|
|
import type { AnimationState, AssetLoader, C3Matrix, C3RendererEditor, Skeleton, SpineBoundsProvider, SpineBoundsProviderType, TextureAtlas, } from "@esotericsoftware/spine-construct3-lib";
|
|
import type { SpineC3PluginType } from "./type";
|
|
|
|
const SDK = globalThis.SDK;
|
|
|
|
const PLUGIN_CLASS = SDK.Plugins.EsotericSoftware_SpineConstruct3;
|
|
|
|
let spine: typeof globalThis.spine;
|
|
|
|
class SpineC3PluginInstance extends SDK.IWorldInstanceBase {
|
|
private layoutView?: SDK.UI.ILayoutView;
|
|
private renderer?: SDK.Gfx.IWebGLRenderer;
|
|
|
|
private textureAtlasSID = -1;
|
|
private textureAtlas?: TextureAtlas;
|
|
private textureAtlasBasePath?: string;
|
|
|
|
skeleton?: Skeleton;
|
|
state?: AnimationState;
|
|
skins: string[] = [];
|
|
animation?: string;
|
|
|
|
private assetLoader: AssetLoader;
|
|
private skeletonRenderer?: C3RendererEditor;
|
|
private matrix: C3Matrix;
|
|
|
|
// position mode
|
|
private positioningBounds = false;
|
|
private positionModePrevX = 0;
|
|
private positionModePrevY = 0;
|
|
private positionModePrevAngle = 0;
|
|
private positionModePrevWidth = 0;
|
|
private positionModePrevHeight = 0;
|
|
|
|
/*
|
|
* C3 GameObjects have two sizes:
|
|
* - the original size that is determined implementing GetOriginalWidth/GetOriginalHeight
|
|
* - the current size that is set using SetSize or SetWidth/SetHeight
|
|
* The ratio between this two size determines the C3 GameObjects scale.
|
|
*
|
|
* The origin is by default in the center and set using SetOrigin;
|
|
* it's usually moved in the Image Editor, but that's disable with Spine C3 GameObjects.
|
|
*
|
|
* In a Spine C3 GameObject:
|
|
* - 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
|
|
* This information is stored into (PROP_SKELETON_OFFSET_SCALE_X and Y) and later passed to the runtime
|
|
* - the origin is position at the skeleton root
|
|
*
|
|
* positioningBounds allows to offset the position and the size of the C3 GameObject
|
|
* with the one of the skeleton. When selected it allows to:
|
|
* - move the C3 GameObjects position (visually the rectangle) keeping the skeleton still.
|
|
* 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
|
|
* - scale the C3 GameObjects keeping the skeleton.scaleX/Y as-is.
|
|
*/
|
|
private spineBounds = {
|
|
x: 0, // determine the origin x (-x/width)
|
|
y: 0, // determine the origin y (-y/height)
|
|
width: 200, // determine the original width (and the origin x)
|
|
height: 200, // determine the original height (and the origin y)
|
|
};
|
|
private spineBoundsInit = false;
|
|
|
|
// errors
|
|
private errorTextureAtlas?: string;
|
|
private errorSkeleton?: string;
|
|
private errorTextC3?: SDK.Gfx.IWebGLText;
|
|
|
|
constructor (sdkType: SDK.ITypeBase, inst: SDK.IWorldInstance) {
|
|
super(sdkType, inst);
|
|
|
|
if (!spine) spine = globalThis.spine;
|
|
spine.Skeleton.yDown = true;
|
|
|
|
this.assetLoader = new spine.AssetLoader();
|
|
this.matrix = new spine.C3Matrix();
|
|
}
|
|
|
|
Release () {
|
|
this.textureAtlas?.dispose();
|
|
}
|
|
|
|
OnCreate () {
|
|
this._inst.SetPropertyValue(PLUGIN_CLASS.PROP_BOUNDS_PROVIDER_MOVE, false);
|
|
}
|
|
|
|
OnPlacedInLayout () {
|
|
this.OnMakeOriginalSize();
|
|
}
|
|
|
|
async OnPropertyChanged (id: string, value: EditorPropertyValueType) {
|
|
if (id === PLUGIN_CLASS.PROP_ATLAS) {
|
|
this.textureAtlasSID = -1;
|
|
this.textureAtlas?.dispose();
|
|
this.textureAtlas = undefined;
|
|
this.skins = [];
|
|
this.skeleton = undefined;
|
|
this.spineBoundsInit = false;
|
|
this.resetBounds();
|
|
this.layoutView?.Refresh();
|
|
return;
|
|
}
|
|
|
|
if (id === PLUGIN_CLASS.PROP_SKELETON) {
|
|
this.errorSkeleton = undefined;
|
|
this.skeleton = undefined;
|
|
this.skins = [];
|
|
this.spineBoundsInit = false;
|
|
this.resetBounds();
|
|
this.layoutView?.Refresh();
|
|
return;
|
|
}
|
|
|
|
if (id === PLUGIN_CLASS.PROP_LOADER_SCALE) {
|
|
this.skeleton = undefined;
|
|
this.skins = [];
|
|
this.spineBoundsInit = false;
|
|
this.resetBounds();
|
|
this.layoutView?.Refresh();
|
|
return;
|
|
}
|
|
|
|
if (id === PLUGIN_CLASS.PROP_SKIN) {
|
|
this.skins = [];
|
|
this.setSkin();
|
|
this.layoutView?.Refresh();
|
|
return;
|
|
}
|
|
|
|
if (id === PLUGIN_CLASS.PROP_ANIMATION) {
|
|
this.setAnimation();
|
|
this.layoutView?.Refresh();
|
|
return;
|
|
}
|
|
|
|
if (id === PLUGIN_CLASS.PROP_BOUNDS_PROVIDER) {
|
|
this._inst.SetPropertyValue(PLUGIN_CLASS.PROP_BOUNDS_PROVIDER_MOVE, false);
|
|
this.positioningBounds = false;
|
|
this.resetBounds(true);
|
|
this.layoutView?.Refresh();
|
|
return;
|
|
}
|
|
|
|
if (id === PLUGIN_CLASS.PROP_BOUNDS_PROVIDER_MOVE) {
|
|
value = value as boolean
|
|
if (value) {
|
|
this.positionModePrevX = this._inst.GetX();
|
|
this.positionModePrevY = this._inst.GetY();
|
|
this.positionModePrevAngle = this._inst.GetAngle();
|
|
this.positionModePrevWidth = this._inst.GetWidth();
|
|
this.positionModePrevHeight = this._inst.GetHeight();
|
|
}
|
|
this.positioningBounds = value;
|
|
return;
|
|
}
|
|
|
|
if (id === PLUGIN_CLASS.PROP_ENABLE_COLLISION) {
|
|
this.managePropCollision(value as boolean);
|
|
return;
|
|
}
|
|
}
|
|
|
|
Draw (iRenderer: SDK.Gfx.IWebGLRenderer, iDrawParams: SDK.Gfx.IDrawParams) {
|
|
this.layoutView ||= iDrawParams.GetLayoutView();
|
|
this.renderer ||= iRenderer;
|
|
|
|
this.loadAtlas();
|
|
this.loadSkeleton();
|
|
this.initBounds();
|
|
|
|
const { _inst, skeleton } = this;
|
|
const errorsString = this.getErrorsString();
|
|
if (skeleton && this.textureAtlas && !errorsString) {
|
|
this.setAnimation();
|
|
this.setSkin();
|
|
|
|
const rectX = _inst.GetX();
|
|
const rectY = _inst.GetY();
|
|
|
|
if (this.positioningBounds) {
|
|
const rectAngle = _inst.GetAngle();
|
|
|
|
this.propOffsetX += this.positionModePrevX - rectX;
|
|
this.propOffsetY += this.positionModePrevY - rectY;
|
|
this.propOffsetAngle = this.propOffsetAngle + this.positionModePrevAngle - rectAngle;
|
|
|
|
this.positionModePrevX = rectX;
|
|
this.positionModePrevY = rectY;
|
|
this.positionModePrevAngle = rectAngle;
|
|
|
|
const currentWidth = _inst.GetWidth();
|
|
const currentHeight = _inst.GetHeight();
|
|
if (currentWidth !== this.positionModePrevWidth || currentHeight !== this.positionModePrevHeight) {
|
|
this.propScaleX = this.propScaleX * this.positionModePrevWidth / currentWidth;
|
|
this.propScaleY = this.propScaleY * this.positionModePrevHeight / currentHeight;
|
|
this.positionModePrevWidth = currentWidth;
|
|
this.positionModePrevHeight = currentHeight;
|
|
}
|
|
}
|
|
|
|
this.update(0);
|
|
this.skeletonRenderer ||= new spine.C3RendererEditor(iRenderer, this.matrix);
|
|
const color = _inst.GetColor();
|
|
this.skeletonRenderer.draw(skeleton, [color.getR(), color.getG(), color.getB()], color.getA() * _inst.GetOpacity());
|
|
const quad = _inst.GetQuad();
|
|
if (_inst.GetPropertyValue(PLUGIN_CLASS.PROP_DEBUG_SKELETON) as boolean)
|
|
this.skeletonRenderer.drawDebug(skeleton, rectX, rectY, quad);
|
|
this.skeletonRenderer.renderGameObjectBounds(rectX, rectY, quad, _inst.GetPropertyValue(PLUGIN_CLASS.PROP_ENABLE_COLLISION) as boolean);
|
|
|
|
} else {
|
|
iRenderer.SetAlphaBlend();
|
|
|
|
const logo = (this._sdkType as SpineC3PluginType).getSpineLogo(iRenderer, this.layoutView);
|
|
if (logo) {
|
|
iRenderer.SetColorRgba(1, 1, 1, errorsString ? 0.25 : 1);
|
|
iRenderer.SetTexture(logo);
|
|
} else {
|
|
iRenderer.SetColorFillMode();
|
|
iRenderer.SetColorRgba(0.25, 0, 0, 0.25);
|
|
}
|
|
const quad = _inst.GetQuad();
|
|
iRenderer.Quad(quad);
|
|
|
|
if (errorsString) {
|
|
const webglText = this.getErrorTextC3(iRenderer, this.layoutView);
|
|
webglText.SetSize(_inst.GetWidth(), _inst.GetHeight(), this.layoutView.GetZoomFactor());
|
|
webglText.SetText(errorsString);
|
|
|
|
const texture = webglText.GetTexture();
|
|
if (!texture) return;
|
|
|
|
iRenderer.SetColorRgba(1, 1, 1, 1);
|
|
iRenderer.SetTexture(texture);
|
|
iRenderer.Quad3(quad, webglText.GetTexRect());
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
private setAnimation () {
|
|
const propValue = this._inst.GetPropertyValue(PLUGIN_CLASS.PROP_ANIMATION) as string;
|
|
this.animation = propValue === "" ? undefined : propValue;
|
|
}
|
|
|
|
private setSkin () {
|
|
const { skeleton } = this;
|
|
if (!skeleton) return;
|
|
|
|
const propValue = this._inst.GetPropertyValue(PLUGIN_CLASS.PROP_SKIN) as string;
|
|
|
|
const skins = propValue === "" ? [] : propValue.split(",");
|
|
this.skins = skins;
|
|
|
|
if (skins.length === 0) {
|
|
skeleton.setSkin(null);
|
|
} else if (skins.length === 1) {
|
|
const skinName = skins[0];
|
|
const skin = skeleton.data.findSkin(skinName);
|
|
if (!skin) return;
|
|
skeleton.setSkin(skins[0]);
|
|
} else {
|
|
const customSkin = new spine.Skin(propValue);
|
|
for (const s of skins) {
|
|
const skin = skeleton.data.findSkin(s)
|
|
if (!skin) return;
|
|
customSkin.addSkin(skin);
|
|
}
|
|
skeleton.setSkin(customSkin);
|
|
}
|
|
|
|
skeleton.setupPose();
|
|
this.update(0);
|
|
}
|
|
|
|
private async loadAtlas () {
|
|
if (!this.renderer) return;
|
|
this.checkAtlasTexturesValidity();
|
|
|
|
const propValue = this._inst.GetPropertyValue(PLUGIN_CLASS.PROP_ATLAS) as number;
|
|
if (this.textureAtlasSID === propValue && !this.errorTextureAtlas) return;
|
|
this.textureAtlasSID = propValue;
|
|
|
|
const result = await this.assetLoader.loadAtlasEditor(propValue, this._inst, this.renderer)
|
|
.catch((error: Error) => {
|
|
this.errorTextureAtlas = error.message;
|
|
this.layoutView?.Refresh();
|
|
});
|
|
if (!result) return;
|
|
|
|
this.errorTextureAtlas = undefined;
|
|
this.textureAtlas = result.textureAtlas;
|
|
this.textureAtlasBasePath = result.basePath;
|
|
this.layoutView?.Refresh();
|
|
}
|
|
|
|
private async loadSkeleton () {
|
|
if (!this.renderer || !this.textureAtlas) return;
|
|
if (this.skeleton) return;
|
|
|
|
const propValue = this._inst.GetPropertyValue(PLUGIN_CLASS.PROP_SKELETON) as number;
|
|
const loaderScale = this._inst.GetPropertyValue(PLUGIN_CLASS.PROP_LOADER_SCALE) as number;
|
|
const skeletonData = await this.assetLoader.loadSkeletonEditor(propValue, this.textureAtlas, loaderScale, this._inst)
|
|
.catch((error) => {
|
|
if (!this.errorSkeleton) this.layoutView?.Refresh();
|
|
this.errorSkeleton = `${error.message}\n. Likely Atlas and Skeleton are not corresponding.`;
|
|
});
|
|
if (!skeletonData) return;
|
|
|
|
this.errorSkeleton = undefined;
|
|
this.skeleton = new spine.Skeleton(skeletonData);
|
|
const animationStateData = new spine.AnimationStateData(skeletonData);
|
|
this.state = new spine.AnimationState(animationStateData);
|
|
|
|
this.setSkin();
|
|
this.setAnimation();
|
|
this.update(0);
|
|
|
|
this.layoutView?.Refresh();
|
|
}
|
|
|
|
private setBoundsFromBoundsProvider () {
|
|
const propValue = this._inst.GetPropertyValue(PLUGIN_CLASS.PROP_BOUNDS_PROVIDER) as SpineBoundsProviderType;
|
|
|
|
let spineBoundsProvider: SpineBoundsProvider;
|
|
if (propValue === "animation-skin") {
|
|
const { skins, animation } = this;
|
|
if ((skins && skins.length > 0) || animation) {
|
|
spineBoundsProvider = new spine.SkinsAndAnimationBoundsProvider(animation, skins);
|
|
} else {
|
|
return false;
|
|
}
|
|
} else if (propValue === "setup") {
|
|
spineBoundsProvider = new spine.SetupPoseBoundsProvider();
|
|
} else {
|
|
spineBoundsProvider = new spine.AABBRectangleBoundsProvider(0, 0, 100, 100);
|
|
}
|
|
|
|
this.spineBounds = spineBoundsProvider.calculateBounds(this);
|
|
|
|
return true;
|
|
}
|
|
|
|
public resetBounds (keepScale = false) {
|
|
const { _inst } = this;
|
|
|
|
if (!this.skeleton || !this.textureAtlas) {
|
|
_inst.SetSize(200, 200);
|
|
this.spineBounds.width = 200;
|
|
this.spineBounds.height = 200;
|
|
this.propOffsetX = 0;
|
|
this.propOffsetY = 0;
|
|
this.propOffsetAngle = 0;
|
|
this.propScaleX = 1;
|
|
this.propScaleY = 1;
|
|
return;
|
|
}
|
|
|
|
const { width: oldBoundsWidth, height: oldBoundsHeight } = this.spineBounds;
|
|
this.setBoundsFromBoundsProvider();
|
|
if (this.getErrorsString()) {
|
|
this.spineBoundsInit = false;
|
|
return;
|
|
};
|
|
|
|
this.spineBoundsInit = true;
|
|
|
|
let { x, y, width, height } = this.spineBounds;
|
|
_inst.SetOrigin(-x / width, -y / height);
|
|
|
|
if (keepScale) {
|
|
width *= (_inst.GetWidth() / oldBoundsWidth) * this.propScaleX;
|
|
height *= (_inst.GetHeight() / oldBoundsHeight) * this.propScaleY;
|
|
}
|
|
|
|
_inst.SetSize(width, height);
|
|
_inst.SetXY(_inst.GetX() + this.propOffsetX, _inst.GetY() + this.propOffsetY);
|
|
_inst.SetAngle(_inst.GetAngle() + this.propOffsetAngle);
|
|
|
|
this.propOffsetX = 0;
|
|
this.propOffsetY = 0;
|
|
this.propOffsetAngle = 0;
|
|
this.propScaleX = 1;
|
|
this.propScaleY = 1;
|
|
return;
|
|
}
|
|
|
|
private initBounds () {
|
|
if (this.spineBoundsInit || !this.skeleton) return;
|
|
|
|
const matchesOldBounds = this._inst.GetWidth() === this.spineBounds.width && this._inst.GetHeight() === this.spineBounds.height;
|
|
|
|
this.setBoundsFromBoundsProvider();
|
|
|
|
const { x, y, width, height } = this.spineBounds;
|
|
this._inst.SetOrigin(-x / width, -y / height);
|
|
|
|
if (matchesOldBounds) this._inst.SetSize(width, height);
|
|
|
|
this.spineBoundsInit = true;
|
|
}
|
|
|
|
private checkAtlasTexturesValidity () {
|
|
const atlasSid = this._inst.GetPropertyValue(PLUGIN_CLASS.PROP_ATLAS) as number;
|
|
if (atlasSid === -1) return;
|
|
|
|
const { textureAtlas, textureAtlasBasePath } = this;
|
|
if (!textureAtlas || textureAtlasBasePath === undefined) return;
|
|
|
|
for (const page of textureAtlas.pages) {
|
|
if (!this._inst.GetProject().GetProjectFileByExportPath(textureAtlasBasePath + page.name)) {
|
|
this.skeleton = undefined;
|
|
this.OnPropertyChanged(PLUGIN_CLASS.PROP_ATLAS, atlasSid);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
private getErrorsString () {
|
|
const { skins, animation, spineBounds, skeleton } = this;
|
|
const errors = [];
|
|
|
|
const boundsType = this._inst.GetPropertyValue(PLUGIN_CLASS.PROP_BOUNDS_PROVIDER) as SpineBoundsProviderType;
|
|
if (boundsType === "animation-skin" && (skins.length === 0 && !animation))
|
|
errors.push("Animation/Skin bounds provider requires one between skin and animation to be set.");
|
|
|
|
if (Boolean(!animation || skeleton?.data.findAnimation(animation)) === false)
|
|
errors.push("Not existing animation");
|
|
|
|
if (skins.length > 0) {
|
|
const missingSkins = skins.filter(skin => !skeleton?.data.findSkin(skin)).join(", ");
|
|
if (missingSkins)
|
|
errors.push("Not existing skin(s): ", missingSkins);
|
|
}
|
|
|
|
const { width, height } = spineBounds;
|
|
if (width <= 0 || height <= 0)
|
|
errors.push("A bounds cannot have negative dimensions. This might happen when the setup pose is empty. Try to set a skin and the Animation/Skin bounds provider.");
|
|
|
|
if (this.errorTextureAtlas) errors.push(this.errorTextureAtlas);
|
|
if (this.errorSkeleton) errors.push(this.errorSkeleton);
|
|
|
|
if (errors.length === 0) return "";
|
|
|
|
return errors.join("\n");
|
|
}
|
|
private getErrorTextC3 (iRenderer: SDK.Gfx.IWebGLRenderer, iLayoutView: SDK.UI.ILayoutView) {
|
|
if (this.errorTextC3) return this.errorTextC3;
|
|
|
|
const errorTextC3 = iRenderer.CreateRendererText()
|
|
this.errorTextC3 = errorTextC3;
|
|
this.errorTextC3.SetFontSize(12);
|
|
this.errorTextC3.SetColorRgb(1, 0, 0);
|
|
this.errorTextC3.SetTextureUpdateCallback(() => iLayoutView.Refresh());
|
|
return this.errorTextC3;
|
|
}
|
|
|
|
private update (delta: number) {
|
|
const { state, skeleton } = this;
|
|
|
|
if (!skeleton || !state) return;
|
|
|
|
state.update(delta);
|
|
skeleton.update(delta);
|
|
state.apply(skeleton);
|
|
|
|
const actualScaleX = (this._inst.GetWidth() / this.spineBounds.width) * this.propScaleX;
|
|
const actualScaleY = (this._inst.GetHeight() / this.spineBounds.height) * this.propScaleY;
|
|
|
|
this.matrix.update(
|
|
this._inst.GetX() + this.propOffsetX,
|
|
this._inst.GetY() + this.propOffsetY,
|
|
this._inst.GetAngle() + this.propOffsetAngle,
|
|
actualScaleX,
|
|
actualScaleY);
|
|
skeleton.updateWorldTransform(spine.Physics.update);
|
|
}
|
|
|
|
private get propScaleX () {
|
|
return this._inst.GetPropertyValue(PLUGIN_CLASS.PROP_SKELETON_OFFSET_SCALE_X) as number
|
|
}
|
|
|
|
private set propScaleX (value: number) {
|
|
this._inst.SetPropertyValue(PLUGIN_CLASS.PROP_SKELETON_OFFSET_SCALE_X, value);
|
|
}
|
|
|
|
private get propScaleY () {
|
|
return this._inst.GetPropertyValue(PLUGIN_CLASS.PROP_SKELETON_OFFSET_SCALE_Y) as number
|
|
}
|
|
|
|
private set propScaleY (value: number) {
|
|
this._inst.SetPropertyValue(PLUGIN_CLASS.PROP_SKELETON_OFFSET_SCALE_Y, value);
|
|
}
|
|
|
|
private get propOffsetX () {
|
|
return this._inst.GetPropertyValue(PLUGIN_CLASS.PROP_BOUNDS_OFFSET_X) as number;
|
|
}
|
|
|
|
private set propOffsetX (value: number) {
|
|
this._inst.SetPropertyValue(PLUGIN_CLASS.PROP_BOUNDS_OFFSET_X, value);
|
|
}
|
|
|
|
private get propOffsetY () {
|
|
return this._inst.GetPropertyValue(PLUGIN_CLASS.PROP_BOUNDS_OFFSET_Y) as number;
|
|
}
|
|
|
|
private set propOffsetY (value: number) {
|
|
this._inst.SetPropertyValue(PLUGIN_CLASS.PROP_BOUNDS_OFFSET_Y, value);
|
|
}
|
|
|
|
private get propOffsetAngle () {
|
|
return this._inst.GetPropertyValue(PLUGIN_CLASS.PROP_BOUNDS_OFFSET_ANGLE) as number;
|
|
}
|
|
|
|
private set propOffsetAngle (value: number) {
|
|
this._inst.SetPropertyValue(PLUGIN_CLASS.PROP_BOUNDS_OFFSET_ANGLE, value);
|
|
}
|
|
|
|
private async managePropCollision (value: boolean) {
|
|
const project = this._inst.GetProject();
|
|
const objectType = this._inst.GetObjectType();
|
|
const objectTypeName = objectType.GetName();
|
|
const collisionSpriteName = `${objectTypeName}_CollisionBody`;
|
|
const spriteType = project.GetObjectTypeByName(collisionSpriteName);
|
|
|
|
const type = PLUGIN_CLASS.PROP_ENABLE_COLLISION;
|
|
if (!value) {
|
|
for (const inst of objectType.GetAllInstances()) {
|
|
if (inst !== this._inst && inst.GetPropertyValue(PLUGIN_CLASS.PROP_ENABLE_COLLISION) as boolean) return;
|
|
}
|
|
if (spriteType) {
|
|
const action = await spine.showModal({
|
|
darkMode: false,
|
|
maxWidth: 500,
|
|
title: this.lang(`showModal.${type}.title`),
|
|
text: this.lang(`showModal.${type}.text`, [collisionSpriteName]),
|
|
buttons: [
|
|
{
|
|
text: this.lang(`showModal.${type}.buttons.${0}`),
|
|
value: 'disable'
|
|
},
|
|
{
|
|
text: this.lang(`showModal.${type}.buttons.${1}`, [collisionSpriteName]),
|
|
color: '#d9534f',
|
|
value: 'delete'
|
|
}
|
|
]
|
|
});
|
|
|
|
switch (action) {
|
|
case 'disable': break;
|
|
case 'delete': spriteType.Delete(); break;
|
|
default: this._inst.SetPropertyValue(PLUGIN_CLASS.PROP_ENABLE_COLLISION, true); break;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (!spriteType) await project.CreateObjectType("Sprite", collisionSpriteName);
|
|
}
|
|
|
|
public async selectAnimation () {
|
|
console.log('[Spine] Select animation dialog called');
|
|
}
|
|
|
|
private lang (stringKey: string, interpolate: (string | number)[] = []): string {
|
|
const pluginContext = "plugins.esotericsoftware_spineconstruct3.custom_ui.";
|
|
let intlString = globalThis.lang(`${pluginContext}${stringKey}`);
|
|
interpolate.forEach((toInterpolate, index) => {
|
|
intlString = intlString.replace(`{${index}}`, `${toInterpolate}`);
|
|
})
|
|
return intlString;
|
|
}
|
|
|
|
GetTexture () {
|
|
const image = this.GetObjectType().GetImage();
|
|
return super.GetTexture(image);
|
|
}
|
|
|
|
IsOriginalSizeKnown () {
|
|
return true;
|
|
}
|
|
|
|
GetOriginalWidth () {
|
|
return this.spineBounds.width;
|
|
}
|
|
|
|
GetOriginalHeight () {
|
|
return this.spineBounds.height;
|
|
}
|
|
|
|
OnMakeOriginalSize () {
|
|
this._inst.SetSize(this.spineBounds.width, this.spineBounds.height);
|
|
}
|
|
|
|
HasDoubleTapHandler () {
|
|
return false;
|
|
}
|
|
|
|
OnDoubleTap () { }
|
|
|
|
LoadC2Property (_name: string, _valueString: string) {
|
|
return false;
|
|
}
|
|
};
|
|
|
|
PLUGIN_CLASS.Instance = SpineC3PluginInstance;
|
|
|
|
export type { SpineC3PluginInstance as SDKEditorInstanceClass };
|