Creation of a Ragdoll with Flash part 2: Constraints

It’s time to introduce another feature involved in Ragdoll creation.
In part 1 you saw Verlet integration, now it’s time to see what is a constraint.

In mathematics, a constraint is a condition that a solution to an optimization problem must satisfy. So basically a constraint is a condition that must be respected. In our ragdoll world, this condition (or those conditions) refers to a distance from a particle to another, no matter if particles are moving or not.

Let’s imagine a real world example: a steel, rigid, unbendable, unbreakable, undeformable stick. This stick has two important points, one at its beginning and one at its end. Being the stick rigid, unbreakable and so on, we can say that the distance between those two points will always be the same, no matter what happens to the stick.

This is a constraint: the condition that says that the distance between start and end points must always be the same.

This is very easy to satisfy when the points do not move: we simply place two points at a given distance and the game is done.

Real problems come when there is a reason (any reason) why points could move, and we have to find a way to move points and satisfy constraints at the same time.

The first thing we are going to do is coding a function where we pass two points (in our case two movieclips) and the distance (in pixels) between them. The function will output the points in a way that satisfies the distance.

1
2
3
4
5
6
7
8
9
10
11
function constraint(particle1, particle2, distance) {
	dist_x = particle1._x-particle2._x;
	dist_y = particle1._y-particle2._y;
	actual_distance = Math.sqrt(dist_x*dist_x+dist_y*dist_y);
	actual_angle = -Math.atan2(dist_x, dist_y);
	error = actual_distance-distance;
	particle1._x += (error/2)*Math.sin(actual_angle);
	particle1._y -= (error/2)*Math.cos(actual_angle);
	particle2._x -= (error/2)*Math.sin(actual_angle);
	particle2._y += (error/2)*Math.cos(actual_angle);
}

Line 1: Function declaration: particle1 and particle2 are the movieclips, and distance is the distance in pixels between movieclips (the constraint).

Line 2: Calculating the horizontal distance between particles

Line 3: Calculating the vertical distance between particles

Line 4: Calculating the distance between particles using the Pythagorean Theorem

Line 5: Calculating the angle between particles using Trigonometry. You can find more information about trigonometry in this tutorial

Line 6: Defining the error: the error is the difference between the actual distance as calculated in line 4 and the constraint distance passed in line 1

Now that we know the error, we have to fix it. How can we fix it? Simply moving the points in order to have an error = 0

Line 7: Calculating the new _x position of the first particle using trigonometry

Line 8: Same thing with the _y position

Lines 9-10: Same thing with the second particle

Notice that in this function the error is splitted in two equal parts (error/2): this means we are going to move both particles in the same amount of pixels in order to fix the error. This is the default and more intuitive way of moving particles, but we’ll see later how in some cases we have to distribute the error in other ways.

Now that this principle is clear (I hope so), let’s see a real Flash example.

I created only one movieclip with a circle in it, and linked as “part” (just like in the verlet tutorial, but this time I have only one movieclip)

Then, this is the actionscript:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
_root.createEmptyMovieClip("line", 1);
_root.attachMovie("part", "part", 2, {_x:240, _y:165});
_root.attachMovie("part", "part2", 3, {_x:240, _y:165});
_root.onEnterFrame = function() {
	part._x = _root._xmouse;
	part._y = _root._ymouse;
	line.clear();
	line.lineStyle(3, 0xff0000);
	constraint(part, part2, 100);
};
function constraint(particle1, particle2, distance) {
	dist_x = particle1._x-particle2._x;
	dist_y = particle1._y-particle2._y;
	actual_distance = Math.sqrt(dist_x*dist_x+dist_y*dist_y);
	actual_angle = -Math.atan2(dist_x, dist_y);
	error = actual_distance-distance;
	particle1._x += (error/2)*Math.sin(actual_angle);
	particle1._y -= (error/2)*Math.cos(actual_angle);
	particle2._x -= (error/2)*Math.sin(actual_angle);
	particle2._y += (error/2)*Math.cos(actual_angle);
	line.moveTo(particle1._x, particle1._y);
	line.lineTo(particle2._x, particle2._y);
}

Line 1: Creation of an empty movie clip called “line”, where I will draw the line connecting the particles

Line 2: Placing the first particle on the movie

Line 3: Placing the second particle on the movie. Please notice that both particles are placed at _x:240 and _y:165. This means that particles are in the same point, so their distance is zero. You will see how the script will place both particle in order to satisfy constraints.

Line 4: Beginning of the main routine to be executed at every frame

Lines 5-6: Placing the first particle in the mouse position. This is just to let you move the particle and see what happens

Line 7: Clearing the line movieclip

Line 8: Defining the drawing style as a red 3 pixels thick pencil. To know more about drawing styles refer to this tutorial

Line 9: Call the constraint function between particles 1 and 2 with a distance of 100

Lines 21-22: This is the only change to the constraint function you saw before, to draw a line between particles

Here you are: you can move the draggable particle as you want, and the constraint is always respected.

This was a very easy case, because we were moving a particle while the other did not move by itself, just followed the dragged particle. I’ll demonstrate how this function will work even in an environment where all particles have their own lives, but now it’s time to talk about error distribution.

In the previous function, once determined the error, it was distributed half in the first particle and half in the second particle, so particles position were adjusted by the same amount of pixels.

This limits our function, because in real life there should be fixed particles or particles that move less than others.

To manage error distribution, we need to change a bit the main function

1
2
3
4
5
6
7
8
9
10
11
12
function constraint(particle1, particle2, distance, error_on_1st) {
	error_on_2nd = 1-error_on_1st;
	dist_x = particle1._x-particle2._x;
	dist_y = particle1._y-particle2._y;
	actual_distance = Math.sqrt(dist_x*dist_x+dist_y*dist_y);
	actual_angle = -Math.atan2(dist_x, dist_y);
	error = actual_distance-distance;
	particle1._x += (error*error_on_1st)*Math.sin(actual_angle);
	particle1._y -= (error*error_on_1st)*Math.cos(actual_angle);
	particle2._x -= (error*error_on_2nd)*Math.sin(actual_angle);
	particle2._y += (error*error_on_2nd)*Math.cos(actual_angle);
}

Line 1: The function has a new parameter: error_on_1st. This is a value from 0 to 1 that represents the amount of error distribution to the 1st particle. 0 means there is no error distribution (all adjustments are done by moving the 2nd particle while the first remains fixed), 1 means there is full error distribution (all adjustments are done moving the 1st particle while the second remains fixed) and all values in between balance the error distribution more in a particle or in another. The previous function worked as if error_on_1st was 0.5

Line 2: Obviously, error_on_2nd, the error distribution on the second particle, is determined by 1-error_on_1st

Lines 8-11: Particle adjustment are done according to error_on_1st and error

Thanks to this new feature, we can have fixed particles that respect constraint as in this example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
_root.createEmptyMovieClip("line", 1);
_root.attachMovie("part", "part", 2, {_x:240, _y:165});
_root.attachMovie("part", "part2", 3, {_x:240, _y:165});
_root.onEnterFrame = function() {
	part._x = _root._xmouse;
	part._y = _root._ymouse;
	line.clear();
	line.lineStyle(3, 0xff0000);
	constraint(part, part2, 100,1);
};
function constraint(particle1, particle2, distance, error_on_1st) {
	error_on_2nd = 1-error_on_1st;
	dist_x = particle1._x-particle2._x;
	dist_y = particle1._y-particle2._y;
	actual_distance = Math.sqrt(dist_x*dist_x+dist_y*dist_y);
	actual_angle = -Math.atan2(dist_x, dist_y);
	error = actual_distance-distance;
	particle1._x += (error*error_on_1st)*Math.sin(actual_angle);
	particle1._y -= (error*error_on_1st)*Math.cos(actual_angle);
	particle2._x -= (error*error_on_2nd)*Math.sin(actual_angle);
	particle2._y += (error*error_on_2nd)*Math.cos(actual_angle);
	line.moveTo(particle1._x, particle1._y);
	line.lineTo(particle2._x, particle2._y);
}

Line 9: Calling the constraint in this way, will make particle error adjust only on one particle. The result is a fixed particle in the centre of the movie and the other one following the mouse but respecting the constraint.

As you can imagine, an accurate error balance can create different gameplays starting from the same actionscript.

Now, let’s introduce more particles… we’ll create a triangle

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
_root.createEmptyMovieClip("line", 1);
_root.attachMovie("part", "part", 2, {_x:240, _y:165});
_root.attachMovie("part", "part2", 3, {_x:240, _y:165});
_root.attachMovie("part", "part3", 4, {_x:240, _y:165});
_root.onEnterFrame = function() {
	part._x = _xmouse;
	part._y = _ymouse;
	line.clear();
	line.lineStyle(3, 0xff0000);
	constraint(part, part2, 100, 0.5);
	constraint(part2, part3, 100, 0.5);
	constraint(part, part3, 100, 0.5);
};
function constraint(particle1, particle2, distance, error_on_1st) {
	error_on_2nd = 1-error_on_1st;
	dist_x = particle1._x-particle2._x;
	dist_y = particle1._y-particle2._y;
	actual_distance = Math.sqrt(dist_x*dist_x+dist_y*dist_y);
	actual_angle = -Math.atan2(dist_x, dist_y);
	error = actual_distance-distance;
	particle1._x += (error*error_on_1st)*Math.sin(actual_angle);
	particle1._y -= (error*error_on_1st)*Math.cos(actual_angle);
	particle2._x -= (error*error_on_2nd)*Math.sin(actual_angle);
	particle2._y += (error*error_on_2nd)*Math.cos(actual_angle);
	line.moveTo(particle1._x, particle1._y);
	line.lineTo(particle2._x, particle2._y);
}

There is not much to say about this script, except three things:

1) If you move the mouse quickly, you will see how the triangle deforms. This will be fixed later

2) This is a very dirty way to create actionscript and manage constraints… imagine a complex figure (a ragdoll?) created in this way… we need to store all constraints in an array

3) I designed the constraints in a way they can be satisfied at the same time. A complex environment can have constraints that cannot be satisfied so we will handle non-fixed errors

We’ll see this, and more, in next tutorial. Meanwhile drawing your ragdoll on a paper and measuring the right constraints, you should be able to design some kind of ragdoll

Then, download the source code and give me feedback

Rate this post: 1 Star2 Stars3 Stars4 Stars5 Stars (11 votes, average: 5.00 out of 5)
Loading ... Loading ...
If you found this post useful, please consider a small donation.
» Flash Templates provided by Template Monster are pre-made web design products developed using Flash technology.
They can be easily customized to meet the unique requirements of your project.

32 Responses to “Creation of a Ragdoll with Flash part 2: Constraints”

  1. hansa on August 26th, 2007 10:49 pm

    thanks

  2. Keith Peters on August 26th, 2007 10:54 pm

    Math.atan2 should be called with y, x distances, not x, y. And x components are usually mapped to cos, while y components mapped to sin. In this case these two things seem to cancel each other out so you get the same results, but if you were to draw out the angles and distances and relationships, it would get pretty confusing.

    When doing this, I’ve generally created a Point object and a Stick object. The stick is composed of two points and the constraint between them. Points are stored in a points array and sticks are stored in a sticks array. Both are updated each frame with for loops. I don’t see it as particularly messy, but I’m interested in what you’ve come up with.

  3. Jon B on August 29th, 2007 2:20 am

    is it possible to do something like the first example of this page but constrain the “non-mouse” controlled point (the pushed/pulled point) to a path (bezier or similar)? I’m interested in the dragging dragging of things on along path.

    J

  4. Luiz Fernando on September 4th, 2007 6:37 pm

    Cool. In opensources flash physics engine, like flade, the script is similar, but more powerfull, that is, can handle collisions and everything more.

  5. Eblup on September 10th, 2007 3:36 am

    hey iam trying to make a mace i can swing around
    or a sword and all i have is youre code written
    in moive clips. can you help?

    +++++

    onClipEvent (load) {
    dx = 0;
    dy = 0;
    dis = 0;
    angle = 0;
    Max = 100;
    }

    onClipEvent (enterFrame) {
    dx = _root.dot._x-_x;
    dy = _root.dot._y-_y;
    dis = Math.sqrt((dx*dx)+(dy*dy));
    angle = ((Math.atan2(dx,dy)/(Math.PI/180))-180);
    if (dis!=max) {
    _x = _root.dot._x+100*Math.sin(angle*(Math.PI/180));
    _y = _root.dot._y+100*Math.cos(angle*(Math.PI/180));
    }
    }

  6. Anchises on September 11th, 2007 9:56 pm

    Emanuele, thanks for these tutorials. They are very clear and easy to follow. Looking forward to the next installment!

  7. Kit Evans on September 13th, 2007 12:44 pm

    These are fantastic – the only place I’ve found a tutorial that doesn’t use stupidly long words! Please update soon! Cheers :)

  8. damen on September 14th, 2007 9:54 am

    sweet!!!! thanx!

  9. Questo on October 10th, 2007 2:33 am

    how do you make a stickfigure

  10. N00b on October 23rd, 2007 4:38 pm

    coooooooooooooolnesssssssssssss

  11. Hawdon on November 23rd, 2007 3:39 am

    I would like to make gravity so that my ragdoll is beeing pulled down so that it looks less fake… I dont know how so could some1 tell me. Please!

  12. emanuel on January 14th, 2008 3:34 am

    hi nice tut when r u going to make the third part i always wandered hoe to make a good ragdoll

  13. Orava on January 20th, 2008 2:07 am

    Is it possible to add gravity to those balls?
    (now i got 1 ball (o) in the middle, holding up 3 other balls by sticks (\|/) but i don’t seem to get gravity involved)

    O O O
    \|/
    o
    so the 3 other balls (O) would go like
    o
    /|\
    O O O

    i hope my little drawings made it more clear

  14. PLEASE... on March 10th, 2008 3:43 pm

    …submit the next tutorial! PLEASE!

  15. Nathan on April 25th, 2008 11:35 pm

    Are you going to make the third step, I would really like it!

  16. Spider on April 29th, 2008 4:17 pm

    Hi… i have a little problem…
    My circles are going in a strange line when I put 3 joins…(on 2 its working)
    plz contact me :(

    onClipEvent (load) {
    var vecx = _x-_root.ball2._x;
    var vecy = _y-_root.ball2._y;
    var dist1 = Math.sqrt(vecx*vecx+vecy*vecy);
    vecx = _root.ball2._x – _root.ball3._x;
    vecy = _root.ball2._y – _root.ball3._y;
    var dist2 = Math.sqrt(vecx*vecx+vecy*vecy);
    vecx = _root.ball3._x – _x;
    vecy = _root.ball3._y – _y;
    var dist3 = Math.sqrt(vecx*vecx+vecy*vecy);
    function react(m1:MovieClip, m2:MovieClip, distance) {
    vecx = m1._x-m2._x;
    vecy = m1._y-m2._y;
    var tmpdist = Math.sqrt(vecx*vecx+vecy*vecy);
    var error = tmpdist-distance;
    if (error != 0) {
    var angle = Math.atan2(vecy, vecx);
    m1._x = m1._x+Math.cos(angle)*error/2;
    m1._y = m1._y+Math.sin(angle)*error/2;
    m2._x = m2._x+Math.cos(angle)*error/2;
    m2._y = m2._y+Math.sin(angle)*error/2;
    }
    }
    }
    onClipEvent (mouseMove) {
    _x = _root._xmouse;
    _y = _root._ymouse;
    react(this,_root.ball2,dist1);
    react(this,_root.ball3,dist3);
    react(_root.ball3,_root.ball2,dist2);
    }

  17. Spider on April 29th, 2008 4:27 pm

    solved! :)

    m1._x = m1._x+Math.cos(angle)*error/2;
    m1._y = m1._y+Math.sin(angle)*error/2;
    m2._x = m2._x+Math.cos(angle)*error/2;
    m2._y = m2._y+Math.sin(angle)*error/2;

    =

    m1._x = m1._x-Math.cos(angle)*error/2;
    m1._y = m1._y-Math.sin(angle)*error/2;
    m2._x = m2._x+Math.cos(angle)*error/2;
    m2._y = m2._y+Math.sin(angle)*error/2;

  18. Niall Lavigne on May 9th, 2008 8:03 pm

    Hey, has the next part of this article become vapourware? Are you ever going to continue it? I’m really interested by ragdoll physics, and I was very dissappointed to find that a 3rd part had never been made :(
    Please make it!

  19. shp on July 5th, 2008 2:02 am

    you can remove the sin, cos and atan to speed up the processing

    function constraint(particle1, particle2, distance) {
    dist_x = particle1._x-particle2._x;
    dist_y = particle1._y-particle2._y;
    actual_distance = Math.sqrt(dist_x*dist_x+dist_y*dist_y);

    error = actual_distance-distance;

    particle1._x -= (error/2)*dist_x/actual_distance;
    particle1._y -= (error/2)*dist_y/actual_distance;

    particle2._x += (error/2)*dist_x/actual_distance;
    particle2._y += (error/2)*dist_y/actual_distance;
    line.moveTo(particle1._x, particle1._y);
    line.lineTo(particle2._x, particle2._y);
    }

  20. Xodus on July 6th, 2008 9:00 am

    I would really like the 3rd part…..

  21. jamie on July 10th, 2008 9:03 am

    Please publish a 3rd part to this tutorial ; ) its great and i would love to see the next steps.

  22. rolf on July 27th, 2008 12:37 pm

    Searched your site with google for a while now… but it seems the third article keeps missing. Would love to see it, as your style to write and explain makes it easy to understand and learn.

    Thanks so far :)

  23. Tijn on August 1st, 2008 9:35 pm

    Hi,

    Really like the scripts, but I was wondering if they could be converted to act like a train for example. I have build 2 rollercoaster simulations, but want to improve them with some good scripts. I think I need the ragdoll maths to do it, car1 follows the track, but car 2 till 6 f.e. get off track.

    Thnx :)

  24. Ryan on August 13th, 2008 2:53 pm

    make the third bit >:

    i want to know how to stop the triangle deforming

  25. Bruce on January 1st, 2009 7:13 pm

    Great stuff. Are there more lessons in this ragdoll series?

  26. Tyler on May 7th, 2009 10:15 pm

    Do you know when or if you are going to be pasting another tutorial because this one was extremely helpful?

  27. nminhtai on July 6th, 2009 6:21 am

    Thanks for great guides!

  28. antoan on January 1st, 2010 4:01 pm

    I’d really like to see the third part of this tutorial. I’ve always thought making ragdolls in flash is impossible for me, but thanks to your tutorials it seems not as hard as I thought :)

Leave a Reply




Trackbacks

  1. James on September 12th, 2007 2:15 pm

    James

    I learn something new every day, this is a new one to the list!! :-)

  2. APE - Actionscript Physics Engine tutorial : Emanuele Feronato - blog of an italian geek on October 14th, 2007 12:57 am

    [...] Line 28: The feature I love: the spring constraint. The spring constraint is a spring-like constraint that connects two particles. If you need more information about constraints, refer to Creation of a Ragdoll with Flash part 2: Constraints. [...]

  3. Creation of a Ragdoll with Flash part 2: Constraints at Flash Game Script on October 29th, 2007 11:09 am

    [...] Original post by Emanuele Feronato – italian geek and PROgrammer [...]

  4. MochiLand » Blog Archive » Lesson 1 on December 5th, 2007 9:07 pm

    [...] Line 28: The feature I love: the spring constraint. The spring constraint is a spring-like constraint that connects two particles. If you need more information about constraints, refer to Creation of a Ragdoll with Flash Part 2: Constraints. [...]

flash games company