mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-02-15 03:21:35 +08:00
Changed Updated Bone Pose to Set Bone Pose with the ability of holding a pose, and modif rotation and scale too. Add Release Bone Hold.
This commit is contained in:
parent
f46c3fa67e
commit
3e15e3f688
@ -95,4 +95,15 @@ export class C3Matrix {
|
||||
return this.skeletonToGame(applied.worldX, applied.worldY);
|
||||
}
|
||||
|
||||
public gameToBoneRotation (gameAngleDeg: number, bone: Bone) {
|
||||
return bone.applied.worldToLocalRotation(this.gameToSkeletonRotation(gameAngleDeg)) - 180;
|
||||
}
|
||||
|
||||
public gameToSkeletonRotation (gameAngleDeg: number) {
|
||||
const rad = gameAngleDeg * Math.PI / 180;
|
||||
const sin = Math.sin(rad), cos = Math.cos(rad);
|
||||
const { a, b, c, d } = this;
|
||||
return Math.atan2(a * sin - b * cos, d * cos - c * sin) * (180 / Math.PI);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -392,21 +392,63 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "update-bone-pose",
|
||||
"scriptName": "UpdateBonePose",
|
||||
"id": "set-bone-pose",
|
||||
"scriptName": "SetBonePose",
|
||||
"highlight": false,
|
||||
"params": [
|
||||
{
|
||||
"id": "bone-name",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"id": "mode",
|
||||
"type": "combo",
|
||||
"items": ["local", "game"]
|
||||
},
|
||||
{
|
||||
"id": "apply-mode",
|
||||
"type": "combo",
|
||||
"items": ["once", "hold"]
|
||||
},
|
||||
{
|
||||
"id": "x",
|
||||
"type": "number"
|
||||
"type": "any",
|
||||
"initialValue": ""
|
||||
},
|
||||
{
|
||||
"id": "y",
|
||||
"type": "number"
|
||||
"type": "any",
|
||||
"initialValue": ""
|
||||
},
|
||||
{
|
||||
"id": "rotation",
|
||||
"type": "any",
|
||||
"initialValue": ""
|
||||
},
|
||||
{
|
||||
"id": "scaleX",
|
||||
"type": "any",
|
||||
"initialValue": ""
|
||||
},
|
||||
{
|
||||
"id": "scaleY",
|
||||
"type": "any",
|
||||
"initialValue": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "release-bone-hold",
|
||||
"scriptName": "ReleaseBoneHold",
|
||||
"highlight": false,
|
||||
"params": [
|
||||
{
|
||||
"id": "bone-name",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"id": "reset-to-setup",
|
||||
"type": "boolean"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@ -121,8 +121,21 @@ C3.Plugins.EsotericSoftware_SpineConstruct3.Acts =
|
||||
this.resetSlotColors(slotName);
|
||||
},
|
||||
|
||||
UpdateBonePose (this: SDKInstanceClass, x: number, y: number, boneName: string) {
|
||||
this.updateBonePose(x, y, boneName);
|
||||
SetBonePose (this: SDKInstanceClass, boneName: string, mode: 0 | 1, applyMode: 0 | 1, x: number | string, y: number | string, rotation: number | string, scaleX: number | string, scaleY: number | string) {
|
||||
this.setBonePose(
|
||||
boneName,
|
||||
mode === 0 ? "local" : "game",
|
||||
applyMode === 0 ? "once" : "hold",
|
||||
toNumberOrUndefined(x),
|
||||
toNumberOrUndefined(y),
|
||||
toNumberOrUndefined(rotation),
|
||||
toNumberOrUndefined(scaleX),
|
||||
toNumberOrUndefined(scaleY),
|
||||
);
|
||||
},
|
||||
|
||||
ReleaseBoneHold (this: SDKInstanceClass, boneName: string, resetToSetup: boolean) {
|
||||
this.releaseBoneHold(boneName, resetToSetup);
|
||||
},
|
||||
|
||||
AttachInstanceToBone (this: SDKInstanceClass, uid: number, boneName: string, offsetX: number, offsetY: number, offsetAngle: number) {
|
||||
@ -142,3 +155,19 @@ C3.Plugins.EsotericSoftware_SpineConstruct3.Acts =
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
function toNumberOrUndefined (x: number | string): number | undefined {
|
||||
if (typeof x === "number") {
|
||||
return Number.isFinite(x) ? x : undefined;
|
||||
}
|
||||
|
||||
if (typeof x === "string") {
|
||||
const trimmed = x.trim();
|
||||
if (trimmed === "") return undefined;
|
||||
|
||||
const n = Number(trimmed);
|
||||
return Number.isFinite(n) ? n : undefined;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
@ -27,13 +27,15 @@
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
import type { AnimationState, AssetLoader, Bone, C3Matrix, C3RendererRuntime, Event, NumberArrayLike, Skeleton, Skin, Slot, SpineBoundsProvider, SpineBoundsProviderType, TextureAtlas, } from "@esotericsoftware/spine-construct3-lib";
|
||||
import type { AnimationState, AssetLoader, Bone, BoneLocal, C3Matrix, C3RendererRuntime, Event, NumberArrayLike, Skeleton, Skin, Slot, SpineBoundsProvider, SpineBoundsProviderType, TextureAtlas, } from "@esotericsoftware/spine-construct3-lib";
|
||||
|
||||
const C3 = globalThis.C3;
|
||||
const spine = globalThis.spine;
|
||||
|
||||
spine.Skeleton.yDown = true;
|
||||
|
||||
type BoneOverride = Partial<BoneLocal> & { mode: "game" | "local" };
|
||||
|
||||
class SpineC3Instance extends globalThis.ISDKWorldInstanceBase {
|
||||
propAtlas = "";
|
||||
propSkel = "";
|
||||
@ -87,6 +89,8 @@ class SpineC3Instance extends globalThis.ISDKWorldInstanceBase {
|
||||
|
||||
private boneFollowers = new Map<string, { uid: number, offsetX: number, offsetY: number, offsetAngle: number }>();
|
||||
|
||||
private bonesOverride: Map<Bone, BoneOverride> = new Map();
|
||||
|
||||
private dragHandles = new Set<{
|
||||
slot?: Slot,
|
||||
bone: Bone,
|
||||
@ -180,6 +184,8 @@ class SpineC3Instance extends globalThis.ISDKWorldInstanceBase {
|
||||
|
||||
this.updateHandles(skeleton, matrix);
|
||||
|
||||
this.updateBonesOverride();
|
||||
|
||||
skeleton.updateWorldTransform(physicsMode);
|
||||
|
||||
this.updateBoneFollowers(matrix);
|
||||
@ -846,6 +852,37 @@ class SpineC3Instance extends globalThis.ISDKWorldInstanceBase {
|
||||
}
|
||||
}
|
||||
|
||||
private updateBonesOverride () {
|
||||
for (const [bone, override] of this.bonesOverride) {
|
||||
this.updateBonePoseOnce(bone, override);
|
||||
}
|
||||
}
|
||||
|
||||
private updateBonePoseOnce (bone: Bone, boneOverride: BoneOverride) {
|
||||
const { mode, x, y, rotation, scaleX, scaleY } = boneOverride;
|
||||
if (mode === "game") {
|
||||
if (x !== undefined || y !== undefined) {
|
||||
const locals = this.matrix.gameToBone(
|
||||
x ?? this.matrix.boneToGame(bone).x,
|
||||
y ?? this.matrix.boneToGame(bone).y,
|
||||
bone);
|
||||
bone.pose.x = locals.x;
|
||||
bone.pose.y = locals.y;
|
||||
}
|
||||
|
||||
if (rotation !== undefined) bone.pose.rotation = this.matrix.gameToBoneRotation(rotation, bone);
|
||||
}
|
||||
|
||||
if (mode === "local") {
|
||||
if (x !== undefined) bone.pose.x = x;
|
||||
if (y !== undefined) bone.pose.y = y;
|
||||
if (rotation !== undefined) bone.pose.rotation = rotation;
|
||||
}
|
||||
|
||||
if (scaleX !== undefined) bone.pose.scaleX = scaleX;
|
||||
if (scaleY !== undefined) bone.pose.scaleY = scaleY;
|
||||
}
|
||||
|
||||
/**********/
|
||||
|
||||
/*
|
||||
@ -957,13 +994,29 @@ class SpineC3Instance extends globalThis.ISDKWorldInstanceBase {
|
||||
return point.y;
|
||||
}
|
||||
|
||||
public updateBonePose (c3X: number, c3Y: number, boneName: string) {
|
||||
public setBonePose (boneName: string, mode: "game" | "local", applyMode: "once" | "hold", c3X?: number, c3Y?: number, c3Rotation?: number, scaleX?: number, scaleY?: number) {
|
||||
const bone = this.getBone(boneName);
|
||||
if (!bone) return;
|
||||
if (applyMode === "hold") {
|
||||
const existing = this.bonesOverride.get(bone);
|
||||
this.bonesOverride.set(bone, {
|
||||
mode,
|
||||
x: c3X ?? existing?.x,
|
||||
y: c3Y ?? existing?.y,
|
||||
rotation: c3Rotation ?? existing?.rotation,
|
||||
scaleX: scaleX ?? existing?.scaleX,
|
||||
scaleY: scaleY ?? existing?.scaleY,
|
||||
});
|
||||
} else {
|
||||
this.updateBonePoseOnce(bone, { mode, x: c3X, y: c3Y, rotation: c3Rotation, scaleX, scaleY });
|
||||
}
|
||||
}
|
||||
|
||||
const { x, y } = this.matrix.gameToBone(c3X, c3Y, bone);
|
||||
bone.applied.x = x;
|
||||
bone.applied.y = y;
|
||||
public releaseBoneHold (boneName: string, resetToSetup: boolean) {
|
||||
const bone = this.getBone(boneName);
|
||||
if (!bone) return;
|
||||
this.bonesOverride.delete(bone);
|
||||
if (resetToSetup) bone.setupPose();
|
||||
}
|
||||
|
||||
private getBone (boneName: string | Bone) {
|
||||
|
||||
@ -483,22 +483,65 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"update-bone-pose": {
|
||||
"list-name": "Update bone pose",
|
||||
"display-text": "Set bone {2} position to ({0}, {1})",
|
||||
"description": "Update the pose position of a bone to specific world coordinates.",
|
||||
"set-bone-pose": {
|
||||
"list-name": "Set bone pose",
|
||||
"display-text": "Set bone {0} position ({3}, {4}), rotation {5}, scale ({6}, {7}) in {1} mode, {2}",
|
||||
"description": "Set a bone's pose. Once: applied for the current frame only. Hold: re-applied every frame, merging with previously held values. Use Release bone hold to stop holding. Position and rotation use the selected coordinate space (game or local). Scale is always local.",
|
||||
"params": {
|
||||
"x": {
|
||||
"name": "X",
|
||||
"desc": "X world coordinate"
|
||||
},
|
||||
"y": {
|
||||
"name": "Y",
|
||||
"desc": "Y world coordinate"
|
||||
},
|
||||
"bone-name": {
|
||||
"name": "Bone name",
|
||||
"desc": "Name of the bone to update"
|
||||
},
|
||||
"mode": {
|
||||
"name": "Mode",
|
||||
"desc": "Coordinate space for position and rotation. Local: values are applied directly as the bone's local pose. Game: values are converted from game space.",
|
||||
"items": {
|
||||
"local": "Local",
|
||||
"game": "Game"
|
||||
}
|
||||
},
|
||||
"apply-mode": {
|
||||
"name": "Apply",
|
||||
"desc": "Once: the pose is applied for the current frame only. Hold: the pose is re-applied every frame until released. In hold mode, values left as empty string preserve the previously held value on that axis.",
|
||||
"items": {
|
||||
"once": "Once",
|
||||
"hold": "Hold"
|
||||
}
|
||||
},
|
||||
"x": {
|
||||
"name": "X",
|
||||
"desc": "X position. Game space in game mode, bone-local in local mode. Leave as empty string to keep current value."
|
||||
},
|
||||
"y": {
|
||||
"name": "Y",
|
||||
"desc": "Y position. Game space in game mode, bone-local in local mode. Leave as empty string to keep current value."
|
||||
},
|
||||
"rotation": {
|
||||
"name": "Rotation",
|
||||
"desc": "Rotation in degrees. Game space in game mode, bone-local in local mode. Leave as empty string to keep current value."
|
||||
},
|
||||
"scaleX": {
|
||||
"name": "Scale X",
|
||||
"desc": "Bone's local scale X. Always applied as local scale regardless of mode. Leave as empty string to keep current value."
|
||||
},
|
||||
"scaleY": {
|
||||
"name": "Scale Y",
|
||||
"desc": "Bone's local scale Y. Always applied as local scale regardless of mode. Leave as empty string to keep current value."
|
||||
}
|
||||
}
|
||||
},
|
||||
"release-bone-hold": {
|
||||
"list-name": "Release bone hold",
|
||||
"display-text": "Release hold on bone {0}, reset to setup pose: {1}",
|
||||
"description": "Release a held bone pose, allowing the animation to control the bone again. Optionally resets the bone to its setup pose.",
|
||||
"params": {
|
||||
"bone-name": {
|
||||
"name": "Bone name",
|
||||
"desc": "Name of the bone to release"
|
||||
},
|
||||
"reset-to-setup": {
|
||||
"name": "Reset to setup pose",
|
||||
"desc": "If checked, resets the bone to its setup pose after releasing the hold."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -483,22 +483,65 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"update-bone-pose": {
|
||||
"list-name": "更新骨骼姿势",
|
||||
"display-text": "将骨骼{2}的位置设置为({0}, {1})",
|
||||
"description": "将骨骼的姿势位置更新为特定的世界坐标。",
|
||||
"set-bone-pose": {
|
||||
"list-name": "设置骨骼姿势",
|
||||
"display-text": "设置骨骼{0}位置({3}, {4}),旋转{5},缩放({6}, {7}),{1}模式,{2}",
|
||||
"description": "设置骨骼姿势。一次:仅在当前帧应用。保持:每帧重新应用,与之前保持的值合并。使用释放骨骼保持操作停止保持。位置和旋转使用所选的坐标空间(游戏或本地)。缩放始终为本地值。",
|
||||
"params": {
|
||||
"x": {
|
||||
"name": "X",
|
||||
"desc": "X世界坐标"
|
||||
},
|
||||
"y": {
|
||||
"name": "Y",
|
||||
"desc": "Y世界坐标"
|
||||
},
|
||||
"bone-name": {
|
||||
"name": "骨骼名称",
|
||||
"desc": "要更新的骨骼名称"
|
||||
},
|
||||
"mode": {
|
||||
"name": "模式",
|
||||
"desc": "位置和旋转的坐标空间。本地:值直接作为骨骼本地姿势应用。游戏:值从游戏空间转换。",
|
||||
"items": {
|
||||
"local": "本地",
|
||||
"game": "游戏"
|
||||
}
|
||||
},
|
||||
"apply-mode": {
|
||||
"name": "应用方式",
|
||||
"desc": "一次:仅在当前帧应用姿势。保持:每帧重新应用姿势,直到释放。保持模式下,留为空字符串的值将保留该轴之前的保持值。",
|
||||
"items": {
|
||||
"once": "一次",
|
||||
"hold": "保持"
|
||||
}
|
||||
},
|
||||
"x": {
|
||||
"name": "X",
|
||||
"desc": "X位置。游戏模式下为游戏空间坐标,本地模式下为骨骼本地坐标。设为空字符串保持当前值。"
|
||||
},
|
||||
"y": {
|
||||
"name": "Y",
|
||||
"desc": "Y位置。游戏模式下为游戏空间坐标,本地模式下为骨骼本地坐标。设为空字符串保持当前值。"
|
||||
},
|
||||
"rotation": {
|
||||
"name": "旋转",
|
||||
"desc": "旋转角度(度)。游戏模式下为游戏空间角度,本地模式下为骨骼本地角度。设为空字符串保持当前值。"
|
||||
},
|
||||
"scaleX": {
|
||||
"name": "缩放X",
|
||||
"desc": "骨骼的本地缩放X。无论模式如何,始终作为本地缩放应用。设为空字符串保持当前值。"
|
||||
},
|
||||
"scaleY": {
|
||||
"name": "缩放Y",
|
||||
"desc": "骨骼的本地缩放Y。无论模式如何,始终作为本地缩放应用。设为空字符串保持当前值。"
|
||||
}
|
||||
}
|
||||
},
|
||||
"release-bone-hold": {
|
||||
"list-name": "释放骨骼保持",
|
||||
"display-text": "释放骨骼{0}的保持,重置为初始姿势:{1}",
|
||||
"description": "释放已保持的骨骼姿势,允许动画重新控制该骨骼。可选择将骨骼重置为初始姿势。",
|
||||
"params": {
|
||||
"bone-name": {
|
||||
"name": "骨骼名称",
|
||||
"desc": "要释放的骨骼名称"
|
||||
},
|
||||
"reset-to-setup": {
|
||||
"name": "重置为初始姿势",
|
||||
"desc": "如果勾选,释放保持后将骨骼重置为初始姿势。"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user