-
Notifications
You must be signed in to change notification settings - Fork 111
Joints Guide
Joints are used to connect two physical objects together. The type of joint will determine how the joint will behave when these objects are submitted to physical forces.
This guide's main goal is to illustrate the difference between various joint types and the functions related to their use.
It is strongly recommended that you read the Physics Guide in order to familiarize yourself with how Torque2D and Box2D handle physics.
There are several types of joints you can use :
-
**GearJoint (currently unimplemented)
**Take note that not all joint-related functions were exposed to script. This guide focuses solely on the script side of things. If you need any of the unbinded functions in your program, you will have to create the appropriate ConsoleMethod by basing yourself on the contents of file Scene_Scriptbinding.h.
The information contained in Chapters 1 and 2 will apply to every type of joint unless otherwise specified. It is recommended that you read these chapters sequentially before looking at the distinct joint types in Chapter 3.
You can create and destroy joints to your heart's content during the course of the simulation.
Bear in mind that trying to anchor a rapidly moving object to a joint might produce some unexpected behavior and even (rarely) crash the engine. It is wise to either slow down or completely stop the linear and angular velocity of an object before anchoring a joint to it.
You can create a joint using the corresponding creation function listed in the section Types of Joints.
As an example, we will use one of the most common forms of a joint of type RevoluteJoint
.
%jointID = Scene.createRevoluteJoint(sceneobjectA, sceneobjectB, localAnchorA, localAnchorB);
- Sceneobjects A and B can be any type of Sceneobject which has a valid physical body / collision shape.
- localAnchorA refers to a local point in sceneobjectA where the joint will be anchored.
- localAnchorB refers to a local point in sceneobjectB where the joint will be anchored.
- The function will return the jointID of the newly-created joint. In the example, this value would be stored in variable %jointID.
Note that omitting parameters localAnchorA and / or localAnchorB will force the joint to use the corresponding object's "0 0" position as an anchor point. An object's local position of "0 0" is usually located at the center of the object.
Once created, a joint will exist in the Scene and can be referred to by its Joint ID.
Note that the Joint ID is not the same as the ID of sceneobjects! While a typical Sceneobject's ID might be something like 1455, a Joint ID will typically be 1,2,3. The first joint created in the scene will have Joint ID 1, the second one Joint ID 2, and so on.
Destroying a joint only deletes the joint itself; the objects that were linked by the joint will still exist.
Destroying a joint will release both anchored sceneobjects from the joint's constraints. They will both immediately start behaving as singular entities in the physical simulation.
As opposed to the joint creation functions, all joints can be destroyed using the following function, regardless of their type:
Scene.deleteJoint(jointID);
You will have very little control over the joint's behavior during its existence.
For instance, you cannot directly change a joint's position or angle.
You would have to manipulate one of the objects linked to the joint to achieve results; The joint will influence the other linked object according to the joint type and parameters as well as each object's physical properties (density, friction, restitution, etc.).
During the course of your game, feel free to destroy a joint before making necessary modifications to the objects. Simply recreate the joint once modifications are made. Since these modifications will be made during a single frame, the user will never notice that the joints were destroyed and recreated.
All functions that allow you to modify or query the joint's properties exist in the Scene namespace and are unique to each type of joint. You will find all such additional functions listed under the appropriate joint type in section 3.
There are three Scene functions that can be used to query any Joint, regardless of its type.
Scene.getJointCount();
- Gets the number of joints which exist in the Scene.
Scene.getJointType(%jointID);
- This will return the type of joint of the selected %jointID. i.e. RevoluteJoint, DistanceJoint, etc.
Scene.deleteJoint(%jointID);
- Deletes the selected joint.
Note that you will not be able to get a joint's jointID through its anchored objects. It is common practice to store the jointID in custom variables on the SceneObjects for later reference.
Most of the time you will use joints to connect 2 objects together but you have the option of omitting one of these objects, in which case the defined object will be anchored to the Scene Ground Object.
Scene.createRevoluteJoint(sceneobjectA, localAnchorA);
The Scene Ground Object resides at Scene Position "0 0" and is static, thus will not respond to physical forces. You cannot access or modify this object's properties.
If you need to specify additional joint parameters upon joint creation and want to use the SceneGround Object, simply use an empty string "" as in this example function :
Scene.createRevoluteJoint(sceneobjectA, localAnchorA, "", "0 0", parameter1, %parameter2);
In most situations, you will want to set the joint's anchor points to the center of the sceneobjects linked by the joint.
To do so, you can either use "0 0" in lieu of localAnchorA/B or omit the parameter entirely.
The anchor points are always specified in local coordinates, meaning that 0,0 represents the center of the corresponding object. A local position of "5 0" would be located 5 units to the right of the current sceneobject. A local position of "-5 0" would be located 5 units to the left of the current sceneobject.
In comparison, a world point in world coordinates of "5 0" would be located 5 units to the right of the center of the scene.
Be aware that joint types will sometimes modify the current position of the sceneobjects upon creation, which will lead to unexpected behavior if you are not careful.
For example, a DistanceJoint's main function is to keep a specific distance between 2 sceneobjects. When first created, it will thus move sceneobjectB to be at the specified distance from sceneobjectA.
When applying linear and angular velocity to sceneobjects which are anchored by a joint, always keep in mind that the joint will constrain the allowed movement of these objects!
For instance, applying Angular velocity to an object linked to another by a RevoluteJoint will spin the object around the other. The same force, applied to an independent object, would make the object spin on itself instead.
A distance joint will try to keep a minimal distance between two objects. If you specify a frequency other than 0, the joint will contract and expand while trying to maintain the specified distance.
Scene.createDistanceJoint(%sceneobjectA, %sceneobjectB, %localAnchorA, %localAnchorB, distance, frequency, Dampingratio, CollideConnected);
distance - The distance between both SceneObjects, defaults to the current distance between the SceneObjects.
frequency - The mass-spring-damper frequency in Hertz(Hz).
- Values above 0 will allow the joint to contract and expand, resulting in a bouncier effect. 0 means a hard joint.
- The value is relative to the engine's physics update frequency, which by default is 60 hertz.
- A frequency of 1 will produce an effect as if both objects were tied with an elastic band.
Dampingratio - The amount of damping to apply to the spring's softness. Between 0 and 1, where 0 means no damping at all and 1 means total stiffness.
CollideConnected - If set to true, allows both anchored objects to collide with one another. Defaults to false.
Scene.setDistanceJointDampingRatio(%jointID, DampingRatio);
- DampingRatio is between 0 and 1
Scene.setDistanceJointFrequency(%jointID, Frequency);
- Frequency is in Hertz
Scene.setDistanceJointLength(%jointID, Length);
- Length is in World Units
Scene.getDistanceJointDampingRatio(%jointID);
- Returns the joint's damping factor, usually between 0 (no damping) and 1 (full damping)
Scene.getDistanceJointFrequency(%jointID);
- Returns the joint spring-damper frequency in Hertz
Scene.getDistanceJointLength(%jointID);
- Returns the current distance between both objects
A friction joint basically limits motion. If one sceneobject's motion is generating more force than MaximumForce, the second sceneobject will not follow suit and stay behind instead of being pulled by the first sceneobject. Inversely, if sceneobjectA is moving slowly, sceneobjectB will be pulled along, following the movements.
MaximumTorque follows the same logic, using rotational forces instead.
Scene.createFrictionJoint(%sceneobjectA, %sceneobjectB, %localAnchorA, %localAnchorB, MaximumForce, MaximumTorque, CollideConnected);
MaximumForce - Maximum Force in Newtons. When the motion of one object exerts a force superior to MaximumForce on the second object, the second object will not be influenced by the motion.
MaximumTorque - Maximum Torque in Newton-metres. When the motion of one object exerts a rotational force superior to MaximumTorque on the second object, the second object will not be influenced by the motion.
CollideConnected - If set to true, allows both anchored objects to collide with one another. Defaults to false.
Scene.setFrictionJointMaxForce(jointID, MaxForce);
- MaxForce is expressed in Newtons
Scene.setFrictionJointMaxTorque(jointID, MaxTorque);
- MaxTorque is expressed in Newton-metres
Scene.getFrictionJointMaxForce(jointID);
- Returns the Maximum force, expressed in Newtons
Scene.getFrictionJointMaxTorque(jointID);
- Returns the Maximum Torque, expressed in Newton-metres
The Weld Joint tries to restrict all motion between objects.
Scene.createWeldJoint(%sceneobjectA, %sceneobjectB, localanchorA, localanchorB, frequency, DampingRatio, CollideConnected);
frequency - The mass-spring-damper frequency in Hertz(Hz).
- Values above 0 will allow the joint to contract and expand, resulting in a bouncier effect. 0 means a hard joint.
- The value is relative to the engine's physics update frequency, which by default is 60 hertz.
- A frequency of 1 will produce an effect as if both objects were tied with an elastic band.
DampingRatio - The amount of damping to apply to the spring's softness. Between 0 and 1, where 0 means no damping at all and 1 means total stiffness.
CollideConnected - If set to true, allows both anchored objects to collide with one another. Defaults to false.
Scene.setWeldJointFrequency(jointID, frequency);
- Frequency is in Hertz
Scene.setWeldJointDampingRatio(jointID, DampingRatio);
- DampingRatio is between 0 and 1
Scene.getWeldJointFrequency(jointID);
- Returns the joint spring-damper frequency in Hertz
Scene.getWeldJointDampingRatio(jointID);
- Returns the joint's damping factor, usually between 0 (no damping) and 1 (full damping)
The Rope Joint behaves as if both objects were tied together with a segmented rope.
Scene.createRopeJoint(%sceneobjectA, %sceneobjectB, localanchorA, localanchorB, MaxLength, CollideConnected);
MaxLength - The maximum length of the rope. CollideConnected - If set to true, allows both anchored objects to collide with one another. Defaults to false.
Scene.setRopeJointMaxLength(jointID, MaxLength);
- Maximum length of the rope joint.
Scene.getRopeJointMaxLength(jointID);
- Returns the Maximum length of the rope joint.
The Wheel Joint restricts a point on sceneobjectB to a line on sceneobjectA. This type of joint also provides you with a suspension spring.
Note that in this case, you must make sure that sceneobjectB represents the Wheel object.
Scene.createWheelJoint(%sceneobjectA, %sceneobjectB, localanchorA, localanchorB, WorldAxis, CollideConnected);
WorldAxis - The joint will draw a line from localanchorA to localanchorB along this axis. You will find that "0 1" is used in most cases. CollideConnected - If set to true, allows both anchored objects to collide with one another. Defaults to false.
Scene.setWheelJointMotor(jointID, enableMotor, MotorSpeed, MotorMaxTorque);
-
Activates the joint motor, which will apply the value of MotorSpeed as a constant force. The motor's Torque will be limited to MotorMaxTorque and never exceed its value.
-
enableMotor : Starts the motor if true
-
MotorSpeed : force that will be applied in degrees per second
-
MotorMaxTorque : Used to limit the top speed of the motor joint.
Scene.setWheelJointFrequency(jointID, frequency);
- Frequency is in Hertz
Scene.setWheelJointDampingRatio(jointID, dampingRatio);
- DampingRatio is between 0 and 1
Scene.getWheelJointMotor();
- returns the respective values of 'enableMotor MotorSpeed MotorMaxTorque' as a space-separated string
Scene.getWheelJointFrequency(jointID);
- Returns the joint spring-damper frequency in Hertz
Scene.getWheelJointDampingRatio(jointID);
- Returns the joint's damping factor, usually between 0 (no damping) and 1 (full damping)
A pulley joint simulates an idealized pulley system. Look at the diagram below.
sceneobjectA
is tied to worldGroundAnchorA
and sceneObjectB
is tied to worldGroundAnchorB
.
Both worldGroundAnchors are simply 'dummy' objects which will not be affected by outside forces : Only their positions matter.
If you add lengthA
and lengthB
, you will get the total length of the 'rope'; the distance between worldGroundAnchors does not factor in the equation.
With the default settings, when one of the sceneobjects is lowered by 1 distance unit, the opposing sceneobject is pulled up by 1 distance unit.
It gets a bit more complicated when you change the default ratio between both sides of the pulley joint.
The ratio determines the variation rate of lengthA
vs. lengthB
while also affecting the constraint force.
lengthA + (ratio * lengthB) = total length
This means that with a ratio of 2, every time sceneObjectA would be pulled down by 1 distance unit, sceneobjectB would be pulled up by 2 distance units.
The constraint force of lengthA
would also be half of that of lengthB
.
In simpler terms, when lengthA
is fully extended, it will reach twice as far as when lengthB
is fully extended.
You will have to experiment with the system to gain an intuitive understanding of its inner workings.
Scene.createPulleyJoint(%sceneobjectA, %sceneobjectB, localanchorA, localanchorB, worldGroundAnchorA, worldGroundAnchorB, ratio, lengthA, lengthB, CollideConnected);
worldGroundAnchorA - "X/Y" - World position of the first static anchor
worldGroundAnchorB - "X/Y" - World position of the seconmd static anchor
ratio - Determines the extension rate's difference between lengthA and lengthB. Defaults to 1
lengthA - The initial distance between sceneobjectA and worldGroundAnchorA. Defaults to the actual physical distance between sceneObjectA and worldGroundAnchorA.
lengthB - The initial distance between sceneobjectB and worldGroundAnchorB. Defaults to the actual physical distance between sceneObjectA and worldGroundAnchorA.
CollideConnected - If set to true, allows both anchored objects to collide with one another. Defaults to false.
There are no additional functions at the moment for the pulley joint.
The Target Joint takes only one sceneobject and applies force to try to keep it at a position indicated by 'worldtarget'
Scene.createTargetJoint(%sceneobjectA, worldTarget, MaxForce, UseCenterofMass, frequency, Dampingratio, CollideConnected);
worldTarget - "X Y" -The world position to move the sceneobject to
MaxForce - the maximum force to use to send sceneobjectA to worldTarget
UseCenterofMass - boolean - Whether to use the object's center of mass as the anchor point. Defaults to false
frequency - The mass-spring-damper frequency in Hertz(Hz).
- Values above 0 will allow the joint to contract and expand, resulting in a bouncier effect. 0 means a hard joint.
- The value is relative to the engine's physics update frequency, which by default is 60 hertz.
- A frequency of 1 will produce an effect as if both objects were tied with an elastic band.
DampingRatio - The amount of damping to apply to the spring's softness. Between 0 and 1, where 0 means no damping at all and 1 means total stiffness.
CollideConnected - If set to true, allows both anchored objects to collide with one another. Defaults to false.
Scene.setTargetJointTarget(jointID, worldTargetX/Y );
- X/Y position of the world target
Scene.setTargetJointFrequency(jointID, frequency);
- Frequency is in Hertz
Scene.setTargetJointDampingRatio(jointID, DampingRatio);
- DampingRatio is between 0 and 1
Scene.getTargetJointTarget(jointID);
- Returns the world target coordinates
Scene.getTargetJointFrequency(jointID);
- Returns the joint spring-damper frequency in Hertz
Scene.getTargetJointDampingRatio(jointID);
- Returns the joint's damping factor, usually between 0 (no damping) and 1 (full damping)
A Prismatic joint allows for relative translation of two bodies along a specified axis. It is generally a good idea that one of the objects have its BodyType set to static to fully appreciate the joint's possibilities.
Scene.createPrismaticJoint(%sceneobjectA, %sceneobjectB, localanchorA, localanchorB, worldAxis, CollideConnected);
worldAxis - The world axis defining the translational degree of freedom.
- example : "1 0" would mean that the joint's movement would be limited to the X axis, regardless of y-axis forces.
CollideConnected - If set to true, allows both anchored objects to collide with one another. Defaults to false.
Scene.setPrismaticJointLimit(jointID, enableLimit, LowerTranslation, UpperTranslation);
enableLimit : Enables the Limit, when sceneobjectB reaches lowerTranslation or upperTranslation, it will stop moving. lowerTranslation (optional) : the smallest distance allowed between sceneobjectA and sceneobjectB along the worldAxis upperTranslation (optional) : the farthest distance allowed between sceneobjectA and sceneobjectB along the worldAxis
Scene.setPrismaticJointMotor(enableMotor, MotorSpeed, MotorMaxTorque);
Activates the joint motor, which will apply the value of MotorSpeed as a constant force until MotorMaxTorque is reached.
enableMotor - Starts the motor if true MotorSpeed - force that will be applied in degrees per second (even though this is a translation) MotorMaxTorque - Used to limit the top speed of the motor joint.
Scene.getPrismaticJointLimit(jointID);
- returns the respective values of 'enableLimit LowerTranslation UpperTranslation' as a space-separated string
Scene.getPrismaticJointMotor(jointID);
- returns the respective values of 'enableMotor MotorSpeed MotorMaxTorque' as a space-separated string
A Motor joint will place both sceneobjects relative to a LinearOffset and an angularOffset. Moving either sceneobject will exert the appropriate force on the other object, allowing users to create complex mechanical behaviors.
Note that the creation function does not feature localAnchorA and localAnchorB.
Scene.createMotorJoint(%sceneobjectA, %sceneobjectB, LinearOffset, angularOffset, maxForce, maxTorque, correctionFactor, CollideConnected);
LinearOffset - The Linear Offset in SceneObjectA local space
AngularOffset - The angular Offset between the two objects
MaximumForce - Maximum Force in Newtons. When the motion of one object exerts a force superior to MaximumForce on the second object, the second object will not be influenced by the motion.
MaximumTorque - Maximum Torque in Newton-metres. When the motion of one object exerts a rotational force superior to MaximumTorque on the second object, the second object will not be influenced by the motion.
correctionFactor - Also known as tolerance, determines how 'imprecise' the calculations are allowed to be. Defaults to 0.3.
CollideConnected - If set to true, allows both anchored objects to collide with one another. Defaults to false.
Scene.setMotorJointLinearOffset(jointID, LinearOffset);
- LinearOffset of SceneObjectB in SceneObjectA's local space
Scene.setMotorJointAngularOffset(jointID, AngularOffset);
- AngularOffset is the angle difference between sceneobjectA and sceneobjectB
Scene.setMotorJointMaxForce(jointID, MaxForce);
- MaxForce determines the maximum force that the motor applies to the joint.
Scene.setMotorJointMaxTorque(jointID, MaxTorque);
- MaxTorque determines the maximum rotational force that the motor applies to the joint
Scene.getMotorJointLinearOffset(jointID);
- Returns the linear offset between both sceneobjects "X/Y" format
Scene.getMotorJointAngularOffset(jointID);
- Returns the angular offset between both objects in degrees
Scene.getMotorJointMaxForce(jointID);
- Returns the maximum Force, expressed in Newtons
Scene.getMotorJointMaxTorque(jointID);
- Returns the maximum rotational force or Torque in Newton-metres
A Revolute joint will make both Sceneobjects revolve around a common anchor point.
Note that the implementation of Revolute Joints in Torque2D is different from what you'd expect in a straight Box2D implementation. This was done to maintain a level of consistency between Joint Types. It allows us to pull some really intricate behaviors with RevoluteJoints but as a downside, it forces users to be EXTREMELY careful in their handling of Revolute Joints.
Playing with these WILL yield unpredictable results at times.
To fully appreciate the expected behavior of revolute joints, It is best practice to set one of the sceneobjects involved to be static. Nothing stops you from using dynamic objects, however.
Look to the end of the section on Revolute Joints for possible scenarios.
Scene.createRevoluteJoint(%sceneobjectA, %sceneobjectB, localanchorA, localanchorB, CollideConnected);
CollideConnected - If set to true, allows both anchored objects to collide with one another. Defaults to false.
Scene.setRevoluteJointLimit(jointID, enableLimit, lowerAngle, upperAngle);
enableLimit : Enables the Limit, when the joint's angle reaches lowerAngle or upperAngle, the moving object will stop moving. LowerLimit (optional) : the smallest angle allowed between sceneobjectA and sceneobjectB, expressed in degrees upperAngle (optional) : the largestsmallest angle allowed between sceneobjectA and sceneobjectB, expressed in degrees
Scene.setRevoluteJointMotor()(jointID, enableMotor, MotorSpeed, MotorMaxTorque);
-
Activates the joint motor, which will apply the value of MotorSpeed as a constant force. The motor's Torque will be limited to MotorMaxTorque and never exceed its value.
-
enableMotor : Starts the motor if true
-
MotorSpeed : force that will be applied in degrees per second
-
MotorMaxTorque : Used to limit the top speed of the motor joint.
Scene.getRevoluteJointLimit(jointID);
- returns the respective values of 'enableLimit lowerAngle upperAngle' as a space-separated string
Scene.getRevoluteJointMotor(jointID);
- returns the respective values of 'enableMotor MotorSpeed MotorMaxTorque' as a space-separated string
Scene.getRevoluteJointAngle(jointID);
- returns the current Joint Angle in degrees
Scene.getRevoluteJointSpeed(jointID);
- returns the current Angular Velocity of the joint
Both objects will be centered at the same exact location. The pivot point for the joint will also be at the same position.
Scene.createRevoluteJoint( SceneobjectA, SceneobjectB );
The first joint anchor will be at the center of object1, the second joint anchor will be at the center of object2. SceneObject2 will revolve around SceneObject1. However, the localAnchor will determine the distance between the two objects.
Scene.createRevoluteJoint( SceneobjectA, SceneobjectB, "5 5" );
In the following screenshot, the LocalAnchorA is set to "5 5".
Note that the LocalAnchor value will be relative to the current position of the sceneobjects.
Scenario 1 - Hinged Revolute joint (evenly divided)
Scene.createRevoluteJoint( SceneobjectA, SceneobjectB, SceneobjectA.getLocalPoint("-5 0"), SceneobjectB.getLocalPoint("5 0"));
Both objects will be positioned at the World Positions specified ("-5 0" and "5 0" respectively) and separated by a joint of length 10.
The joint's hinge will be positioned at the exact half-way point between both object.
Scenario 2 - Hinged Revolute joint (unevenly divided)
Scene.createRevoluteJoint( SceneobjectA, SceneobjectB, SceneobjectA.getLocalPoint("-5 0"), SceneobjectB.getLocalPoint("2 0"));
Both objects will be positioned at the World Positions specified with a joint of length 7 but the joint's hinge will placed 2 world units away from one object and 5 world units the other.
Scenario 3 - Using current object positions
Scene.createRevoluteJoint(SceneobjectA, SceneobjectB, SceneobjectA.getPosition(), SceneobjectB.getPosition() );
This leaves the objects exactly where they were in the scene before joint creation, with the joint hinge at the half-way point between both objects.
Note that LocalAnchorA corresponds to the position of SceneobjectB and LocalAnchorB corresponds to the position of SceneObjectA.