Table of Contents

Scripting API

Brief description of the most important classes and functions.
Almost every parameter in every component can be changed runtime from code.
For exact behaviour and more - check source code.

DragonWaterManager

This is main singleton class that controls everything about the water.

public class DragonWaterManager
{
    // static getter to get singleton instance
    public static DragonWaterManager Instance { get; }
    // acces to configuration scriptable object
    public DragonWaterConfig Config { get; }
 
 
    public float Time { get; set; } // current simulation time - if you disable auto simulation, you''ll have to update this value manually every frame
    public WaterSurface UnderwaterSurface { get; } // surface that main camera is currently below; null if none
    public WaterSampler.HitResult CameraHitResult { get; } // last camera hit test against water, used for auto snapping and underwater detection
    public Camera MainCamera { get; } // refers to Camera.Main
 
 
    // subscribe to this event to receive callback when camera changes underwater state
    // both, from and to param can be null depending on if you dive, resurface, or change between surface under water
    public delegate void UnderwaterChangedCallback(WaterSurface from, WaterSurface to);
    public event UnderwaterChangedCallback OnUnderwaterChanged;
 
 
    // collections
    public IReadOnlyList<WaterSurface> Surfaces { get; } // currently active surfaces
    public WaterSurface DefaultSurface { get; } // current default and active surface, null if not set
    public IReadOnlyList<WaterRippleProjector> { get; } // currently active ripple projectors
    public IReadOnlyList<LocalWaveArea> { get; } // currently active local wave areas
 
 
    // culling - used internally by WaterSampler
    public void CullWaterSurfaces(ref Bounds bounds, ref List<WaterSurface> list);
    public void CullCutoutColliders(ref Bounds bounds, ref List<Collider> list);
 
 
    // sampling water
    public WaterSampler.HitResult SampleWater(Vector3 position); // calls SampleWater with considerCutouts=true as default
    public WaterSampler.HitResult SampleWater(Vector3 position, bool considerCutouts);
}

WaterSampler

The most usefull class.
Create it's object to sample water.
You can reuse this object (keep calling Schedule and Complete) but always remember to Dispose() when no longer needed.

public class WaterSampler : IDisposable
{
    public static IReadOnlyList<WaterSampler> ActiveSamplers { get; } // access to all currently active samplers
 
 
    public enum SurfaceDetectionMode
    {
        Default, // use default surface, if its null then fallbacks to AutoCull
        AutoCull, // automatically scans nearby surfaces
        Custom // tests only aainst provided surfaces in SurfaceDetectionCustom list
    }
 
    public enum CutoutDetectionMode
    {
        AutoCull, // automatically scans nearby colliders to cutout
        DontCutout, // do not care about cutouts at all - always sample
        Custom // tests only against provided cutouts in CutoutDetectionCustom list
    }
 
 
    public int Precision = 0; // amount of iterations; 0 refers to Constants.DefaultSamplerPrecision and 3-4 is most cases is more than enough
    public SurfaceDetectionMode SurfaceDetection = SurfaceDetectionMode.Default; // how nearby surfaces are detected
    public CutoutDetectionMode CutoutDetection = CutoutDetectionMode.AutoCull; // how to to process cutout volumes
    public List<WaterSurface> SurfaceDetectionCustom = null; // you NEED to assign it in case of Custom mode
    public List<WaterCutoutVolume> CutoutDetectionCustom = null; // you NEED to assign it in case of Custom mode
    public Bounds CullingBox = default; // if left default, it will automatically create bounding box for all points before scheduling
 
    public int MaxSize { get; }; // maximum amount of points this sampler can process; assigned via constructor
    public bool IsRunning { get; }; // if its running, you shouldn't modify anything here
    public HitResult[] Results { get; } // access to array of results of last scan - it's size is always MaxSize even if you ran it for less points
    public int ResultCount { get; } // actual amount of available results in Results array
 
 
    public WaterSampler(int maxSize = 1); // construction - you have to specify  max size
    public void Dispose(); // ALWAYS remember to dispose no longer needed sampler
    public void Resize(int newMaxSize); // resize to new MaxSize
 
    // fill points to test against water
    public void SetPoint(int index, Vector3 point); // set single point and specified index
    public void SetPoints(IReadOnlyList<Vector3> points); // set points since index 0 
    public void SetPoints(ArraySegment<Vector3> points); // set points since index 0 
 
    // schedule
    public void Schedule(); // calls Schedule with MaxSize
    public void Schedule(int pointsCount); // amount of points to process starting from index 0; can't be larger than MaxSize
    public void Complete(); // complete processing - recommended to call next frame
 
    // caching auto culls
    // be default AutoCull modes are very expensive - with this functions you can temporarily cache their result so it will act like Custom options
    public void CacheAutoCull(); // calls CacheAutoCull with MaxSize
    public void CacheAutoCull(int points);
    public void ClearAutoCullCache();
 
    // clears sampler until next scheduling, so it acts like not scheduled yet at all with no results
    public void ClearState();
}

WaterSampler.HitResult

Struct that contains sampling result of a single point against water.

public struct HitResult
{
    public Vector3 sampledPoint;
 
    public WaterSurface surface; // null if no surface detected
    public Vector3 hitPoint; // actual position on water surface, possibly very closely to sampledPoint
    public Vector3 hitNormal; // normal vector of water here
 
    public bool HasHit => surface != null;
    public bool IsUnderwater => sampledPoint.y < hitPoint.y;
    public float Depth => hitPoint.y - sampledPoint.y; // positive when under water
    public float Height => sampledPoint.y - hitPoint.y; // positive when above water
    public float WaterLevel => hitPoint.y;
}

WaterBehaviour

Abstract MonoBehaviour class than can be used as utility for easier water sampling from your game object component.
You can treat is as a MonoBehaviour facade to WaterSampler.
It uses WaterSampler asyncrhonously, so it always generates one frame delay while sampling water, which is totally fine in most cases.

public abstract class WaterBehaviour : MonoBehaviour
{
    // used to configure component from inspector
    [Serializable]
    public class WaterSamplerConfig { /* .. */ }
 
    // access to config that is exposed for configuration via inspector
    public WaterSamplerConfig SamplerConfig {get; };
    public int FrameDividerOffset { get; };
 
    // check this inside InnerFixedUpdate if you want to schedule
    protected bool isSchedulingFrane { get; };
    // direct access to array with points you are going to sample
    protected Vector3[] samplingPoints { get; };
 
 
    // Unity callbacks
    protected virtual void Awake();
    protected virtual void OnEnable();
    protected virtual void OnDisable();
    protected virtual void OnDestroy();
    protected virtual void OnFixedUpdate(); // in most cases, do not override this function (see InnerFixedUpdate)
 
 
    // prepare sampler side before settingg any points
    protected void PrepareSamplingPointsSize(int size);
    protected void SetSamplingPoint(int index, Vector3 point);
    protected void SetSamplingPoints(Vector3[] points);
 
    protected int GetResultCount(); // return amount of available results
    protected WaterSampler.HitResult GetResult(int index);
    protected void GetResults(out ArraySegment<WaterSampler.HitResult> results);
 
 
    // override this function for actual fixed update behaviour
    // only inside this function you can schedule points
    protected virtual void InnerFixedUpdate() { }
}

FloatingBody

FloatingBody is built-in implementation of WaterBehaviour.

public class FloatingBody : WaterBehaviour
{
    // getters and setters for all properties you see in the inspector
    public Vector3 CenterOfMass { get; set; }
    public float BuoyancyForce { get; set; }
    public bool IgnoreMass { get; set; }
    public float NormalAlignment { get; set; }
    public float Instability { get; set; }
    public float SubmergeDrag { get; set; }
    public float SubmergeAngularDrag { get; set; }
 
 
    public float CurrentSubmergeLevel { get; } // range 0-1 thats means fraction of points that are under water
    public Rigidbody Rigidbody { get; } // return attached rigidbody
 
 
    // modify contact points (all in local space of game object)
    public Vector3 GetContactPoint(int index);
    public void SetContactPoint(int index, Vector3 point);
    public void RemoveContactPoint(int index);
    public void AddContactPoint(Vector3 point);
 
    // return last hit result for point with given index
    // useful e.g. to check if given part of body is above or under water without creating additional sampler or object for that
    public WaterSampler.HitResult GetContactPointHit(int index);
}