• Runtimes
  • [Cocos2d-x 3.3] How to make bone to follow box2d body

Hi,

I have a problem. When i want physics to take over the control of bones to make ragdoll effect i cant find proper way to update bone positions. Here what i'm currently using, but the bones are way of the box2d bodies.

spBone_worldToLocal(slot->bone, body->GetWorldCenter().x * PTM_RATIO, body->GetWorldCenter().y * PTM_RATIO, &localX, &localY);
slot->bone->x = localX;
slot->bone->y = localY;
slot->bone->rotation = -1 * MATH_RAD_TO_DEG(body->GetAngle());;

What am i doing wrong ? Any help is appreciate!

BR

Related Discussions
...

Did you call spSkeleton_updateWorldTransform on the skeleton after making the modifications? Is animationState also being applied after your code? These are things that might prevent your changes from reflecting on the visible skeleton.

You should also consider storing the spBone* from slot->bone. Those pointer jumps can add up.

Yeah, i'm calling updateWorldTransform on Skeleton. Yeah this is just temporary code to check if it works. Movement of bones looks chaotic with no specific order.

Okay. At least we ruled that out.
I'm not too familiar with the worldToLocal function though, or where localX and localY values are coming from.

I hope Nate can help you.

As i understand it i need to use worldToLocal function to convert my world coordinates ( box2d body position) to local coordinates in Spine Skeleton. localX and localY is just output from worldToLocal function, and calling updateWorldTransform should calculate world x and y based on local x and y. But for some reason its not working as would like it to work. Nate please give me some feedback 🙂


Ok, so i think that i might found the problem, looking at the source code and debugging i misunderstood it completely. Correct me if i'm mistaken, so bone worldX and worldY are relative to SkeletonAnimation node anchor point that is relative to parent node, and that is ok. But, since we cant directly modify worldX and worldY we have to find bone localX and localY that is relative to SRT (and not to SkeletonAnimatioN) from our parent node (where box2d bodies are), if i'm correct. So i thought that worldToLocal would give me this coordinates but apparently it is not working.

The process i'm using is something like this

1) get box2d body coordinates, multiple by PTM
2) find that coordinates relative to SkeletonAnimation node
3) user worldToLocal method to convert coordinates from step 2 to local bones coordinates
4) update bone.

@Nate can you shed some light on this problem.

7 days later

Very sorry I'm so late. 🙁

Pharan wrote

You should also consider storing the spBone* from slot->bone. Those pointer jumps can add up.

Likely the compiler replaces accessing the struct members entirely. slot->bone->x means jump X bytes in memory from slot to bone, and X bytes in memory from bone to x, which probably gets turned into: jump X bytes from slot to x.

As Pharan mentioned, worldToLocal uses the bone world transform, so you need to updateWorldTransform after posing the skeleton before using worldToLocal.

Next, bones are positioned relative to their parent, so you want something like spBone_worldToLocal(slot->bone->parent, .... Be careful, the root bone parent is null (it is positioned in world space, so you don't need worldToLocal for it).

I think with that you should be working!

Ok, so basically here is what i'm currently doing, and its not working 🙂

float localX, localY;
float worldX = body->GetWorldCenter().x * PTM_RATIO - _idleSkeleton->getPosition().x;
float worldY = body->GetWorldCenter().y * PTM_RATIO - _idleSkeleton->getPosition().y;
spBone_worldToLocal(bone->parent, worldX, worldY, &localX, &localY);
bone->x = localX;
bone->y = localY;
bone->rotation = -MATH_RAD_TO_DEG(body->GetAngle());;
spBone_updateWorldTransform(bone);

I'm not sure if i'm getting this right.
Are world coordinates local to skeleton node, or skeleton node parent ?

There are many coordinate systems so it can be a pain to keep them straight. When I say world coordinates I mean skeleton world coordinates. 0,0 in skeleton world coordinates corresponds to the cocos2d-x node position. In other words, skeleton world coordinates are the same as the cocos2d-x skeleton node local coordinates.

First translate your coordinates from screen coordinates (or whatever your body is positioned with) to the cocos2d-x skeleton node local coordinates. Pass the result thru spBone_worldToLocal to go from skeleton world coordinates to the parent bone's local coordinates and use that to position your bone.

Ignore bone rotation until you get position to work. Maybe setup a test where you move the bone to the mouse position so you can more easily see how the positioning reacts.

Hi Nate,

It makes sense, i've managed to translate position and rotation correctly now. I thought that bone x,y are independent of parent bone. Here is solution if anyone needs it.

float localX, localY;
float worldX = body->GetPosition().x * PTM_RATIO;
float worldY = body->GetPosition().y * PTM_RATIO;
Vec2 translateVector(worldX, worldY);
translateVector = convertToWorldSpace(translateVector);
translateVector = _idleSkeleton->convertToNodeSpace(translateVector);
spBone_worldToLocal(bone->parent, translateVector.x, translateVector.y, &localX, &localY);
bone->x = localX;
bone->y = localY;
float angle = MATH_RAD_TO_DEG(body->GetAngle()) - bone->parent->worldRotation;
bone->rotation = angle;
bone->rotationIK = angle;
spBone_updateWorldTransform(bone);


Great, glad you got it working! :happy: I see you also worked out getting rotation correct.

Nate wrote
Pharan wrote

You should also consider storing the spBone* from slot->bone. Those pointer jumps can add up.

Likely the compiler replaces accessing the struct members entirely. slot->bone->x means jump X bytes in memory from slot to bone, and X bytes in memory from bone to x, which probably gets turned into: jump X bytes from slot to x.

Sorry for the tangent. This intrigued me.
C struct members are contiguously laid out in memory but slot and bone are pointer types, which is why the chain needed a -> operator and not just a . operator.
So slot->bone->x means "jump X bytes then use the value in that spot to find the block of memory i want, then jump Y bytes and then use the value at that spot to find the next block of memory i want". It can't be compiled away because spBone is allocated with a NEW which returns a (non-relative) memory address that's not available at compile time.... or is it?

@milos Good job getting it to work though!

@Nate Yeah, as soon as i read your previous post, i saw where i was making a mistake. I couldn't find anywhere in documentation or forums about bone coordinate system and relationship with parent bones (or i was too lazy,blind..)

BTW Thanks!

@Pharan, only way to really know is to disassemble. It could be optimized away, I don't know for sure.

2 years later

Hi,
I have the same problem with these local variables.
I did the same procedure that mentioned above but some weird things happened.
Here is the code :

float localX, localY;
float worldX = _body->GetPosition().x * SCALE_RATIO;
float worldY = _body->GetPosition().y * SCALE_RATIO;
cocos2d::Vec2 translateVector(worldX, worldY);
translateVector = skeletonAnimation->getParent()->convertToWorldSpace(translateVector);
translateVector = skeletonAnimation->convertToNodeSpace(translateVector);
spBone_worldToLocal(slot->bone->parent, translateVector.x,
      translateVector.y, &localX, &localY);
slot->bone->x = localX;
slot->bone->y = localY;
spBone_updateWorldTransform(slot->bone);
cocos2d::Point temp;
temp.x = slot->bone->worldX;
temp.y = slot->bone->worldY;
debugSprite->setPosition(temp);

I have also add some sprite for debugging. The debugSprite is the child of skeletonAnimation and I changed it's position after updating bone; the weird thing is that the position of debugSprite is exactly the position that we expected but the position of bone is not what we want....well for a bone that it's parent was root the things went perfectly but for others the position had some minor offset (about 50 pixels) from their real position and the offset became larger with the time.
My apologies for questioning long time after this topic.