Physics

You can add physics to your game by adding the RigidBody component to your objects. See the documentation for RigidBody for details. Under the hood, it uses Jolt Physics.

Physics updates run in the same thread as logic. It has its own FPS, but tries to catch up with logic frames by simulating multiple sub-frames. Keep the following in mind:

  • You can set and get physical properties of actual physical bodies directly in the code. The changes will be applied immediately.

  • There are two types of external forces: continuous (force, torque) and singular (impulse). Continuous forces should be called every update and are applied at every physics sub-frame for the duration of an update.

Physics is provided by the engine.physics_core module, which is part of the engine.core module.

Physics is represented by components:

  • RigidBody - the main component that enables physics for objects.

You can query the scene with traces and overlaps:

With these global functions, you can also:

Friction and Damping Tweaking Guide

To simulate the loss of velocity of a body, the physics engine uses three parameters: friction, damping, and angularDamping. These parameters represent two main concepts: friction and damping. They both contribute to the result, so don’t forget to set them both.

Friction

A linear decrease in body speed when it is in contact with another body.

Setting friction values can be tricky. For simple games, consider setting friction = -1 for all bodies to use the default friction, and then set the desired friction with set_default_friction().

Note that friction is not calculated using the friction value of a single body, but with the Combined Coefficient of Friction (COF). The COF is calculated as:

Combined Coefficient of Friction = sqrt(friction_of_body_1 * friction_of_body_2)

Consider these values as guides to choose your own friction:

Body 1

Body 2

Combined Coefficient of Friction (approx.)

Rubber, Tires

Asphalt (dry)

1.0

Rubber, Tires

Asphalt (wet)

0.3

Rubber, Tires

Ice

0.15

The COF can be determined in different scenarios:

  • Sliding Friction: If you know the time or distance it takes for a body to stop sliding on a horizontal surface, you can calculate the COF using the formulas:

    Combined Coefficient of Friction = starting_velocity_of_body_1 / (strength_of_the_gravity * time_before_body_1_stops)

    or

    Combined Coefficient of Friction = (starting_velocity_of_body_1)^2 / (2 * strength_of_the_gravity * distance_before_body_1_stops)

  • Static Friction: To prevent a body from sliding on a slope, the COF should be greater than or equal to the tangent of the slope angle:

    Combined Coefficient of Friction >= tan(angle_of_the_slope)

  • Rolling Friction: There is no special implementation of rolling friction. When a body is rolling, the friction value only matters if the COF is too low, allowing the body to slide instead of roll. Otherwise, the body will move as if there is no friction, and the only velocity loss is due to damping.

Damping

An exponential decrease in body speed at every frame.

This can be used to add damping to a spring, or to decrease the speed of a moving body over time.

The damping value should be 0 or more, with higher values causing faster velocity loss.

Every frame, the velocity of a body is multiplied by (1 - damping * frame_time). This can be expressed with the formulas:

  • v(t) = v_0 * e^(-damping * t)

  • x(t) = x_0 + v_0 * (-1 / damping) * (e^(-damping * t) - 1)

The body will travel a distance of v_0 / damping and reach half that distance in ln(2) / damping seconds.

To select the appropriate damping value, choose the desired distance the body should travel from a given initial velocity, and calculate the damping using the formula:

Damping = initial_velocity_of_a_body / distance_that_it_should_travel

The angularDamping works the same way as damping, but it affects the rotation of the body.

Physics Simulation in the Main Loop

See Main Loop

  • Physics Simulation

    • Simulate several physics frames

      • Physics calculations require precision, which is achieved by running multiple physics frames for a single logic frame, with a fixed FPS (see set_physics_fps()).

      • The overall principle is that the physics simulation tries to keep up with the logic. For example, if the logic simulates at 30FPS and the physics at 60FPS, there will be 2 frames of physics simulation for each logic frame.

      • Conversely, if the logic simulates at 30FPS and the physics at 15FPS, there will be logic frames with no physics update (every second logic frame in this case).

    • Collision callbacks

Type aliases

bitfield AllowedDofs : uint8

Allowed degrees of freedom for a body component (Dofs = degrees of freedom)

Fields:
  • AllowTranslationX (0x1) - Allow translation along the X axis

  • AllowTranslationY (0x2) - Allow translation along the Y axis

  • AllowTranslationZ (0x4) - Allow translation along the Z axis

  • AllowRotationX (0x8) - Allow rotation around the X axis

  • AllowRotationY (0x10) - Allow rotation around the Y axis

  • AllowRotationZ (0x20) - Allow rotation around the Z axis

  • AllowedDofsAll (0x3f) - All degrees of freedom allowed

  • AllowedDofs2D (0x23) - 2D degrees of freedom allowed

Constants

MAX_PHYS_LAYER = 31

Maximum value for a collision layer

Enumerations

CharacterState

The return value of the CharacterController.state and the state of a character’s contacts.

Values:
  • OnGround = 0 - The character is standing firmly on the ground. Consider to allow jumping in this state only.

  • OnSteepGround = 1 - The character is standing on a steep slope, or touching a wall. The character cannot stay still and should slide down by adding the gravity to the character’s velocity.

  • NotSupported = 2 - The character is touching something, but not standing on it (hence “not supported by the ground”). Most likely it’s touching a ceiling. For most uses, it is like InAir.

  • InAir = 3 - The character is not touching anything.

AxisDirection

Axis direction for capsule and cylinder colliders

Values:
  • XAxis = 0 - X-Axis

  • YAxis = 1 - Y-Axis

  • ZAxis = 2 - Z-Axis

CollisionDetection

Whether the body should integrate using Continuous Collision Detection (CCD) or not. See also Jolt Documentation

The discrete collision detection is the default method, and is faster but less precise, as it may not detect all collisions that occur between two objects.

The continuous collision detection method is more precise, as it can detect fast-moving objects passing through stationary thin objects. It works by casting the shape of a body in the direction of movement and checking for collisions along the way. This method is more computationally heavy but provides more accurate results.

Values:
  • Discrete = 0 - Discrete integration

  • Continuous = 1 - Use Continuous Collision Detection

Interpolation

Interpolation of a visual position of a body

  • None - No interpolation is performed, resulting in a visual jitter for fast-moving objects.

  • Extrapolate - The body’s visual position is predicted based on its previous movements, creating a smoother and more realistic appearance. However, this method can be less accurate for objects that change velocity often.

  • Interpolate - The body’s position and rotation are calculated based on its previous movements, but with a small lag in their movement. This method is more precise than Extrapolate, but may introduce a small lag to the body movement.

Values:
  • None = 0

  • Interpolate = 1

  • Extrapolate = 2

MotionType

Whether the body should be static, dynamic, or kinematic.

A Dynamic body reacts to collisions, has mass and velocity, and can be affected by gravity and other forces.

A Kinematic body has velocity but has no mass and does not react to collisions. Other objects can respond to these collisions, but the kinematic body will continue to move as if it were unaffected by the collision.

Note that to create a static, non-moving body you should only add Collider component. RigidBody component adds movement to the object.

About collision callbacks:

When receiving collision callbacks with other bodies through the on_collision function or the CollisionListener component, the cheapest option would be a Collider-only (static) body. However, a static body cannot detect collisions with other static bodies, and to detect collisions with Kinematic bodies, it needs to be a sensor (see RigidBody.isSensor). If these limitations are unacceptable, consider using the Kinematic or Dynamic motion type. Despite the fact that Kinematic bodies do not react to collisions, they will generate collision callbacks.

Also, note that a Dynamic body will generate an exit event if it would go to sleep while inside a sensor. This is because sleeping bodies exit the simulation until something collides with them (the sensor does not count).

In any case, you can always use the overlap function for a less cheap but more reliable collision handling.

Values:
  • Dynamic = 0 - Dynamic body

  • Kinematic = 1 - Kinematic body

Structures

RayCast

Represents a raycast request.

Fields:
  • ray : Ray - The ray that will be casted

  • maxDistance : float = FLT_MAX - The maximum distance the ray can travel

  • collisionMask : uint = UINT_MAX - The collision mask to use for filtering collisions

NodeCast

Represents a ShapeCast request with the shape of the node. ShapeCast is like raycast, but it doesn’t seep through small holes.

Fields:
  • nodeId : NodeId - The node whose shape will be used for the cast

  • from : float3 - The starting position of the cast

  • to : float3 - The ending position of the cast

  • collisionMask : uint = UINT_MAX - The collision mask to use for filtering collisions

BoxCast

Represents a ShapeCast request with a box shape. ShapeCast is like raycast, but it doesn’t seep through small holes.

Fields:
  • position : float3 - The position of the box

  • size : float3 = float3(1f,1f,1f) - The lengths of sides of the box

  • rotation : quat4 = quat4() - The rotation of the box

  • rayDirection : float3 - The direction of the cast

  • maxDistance : float = FLT_MAX - The maximum distance the cast can travel

  • collisionMask : uint = UINT_MAX - The collision mask to use for filtering collisions

SphereCast

Represents a ShapeCast request with a sphere shape. ShapeCast is like raycast, but it doesn’t seep through small holes.

Fields:
  • position : float3 - The position of the sphere

  • radius : float = 0.5f - The radius of the sphere

  • rayDirection : float3 - The direction of the cast

  • maxDistance : float = FLT_MAX - The maximum distance the cast can travel

  • collisionMask : uint = UINT_MAX - The collision mask to use for filtering collisions

CapsuleCast

Represents a ShapeCast request with a capsule shape. ShapeCast is like raycast, but it doesn’t seep through small holes.

Fields:
  • position : float3 - The center of the capsule

  • height : float = 2f - The full height of the whole capsule (including the spherical caps)

  • radius : float = 0.5f - The radius of the spherical caps

  • axis : AxisDirection = public_components::AxisDirection.YAxis - The axis of the orientation of the capsule

  • rotation : quat4 = quat4() - The additional orientation of the capsule

  • rayDirection : float3 - The direction of the cast

  • maxDistance : float = FLT_MAX - The maximum distance the cast can travel

  • collisionMask : uint = UINT_MAX - The collision mask to use for filtering collisions

BoxQuery

Represents a box for overlap queries.

Fields:
  • position : float3 - The position of the box

  • size : float3 = float3(0.5f,0.5f,0.5f) - The lengths of sides of the box

  • rotation : quat4 = quat4() - The rotation of the box

  • collisionMask : uint = UINT_MAX - The collision mask to use for filtering collisions

SphereQuery

Represents a sphere for overlap queries.

Fields:
  • position : float3 - The position of the sphere

  • radius : float = 1f - The radius of the sphere

  • collisionMask : uint = UINT_MAX - The collision mask to use for filtering collisions

CapsuleQuery

Represents a capsule for overlap queries.

Fields:
  • position : float3 - The center of the capsule

  • height : float = 2f - The full height of the whole capsule (including the spherical caps)

  • radius : float = 0.5f - The radius of the spherical caps

  • axis : AxisDirection = public_components::AxisDirection.YAxis - The axis of the orientation of the capsule

  • rotation : quat4 = quat4() - The additional orientation of the capsule

  • collisionMask : uint = UINT_MAX - The collision mask to use for filtering collisions

Handled structures

RaycastHit

The result of traces and overlaps.

Fields:
  • nodeId : NodeId - NodeId of the hitted object

  • position : Point3 - the position of the hit

  • normal : Point3 - the normal of the surface at the hit position. Faced generally in the opposite direction of the ray

  • distance : float - the distance to the hit position

OverlapHit

The result of an overlap query.

Fields:
  • nodeId : NodeId - NodeId of the hitted object

  • pointOnShape : Point3 - the point on the surface of the overlap shape

  • pointOnBody : Point3 - the point on the surface of the object that was hit

  • normal : Point3 - the normal of the surface at the hit position

  • distance : float - the distance from the center of the overlap shape to the pointOnBody

ShapeCastHit

The result of a shape casts.

Fields:
  • nodeId : NodeId - NodeId of the hitted object

  • offset : Point3 - the offset that the casted shape can move before it will collide with another object

  • normal : Point3 - the normal of the surface at the hit position. Faced generally in the opposite direction of the ray

  • distance : float - the length of the offset

Functions

apply_rigid_body_impulse(rigidBody: RigidBody?; impulse: float3)

Applies an impulse to the rigid body in the center of mass. This function implies that the impact is singular. To apply a continuous force, consider using apply_rigid_body_force().

Arguments:
  • rigidBody : RigidBody ? - RigidBody component

  • impulse : float3 - the impulse vector to apply

Usage example:

// Pushes the body in the positive X direction
get_component(nodeId) $(var rigidBody: RigidBody?) {
    apply_rigid_body_impulse(rigidBody, float3(1, 0, 0))
}
apply_rigid_body_impulse(rigidBody: RigidBody?; impulse: float3; worldPosition: float3)

Applies an impulse to the rigid body at a given point in world coordinates. This function implies that the impact is singular. To apply a continuous force, consider using apply_rigid_body_force().

Arguments:
  • rigidBody : RigidBody ? - RigidBody component

  • impulse : float3 - the impulse vector to apply

  • worldPosition : float3 - the point in world coordinates at which to apply the impulse

Usage example:

// Pushes the body in the positive X direction at the center of the body
get_component(nodeId) $(var rigidBody: RigidBody?) {
    apply_rigid_body_impulse(rigidBody, float3(1, 0, 0), nodeId.worldPosition)
}
apply_rigid_body_angular_impulse(rigidBody: RigidBody?; angularImpulse: float3)

Applies an angular impulse to the rigid body. This function implies that the impact is singular. To apply a continuous force, consider using apply_rigid_body_torque().

Arguments:
  • rigidBody : RigidBody ? - RigidBody component

  • angularImpulse : float3 - the angular impulse to apply

Usage example:

// Pushes the body to rotate around X axis
get_component(nodeId) $(var rigidBody: RigidBody?) {
    apply_rigid_body_angular_impulse(rigidBody, float3(1, 0, 0))
}
apply_rigid_body_force(rigidBody: RigidBody?; force: float3)

Applies a continuous force to the rigid body in the center of mass. This function should be called every frame while the force is active. For a singular impact use apply_rigid_body_impulse(), or just change the velocity.

Arguments:
  • rigidBody : RigidBody ? - RigidBody component

  • force : float3 - the force vector to apply

Usage example:

// Applies a continuous force in the positive X direction
get_component(nodeId) $(var rigidBody: RigidBody?) {
    apply_rigid_body_force(rigidBody, float3(1, 0, 0))
}
apply_rigid_body_force(rigidBody: RigidBody?; force: float3; worldPosition: float3)

Applies a continuos force to the rigid body at a given point in world coordinates. This function should be called every frame while the force is active. For a singular impact use apply_rigid_body_impulse(), or just change the velocity.

Arguments:
  • rigidBody : RigidBody ? - RigidBody component

  • force : float3 - the force vector to apply

  • worldPosition : float3 - the point in world coordinates at which to apply the force

Usage example:

// Applies a continuous force in the positive X direction at the center of the body
get_component(nodeId) $(var rigidBody: RigidBody?) {
    apply_rigid_body_force(rigidBody, float3(1, 0, 0), nodeId.worldPosition)
}
apply_rigid_body_torque(rigidBody: RigidBody?; torque: float3)

Applies a continuos torque to the rigid body. This function should be called every frame while the torque is active. For a singular impact use apply_rigid_body_impulse(), or just change the velocity.

Arguments:
  • rigidBody : RigidBody ? - RigidBody component

  • torque : float3 - the torque vector to apply

Usage example:

// Applies a continuous torque in the positive X direction
get_component(nodeId) $(var rigidBody: RigidBody?) {
    apply_rigid_body_torque(rigidBody, float3(1, 0, 0))
}
get_rigid_body_velocity_at(rigidBody: RigidBody?; worldPosition: float3): float3

Gets the velocity of the rigid body at a given point in world coordinates.

Arguments:
  • rigidBody : RigidBody ? - RigidBody component

  • worldPosition : float3 - the point in world coordinates at which to get the velocity

Returns:
  • float3 - the velocity vector at the specified position

Usage example:

// Gets the local velocity of the body at the given point
get_component(nodeId) $(var rigidBody: RigidBody?) {
    let velocity = get_rigid_body_velocity_at(rigidBody, nodeId.worldPosition + float3(1, 1, 1))
}
set_collision_between_all_layers(shouldCollide: bool)

Set whether collision should occur between all layers. By default, all layers collide with each other.

Arguments:
  • shouldCollide : bool - whether collision should occur

Usage example:

enum CollisionLayer {
    Layer1 = 0
    Layer2 = 1
}

// Disable collision between all layers to then only allow collision between Layer1 and Layer2
set_collision_between_all_layers(false)
set_collision_between_layers(CollisionLayer Layer1, CollisionLayer Layer2, false)
set_gravity(direction: float3)

Sets the gravity direction in the scene. Note: default gravity is float3(0.0, -9.8065, 0.0)

Arguments:
  • direction : float3 - the gravity direction vector

Usage example:

// Sets the gravity to point downward
set_gravity(float3(0.0, -9.8, 0.0))
get_gravity(): float3

Gets the gravity direction in the scene.

Returns:
  • float3 - the gravity direction vector

Usage example:

let gravity = get_gravity()
set_default_friction(friction: float)

Sets the default friction value for new colliders. Note: default friction is 0.5

See the guide on how to setup friction.

Arguments:
  • friction : float - the friction value to set

Usage example:

set_default_friction(1.)
get_default_friction(): float

Gets the default friction value for new colliders.

Returns:
  • float - the default friction value

Usage example:

let friction = get_default_friction()
set_default_restitution(restitution: float)

Sets the default restitution value for new colliders. Note: default restitution is 0.

Arguments:
  • restitution : float - the restitution value to set

Usage example:

set_default_restitution(0.5)
get_default_restitution(): float

Gets the default restitution value for new colliders.

Returns:
  • float - the default restitution value

Usage example:

let restitution = get_default_restitution()
trace(req: RayCast; blk: block<(hit:RaycastHit):void>): bool

Performs a raycast, and if there is a hit, calls the provided block for the resulting hit.

Arguments:
  • req : RayCast - the raycast request

  • blk : block<(hit: RaycastHit ):void> - the block to call for the resulting hit

Returns:
  • bool - true if any hits occurred, false otherwise

Usage example:

let hadHit = trace(RayCast(ray = Ray(float3(0), float3(1, 0, 0)))) $(hit) {
    print("Resulted position: {hit.position}")
    print("Resulted node: {hit.nodeId}")
    print("Resulted normal: {hit.normal}")
    print("Resulted distance: {hit.distance}")
}
trace_all(req: RayCast; blk: block<(hits:array<RaycastHit>#):void>): int

Performs a raycast, collects all hits into an array, and calls the provided block for the array of hits. Hits are not sorted by distance.

Arguments:
  • req : RayCast - the raycast request

  • blk : block<(hits:array< RaycastHit >#):void> - the block to call for the array of hits

Returns:
  • int - the number of hits

Usage example:

trace_all(RayCast(ray = Ray(float3(0), float3(1, 0, 0)))) $(hits : array<RaycastHit>#) {
    for (hit in hits) {
        print("Resulted position: {hit.position}")
        print("Resulted node: {hit.nodeId}")
        print("Resulted normal: {hit.normal}")
        print("Resulted distance: {hit.distance}")
    }
}
sweep_test(req: NodeCast; blk: block<(hit:ShapeCastHit):void>): bool

Checks if a node would collide with anything if it moved in a given direction, and if there is a hit, calls the provided block for the resulting hit. The node is required to have a Collider component.

  • Does not collide with the source body.

  • Returns the single nearest collision only, or none.

Arguments:
  • req : NodeCast - the node shape cast request

  • blk : block<(hit: ShapeCastHit ):void> - the block to call for the resulting hit

Returns:
  • bool - true if any hits occurred, false otherwise

Usage example:

sweep_test(NodeCast(nodeId=someNodeId, from=float3(0), to=float3(1, 0, 0))) $(hit) {
    pos = pos + hit.offset
}
trace(req: BoxCast; blk: block<(hit:ShapeCastHit):void>): bool

Performs a box shape cast, and if there is a hit, calls the provided block for the resulting hit.

Arguments:
  • req : BoxCast - the box shape cast request

  • blk : block<(hit: ShapeCastHit ):void> - the block to call for the resulting hit

Returns:
  • bool - true if any hits occurred, false otherwise

Usage example:

trace(BoxCast(position=float3(0), size=float3(1.0), rayDirection=float3(1, 0, 0))) $(hit) {
    pos = pos + hit.offset
}
trace(req: SphereCast; blk: block<(hit:ShapeCastHit):void>): bool

Performs a sphere shape cast, and if there is a hit, calls the provided block for the resulting hit.

Arguments:
  • req : SphereCast - the sphere shape cast request

  • blk : block<(hit: ShapeCastHit ):void> - the block to call for the resulting hit

Returns:
  • bool - true if any hits occurred, false otherwise

Usage example:

trace(SphereCast(position=float3(0), radius=0.5, rayDirection=float3(1, 0, 0))) $(hit) {
    pos = pos + hit.offset
}
trace(req: CapsuleCast; blk: block<(hit:ShapeCastHit):void>): bool

Performs a capsule shape cast, and if there is a hit, calls the provided block for the resulting hit.

Arguments:
  • req : CapsuleCast - the capsule shape cast request

  • blk : block<(hit: ShapeCastHit ):void> - the block to call for the resulting hit

Returns:
  • bool - true if any hits occurred, false otherwise

Usage example:

trace(CapsuleCast(position = float3(0), height = 2., radius = 0.5, rayDirection=float3(1, 0, 0))) $(hit) {
    pos = pos + hit.offset
}
trace_all(req: BoxCast; blk: block<(hits:array<ShapeCastHit>#):void>): int

Performs a box shape cast, collects all hits into an array, and calls the provided block for the array of hits. Hits are not sorted by distance.

Arguments:
  • req : BoxCast - the box shape cast request

  • blk : block<(hits:array< ShapeCastHit >#):void> - the block to call for the array of hits

Returns:
  • int - the number of hits

Usage example:

trace_all(BoxCast(position=float3(0), size=float3(1.0), rayDirection=float3(1, 0, 0))) $(hits : array<ShapeCastHit>#) {
    sort(hits) $(a, b : ShapeCastHit) { return a.distance < b.distance; }
    for (hit in hits) {
        print("Hit: {pos + hit.offset}")
    }
}
trace_all(req: SphereCast; blk: block<(hits:array<ShapeCastHit>#):void>): int

Performs a sphere shape cast, collects all hits into an array, and calls the provided block for the array of hits. Hits are not sorted by distance.

Arguments:
  • req : SphereCast - the sphere shape cast request

  • blk : block<(hits:array< ShapeCastHit >#):void> - the block to call for the array of hits

Returns:
  • int - the number of hits

Usage example:

trace_all(SphereCast(position=float3(0), radius=0.5, rayDirection=float3(1, 0, 0))) $(hits : array<ShapeCastHit>#) {
    sort(hits) $(a, b : ShapeCastHit) { return a.distance < b.distance; }
    for (hit in hits) {
        print("Hit: {pos + hit.offset}")
    }
}
trace_all(req: CapsuleCast; blk: block<(hits:array<ShapeCastHit>#):void>): int

Performs a capsule shape cast, collects all hits into an array, and calls the provided block for the array of hits. Hits are not sorted by distance.

Arguments:
  • req : CapsuleCast - the capsule shape cast request

  • blk : block<(hits:array< ShapeCastHit >#):void> - the block to call for the array of hits

Returns:
  • int - the number of hits

Usage example:

trace_all(CapsuleCast(position = float3(0), height = 2., radius = 0.5, rayDirection=float3(1, 0, 0))) $(var hits : array<ShapeCastHit>#) {
    sort(hits) $(a, b : ShapeCastHit) { return a.distance < b.distance; }
    for (hit in hits) {
        print("Hit: {pos + hit.offset}")
    }
}
overlap(req: BoxQuery; blk: block<(hits:array<OverlapHit>#):void>): int

Gets all nodes that overlap with the box, collects them into an array, and calls the provided block for the array of hits. Hits are not sorted by distance.

Arguments:
  • req : BoxQuery - the box query request

  • blk : block<(hits:array< OverlapHit >#):void> - the block to call for the array of hits

Returns:
  • int - the number of hits

Usage example:

overlap(BoxQuery(position=float3(0), size=float3(1.0))) $(hits : array<OverlapHit>#) {
    for (hit in hits) {
        print("Collided with node: {hit.nodeId}")
    }
}
overlap(req: SphereQuery; blk: block<(hits:array<OverlapHit>#):void>): int

Gets all nodes that overlap with the sphere, collects them into an array, and calls the provided block for the array of hits. Hits are not sorted by distance.

Arguments:
  • req : SphereQuery - the sphere query request

  • blk : block<(hits:array< OverlapHit >#):void> - the block to call for the array of hits

Returns:
  • int - the number of hits

Usage example:

overlap(SphereQuery(position=float3(0), radius=0.5)) $(hits : array<OverlapHit>#) {
    for (hit in hits) {
        print("Collided with node: {hit.nodeId}")
    }
}
overlap(req: CapsuleQuery; blk: block<(hits:array<OverlapHit>#):void>): int

Gets all nodes that overlap with the capsule, collects them into an array, and calls the provided block for the array of hits. Hits are not sorted by distance.

Arguments:
  • req : CapsuleQuery - the capsule query request

  • blk : block<(hits:array< OverlapHit >#):void> - the block to call for the array of hits

Returns:
  • int - the number of hits

Usage example:

overlap(CapsuleQuery(position = float3(0), height = 2., radius = 0.5)) $(hits : array<OverlapHit>#) {
    for (hit in hits) {
        print("Collided with node: {hit.nodeId}")
    }
}
set_physics_fps(fps: float)

Sets the fixed frames-per-second for the physics. This have no effect on the get_delta_time() and has influence only on the stability of the physics. The more FPS, the more stability. Note that if you want to set the maximum FPS, you better use set_target_fps.

Minimum value is 30

Default value is 60

Maximum value is 120

Usage example:

set_physics_fps(100f)
Arguments:
  • fps : float - the new fixed frames-per-second for the physics

get_physics_fps(): float

Gets the fixed timestep for the physics. Note that if you want to get the frame time, you should use get_delta_time().

Returns:
  • float - frames per second of the physics simulation

on_collision(nodeId: NodeId; callback: lambda<(collision:Collision):void>): LambdaCollisionListener?

Registers a callback that is called when another node with a RigidBody component collides with this node.

The callback is called for every new collision.

Returns an added collision handler component.

Note

MotionType Static is a cheapest option to receive callbacks, but it has its limitations, see MotionType

Usage example:

on_collision(node, @(collision) {
    print("Collision between {node} and {collision.node}")
})
Arguments:
on_collision_stay(nodeId: NodeId; callback: lambda<(collision:Collision):void>): LambdaCollisionListener?

Registers a callback that is called when another node with a RigidBody component collides with this node.

The callback is called every frame for every collision that stays between frames.

Returns an added collision handler component.

Note

MotionType Static is a cheapest option to receive callbacks, but it has its limitations, see MotionType

Usage example:

on_collision_stay(node, @(collision) {
    print("Collision stays between {node} and {collision.node}")
})
Arguments:
on_collision_exit(nodeId: NodeId; callback: lambda<(otherNode:NodeId):void>): LambdaCollisionListener?

Registers a callback that is called when another node with a RigidBody component collides with this node.

The callback is called for every ended collision.

Returns an added collision handler component.

Note

MotionType Static is a cheapest option to receive callbacks, but it has its limitations, see MotionType

Usage example:

on_collision_exit(node, @(otherNode) {
    print("Collision finished between {node} and {otherNode}")
})
Arguments:
  • nodeId : NodeId

  • callback : lambda<(otherNode: NodeId ):void>

set_collision_layer_mask(layer: auto(TT)|int; collisionMask: uint)

Enables or disables collisions between the specified layer and all other layers using bitmask. Note that this function affects not only the collision mask of the specified layer but also the collision masks of all other layers with respect to the specified layer, because of the symmetric nature of collisions.

Arguments:
  • layer : option<auto(TT)|int> - the collision layer to set

  • collisionMask : uint - the collision mask to set

Usage example:

enum CollisionLayer {
    Default = 0
    Layer1 = 1
    Layer2 = 2
}

// After this call, Default layer will collide with Layer1 and Layer2 but not with other layers
set_collision_layer_mask(CollisionLayer Default, 1u << uint(CollisionLayer Layer1) | 1u << uint(CollisionLayer Layer2))
set_collision_between_layers(layer1: auto(TT)|int; layer2: auto(TT)|int; shouldCollide: bool)

Sets whether collision should occur between two layers.

Arguments:
  • layer1 : option<auto(TT)|int> - the first collision layer

  • layer2 : option<auto(TT)|int> - the second collision layer

  • shouldCollide : bool - whether collision should occur between the layers

Usage example:

enum CollisionLayer {
    Layer1 = 0
    Layer2 = 1
}

// Disable collision between Layer1 and Layer2
set_collision_between_layers(CollisionLayer Layer1, CollisionLayer Layer2, false)