2D bone animation - point gun at mouse

How is this handled?

https://www.youtube.com/watch?v=egk3yD1w4Wo

It's some form of kinematics which I kinda understand and I have a very solid grip of trig, but I don't know how to start with having both hands attached to a single weapon with each arm moving independently and the torso also moving to accommodate extreme angles. How would one keep the gun completely static (It appears to have an origin/rotation point at the shoulder) while having the arms bend with the hands static on the gun?

Is the gun rotated to point at the mouse and that motion propagates back through the arms? Or does it figure out where the gun needs to be pointed and then rotate the arms to match?

How is the movement handled for the torso? Is the torso only rotated when the arms reach a limit?

etc.

As you can see I have a lot of questions. I'd just like a starting point to begin with, or someone to suggest a way to approach it all. An example or tutorial would be great if you could point me to one.

What I'd like to avoid is using a third party program like DragonBones, Spriter or Spine. I'd like to start at the beginning of the problem to get a fuller understanding of it all.

Thanks!

• Are you asking strictly for theoreticals or do you have a specific application/environment in mind?
– Dan
Feb 21, 2017 at 0:02
• @Dan I'm technically using XNA and C#, but theoretical is great too. Feb 21, 2017 at 3:42
• Well, I do all my model rigging in Blender, and there's lots of tutorials out there that explain different principles and approaches to these things if you're interested. But basically, I'd start by modeling the rig to move how you want, and just constrain the gun position to the hand.
– Dan
Feb 21, 2017 at 3:47

1 Answer

So, I solved it. Turns out I just needed to get away from the problem a bit.

Just in case anyone else wants to replicate this, I got it working and I made a function in C# (Although it should be really easy to transpose it to any other language) that solves 2D arms. Arms being assumed to be made up of two segments with shoulder and elbow joints. All angles are in radians. It could probably be done better but this is what I've got in my test program:

public Vector2 SolveLimb(Vector2 startPosition, Vector2 endPosition, float     length)
{
//startPosition is the where the first joint is. e.g. The shoulder.
//endPosition is where the desired final position is for the last joint. e.g Where the hand should end up
//length is the length of each arm segment. Could use separate values if you want arm segments of     different lengths

//Get the direction vector between the hand and shoulder
Vector2 dir = endPosition - startPosition;
dir.Normalize();

//Measure the distance between hand and shoulder to get length of one side of the triangle
//The other two sides we already know because they're the lengths of the arm segments
float distToEnd = Vector2.Distance(startPosition, endPosition);
distToEnd = MathHelper.Clamp(distToEnd, 0, length * 2);

//Lengths
float ShoulderToHand = distToEnd;
float ShoulderToElbow = ArmTexture.Width;
float ElbowToHand = ArmTexture.Width;

//Use the Law of Cosines to figure out what the two angles are that we want.
//Helpful links: https://www.mathsisfun.com/algebra/trig-solving-sss-triangles.html
//              https://en.wikipedia.org/wiki/Law_of_cosines
//Math.Acos(angle) == Cos^1(angle)
float ElbowA = (Square(ShoulderToElbow) + Square(ElbowToHand) - Square(ShoulderToHand)) / (2 * (ShoulderToElbow * ElbowToHand));
float ElbowAngle = (float)Math.Acos(ElbowA);

float ShoulderA = (Square(ShoulderToElbow) + Square(ShoulderToHand) - Square(ElbowToHand)) / (2 * (ShoulderToElbow * ShoulderToHand));
float ShoulderAngle = (float)Math.Acos(ShoulderA);

//Additional rotation that needs to be made to both arm segments
float rot = (float)Math.Atan2(dir.Y, dir.X);
Vector2 result;

//Return the angles for the Shoulder and Elbow.
//I used a Vector2 to return both numbers at the same time. x = Shoulder, y = Elbow
//Added Pi to the elbow rotation to fix a problem where it was rotated 180 degrees incorrectly.
//Probably messed up somewhere else, but it works
result = new Vector2(ShoulderAngle + rot, ElbowAngle + ShoulderAngle + rot + (float)Math.PI);
return result;
}


The rotation of the torso is really simple. I just did this:

Torso.Rotation = Gun.Rotation * 0.8f;


Hope this helps someone who stumbles across this.