🔗LBJT: Prismatic Joint 5️⃣.

LibGDX Box2d Joints Tutorials.

Vladislav Shesternin
7 min readDec 29, 2023
LBJT Application: Download for ease of study!
Prismatic Joint

Text separators are set using chatGPT.

This article is about the Prismatic Joint, its settings, and how to make it work.

To make it easier to understand what is happening and how to perform practical tasks, start with the article: 🔗LBJT: Сommon in Joints 1️⃣.

And so the… Prismatic Joint

Prismatic joint — a joint designed to connect two bodies together and move them along the same axis, such as an elevator or a platform.

First, we need to set up the joint. For this, each joint has its own implementation of JointDef. You can read about this in the article: 🔗LBJT: Common in Joints 1️⃣. And now let’s talk about the PrismaticJointDef.

PrismaticJointDef settings:

(Mandatory)

  • bodyA — first joint body.
  • bodyB — second joint body.
  • collideConnected — <boolean> Specifies whether bodyA should collide with bodyB.

(Optional)

  • localAnchorA — the local anchor point relative to bodyA origin.
  • localAnchorB — the local anchor point relative to bodyB origin.
  • localAxisA — the angle of the axis relative to the bodyA.
  • referenceAngle — initial angle between bodyA and bodyB, (default 0 | measured in radians).
  • lowerTranslation— The lower point of the limit (default 0 | measured in meters).
  • upperTranslation —The upper point of the limit (default 0 | measured in meters).
  • enableLimit — flag to enable lower and upper limits (default false).
  • motorSpeed —the desired number of meters per second (meters per second).
  • maxMotorForce — force for motion, the greater the force, the faster the motor picks up motion specified in motorSpeed (measured in N-m Newton-meters).
  • enableMotor — flag to enable the motor (default false).
PrismaticJointDef settings

Practice. How to create a Prismatic Joint?

In order for you to be able to test Joints, you need to interact with bodies, for this you need to implement MouseJoint. How to implement it is described in article 🔗LBJT: Mouse Joint 2️⃣.

To better understand the test, turn off gravity.

val gravity = Vector2(0f, 0f)
val world = World(GRAVITY)

Our example will consist of a static rectangle (blue) and a dynamic circle (green) connected by PrismaticJoint.

Prismatic Joint

1️⃣Create 2 bodies: a static rectangle and a dynamic circle:

val staticRect  = StaticBody
val dynamicCirc = DynamicBody

2️⃣Create PrismaticJoint:

world.createJoint(PrismaticJointDef().apply {
bodyA = staticRect
bodyB = dynamicCirc
collideConnected = true
}

That’s it, you can already run the program and the bodies will be connected, but there is one but, they will be connected at their anchor points, where the center of mass is specified, to fix this, you need to configure localAnchorA, localAnchorB about how I explained how to calculate anchor points in the article: 🔗LBJT: Сommon in Joints 1️⃣.

3️⃣Configure: localAnchorA, localAnchorB:

world.createJoint(PrismaticJointDef().apply {
bodyA = staticRect
bodyB = dynamicCirc
collideConnected = true

localAnchorA.set(Vector2(2.5f, 10.0f))
localAnchorB.set(Vector2(2.5f, 2.5f))
}

Do not forget that the values are indicated in meters.
About the units of measurement of meters, kilograms, seconds (MKS) is written in the article: 🔗LBJT: Сommon in Joints 1️⃣.

4️⃣Configure: localAxisA:

localAxisA — this is the angle of the axis of motion of bodyB relative to bodyA.

By default = Vector2(1f, 0f)

PrismaticJoint localAxisA

localAxisA — X and Y values are normalized, which means that they can only take values from 0 to 1.

(1, 0) — movement along the X axis.
(0, 1) — movement along the Y axis.
(1, 1) — diagonal movement.
(0, 0) — the behavior seems to have no connection.

But yes, as I am a genius, I found out in a practical way that you can set any angle of the axis that you want 😎.

To begin with, let’s understand that we are not setting 1 or 0, which do you think now means choosing the X or Y axis, you are setting the ratio of the legs to the hypotenuse.

explanation localAxisA

Now I hope you understand what happens when you insert a value from 0 to 1 for localAxisA.

Now, thanks to this tool, we can set the angle we want, we just need to find the value of the angle for the legs, which are the usual cosine for X and sine for Y.

An example for localAxisA:

Let’s set the axis angle to 60 degrees.

localAxisA cos(60) | sin(60)
localAxisA cos(60) | sin(60)

Note: that the angle is calculated from the anchor point and the beginning of the countdown goes counter-clockwise.

world.createJoint(PrismaticJointDef().apply {
...
val angle = 60f
localAxisA.set(cos(angle), sin(angle))
}

This way we can adjust the axis to any angle from 0 to 360 degrees just replace the (val angle) field with the desired angle value.

If you run it now the angle will be wrong and that’s because:

Do not forget that all angles in Box2d are measured in radians.

Let’s fix that by converting DEGREES to RADIANS:

const val PI: Double = 3.141592653589793
const val DEGTORAD = (PI / 180f).toFloat()

world.createJoint(PrismaticJointDef().apply {
...
val angle = 60f * DEGTORAD
localAxisA.set(cos(angle), sin(angle))
}

To convert radians to degrees, we need to multiply the radians by our constant:

const val PI: Double = 3.141592653589793
const val RADTODEG = (180f / PI).toFloat()

val degrees = body.angle * RADTODEG

Number PI and sin(), cos() — is usually found in a standard mathematical package.

5️⃣Configure: referenceAngle:

For the easier continuation, let’s change localAxisA to move the bodyB horizontally.
As we already know we can do it in 3 ways:

world.createJoint(PrismaticJointDef().apply {
...
// 1 way
localAxisA.set(1f, 0f)
// 2 way
val angle = 0f * DEGTORAD
localAxisA.set(cos(angle), sin(angle))
// 3 way
val angle = 360f * DEGTORAD
localAxisA.set(cos(angle), sin(angle))
}

Now we can go to the сonfigure referenceAngle.

referenceAngle — just changes the angle between bodyA and bodyB.

Do not forget that all angles in Box2d are measured in radians.

Clockwise negative angles.
Counter-clockwise are positive angles.

PrismaticJoint referenceAngle
world.createJoint(PrismaticJointDef().apply {
...
referenceAngle = 45f * DEGTORAD
}

6️⃣Configure: limits:

At the moment, bodies can move on their axis infinitely far away to change this, you can use limits, they allow you to specify the maximum and minimum length of movement.

world.createJoint(PrismaticJointDef().apply {
...
lowerTranslation = -5f
upperTranslation = 5f
}

lowerTranslation must be smaller than upperTranslation but not necessarily negative.

Do not forget that the values are indicated in meters.
About the units of measurement of meters, kilograms, seconds (MKS) is written in the article: 🔗LBJT: Сommon in Joints 1️⃣.

It won’t work at this point. Why?
Because by default the limits are disabled and we need to enable them!

world.createJoint(PrismaticJointDef().apply {
...
lowerTranslation = -5f
upperTranslation = 5f
enableLimit = true
}
PrismaticJoint limits
  • if you need only lowerTranslation then upperTranslation should be very large so that it is not reached and similarly if you need only upperTranslation then lowerTranslation should be very small.
  • setting of almost identical values for the lowerTranslation and upperTranslation (for example a = 4.99f | b = 5.0f) will move the body to a given length.

7️⃣Configure: motor:

world.createJoint(PrismaticJointDef().apply {
...
motorSpeed = 5f
maxMotorForce = 100f
}
  • motorSpeed — length per second (meters per second) this is not the guaranteed speed of the motor, it means what maximum speed can be reached, and may not be reached if there is a very small value of maxMotorForce.
  • maxMotorForce— motion force, the greater the force, the faster the engine gains the motion indicated in motorSpeed (measured in N-m. Newton meter).
  • To move forward set a positive value for motorSpeed.
  • To move back, set a negative value for motorSpeed.

The heavier the body, the greater the force should be, if the values are small, the motor will slowly gain speed to reach the motorSpeed or will not move at all if the force is very small, so be careful when adjusting the maxMotorForce.

It won’t work at this point. Why?
Because by default the motor are disabled and we need to enable them!

world.createJoint(PrismaticJointDef().apply {
...
motorSpeed = 5f
maxMotorForce = 100f
enableMotor = true
}
PrismaticJoint motor

As you can see, the motor slowly reaches the move specified in motorSpeed, the greater the maxMotorForce, the faster the motor will reach motorSpeed, so that the move is like a rusty wheel, or to slow down the speed of move, set motorSpeed to a small value.

It works and it’s great 😎.

PS. Vel_daN: Love what You DO 💚.

--

--

Vladislav Shesternin
Vladislav Shesternin

Written by Vladislav Shesternin

LibGDX | Android Developer | Enthusiast.

No responses yet