Ultimate Water 2D / Docs v1.0.0Web demo Back to website

Water Sensor 2D

Purpose and setup

WaterSensor2D answers questions such as “Is this character touching water?”, “Is its head submerged?”, and “What current acts at its chest?” It reports information and events; it does not move the object.

  1. Add Water Sensor 2D to the object being measured.
  2. Assign Touch Collider to the collider that first meets the water. Assign Body Collider to the collider used to calculate how much of the object is submerged. If you leave these empty, the sensor looks for a collider on the same object and reuses the touch collider for both jobs.
  3. Start with Collider Based sampling. For most characters and objects, the collider calculation is sufficient and the shallow, half, swimming, and full-submersion thresholds provide enough control over state transitions.
  4. Add feet, waist, swim/chest, and head transforms only when the character needs more precise control than collider percentages provide. These sample points are optional. After assigning them, choose Point Based for point-driven states or Hybrid for point-driven states with collider-based Submersion01.
  5. Optionally assign Force Sample Point when currents and ripple forces should be sampled at a deliberate body position. Otherwise the sensor uses the swim point, waist point, or object transform in that order.
  6. Keep automatic FixedUpdate sampling for ordinary physics use. For your own movement loop, disable Update Automatically and call Refresh() before reading the data.
  7. Enter Play mode and inspect the state as the object crosses the waterline. Tune collider thresholds first; add explicit sample points only if the resulting transitions need more control.

The sensor finds nearby water automatically, including when the object starts the scene already submerged. In normal use, add and configure the sensor without touching any generated water-trigger objects. Advanced code can assign a known water surface with SetCurrentWater(...) when automatic detection is not appropriate.

Sampling modes

Mode State calculation Submersion01
ColliderBased Uses configurable body-submersion thresholds. Estimated percentage of the collider bounds’ vertical height below the sampled surface.
PointBased Uses feet, waist, swim, and head positions. A representative value derived from the point state.
Hybrid Uses sample-point states. Uses the collider percentage when a body collider exists; otherwise it calculates a representative value from the assigned points.

Collider Based is the recommended starting point for most use cases. It estimates submersion from the collider’s vertical bounds and exposes thresholds for each meaningful state, which is generally sufficient for swimming, animation, oxygen, and movement transitions. It is an estimate rather than an exact submerged area or volume calculation for an arbitrary shape.

Point Based and Hybrid are optional refinements. Use them when a character’s proportions, pose, collider shape, or gameplay rules require named body locations to cross the waterline at exact moments. Missing point transforms fall back to positions derived from the collider bounds, but assigning only the points you genuinely need keeps the setup easier to maintain.

Default collider thresholds are 0.15 (shallow), 0.45 (half), 0.65 (swimming), and 0.95 (full). They are kept in ascending order. These are component defaults; a prefab can override them.

States and readings

The states are Dry, TouchingWater, ShallowSubmerged, HalfSubmerged, SwimmingDepth, and FullySubmerged. When optional point sampling is enabled, the feet, waist, swim, and head positions define increasing levels of submersion.

Reading Use
State, PreviousState Animation and gameplay transitions.
HasWater A water surface is available for the current readings; this alone does not mean the head or body is submerged.
IsTouchingWater Contact with the water region.
IsInsideWater, IsInsideWaterVolume Reports whether the sampled position is inside the usable water area. This is different from the percentage of the body that is submerged.
IsSwimmingDepth, IsFullySubmerged Convenient swimming and head-underwater decisions.
Submersion01 Normalized estimated submersion for blending effects.
SurfaceY, SurfacePoint Sampled waterline location for effects or movement targets.
Depth, NormalizedDepth Depth readings at the force sample point.
HasCurrent, CurrentDirection, CurrentStrength Whether a current was sampled, its normalized direction, and its falloff-adjusted acceleration magnitude. TryGetCurrent(...) reads the same direction and strength together.
CurrentMovementForceDivider Divider for player-authored movement in the sampled current. It is 1 at a zone edge and blends toward the zone’s configured Player Movement Divider through Entry Falloff. The sensor reports the value; the controller chooses which movement input to divide.
CurrentRaw, CurrentForce, RippleForce, WaterForce CurrentRaw is direction × current strength; CurrentForce is its compatibility alias. Ripple and combined force-like readings remain separate so custom code can choose its own response. WaterCharacter2D does not integrate any current value.
CurrentWater Built-in WaterPhysics, or null for a different compatible surface implementation.
CurrentWaterSurface The surface through the shared sampling interface.

Ignore Ripple Forces removes ripples from the combined WaterForce; it still reports RippleForce separately. This is useful when you want characters to respond to currents without every impact pushing them around.

Events and their meaning

Events are available both as Inspector UnityEvents (onWaterEntered, etc.) and as C# events (WaterEntered, etc.). The event data includes the water reference, previous and new states, surface point, impact amount, submersion, whether the object entered through the surface, and whether it left the usable water area.

  • WaterEntered is filtered for entry through the surface from above. It is suitable for entry splashes; do not use it as the only detector for all dry-to-wet transitions, side entry, or starting underwater.
  • WaterExited fires when the sensor leaves its previous water body. For general wet/dry behavior, also check the current state and whether the object is still inside the usable water area.
  • StateChanged reports changes between the submersion states.
  • Surfaced fires when the state moves from SwimmingDepth or deeper to a shallower state. Leaving the water area can also cause it, so it does not only mean that the character’s head crossed the surface.
  • FullySubmerged fires on entering the FullySubmerged state.

Use enteredThroughSurface to filter splash logic and leftWaterDomain to distinguish an exit from the supported water region. For example, an oxygen system should evaluate IsFullySubmerged each update or use StateChanged; it should not rely on an entry splash event.

Example: read submersion

This component owns the sensor’s update timing. Attach it alongside Water Sensor 2D; do not also refresh that sensor from another custom update owner.

C#
using DW_UWS2D;
using UnityEngine;

[RequireComponent(typeof(WaterSensor2D))]
public sealed class WaterStateReader : MonoBehaviour
{
    private WaterSensor2D sensor;
    public bool HeadUnderwater { get; private set; }
    public float Submersion { get; private set; }

    private void Awake()
    {
        sensor = GetComponent<WaterSensor2D>();
        sensor.updateAutomatically = false;
    }

    private void FixedUpdate()
    {
        sensor.Refresh();
        HeadUnderwater = sensor.HasWater && sensor.IsFullySubmerged;
        Submersion = sensor.HasWater ? sensor.Submersion01 : 0f;
        // Read these values from your animation, audio, or oxygen system.
    }
}