Rigid Bodies

A Rigid Body is an object in the physical game world made of 1 or more child colliders and a variety of possible properties. A rigid body is used for anything in a game that moves or responds to forces or collisions from a game physics standpoint.

It's called a "rigid" body, because it is treated as a solid mass that never bends, stretches or deforms when subjected to forces within the context of our physics engine. A rigid body will always retain it's representative shape made of all its attached child colliders, no matter how significant the forces are that it's subjected to.

Entities & Rigid Bodies

An Entity, is also a Rigid Body. The Entity class inherits from the Rigid Body class. This is very important to remember and will prevent a lot of confusion!

Types Of Rigid Bodies

There are 4 possible types a rigid body can be, defined by RigidBodyType enum, with the default being a dynamicrigid body. The type of a rigid body completely changes the way it behaves.

Here's a breakdown of each of the 4 types.

  • RigidBodyType.DYNAMIC (Default) - The default type, the rigid body will be effected by all external forces, including collisions with other rigid bodies, colliders, gravity, etc.

  • RigidBodyType.FIXED - A rigid body that does not move or rotate from collisions or forces. Velocities for example do not effect it. It's position and rotation can be set explicitly with .setRotation()and .setPosition(), but all physical interactions with other elements will have no effect on it and if it's rotation or position are set directly, the change in rotation and position between the current and next tick will not be accounted for during physics resolution.

  • RigidBodyType.KINEMATIC_POSITION - A rigid body that itself is unaffected by external forces and collisions but can effect other dynamic rigid bodies that contact it. It's velocities (linear and angular), as well as position are assigned each tick with .setNextKinematicPosition()and .setNextKinematicRotation(). The resulting linear and rotational velocities are internally calculated for physics resolution based on the change between the current and next position and rotation.

  • RigidBodyType.KINEMATIC_VELOCITY - A rigid body that itself is unaffected by external forces and collisions but can effect other dynamic rigid bodies that contact it. Its velocities and positions are updated through with .setLinearVelocity()and .setAngularVelocity(). Its velocities once set will remain constant until explicitly set again by you.

Understanding Rigid Body Options

Rigid Body creation starts with rigid body options. Whether you're creating an entity and providing its EntityOptions with rigid body options (very common), or creating a rigid body directly in a world's physics simulation for a different reason (rarely necessary), you'll want to get familiar with rigid body options.

Rigid body options define things like the rigid body type, velocities, mass, continuous collision detection, allowed movements and rotations, and much more.

Most rigid body properties can be updated at any time through the various methods it provides. You can find this methods in the RigidBody methods API Reference here.

Here's a list of available rigid body options. You can find the most up-to-date list in the RigidBodyOptions API Reference here.

Property (? = optional)
Type
Description

additionalMass?

number

(Optional) The additional mass of the rigid body

additionalMassProperties?

(Optional) The additional mass properties of the rigid body.

additionalSolverIterations?

number

(Optional) The additional solver iterations of the rigid body.

angularDamping?

number

(Optional) The angular damping of the rigid body.

angularVelocity?

Vector3Like

(Optional) The angular velocity of the rigid body.

ccdEnabled?

boolean

(Optional) Whether the rigid body has continuous collision detection enabled.

colliders?

(Optional) The colliders of the rigid body, provided as an array of ColliderOptions.

dominanceGroup?

number

(Optional) The dominance group of the rigid body.

enabled?

boolean

(Optional) Whether the rigid body is enabled.

enabledPositions?

Vector3Boolean

(Optional) The enabled axes of positional movement of the rigid body. Setting an axis to false prevents positional movement on that axis.

enabledRotations?

Vector3Boolean

(Optional) The enabled rotations of the rigid body. Setting an axis to false prevents rotations around that axis.

gravityScale?

number

(Optional) The gravity scale of the rigid body. Defaults to 1, effects how gravity effects the rigid body by gravity * gravityScale.

linearDamping?

number

(Optional) The linear damping of the rigid body.

linearVelocity?

Vector3Like

(Optional) The linear velocity of the rigid body.

position?

Vector3Like

(Optional) The position of the rigid body.

rotation?

QuaternionLike

(Optional) The rotation of the rigid body.

simulation?

(Optional) The simulation the rigid body is in. If provided, the rigid body will be automatically added to the simulation.

sleeping?

boolean

(Optional) Whether the rigid body is sleeping.

softCcdPrediction?

number

(Optional) The soft continuous collision detection prediction of the rigid body.

type?

(Optional) The type of the rigid body.

Rigid Body Examples

Here are some examples of how we use rigid body options to define rigid bodies in different contexts.

Creating a block entity with all rotations disabled, and positional changes disabled on the x and y axis. It only moves on the Z-axis.

We'll also add a mass value of 10, which will make it a bit harder to push. Masses in the HYTOPIA engine are calculated by default relative to the non-sensor collider volumes and any additional mass applied to each non-sensor collider of a rigid body, as well as any additional mass of the rigid body overall. All of these masses function together to internally compute center of gravity and other physical properties.

This is great for a situation where we may have to "push" a block into place for a puzzle in a game.

const heavyBlock = new Entity({
  blockTextureUri: 'textures/stone_bricks.png',
  blockHalfExtents: { x: 0.5, y: 0.5, z: 0.5 },
  rigidBodyOptions: {
    type: RigidBodyType.DYNAMIC,
    additionalMass: 10,
    enabledPositions: { x: false, y: false, z: true },
    enabledRotations: { x: false, y: false, z: false },
  },
});

heavyBlock.spawn(world, { x: -4, y: 10, z: -1 });

Another use of rigid body options could be significantly reducing the effects of gravity for a player entity while keeping it as the default dynamic rigid body type. We can do that as follows.

const playerEntity = new PlayerEntity({
  player,
  name: 'Player',
  modelUri: 'models/player.gltf',
  modelLoopedAnimations: [ 'idle' ],
  modelScale: 0.5,
  rigidBodyOptions: {
    gravityScale: 0.1
  }
});

Next let's create a block entity in the sky that is constantly spinning. We'll use a kinematic velocity based rigid body and set its initial angular velocity to achieve this. We'll also spawn it in the air since kinematic rigid bodies do not get effected by gravity.

const spinningBlock = new Entity({
  blockTextureUri: 'textures/stone_bricks.png',
  blockHalfExtents: { x: 1, y: 1, z: 1 }, 
  rigidBodyOptions: {
    type: RigidBodyType.KINEMATIC_VELOCITY,
    angularVelocity: { x: 1, y: 1, z: 1 },
  },
});

spinningBlock.spawn(world, { x: 0, y: 10, z: -6 });

One more common example for good measure. Let's create a block entity that's a platform that moves back and forth. We'll use a kinematic velocity based rigid body that moves back and forth in the air.

const blockPlatform = new Entity({
  blockTextureUri: 'textures/grass', // A texture URI without a file extension will use a folder and look for the textures for each face in the folder (-x.png, +x.png, -y.png, +y.png, -z.png, +z.png)
  blockHalfExtents: { x: 1, y: 0.5, z: 1 },
  rigidBodyOptions: {
    type: RigidBodyType.KINEMATIC_VELOCITY, // Kinematic means platform won't be effected by external physics, including gravity    
    linearVelocity: { x: 0, y: 0, z: 3 }, // A starting velocity that won't change until we change it explicitly with .setLinearVelocity(), because it's kinematic
  },
});

// Clamp the z range the platform moves back and forth between
blockPlatform.onTick = blockPlatform => { 
  const position = blockPlatform.position;

  if (position.z < -9) {
    blockPlatform.setLinearVelocity({ x: 0, y: 0, z: 3 });
  }

  if (position.z > 8) {
    blockPlatform.setLinearVelocity({ x: 0, y: 0, z: -3 });
  }
};

blockPlatform.spawn(world, { x: 3, y: 3, z: -7 });

Updating Rigid Body Properties

Oftentimes you'll need to perform operations like applying an impulse to a rigid body, adding some amount of force to it, applying an angular velocity, stopping its movement, and more. This may need to be done in response to some in-game action, or something else.

We can easily do this using the variety of methods provided by the rigid body.

Here's how we can apply a impulse of linear force.

// Multiplying the impulse value by the mass is equivalent to adding
// the same velocity to the entity's current velocity.
// velocityChange = impulse force * mass
myEntity.applyImpulse({ x: 0 , y: 10 * myEntity.mass , z: 0 });

We can also do things like add a torque. Assuming our entity is a dynamic rigid body type, it's angular velocity will increase relative to the torque applied and internally calculated drag/resistance based on its shape determined by all non-sensor child colliders.

myEntity.addTorque({ x: 0, y: 1, z: 0 })

Rigid bodies expose a number of methods for manipulating their properties at any time in a physically realistic way. Check out the RigidBody methods API Reference here.

Continuous Collision Detection

If an entity or rigid body could have a large force applied to it resulting in a sudden increase linear velocity, or has a very fast velocity, you might notice it seems to "teleport" through walls it should of hit and stopped at. This is due to the entity moving so fast between the current and next tick of physics that it never registers as intersecting a wall in the physics simulation. Or, it goes through a wall far enough between ticks and gets pushed out the other side by physics resolution.

We can easily fix this "tunneling" in our games physics by enabling CCD (Continuous collision detection) on our rigid body.

It might seem tempting to just set ccdEnabled:true on all entities and rigid bodies in your game, but this will cause a noticable degredation of physics performance relative to your number of active rigid bodies.

We recommend only enabling CCD if you think there is a possibility of tunneling due to high velocities or sudden velocity spikes.

For example, we can enable CCD on an entity as follows.

const cow = new Entity({
  name: 'cow',
  modelUri: 'models/cow.gltf',
  modelLoopedAnimations: [ 'idle' ],
  modelScale: 0.5,
  rigidBodyOptions: {
    ccdEnabled: true
  }
});

Last updated