- Editado
RegionAttachment offset
Hi,
I want region attachments to follow box2d bodies for ragdoll effect.
The function spRegionAttachment_computeWorldVertices will compute the world position of attachment vertices and for the box2d body, we have these positions in world coordinate so the problem is that we can only change values called "offset" of the attachment.
First : what is exactly "offset" meaning?(they have values "nan" for some cases)
Second : how can we change these "offset" values to get the same body vertices that calculated by spRegionAttachment_computeWorldVertices?
Thanks in advance
The offset
is the distance from the bone's origin to each of the 4 vertices in the bone's coordinate system. It is calculated by updateOffset
:
spine-runtimes/RegionAttachment.c at master
The offset
is not normally modified directly. Instead updateOffset
computes it using the region attachment's width, height, x, y, rotation, scaleX, and scaleY. It also uses the texture atlas region so it can compensate for whitespace stripping.
You may want to have bones follow the box2d bodies, not region attachments.
Thanks Nate
Well, let me explain my problem again.
I want to add ragdoll effect on my character. For achieving this I saw lots of codes that were trying to make bones to follow the bodies, well I tried and it worked. But more precise approach is set the attachment center to center of the body. For make this happen I was thinking about changing the offsets so the world vertices of the attachment will follow the vertices of body's fixture. But you said that it's not a common way to this so I changed my mind. Now i'm trying to calculate the position of bone. But I have some problems doing this :
First of all we should set the rotation of attachment equals to the bone's. (because calculation of positions become much easier)
I have implemented this code:
float rotationOffset = CC_RADIANS_TO_DEGREES(body->GetAngle()) - bone->worldRotation;
attachment->rotation = rotationOffset;
after this piece of code, attachment world rotation will be body's rotation.
Next we should update bone's position. To achieve this we should know the center of the attachment. and that's easy (I think!) : get region attachment world vertices and then adds front side vertices to get center point.
Now after all these we can get transition vector which computed as below :
Vec2 transitionVector(bone->worldX - center.x, bone->worldY - center.y);
And finally we compute position of the bone :
Point bodyWorldPos = skeletonAnimation->getParent()->convertToWorldSpace(body->GetPosition * SCALE_RATIO);
bodyWorldPos = skeletonAnimation->convertToNodeSpace(bodyWorldPos);
Point boneWorldPos(transitionVector.x + bodyWorldPos.x, transitionVector.y + bodyWorldPos.y);
Point boneLocalPos;
spBone_worldToLocal(slot->bone->parent, boneWorldPos.x, boneWorldPos.y,&boneLocalPos.x, &boneLocalPos.y);
bone->x = boneLocalPos.x;
bone->y = boneLocalPos.y;
This is my approach to make attachment follow the body do you see any problem with this code? do you have a better idea for implementing this?
Thanks in advance
You could change the attachment offset, but keep in mind that attachments are stateless and shared across all skeleton instances.
I don't quite understand why you change the attachment rotation. Also, your rotation subtraction may not work if your bones have nonuniform scale. In that case the bone's coordinate system is squashed in one direction, eg when rotated 360 degrees the bone's tip will trace an ellipse, not a circle. If you don't use nonuniform scale then subtraction is fine.
Do you need to set the bone's local rotation? It may be easiest to adjust the world rotation directly:
spBone_rotateWorld(bone, desiredWorldRotation - bone->worldRotation);
You'd have to do that after updateWorldTransform
though, which means child bones won't be affected. In fact, if the physics bodies specify the transform for every bone, you don't even need to call updateWorldTransform
. Instead you could just set the a, b, c, d, worldX, worldY
for every bone. They are const since this isn't something you normally do, though maybe we should un-const them. You can set them like this:
CONST_CAST(float, bone->a) = 123;
The center of a region attachment is the region attachment x, y
, which is in the attachment's bone's local coordinates. If you want the region attachment center in world coordinates:
float centerX, centerY;
spBone_localToWorld(bone, regionAttachment->x, regionAttachment->y, ¢erX, ¢erY);
I'm not sure exactly what you are doing, but it may be helpful to show how to set a bone's local x, y
to a desired world position. A bone is positioned in its parent bone's coordinates, so:
float localX, localY;
spBone_worldToLocal(bone->parent, desiredWorldX, desiredWorldY, &localX, &localY);
bone->x = localX;
bone->y = localY;
Yes Thank you Nate
The position is now in the right place
But I still have some problem with rotation, as you said the bone's tip is tracing an ellipse shape.
What should I do with it?
Thanks in advance
A bone's world rotation is influenced by the nonuniform scale of its ascendants. You need to use the parent bone's world transform to choose a local rotation that matches a desired world rotation:
float sine = sinDeg(desiredWorldRotation), cosine = cosDeg(desiredWorldRotation);
bone.rotation = atan2(
bone.parent.a * sine - bone.parent.c * cosine,
bone.parent.d * cosine - bone.parent.b * sine
) * radiansToDegress;
We should add a worldToLocalRotation
method (which would replace the slightly bogus worldToLocalRotationX
and worldToLocalRotationY
methods).
Note if you change a bone's local rotation, it doesn't change the bone's world transform until Bone updateWorldTransform
is called. If applying this code to one bone, just change the bone and call Skeleton updateWorldTransform
which causes Bone updateWorldTransform
to be called for the changed bone and all descendants. If you are applying this code to a hierarchy of bones, since the code uses the parent bone's world transform (a, b, c, d
), you'll need to process parents before descendants (skeleton.bones
is already ordered parents first), change the bone's local rotation with the above code, call Bone updateWorldTransform
, and only then process descendants of that bone.
However, if you are using constraints, it's slightly more complicated than that. :nerd: Skeleton computes an "update cache" which is a list of bones and constraints sorted in a specific way. Skeleton updateWorldTransform
goes through the list, calling Bone updateWorldTransform
on bones and applying constraints. You would need to do the same, but you'd adjust the local rotation of bones with the code above before calling Bone updateWorldTransform
.
Note that constraints may cause a bone to be in the update cache more than once. However, given that you are driving bone positions and rotations from physics bodies, you may not care if constraints are applied at all.