terminalCode Example

Automatic water volume setup with buoyancy physics

calendar_today May 12, 2026 schedule ~1 min read person patrickjr verified 50

Automatic Water Volume Setup with Buoyancy

This example shows automatic attachment of WaterVolume components to colliders tagged "water" and buoyancy physics implementation.

CSHARP
using Sandbox.UI;

public partial class WaterVolume : Component, Component.ITriggerListener
{
    List<Rigidbody> Bodies = new();

    protected override void OnFixedUpdate()
    {
        if (Bodies is null) return;

        var collider = GetComponent<BoxCollider>();
        var waterSurface = WorldPosition + Vector3.Up * (collider.Scale.z * 0.5f);
        var waterPlane = new Plane(waterSurface, Vector3.Up);

        for (int i = Bodies.Count - 1; i >= 0; i--)
        {
            var body = Bodies[i];
            if (!body.IsValid())
            {
                Bodies.RemoveAt(i);
                continue;
            }

            body.ApplyBuoyancy(waterPlane, Time.Delta);
        }
    }

    void ITriggerListener.OnTriggerEnter(Collider other)
    {
        var body = other.GameObject.Components.Get<Rigidbody>(FindMode.EverythingInSelfAndParent);
        if (body.IsValid() && !Bodies.Contains(body))
        {
            Bodies.Add(body);
        }
    }

    void ITriggerListener.OnTriggerExit(Collider other)
    {
        var body = other.GameObject.Components.Get<Rigidbody>(FindMode.EverythingInSelfAndParent);
        if (body.IsValid())
        {
            Bodies.Remove(body);
        }
    }
}

public sealed partial class GameManager : ISceneLoadingEvents
{
    void ISceneLoadingEvents.AfterLoad(Scene scene)
    {
        var waterVolumes = scene.GetAll<Collider>().Where(x => x.Tags.Has("water"));
        if (waterVolumes.Count() < 1) return;

        foreach (var volume in waterVolumes)
        {
            volume.GetOrAddComponent<WaterVolume>();
        }
    }
}

Key Features

  • Automatic Setup: GameManager automatically adds WaterVolume to any collider with "water" tag after scene load
  • Buoyancy Physics: Uses Rigidbody.ApplyBuoyancy(Plane, float) for realistic water physics
  • Water Surface Calculation: Calculates water plane from BoxCollider's top face (Scale.z * 0.5)
  • Trigger Tracking: Tracks Rigidbody objects entering/exiting the water volume
  • Cleanup: Automatically removes invalid Rigidbodies from tracking list
  • FindMode.EverythingInSelfAndParent: Finds Rigidbody on the collider or any parent GameObject
  • GetOrAddComponent: Only adds WaterVolume if it doesn't already exist
Was this helpful?