← All notes

@@ -0,0 +1,81 @@ using UnityEngine;

namespace Terminus.WaterPhysics { ///

/// Physics-driven boat controller that injects thrust and rudder forces /// into via its external-force API. /// Requires a sibling and any ApplyWaterForcesTo* component. /// [RequireComponent(typeof(WaterForces))] public class BoatTest : MonoBehaviour { [Header("Input")] [Range(-1f, 1f)] public float throttle; [Range(-1f, 1f)] public float rudder;

    [Header("References")]
    [Tooltip("World-space point at the stern where rudder force is applied. " +
             "If unset, defaults to -transform.forward * 1m.")]
    [SerializeField] private Transform rudderPoint;

    [Header("Thrust")]
    [Tooltip("Maximum forward thrust in Newtons.")]
    [SerializeField] private float thrustForce = 500f;

    [Tooltip("Reverse thrust as a fraction of forward thrust.")]
    [SerializeField, Range(0f, 1f)] private float reverseMultiplier = 0.3f;

    [Header("Rudder")]
    [Tooltip("Maximum lateral force at the rudder point in Newtons.")]
    [SerializeField] private float rudderForce = 300f;

    [Tooltip("How much rudder effectiveness depends on forward speed.\n" +
             "0 = always full rudder, 1 = no rudder when stationary.")]
    [SerializeField, Range(0f, 1f)] private float rudderSpeedDependence = 0.5f;

    [Tooltip("Forward speed (m/s) at which rudder reaches full effectiveness.")]
    [SerializeField] private float rudderFullSpeedReference = 5f;

    private WaterForces waterForces;

    void Awake()
    {
        waterForces = GetComponent<WaterForces>();
    }

    void FixedUpdate()
    {
        Vector3 forward = transform.forward;
        forward.y = 0f;
        forward.Normalize();

        // Thrust
        float force = throttle >= 0f
            ? throttle * thrustForce
            : throttle * thrustForce * reverseMultiplier;

        waterForces.AddForce(forward * force);

        // Rudder  lateral force at stern
        if (Mathf.Abs(rudder) > 0.001f)
        {
            float forwardSpeed = Vector3.Dot(waterForces.LastVelocity, forward);
            float speedRatio = Mathf.Clamp01(Mathf.Abs(forwardSpeed) / Mathf.Max(0.01f, rudderFullSpeedReference));
            float speedFactor = Mathf.Lerp(1f, speedRatio, rudderSpeedDependence);

            Vector3 right = transform.right;
            right.y = 0f;
            right.Normalize();

            Vector3 lateralForce = right * rudder * rudderForce * speedFactor;

            Vector3 applicationPoint = rudderPoint != null
                    ? rudderPoint.position
                    : transform.position - transform.forward;
            waterForces.AddForceAtPosition(lateralForce, applicationPoint);
        }
    }
}

}