Working With Physics

Working with Physics

When adding Components in the Studio like models or avatars, you need to attach a Collider in order for collisions to work on them.

This section of the Getting Started tutorial explains how to attach a Collider on the Studio.

As noted previously, Physics and collisions in oncyber are handled via the Rapier (opens in a new tab) library. Rapier is a fast physics engine written using the Rust programming language.

The settings of the Collider section in the Studio largely mirror the ones used by Rapier. Rigid Bodies can be Fixed, Dynamic, or Kinematic:

  • Fixed: for colliders that don't move or rotate (ie. walls, floors, and other static environmental objects)
  • Dynamic: for rigidbodies that you want to be affected dynamically by the full physics system and pushed around (ie. a moving ball -- keep in mind this is the most performance-heavy option and should be used sparingly)
  • Kinematic: for colliders that can only be moved programmatically by code -- these are not affected by gravity and cannot be pushed by other forces (ie. a moving platform with a set path)

For the Collider type, you can either choose a basic predefined shape (Cube, Sphere, etc), or use the model's actual mesh as the Collider.

Tip: When possible, opt for one of the predefined shapes instead of Mesh for the Collider type, as this will make the physics simulations much faster and more performant.

Colliders can also be Sensors. Unlike standard Colliders, Sensors do not physically block the player or other objects from passing through them -- however, you can still receive collision events from them.

A standard Collider is typically used for an object like an obstacle or terrain that you don't want the player to pass through, whereas a Sensor Collider would be used for an object that needs to pass through the player -- a coin pickup, for example, or a damage area.

To access the created the Rapier entities for a Component, you can use the rigidBody property of the Component. From there you can get the Rapier rigidBody and the Collider:

const myComponent = Components.byId("component-id");
 
// Get our RigidBody wrapper
const rigidBody = myComponent.rigidBody
 
// Get the component collider
const collider = myComponent.collider
 
// Get the rapier rigidBody object
const rigidBodyRapier = myComponent.collider.rigidBody
 
// Get all the rapier colliders
const colliders = myComponent.rigidBody.colliders

For more information on how to use Rapier objects, see the Collider and Rigid Body reference pages.

If you want to take an even deeper dive on how all of this works, you can visit Rapier's documentation for Rigid Bodies (opens in a new tab) and Colliders (opens in a new tab).

Manually Listening for Collision Events

The following code shows how to listen for Collision Events:

const myComponent = Components.byId("component-id");
 
const unsubscribe = myComponent.onCollisionEnter((collision) => {
    console.log("new collision", collision.me, collision.other, collision)
})
 
// ...
 
unsubscribe()

This function will be called when an object enters a Collison zone with our Component.

Using onCollisionExit you can detect when an object is not colliding anymore with it.

onCollisionStay is called every frame between both events.

When one of the two colliding objects is a Sensor, we need to use onSenserEnter instead. For example:

const myComponent = Components.byId("component-id");
 
myComponent.onCollisionEnter(() => console.log("collision enter"))
myComponent.onCollisionStay(() => console.log("is colliding"))
myComponent.onCollisionExit(() => console.log("collision exit"))
myComponent.onSensorEnter(() => console.log("sensor enter"))
myComponent.onSensorStay(() => console.log("in sensor"))
myComponent.onSensorExit(() => console.log("sensor exit"))

Physics for Behaviors and Components

If you're working on the script for a Component or Behavior, you have access to special methods that work with physics directly:

import { ScriptBehavior } from '@oo/scripting'
 
export default class ExampleBehavior extends ScriptBehavior {
 
    onReady = () => {
        const collider = this.host.collider // for components this.collider
        const rigidBody = this.host.rigidBody // for components this.rigidBody
    }
 
    handleCollisionEnter = (collision) => {
        console.log("collision enter", collision)
    }
 
    handleCollisionStay = (collision) => {
        console.log("collision stay", collision)
    }
 
    handleCollisionExit = (collision) => {
        console.log("collision exit", collision)
    }
 
    handleSensorEnter = (collision) => {
        console.log("sensor enter", collision)
    }
 
    handleSensorStay = (collision) => {
        console.log("sensor stay", collision)
    }
 
    handleSensorExit = (collision) => {
        console.log("sensor exit", collision)
    }
}