jakubdev

Hello,

I have 2 spine projects. One contains character and second one contains vfx for this character.

I got "target" bone in on of vfx to move end position of lighting strike. It works fine in Spine I move this bone and it moves the end of lighting strike, but when I exported it to game and try to calculate this bone position everything is messed up.


I tried to get monster "x" and "y" from canvas and use comand temp1.skeleton.findBone('S3-lightning').worldToLocal({x: monsterX, y: monsterY}) , but it gives me strange positions that leads to diffrent places than monster really is.

What is "world" in this context and how I can convert monster "x" and "y" to spine local?



WorldToLocal returns random numbers with same coordinates:
Nemaš dopuštenje za pregledavanje privit(a)ka dodan(og)ih postu.
jakubdev
  • Postovi: 11

Nate

The local, parent, and world axes for translation are described here:
Tools - Spine User Guide: Translate tool

You can switch axes in Spine and look at the translation values in Spine as you move the bone around.

At runtime, if you have a world position (which is relative to the skeleton position) you need to use Bone worldToLocal to translate the world position into the local coordinates of the parent of the bone you want to move. Pseudo code:
bone = skeleton.findBone("lightning")
position = {x: 123, y: 345} // The world position where you want the lightning bone.
bone.parent.worldToLocal(position) // Changes it to the parent's local position.
bone.x = position.x
bone.y = position.y
It wouldn't make sense for a bone to be positioned relative to itself. Bones are positioned relative to their parent, so make sure you use the parent bone. Note that the parent bone will be null if the bone is the root bone.
Avatar
Nate

Nate
  • Postovi: 11942

jakubdev

Hey, Nate

I don't get it right.

I got a viewport that is 844x390. Spine character is {x: 87, y: 361} and from her hand, it comes lighting that needs to be targeted into an enemy that is placed {x: 422, y: 292}.

If world would be viewport. The enemy cordinates would transfer it to local with the below code.

const cords = vfx.skeleton.findBone("S3-lightning").worldToLocal({x: -335, y:0})
vfx.skeleton.findBone("S3-lightning-target").data.x = cords.x
vfx.skeleton.findBone("S3-lightning-target").data.y = cords.y
Does world mean in this context root bone? When I tried:
vfx.skeleton.findBone("root").worldToLocal({x: -335, y:0}) // returns {x: -335: y: 0 }
Can I somehow calculate bone distance relative to viewport?


My take on it:

Blue dots are character/monster and vfx project roots.
Yellow dots are vfx starting point and ending.

I think vfx ending dot should be calculated by enemy.x(in that case 422) - character.x(87) - vfx.x(49) (distance from root) this value should be transfered by worldToLocal of main bone of lighting strike. Cause of lighting strike is 1288px wide by start and it doesn't show that in his bone.x this diffrence will be subtracted from worldOfLocal. I think it should be perfect ,but for some reasons it isn't.


Screenshot how it looks in game.
Nemaš dopuštenje za pregledavanje privit(a)ka dodan(og)ih postu.
jakubdev
  • Postovi: 11

Mario

You've set the x-coordinate on the bone data, not the bone itself. The bone data is the raw data as read from the JSON or .skel file. It serves as a basis for initializing a bone. But once a bone is initialized at runtime, that data is (mostly) never used again.
vfx.skeleton.findBone("S3-lightning-target").data.x = cords.x
should become
vfx.skeleton.findBone("S3-lightning-target").x = cords.x
Avatar
Mario

Mario
  • Postovi: 3089

jakubdev

For some reason this don't work
vfx.skeleton.findBone("S3-lightning-target").x = cords.x

only changing data affects it visually.
vfx.skeleton.findBone("S3-lightning-target").data.x = cords.x
jakubdev
  • Postovi: 11

Nate

The BoneData is the setup pose. All skeleton instances shared the same BoneData, so if you change it, you'll check it for all skeleton instances. It is used when resetting to the setup pose. Also, all animations are relative to the setup pose (BoneData).

When you change x or y on the Bone, you are changing it only for that skeleton instance. Just like after applying an animation (which sets the local transforms), after you've modified a bone's local position you'll need to call Skeleton updateWorldTransform to update the world transform for all the bones (and apply constraints). See here:
Runtime Skeletons - Spine Runtimes Guide: World transforms

Rendering is done using the world transforms. If you change the local transforms and don't call updateWorldTransform, then you won't see your changes rendered. The last computed world transform will be used.

If you apply an animation after changing a Bone position and the animation keys that bone's position, then applying the animation will overwrite your position. It is common to do this every frame:
1) Apply animations.
2) Make changes to bone local transforms.
3) Call Skeleton updateWorldTransform.
4) Render the skeleton.
Avatar
Nate

Nate
  • Postovi: 11942

jakubdev

Hey,

I managed to move bone directly by using bone.x and bone.y instead of bone.data.x and bone.data.y.

Had to set bone to {x:0, y:0 } in spine2d before export. For some reason bones that are already set to predefined values don't move for me in Pixi.spine that utilize yours spine runtimes. This trick managed to fix my Issues... Just for first use. Second use is random.

I tried to after first iteration:
skeleton.bones.map((bone) => bone.updateWorldTransform()
Then apply new cordinates of Another enemy with
cords = skeleton.findBone('bone').worldToLocal(x:cordX, y: cordY)
Skeleton.findBone('targetingBone').x = cords.skeleton.x
Skeleton.findBone('targetingBone').y = cords.skeleton.y

, but this time this is completely off.
jakubdev
  • Postovi: 11

Nate

jakubdev je napisao/la:Had to set bone to {x:0, y:0 } in spine2d before export.
This should not be required. If you are applying an animation that keys the bone, you'll need to move your bone afterward, every frame.

In the last code you showed, is bone the parent bone of targetingBone? Also the code looks wrong for a couple reasons. Try:
var parentBone = skeleton.findBone('bone');
var cords = parentBone.worldToLocal({ x: cordX, y: cordY });
var moveBone = skeleton.findBone('targetingBone');
moveBone.x = cords.x;
moveBone.y = cords.y;
Avatar
Nate

Nate
  • Postovi: 11942

jakubdev

I set up once position of 'bone' and didn't had to set up it everyframe, but can't answer if its keyed as animator did Spine2d stuff.

Yes 'bone' is 'S3-Lighting' and targetingBone is 'S3-Lighting-target'.

Did change code to look more like yours without success of fixing the issue.


I managed to fix it. When I don't change 'S3-Lighting' bone position it calculates it perfectly every time. Video below:
https://drive.google.com/file/d/1DemzYFhTEFXS-gKmUDJRMt2NDmSAEDlJ/view?usp=sharing

I wrote down code that on every onComplete it reverts this value to {x: 0, y:0 } as I want to reuse once preloaded skills spritesheets of spine json and it works perfectly
Video and code below:

https://drive.google.com/file/d/1tLRzMva4WOcyxKgE71_m-rBClHudiETN/view?usp=sharing
preloadedSkills[characterData.data.name].state.onComplete = () => {
if(animationSequence.startingPoint) {
preloadedSkills[characterData.data.name].skeleton.findBone(animationSequence.startingPoint.bone).x = 0;
preloadedSkills[characterData.data.name].skeleton.findBone(animationSequence.startingPoint.bone).y = 0;
}

preloadedSkills[characterData.data.name].skeleton.bones.map((bone) => bone.updateWorldTransform());
}
What I can do to fix this issue and don't need to restart it every onComplete or even better set this position in spine2d to not calculate it here in Spine Runtimes.
Nemaš dopuštenje za pregledavanje privit(a)ka dodan(og)ih postu.
jakubdev
  • Postovi: 11

Nate

jakubdev je napisao/la:can't answer if its keyed as animator did Spine2d stuff.
You can look at the Spine project or JSON data and check if it is keyed. Or you can move the bone every frame, after applying animations.

I can't tell what is going on in the video, not what your code is doing.

You should create a skeleton with two bones and a new Unity project that displays the skeleton, then add code to move a bone. Once you get that working and understand it, you can do the same in your more complex project. It works as I showed above.
Avatar
Nate

Nate
  • Postovi: 11942


Natrag na Runtimes