2
\$\begingroup\$

I have a problem with rotation of my hero in 2D space. The hero is made of body parts such as: head, arms etc. Every part is a separate sprite. When I want to rotate the hero, I have to rotate all of its parts.

I know the hero position, and I save the parts positions as:

head = (heroPosition.x+20, heroPosition.y+20)

In other words I know the positions of all parts while the hero was not rotated. After rotation I know only the hero position and the rotation angle(Grades).

How can I calculate the new position for each part?

Problem ilustration

\$\endgroup\$
3
\$\begingroup\$

This problem is called Forward Kinematics. To solve this problem in general, I recommend creating what is called a Kinematic Chain, or Kinematic Tree. To do this, you will need knowledge of a 2D rotation matrix, or transformation matrix.

A 2D 3x3 transformation matrix is defined as:

H = [xx, xy, tx;
     yx, yy, ty;
      0,  0,  1];

In this case, [xx, yx] represents the x axis of the transformation, [xy, yy] represents the y axis, and [tx, ty] represents the translation of the transformation. For instance, the transformation:

    H = [1, 0, 1;
         0, 1, 0.5;
         0, 0, 1];

translates a part 1 unit in the x direction, and 0.5 units in the y direction. To transform any vector, just pre-mulltiply it by the transformation matrix H. A special case of the transformation matrix is the rotation matrix. In 2D, a pure rotation can be represented as:

H = [cos(theta), -sin(theta), 0;
     sin(theta),  cos(theta), 0;
              0,           0, 1];

where theta is the rotation value in Radians.

Now, to implement a Kinematic tree, you just need to do this:

// Represents a rigid link in a kinematic chain
class Link
{
  public:
    // All links have exactly one or zero parent links. 
    // The "root" of the tree has no parent (NULL)
    Link* parent;
    // All links can have as many children as needed
    std::vector<Link*> children;

    // Represents the transform from the parent link to
    // this link.
    Matrix3x3 localTransform;
    // Reprsents the transform from the world to this link.
    Matrix3x3 globalTransform;


    // Computes the global transform of this link with respect
    // to the world. Then computes the global transforms of all
    // children.
    void ComputeTransformsRecursive()
    {
       // If we're the root link, the global transform is the local transform.
       if(!parent) globalTransform = localTransform;
       else
       {
           // Otherwise, the global transform is the parent's global transform
           // times our transform (matrix multiplication)
           globalTransform = parent->globalTransform * localTransform;


           // Now recursively compute the global transform for each of the children
           for (Link* link : children)
           {
               link->ComputeTransformsRecursive();
           }
       }
    }

}

To build the kinematic tree for your character, you just do this:

// The body is the base link, and starts at (0, 0) with 0 rotation.
Link* bodyLink = new Link();
bodyLink->parent = NULL;
bodyLink->localTransform = Matrix3x3::Identity();

// The left arm, relative to the body, is 50 pixels to the right and 10 up,
// with no local rotation.
Link* leftArm = new Link();
leftArm->parent = bodyLink;
leftArm->localTransform = Matrix3x3::CreateTranslation(50, 10);
bodyLink.children.push_back(leftArm);


// The right arm is 50 right and 10 down.
Link* rightArm= new Link();
rightArm->parent = bodyLink;
rightArm->localTransform = Matrix3x3::CreateTranslation(50, -10);
bodyLink.children.push_back(rightArm);

// The head is 10 right and 0 down
Link* head = new Link();
head->parent = bodyLink;
head->localTransform = Matrix3x3::CreateTranslation(10, 0);
bodyLink.children.push_back(head);

You can rotate/translate the entire character just by moving around the body link:

bodyLink->localTransform.translation = Vector2(xPosition, yPosition);
bodyLink->localTransform.rotation = theta;

Then, to update, you just need to do this:

bodyLink->ComputeTransformsRecursive();

From there, you automatically get the global transforms (their rotations and translations):

Matrix3x3 headTransform     = head->globalTransform;
Matrix3x3 leftArmTransform  = leftArm->globalTransform;
Matrix3x3 rightArmTransform = rightArm->globalTransform;

You can also attach/detach nodes to different parts of the body to do things like make the character hold a gun, for instance.

\$\endgroup\$
1
\$\begingroup\$

You need to move each part to 0,0 then rotate them, then move them back. Without doing this they just rotate around 20 units fron your character

\$\endgroup\$
0
\$\begingroup\$

For each part, it need to have an offset to the hero location. Eg, +20, +20. Its correct position when hero rotation is r is heroPosition.x + rotated(offsetX, r) and same for y. See http://en.wikipedia.org/wiki/Rotation_matrix for more info.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.