mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-02-04 14:24:53 +08:00
Add enable collision property and logic. Add AddEmptyAnimation and ClearTrack actions.
This commit is contained in:
parent
24e8a86081
commit
7dd315e2dd
@ -261,7 +261,7 @@ abstract class C3SkeletonRenderer<
|
||||
}
|
||||
}
|
||||
|
||||
this.renderGameObjectBounds(x, y, quad);
|
||||
this.renderGameObjectBounds(x, y, quad, false);
|
||||
}
|
||||
|
||||
protected abstract setColor (r: number, g: number, b: number, a: number): void;
|
||||
@ -274,7 +274,7 @@ abstract class C3SkeletonRenderer<
|
||||
};
|
||||
|
||||
protected abstract renderSkeleton (vertices: Float32Array, uvs: Float32Array, indices: Uint16Array, colors: Float32Array, texture: Texture, blendMode: BlendMode): void;
|
||||
public abstract renderGameObjectBounds (x: number, y: number, quad: DOMQuad | SDK.Quad): void;
|
||||
public abstract renderGameObjectBounds (x: number, y: number, quad: DOMQuad | SDK.Quad, spriteBody: boolean): void;
|
||||
|
||||
protected circle (x: number, y: number, radius: number) {
|
||||
let segments = Math.max(1, (6 * MathUtils.cbrt(radius)) | 0);
|
||||
@ -432,11 +432,14 @@ export class C3RendererEditor extends C3SkeletonRenderer<SDK.Gfx.IWebGLRenderer,
|
||||
this.renderer.DrawMesh(vertices, uvs, indices, colors);
|
||||
};
|
||||
|
||||
public renderGameObjectBounds (x: number, y: number, quad: SDK.Quad): void {
|
||||
public renderGameObjectBounds (x: number, y: number, quad: SDK.Quad, spriteBody: boolean): void {
|
||||
const { renderer, matrix } = this;
|
||||
renderer.SetAlphaBlend();
|
||||
renderer.SetColorFillMode();
|
||||
renderer.SetColorRgba(0.25, 0, 0, 0.25);
|
||||
if (spriteBody)
|
||||
renderer.SetColorRgba(0, 0, 0.25, 0.25);
|
||||
else
|
||||
renderer.SetColorRgba(0.25, 0, 0, 0.25);
|
||||
renderer.LineQuad(quad);
|
||||
renderer.Line(x, y, matrix.tx, matrix.ty);
|
||||
}
|
||||
|
||||
210
spine-ts/spine-construct3/spine-construct3-lib/src/CustomUI.ts
Normal file
210
spine-ts/spine-construct3/spine-construct3-lib/src/CustomUI.ts
Normal file
@ -0,0 +1,210 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated April 5, 2025. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2025, Esoteric Software LLC
|
||||
*
|
||||
* Integration of the Spine Runtimes into software or otherwise creating
|
||||
* derivative works of the Spine Runtimes is permitted under the terms and
|
||||
* conditions of Section 2 of the Spine Editor License Agreement:
|
||||
* http://esotericsoftware.com/spine-editor-license
|
||||
*
|
||||
* Otherwise, it is permitted to integrate the Spine Runtimes into software
|
||||
* or otherwise create derivative works of the Spine Runtimes (collectively,
|
||||
* "Products"), provided that each user of the Products must obtain their own
|
||||
* Spine Editor license and redistribution of the Products in any form must
|
||||
* include this license and copyright notice.
|
||||
*
|
||||
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
|
||||
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
interface ModalButton<T> {
|
||||
text: string;
|
||||
color?: string;
|
||||
value?: T;
|
||||
style?: 'primary' | 'secondary';
|
||||
}
|
||||
|
||||
interface ModalOptions<T> {
|
||||
darkMode: boolean;
|
||||
title: string;
|
||||
text: string;
|
||||
buttons: ModalButton<T>[];
|
||||
maxWidth?: number;
|
||||
}
|
||||
|
||||
export function showModal<T> (options: ModalOptions<T>): Promise<T | undefined> {
|
||||
return new Promise((resolve) => {
|
||||
const { title, text, buttons, darkMode = false } = options;
|
||||
|
||||
const theme = darkMode ? {
|
||||
overlayBg: 'rgba(0, 0, 0, 0.5)',
|
||||
captionBg: 'rgb(71, 71, 71)', // gray9
|
||||
captionText: 'rgb(214, 214, 214)', // gray27
|
||||
contentBg: 'rgb(87, 87, 87)', // gray11
|
||||
contentText: 'rgb(214, 214, 214)', // gray27
|
||||
buttonBg: 'rgb(71, 71, 71)', // gray9
|
||||
buttonText: 'rgb(214, 214, 214)', // gray27
|
||||
buttonBorder: 'rgb(56, 56, 56)', // gray7
|
||||
buttonHoverBg: 'rgb(79, 79, 79)', // gray10
|
||||
closeColor: 'rgb(168, 168, 168)', // gray21
|
||||
} : {
|
||||
overlayBg: 'rgba(0, 0, 0, 0.3)',
|
||||
captionBg: 'rgb(247, 247, 247)', // gray31
|
||||
captionText: 'rgb(94, 94, 94)', // gray12
|
||||
contentBg: 'rgb(232, 232, 232)', // gray29
|
||||
contentText: 'rgb(94, 94, 94)', // gray12
|
||||
buttonBg: 'rgb(222, 222, 222)', // gray28
|
||||
buttonText: 'rgb(94, 94, 94)', // gray12
|
||||
buttonBorder: 'rgb(199, 199, 199)', // gray25
|
||||
buttonHoverBg: 'rgb(214, 214, 214)', // gray27
|
||||
closeColor: 'rgb(94, 94, 94)', // gray12
|
||||
};
|
||||
|
||||
const overlay = document.createElement('div');
|
||||
overlay.style.cssText = `
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: ${theme.overlayBg};
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 999999;
|
||||
font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
|
||||
font-size: 14px;
|
||||
`;
|
||||
|
||||
const dialog = document.createElement('div');
|
||||
dialog.style.cssText = `
|
||||
background: ${theme.contentBg};
|
||||
border-radius: 6px;
|
||||
min-width: 200px;
|
||||
max-width: 550px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
filter: drop-shadow(0 4px 5px rgba(10,10,10,0.35)) drop-shadow(0 2px 1px rgba(10,10,10,0.5));
|
||||
`;
|
||||
|
||||
const caption = document.createElement('div');
|
||||
caption.style.cssText = `
|
||||
background: ${theme.captionBg};
|
||||
color: ${theme.captionText};
|
||||
padding: 6px 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
user-select: none;
|
||||
border-radius: 6px 6px 0 0;
|
||||
`;
|
||||
|
||||
const titleSpan = document.createElement('span');
|
||||
titleSpan.textContent = title;
|
||||
|
||||
const closeBtn = document.createElement('button');
|
||||
closeBtn.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="${theme.closeColor}"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>`;
|
||||
closeBtn.style.cssText = `
|
||||
background: transparent;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
padding: 2px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 3px;
|
||||
opacity: 0.7;
|
||||
`;
|
||||
closeBtn.onmouseover = () => { closeBtn.style.opacity = '1'; };
|
||||
closeBtn.onmouseout = () => { closeBtn.style.opacity = '0.7'; };
|
||||
|
||||
caption.appendChild(titleSpan);
|
||||
caption.appendChild(closeBtn);
|
||||
|
||||
const contents = document.createElement('div');
|
||||
contents.style.cssText = `
|
||||
padding: 12px 14px;
|
||||
color: ${theme.contentText};
|
||||
line-height: 1.4;
|
||||
`;
|
||||
contents.textContent = text;
|
||||
|
||||
const footer = document.createElement('div');
|
||||
footer.style.cssText = `
|
||||
padding: 8px 14px 12px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 6px;
|
||||
`;
|
||||
|
||||
const cleanup = () => {
|
||||
document.removeEventListener('keydown', handleKeyDown);
|
||||
overlay.remove();
|
||||
};
|
||||
|
||||
closeBtn.addEventListener('click', () => {
|
||||
cleanup();
|
||||
resolve(undefined);
|
||||
});
|
||||
|
||||
buttons.forEach((buttonConfig, index) => {
|
||||
const btn = document.createElement('button');
|
||||
btn.textContent = buttonConfig.text;
|
||||
btn.style.cssText = `
|
||||
padding: 4px 14px;
|
||||
border: 1px solid ${theme.buttonBorder};
|
||||
border-radius: 3px;
|
||||
background: ${theme.buttonBg};
|
||||
color: ${theme.buttonText};
|
||||
font-size: 14px;
|
||||
font-family: inherit;
|
||||
cursor: pointer;
|
||||
`;
|
||||
btn.onmouseover = () => { btn.style.background = theme.buttonHoverBg; };
|
||||
btn.onmouseout = () => { btn.style.background = theme.buttonBg; };
|
||||
|
||||
btn.addEventListener('click', () => {
|
||||
cleanup();
|
||||
resolve(buttonConfig.value);
|
||||
});
|
||||
|
||||
footer.appendChild(btn);
|
||||
|
||||
if (index === buttons.length - 1) {
|
||||
setTimeout(() => btn.focus(), 0);
|
||||
}
|
||||
});
|
||||
|
||||
overlay.addEventListener('click', (e) => {
|
||||
if (e.target === overlay) {
|
||||
cleanup();
|
||||
resolve(undefined);
|
||||
}
|
||||
});
|
||||
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
if (e.key === 'Escape') {
|
||||
cleanup();
|
||||
resolve(undefined);
|
||||
}
|
||||
};
|
||||
document.addEventListener('keydown', handleKeyDown);
|
||||
|
||||
dialog.appendChild(caption);
|
||||
dialog.appendChild(contents);
|
||||
dialog.appendChild(footer);
|
||||
overlay.appendChild(dialog);
|
||||
document.body.appendChild(overlay);
|
||||
});
|
||||
}
|
||||
|
||||
@ -3,4 +3,5 @@ export * from './AssetLoader.js';
|
||||
export * from './C3Matrix.js';
|
||||
export * from './C3SkeletonRenderer.js';
|
||||
export * from './C3Texture.js';
|
||||
export * from './CustomUI.js';
|
||||
export * from './SpineBoundsProvider.js';
|
||||
|
||||
@ -172,6 +172,25 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "add-empty-animation",
|
||||
"scriptName": "AddEmptyAnimation",
|
||||
"highlight": false,
|
||||
"params": [
|
||||
{
|
||||
"id": "track",
|
||||
"type": "number"
|
||||
},
|
||||
{
|
||||
"id": "mix-duration",
|
||||
"type": "number"
|
||||
},
|
||||
{
|
||||
"id": "delay",
|
||||
"type": "number"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "set-attachment",
|
||||
"scriptName": "SetAttachment",
|
||||
@ -328,6 +347,17 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "clear-track",
|
||||
"scriptName": "ClearTrack",
|
||||
"highlight": false,
|
||||
"params": [
|
||||
{
|
||||
"id": "track-index",
|
||||
"type": "number"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "set-slot-color",
|
||||
"scriptName": "SetSlotColor",
|
||||
|
||||
@ -33,6 +33,10 @@ C3.Plugins.EsotericSoftware_SpineConstruct3.Acts =
|
||||
this.stop();
|
||||
},
|
||||
|
||||
AddEmptyAnimation (this: SDKInstanceClass, track: number, mixDuration: number, delay: number) {
|
||||
this.addEmptyAnimation(track, mixDuration, delay);
|
||||
},
|
||||
|
||||
SetEmptyAnimation (this: SDKInstanceClass, track: number, mixDuration: number) {
|
||||
this.setEmptyAnimation(track, mixDuration);
|
||||
},
|
||||
@ -81,6 +85,10 @@ C3.Plugins.EsotericSoftware_SpineConstruct3.Acts =
|
||||
this.setTrackMixBlend(mixBlend, trackIndex);
|
||||
},
|
||||
|
||||
ClearTrack (this: SDKInstanceClass, trackIndex: number) {
|
||||
this.clearTrack(trackIndex);
|
||||
},
|
||||
|
||||
SetSlotColor (this: SDKInstanceClass, slotName: string, color: string) {
|
||||
this.setSlotColor(slotName, color);
|
||||
},
|
||||
|
||||
@ -18,8 +18,11 @@ class SpineC3Instance extends globalThis.ISDKWorldInstanceBase {
|
||||
propScaleY = 1;
|
||||
propDebugSkeleton = false;
|
||||
propBoundsProvider: SpineBoundsProviderType = "setup";
|
||||
propEnableCollision = false;
|
||||
|
||||
isFlippedX = false;
|
||||
collisionSpriteInstance?: IWorldInstance;
|
||||
collisionSpriteClassName = "";
|
||||
isPlaying = true;
|
||||
animationSpeed = 1.0;
|
||||
physicsMode = spine.Physics.update;
|
||||
@ -79,16 +82,19 @@ class SpineC3Instance extends globalThis.ISDKWorldInstanceBase {
|
||||
this.propSkin = skinProp === "" ? [] : skinProp.split(",");
|
||||
this.propAnimation = properties[4] as string;
|
||||
this.propDebugSkeleton = properties[5] as boolean;
|
||||
const boundsProviderIndex = properties[6] as number;
|
||||
this.propEnableCollision = properties[6] as boolean;
|
||||
const boundsProviderIndex = properties[7] as number;
|
||||
this.propBoundsProvider = boundsProviderIndex === 0 ? "setup" : "animation-skin";
|
||||
// properties[7] is PROP_BOUNDS_PROVIDER_MOVE
|
||||
this.propOffsetX = properties[8] as number;
|
||||
this.propOffsetY = properties[9] as number;
|
||||
this.propOffsetAngle = properties[10] as number;
|
||||
this.propScaleX = properties[11] as number;
|
||||
this.propScaleY = properties[12] as number;
|
||||
// properties[8] is PROP_BOUNDS_PROVIDER_MOVE
|
||||
this.propOffsetX = properties[9] as number;
|
||||
this.propOffsetY = properties[10] as number;
|
||||
this.propOffsetAngle = properties[11] as number;
|
||||
this.propScaleX = properties[12] as number;
|
||||
this.propScaleY = properties[13] as number;
|
||||
}
|
||||
|
||||
this.collisionSpriteClassName = `${this.objectType.name}_CollisionBody`;
|
||||
|
||||
this.assetLoader = new spine.AssetLoader();
|
||||
this.matrix = new spine.C3Matrix();
|
||||
|
||||
@ -129,6 +135,8 @@ class SpineC3Instance extends globalThis.ISDKWorldInstanceBase {
|
||||
this.width / this.spineBounds.width * this.propScaleX * (this.isFlippedX ? -1 : 1),
|
||||
this.height / this.spineBounds.height * this.propScaleY);
|
||||
|
||||
this.updateCollisionSprite();
|
||||
|
||||
if (this.isPlaying) this.update(this.dt);
|
||||
}
|
||||
|
||||
@ -394,6 +402,11 @@ class SpineC3Instance extends globalThis.ISDKWorldInstanceBase {
|
||||
this.skeleton = undefined;
|
||||
this.state = undefined;
|
||||
this.dragHandleDispose();
|
||||
|
||||
if (this.collisionSpriteInstance) {
|
||||
this.collisionSpriteInstance.destroy();
|
||||
this.collisionSpriteInstance = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/**********/
|
||||
@ -443,11 +456,34 @@ class SpineC3Instance extends globalThis.ISDKWorldInstanceBase {
|
||||
|
||||
this.update(0);
|
||||
|
||||
this.createCollisionSprite();
|
||||
|
||||
this.skeletonLoaded = true;
|
||||
this._trigger(C3.Plugins.EsotericSoftware_SpineConstruct3.Cnds.OnSkeletonLoaded);
|
||||
}
|
||||
}
|
||||
|
||||
private createCollisionSprite () {
|
||||
if (!this.propEnableCollision) return;
|
||||
|
||||
const objectType = (this.runtime.objects as Record<string, IObjectType<IWorldInstance>>)[this.collisionSpriteClassName];
|
||||
if (!objectType)
|
||||
throw new Error(`[Spine] Collision sprite object type "${this.collisionSpriteClassName}" not found`);
|
||||
|
||||
this.collisionSpriteInstance = objectType.createInstance(this.layer.name, this.x, this.y);
|
||||
this.collisionSpriteInstance.setOrigin(this.originX, this.originY);
|
||||
}
|
||||
|
||||
private updateCollisionSprite () {
|
||||
if (!this.collisionSpriteInstance) return;
|
||||
|
||||
this.collisionSpriteInstance.x = this.x;
|
||||
this.collisionSpriteInstance.y = this.y;
|
||||
this.collisionSpriteInstance.width = this.width;
|
||||
this.collisionSpriteInstance.height = this.height;
|
||||
this.collisionSpriteInstance.angleDegrees = this.angleDegrees;
|
||||
}
|
||||
|
||||
private calculateBounds () {
|
||||
const { skeleton } = this;
|
||||
if (!skeleton) return;
|
||||
@ -489,7 +525,11 @@ class SpineC3Instance extends globalThis.ISDKWorldInstanceBase {
|
||||
this.isPlaying = true;
|
||||
}
|
||||
|
||||
public setEmptyAnimation (track: number, mixDuration = 0) {
|
||||
public addEmptyAnimation (track: number, mixDuration: number, delay: number) {
|
||||
this.state?.addEmptyAnimation(track, mixDuration, delay);
|
||||
}
|
||||
|
||||
public setEmptyAnimation (track: number, mixDuration: number) {
|
||||
this.state?.setEmptyAnimation(track, mixDuration);
|
||||
}
|
||||
|
||||
@ -579,6 +619,15 @@ class SpineC3Instance extends globalThis.ISDKWorldInstanceBase {
|
||||
}
|
||||
}
|
||||
|
||||
public clearTrack (track: number) {
|
||||
const { state } = this;
|
||||
if (!state) return;
|
||||
if (track === -1)
|
||||
state.clearTracks();
|
||||
else
|
||||
state.clearTrack(track);
|
||||
}
|
||||
|
||||
private triggerAnimationEvent (eventName: string, track: number, animation: string, event?: Event) {
|
||||
this.triggeredEventTrack = track;
|
||||
this.triggeredEventAnimation = animation;
|
||||
|
||||
@ -141,7 +141,7 @@ class SpineC3PluginInstance extends SDK.IWorldInstanceBase {
|
||||
this.positioningBounds = false;
|
||||
this.resetBounds(true);
|
||||
this.layoutView?.Refresh();
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
if (id === PLUGIN_CLASS.PROP_BOUNDS_PROVIDER_MOVE) {
|
||||
@ -154,7 +154,12 @@ class SpineC3PluginInstance extends SDK.IWorldInstanceBase {
|
||||
this.positionModePrevHeight = this._inst.GetHeight();
|
||||
}
|
||||
this.positioningBounds = value;
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
if (id === PLUGIN_CLASS.PROP_ENABLE_COLLISION) {
|
||||
this.managePropCollision(value as boolean);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@ -203,7 +208,7 @@ class SpineC3PluginInstance extends SDK.IWorldInstanceBase {
|
||||
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);
|
||||
this.skeletonRenderer.renderGameObjectBounds(rectX, rectY, quad, _inst.GetPropertyValue(PLUGIN_CLASS.PROP_ENABLE_COLLISION) as boolean);
|
||||
|
||||
} else {
|
||||
iRenderer.SetAlphaBlend();
|
||||
@ -513,6 +518,58 @@ class SpineC3PluginInstance extends SDK.IWorldInstanceBase {
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
@ -58,6 +58,10 @@
|
||||
"name": "Debug skeleton",
|
||||
"desc": "Draw debug visualization of the skeleton bones"
|
||||
},
|
||||
"spine-enable-collision": {
|
||||
"name": "Enable collision",
|
||||
"desc": "Enable collision detection by creating a Sprite collision body"
|
||||
},
|
||||
"spine-bounds-offset-x": {
|
||||
"name": "Offset X",
|
||||
"desc": "Offset X"
|
||||
@ -249,6 +253,25 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"add-empty-animation": {
|
||||
"list-name": "Add empty animation",
|
||||
"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",
|
||||
"params": {
|
||||
"track": {
|
||||
"name": "Track",
|
||||
"desc": "Track index"
|
||||
},
|
||||
"mix-duration": {
|
||||
"name": "Mix duration",
|
||||
"desc": "Duration in seconds to mix from the current animation to empty"
|
||||
},
|
||||
"delay": {
|
||||
"name": "Delay",
|
||||
"desc": "Delay in seconds before the animation starts"
|
||||
}
|
||||
}
|
||||
},
|
||||
"set-attachment": {
|
||||
"list-name": "Set attachment",
|
||||
"display-text": "Set slot {0} attachment to {1}",
|
||||
@ -418,6 +441,17 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"clear-track": {
|
||||
"list-name": "Clear track(s)",
|
||||
"display-text": "Clear track {0}",
|
||||
"description": "Clear te given track, or all tracks if -1",
|
||||
"params": {
|
||||
"track-index": {
|
||||
"name": "Track index",
|
||||
"desc": "Track index to clear. -1 to clear all tracks."
|
||||
}
|
||||
}
|
||||
},
|
||||
"set-slot-color": {
|
||||
"list-name": "Set slot color",
|
||||
"display-text": "Set slot {0} color to {1}",
|
||||
@ -643,6 +677,18 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"custom_ui": {
|
||||
"showModal": {
|
||||
"spine-enable-collision": {
|
||||
"title": "Delete Sprite Object Type",
|
||||
"text": "This is the last instance of {0}. You can delete the {0} or just disable the Sprite collision body. Deleting the Sprite collision body will remove all related ACEs in the event sheet, if there's any. How do you want to proceed?",
|
||||
"buttons": {
|
||||
"0": "Just disable",
|
||||
"1": "Disable and delete {0}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -58,6 +58,10 @@
|
||||
"name": "调试骨架",
|
||||
"desc": "绘制骨架骨骼的调试可视化"
|
||||
},
|
||||
"spine-enable-collision": {
|
||||
"name": "启用碰撞",
|
||||
"desc": "通过创建精灵碰撞体启用碰撞检测"
|
||||
},
|
||||
"spine-bounds-offset-x": {
|
||||
"name": "X偏移",
|
||||
"desc": "X偏移"
|
||||
@ -249,6 +253,25 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"add-empty-animation": {
|
||||
"list-name": "添加空动画",
|
||||
"display-text": "在轨道{0}上添加空动画,混合时长{1},延迟{2}",
|
||||
"description": "将空动画添加到特定轨道的动画队列中",
|
||||
"params": {
|
||||
"track": {
|
||||
"name": "轨道",
|
||||
"desc": "轨道索引"
|
||||
},
|
||||
"mix-duration": {
|
||||
"name": "混合时长",
|
||||
"desc": "从当前动画混合到空动画的持续时间(秒)"
|
||||
},
|
||||
"delay": {
|
||||
"name": "延迟",
|
||||
"desc": "动画开始前的延迟时间(秒)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"set-attachment": {
|
||||
"list-name": "设置附件",
|
||||
"display-text": "将插槽{0}的附件设置为{1}",
|
||||
@ -418,6 +441,17 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"clear-track": {
|
||||
"list-name": "清除轨道",
|
||||
"display-text": "清除轨道{0}",
|
||||
"description": "清除给定的轨道,如果为-1则清除所有轨道",
|
||||
"params": {
|
||||
"track-index": {
|
||||
"name": "轨道索引",
|
||||
"desc": "要清除的轨道索引。-1表示清除所有轨道。"
|
||||
}
|
||||
}
|
||||
},
|
||||
"set-slot-color": {
|
||||
"list-name": "设置插槽颜色",
|
||||
"display-text": "设置插槽{0}的颜色为{1}",
|
||||
@ -643,6 +677,18 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"custom_ui": {
|
||||
"showModal": {
|
||||
"spine-enable-collision": {
|
||||
"title": "删除精灵对象类型",
|
||||
"text": "这是{0}的最后一个实例。您可以删除{0}或仅禁用精灵碰撞体。删除精灵碰撞体将移除事件表中所有相关的ACE(如果有)。您想如何继续?",
|
||||
"buttons": {
|
||||
"0": "仅禁用",
|
||||
"1": "禁用并删除{0}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -34,6 +34,7 @@ const PLUGIN_CLASS = class SpineC3Plugin extends SDK.IPluginBase {
|
||||
static PROP_SKELETON_OFFSET_SCALE_X = "spine-offset-scale-x";
|
||||
static PROP_SKELETON_OFFSET_SCALE_Y = "spine-offset-scale-y";
|
||||
static PROP_DEBUG_SKELETON = "spine-debug-skeleton";
|
||||
static PROP_ENABLE_COLLISION = "spine-enable-collision";
|
||||
|
||||
static TYPE_BOUNDS_SETUP: SpineBoundsProviderType = "setup";
|
||||
static TYPE_BOUNDS_ANIMATION_SKIN: SpineBoundsProviderType = "animation-skin";
|
||||
@ -80,6 +81,7 @@ const PLUGIN_CLASS = class SpineC3Plugin extends SDK.IPluginBase {
|
||||
new SDK.PluginProperty("text", SpineC3Plugin.PROP_SKIN, ""),
|
||||
new SDK.PluginProperty("text", SpineC3Plugin.PROP_ANIMATION, ""),
|
||||
new SDK.PluginProperty("check", SpineC3Plugin.PROP_DEBUG_SKELETON, false),
|
||||
new SDK.PluginProperty("check", SpineC3Plugin.PROP_ENABLE_COLLISION, false),
|
||||
|
||||
new SDK.PluginProperty("group", SpineC3Plugin.PROP_BOUNDS_PROVIDER_GROUP),
|
||||
new SDK.PluginProperty("combo", SpineC3Plugin.PROP_BOUNDS_PROVIDER, {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user