Static Colliders
Introduction
Adding static colliders to a Scene takes three simple steps:
- Attach a Quantum Static Collider Script to a Unity GameObject;
- Edit the properties to resemble the desired geometry for the static obstacle in the scene; and,
- Bake the Scene via the
QuantumMapData
Script.
Unity Collider as a Source
The Quantum static collider can also mirror the properties from a Unity collider.
To do that, simply drag and drop the desired collider into the Source Collider
field on the Quantum Static Collider component:
Shapes
The 2D Physics shapes are:
- Circle
- Box
- Polygon
- Edge
N.B.: All of them have a Height
field, which allows the creation of 2.5D shapes. Click here for more information on 2.5D Physics.
The 3D Physics shapes are:
- Sphere
- Box
- Mesh
- Terrain
Configuration
Static colliders can be fitted with a PhysicsMaterial and an Asset. The latter is available in the simulation via the collision callbacks.
Smooth Sphere Mesh Collider
A Static Mesh Collider 3D
has an option called Smooth Sphere Mesh Collision
. When toggling this option on, the physics solver will resolve sphere-mesh collisions as if the mesh was a regular flat and smooth plane. This prevents adding spin to a sphere colliding with triangle edges.
Enable / Disable at Runtime
This section will present several approaches to enable and disable static colliders at runtime in simulation.
Physics Engine
It is possible to toggle static colliders on and off at runtime directly in the Physics Engine.
For a static collider to be toggle-able, its Mode
needs to be set at edit-time (Unity) and baked into the Map
asset. The mode can be set to:
Immutable
(default): the collider cannot be enabled or disabled at runtime.Toggleable Start On
: the collider can be toggled at runtime and starts enabled.Toggleable Start Off
: the collider can be toggled at runtime and starts disabled.
Once a static collider has been marked as toggle-able and baked, it becomes possible to enable and disable the collider at runtime from the simulation (Quantum) using SetStaticColliderEnabled()
in Frame.Physics3D
and Frame.Physics2D
for 3D and 2D static colliders respectively.
The index
to be passed as a parameter is the collider's index in the frame.Map.StaticColliders
array. Collision callbacks return the index (ColliderIndex
) of a static collider as part of the StaticData
in their TriggerInfo
or CollisionInfo
.
IMPORTANT: A disabled static mesh collider is ignored by physics queries and will not trigger collision signals.
Manual Tracking
Although static colliders can be enabled / disabled at the physics engine level, there are various approaches to do the same manually.
Keep a global bitset for the state
If the only purpose is to keep track of which static colliders to ignore or take into account in a collision callback, the most convenient approach is to define a global BitSet
which is of the same length or bigger than the frame.Map.StaticColliders
array. This can be done as part of the Frame
object or as a singleton component.
C#
singleton component StaticColliderState {
bitset[256] colliders;
}
This allows to use the bitset instance with the collider indices to set its bits.
C#
// loops through the bitset to initialize all bits as "On" to mark all colliders as active
public override void OnInit(Frame f)
{
var collidersState = f.Unsafe.GetPointerSingleton<StaticColliderState>();
for (int i = 0; i < collidersState->colliders.Length; i++) {
collidersState->colliders.Set(i);
}
}
public void OnTrigger3D(Frame frame, TriggerInfo3D info)
{
if (info.IsStatic == false) return;
// Use a custom asset slotted in the UserAsset field to identify toggleable colliders
var colliderAsset = frame.FindAsset<MyColliderAsset>(info.StaticData.Asset);
if (colliderAsset == null) return;
var collidersState = frame.Unsafe.GetPointerSingleton<StaticColliderState>();
collidersState->colliders.Clear(info.StaticData.ColliderIndex);
}
The values can then be read using IsSet()
and used to check whether a collision signal should be handled or ignored. This is particularly useful when dealing with static interactable objects, environmental barriers or implementing IKCCCallbacks3D
for movement.
Toggle with Behaviour
Static colliders are assets, i.e. they are stateless and immutable at runtime. However, there are instance where static objects should be enabled / disabled based on dynamic conditions.
For example, pick-ups can usually be represented with a static position and a trigger collider; turning those into static colliders will avoid the over associated with dynamic entities. Unfortunately, the timer commonly to re-spawn a power-up after its pick-up cooldown requires a state. It is possible to solve this conundrum by extending the concept presented in the previous section.
First, the state of the static colliders representing power-ups needs to be held somewhere.
C#
singleton component PowerUps {
[ExcludeFromPrototype] bitset[256] IsPowerUp;
[ExcludeFromPrototype] bitset[256] State;
[ExcludeFromPrototype] array<FP>[256] Timers;
FP SpawnCooldown;
}
Then a system can be created to handled the enabling and disabling of the power-ups.
C#
public unsafe class MyPowerUpSystem : SystemMainThread {
public override void OnInit(Frame f)
{
var powerUps = f.Unsafe.GetPointerSingleton<PowerUps>();
for (int i = 0; i < powerUps->IsPowerUp.Length; i++)
{
var powerUp = f.FindAsset<MyPowerUpAsset>(f.Map.StaticColliders3D[i].StaticData.Asset);
if (powerUp == null) {
powerUps->IsPowerUp.Clear(i);
continue;
}
powerUps->IsPowerUp.Set(i);
powerUps->State.Set(i);
powerUps->Timers[i] = FP._0;
}
}
public override void Update(Frame f){
var powerUps = f.Unsafe.GetPointerSingleton<PowerUps>();
for (int i = 0; i < powerUps->IsPowerUp.Length; i++)
{
if (powerUps->IsPowerUp.IsSet(i) == false) continue;
if (powerUps->State.IsSet(i)) continue;
powerUps->Timers[i] -= f.DeltaTime;
if(powerUps->Timers[i] > 0) continue;
powerUps->State.Set(i);
// Other code visualizing the spawned / re-enabled power-up
// can use frame event to trigger VFX, SFX, re-enable visual / GameObject
}
}
public void OnTrigger3D(Frame f, TriggerInfo3D info)
{
if(info.IsStatic == false) return;
var powerUps = f.Unsafe.GetPointerSingleton<PowerUps>();
if(powerUps->IsPowerUp.IsSet(info.StaticData.ColliderIndex) == false) return;
if(powerUps->State.IsSet(info.StaticData.ColliderIndex) == false) return;
powerUps->State.Clear(info.StaticData.ColliderIndex);
powerUps->Timers[info.StaticData.ColliderIndex] = powerUps->SpawnCooldown;
// Remember to communicate the disabled state visually, e.g. trigger a frame event to disable the GameObject in Unity
}