// ╔════════════════════════════════════════════════════════════════╗ // ║ Copyright © 2025 NWH Coding d.o.o. All rights reserved. ║ // ║ Licensed under Unity Asset Store Terms of Service: ║ // ║ https://unity.com/legal/as-terms ║ // ║ Use permitted only in compliance with the License. ║ // ║ Distributed "AS IS", without warranty of any kind. ║ // ╚════════════════════════════════════════════════════════════════╝ #region using NWH.Common.CoM; using NWH.Common.Input; using NWH.DWP2.WaterObjects; using UnityEngine; using UnityEngine.Serialization; #endregion namespace NWH.DWP2.ShipController { /// /// Enables submarine functionality by controlling ballast mass for diving and surfacing. /// Manages depth control through variable mass and can automatically maintain horizontal orientation. /// Works with VariableCenterOfMass to adjust buoyancy for depth changes. /// [RequireComponent(typeof(AdvancedShipController))] [RequireComponent(typeof(VariableCenterOfMass))] [DefaultExecutionOrder(500)] public class Submarine : MonoBehaviour, IMassAffector { /// /// Speed of change of the ballast mass, as a percentage of maxBallastMass. /// [Tooltip("Speed of change of the ballast mass, as a percentage of maxBallastMass.")] public float ballastChangeSpeed = 0.05f; /// /// If enabled submarine will try to keep horizontal by shifting the center of mass. /// [Tooltip("If enabled submarine will try to keep horizontal by shifting the center of mass.")] public bool keepHorizontal; /// /// Sensitivity of calculation trying to keep the submarine horizontal. Higher number will mean faster reaction. /// [Tooltip( "Sensitivity of calculation trying to keep the submarine horizontal. Higher number will mean faster reaction.")] public float keepHorizontalSensitivity = 1f; /// /// Maximum ballast mass in kg that can be added to make the submarine sink. /// Higher values allow diving deeper and faster but require more time to surface. /// [FormerlySerializedAs("maxAdditionalMass")] [FormerlySerializedAs("maxMassFactor")] [Tooltip( "Maximum additional mass that can be added (taking on water) to the base mass of the rigidbody to make submarine sink.")] public float maxBallastMass = 200000f; /// /// Maximum rigidbody center of mass offset that can be used to keep the submarine level. /// [Tooltip("Maximum rigidbody center of mass offset that can be used to keep the submarine level.")] public float maxMassOffset = 5f; /// /// Reference to the WaterObject used for water level detection. /// public WaterObject ReferenceWaterObject; private Vector3 _centerOfMass; private float _mass; private VariableCenterOfMass _vcom; private float _zOffset; [HideInInspector] [SerializeField] private float depthInput; /// /// Input for depth control from -1 (surface) to 1 (dive). /// Positive values add ballast mass to sink, negative values reduce it to surface. /// public float DepthInput { get { return depthInput; } set { depthInput = Mathf.Clamp(value, -1f, 1f); } } public float GetMass() { return _mass; } public Vector3 GetWorldCenterOfMass() { return _centerOfMass; } public Transform GetTransform() { return transform; } private void Awake() { if (ReferenceWaterObject == null) { ReferenceWaterObject = GetComponentInChildren(); } } private void Start() { _vcom = GetComponentInParent(); if (_vcom == null) { Debug.LogError( $"VariableCenterOfMass script not found on object {name}. If updating from older versions" + "of DWP2 replace CenterOfMass [deprecated] script with VariableCenterOfMass [new] script."); } _vcom.useMassAffectors = true; _vcom.useDefaultMass = false; _vcom.useDefaultCenterOfMass = false; Debug.Assert(ReferenceWaterObject != null, "ReferenceWaterObject not set."); } private void FixedUpdate() { DepthInput = InputProvider.CombinedInput (i => i.SubmarineDepth()); _mass -= DepthInput * maxBallastMass * ballastChangeSpeed * Time.fixedDeltaTime; _mass = Mathf.Clamp(_mass, 0f, Mathf.Infinity); if (keepHorizontal) { float angle = Vector3.SignedAngle(transform.up, Vector3.up, transform.right); _zOffset = Mathf.Clamp(Mathf.Sign(angle) * Mathf.Pow(angle * 0.2f, 2f) * keepHorizontalSensitivity, -maxMassOffset, maxMassOffset); Vector3 position = transform.position; _centerOfMass = new Vector3(position.x, position.y, position.z + _zOffset); } } } }