新增动态水物理插件
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
// ╔════════════════════════════════════════════════════════════════╗
|
||||
// ║ 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 UnityEngine;
|
||||
#if UNITY_EDITOR
|
||||
using NWH.NUI;
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
|
||||
namespace NWH.DWP2.SailController
|
||||
{
|
||||
/// <summary>
|
||||
/// Applies wind drag forces to the hull of a sailing vessel based on the global wind conditions.
|
||||
/// Calculates the projected area of the hull facing the wind and applies an appropriate drag force.
|
||||
/// Does not account for the waterline - dimensions should represent only the hull portion above water.
|
||||
/// Requires a WindGenerator to be present in the scene.
|
||||
/// </summary>
|
||||
public class HullWindApplicator : MonoBehaviour
|
||||
{
|
||||
/// <summary>
|
||||
/// Physical dimensions of the hull above the waterline.
|
||||
/// X represents width, Y represents height, Z represents length.
|
||||
/// These dimensions are used to calculate the projected area when wind hits the hull from different angles.
|
||||
/// </summary>
|
||||
[Tooltip("The dimensions of the object (x = width, y = height, z = length).")]
|
||||
[SerializeField]
|
||||
public Vector3 dimensions;
|
||||
|
||||
/// <summary>
|
||||
/// Drag coefficient applied to the wind force calculation.
|
||||
/// Higher values result in stronger wind resistance.
|
||||
/// Typical values range from 0.5 to 2.0 depending on hull shape and surface characteristics.
|
||||
/// </summary>
|
||||
[Tooltip("The drag coefficient of the object.")]
|
||||
[SerializeField]
|
||||
public float dragCoefficient = 1.0f;
|
||||
|
||||
private Rigidbody _rb;
|
||||
|
||||
|
||||
private void Start()
|
||||
{
|
||||
_rb = GetComponentInParent<Rigidbody>();
|
||||
Debug.Assert(_rb != null, "Rigidbody not found on self or parents.");
|
||||
}
|
||||
|
||||
|
||||
private void FixedUpdate()
|
||||
{
|
||||
if (WindGenerator.Instance == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate the frontal area of the object facing the wind
|
||||
Vector3 windDirection = WindGenerator.Instance.CurrentWind.normalized;
|
||||
float projectedArea = Mathf.Abs(Vector3.Dot(dimensions, windDirection));
|
||||
|
||||
// Calculate the wind force
|
||||
float windSpeed = WindGenerator.Instance.CurrentWind.magnitude;
|
||||
float windForceMagnitude = 0.5f * dragCoefficient * projectedArea * Mathf.Pow(windSpeed, 2);
|
||||
Vector3 windForce = windDirection * windForceMagnitude;
|
||||
|
||||
// Apply the wind force to the Rigidbody
|
||||
_rb.AddForce(windForce);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
namespace NWH.DWP2.SailController
|
||||
{
|
||||
[CustomEditor(typeof(HullWindApplicator))]
|
||||
[CanEditMultipleObjects]
|
||||
public class HullWindApplicatorEditor : DWP2NUIEditor
|
||||
{
|
||||
public override bool OnInspectorNUI()
|
||||
{
|
||||
if (!base.OnInspectorNUI())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
drawer.Field("dimensions");
|
||||
drawer.Field("dragCoefficient");
|
||||
|
||||
drawer.EndEditor(this);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 14e3b9a33474ea2469d9c4fe8e4d8990
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,438 @@
|
||||
// ╔════════════════════════════════════════════════════════════════╗
|
||||
// ║ 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 UnityEngine;
|
||||
#if UNITY_EDITOR
|
||||
using NWH.NUI;
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
|
||||
namespace NWH.DWP2.SailController
|
||||
{
|
||||
/// <summary>
|
||||
/// Manages sail physics by calculating and applying lift and drag forces based on sail geometry, orientation, and wind conditions.
|
||||
/// Uses four corner transforms (a, b, c, d) to define sail shape, enabling dynamic sail configurations including furling and multi-part sails.
|
||||
/// Calculates apparent wind from true wind and vessel velocity, then applies aerodynamic forces at the sail's surface-weighted center.
|
||||
/// The corner transforms follow a clockwise pattern: a (bottom left/front), b (top left/front), c (top right/rear), d (bottom right/rear).
|
||||
/// Corner transforms can be attached to different parents to enable sail furling or complex rigging systems.
|
||||
/// Use SailRotator for user-controlled sail rotation.
|
||||
/// Requires a WindGenerator in the scene and a SailPreset for aerodynamic coefficients.
|
||||
/// </summary>
|
||||
public class SailController : MonoBehaviour
|
||||
{
|
||||
private void Awake()
|
||||
{
|
||||
_rb = GetComponentInParent<Rigidbody>();
|
||||
Debug.Assert(_rb != null, "Rigidbody not found on the sail or any of the parent GameObjects.");
|
||||
}
|
||||
|
||||
|
||||
private void Start()
|
||||
{
|
||||
if (WindGenerator.Instance == null)
|
||||
{
|
||||
Debug.LogError("WindGenerator not found in the scene. SailController will not work.");
|
||||
}
|
||||
|
||||
if (sailPreset == null)
|
||||
{
|
||||
Debug.LogError($"SailPreset not assigned to SailController {name}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void FixedUpdate()
|
||||
{
|
||||
Debug.Assert(a != null, $"{name}: Transform 'a' is not assigned.");
|
||||
Debug.Assert(b != null, $"{name}: Transform 'b' is not assigned.");
|
||||
Debug.Assert(c != null, $"{name}: Transform 'c' is not assigned.");
|
||||
Debug.Assert(d != null, $"{name}: Transform 'd' is not assigned.");
|
||||
|
||||
// Update the sail positions, directions and geometry
|
||||
UpdateCachedPositions();
|
||||
SailCenter = CalculateSurfaceWeightedCenter();
|
||||
SailArea = CalculateSailArea();
|
||||
SailForward = CalculateSailForward();
|
||||
SailUp = CalculateSailUp();
|
||||
SailRight = CalculateSailRight(SailUp, SailForward);
|
||||
|
||||
// Calculate velocities
|
||||
ShipVelocity = _rb.linearVelocity;
|
||||
TrueWind = WindGenerator.Instance.CurrentWind;
|
||||
ApparentWind = CalculateApparentWind(ShipVelocity, TrueWind);
|
||||
AngleOfAttack = CalculateAngleOfAttack(ApparentWind, SailForward);
|
||||
|
||||
// Calculate and apply force
|
||||
SailForce = CalculateSailForce();
|
||||
_rb.AddForceAtPosition(SailForce, SailCenter);
|
||||
}
|
||||
|
||||
|
||||
private void OnDrawGizmos()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (a == null || b == null || c == null || d == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateCachedPositions();
|
||||
|
||||
Vector3 center = CalculateSurfaceWeightedCenter();
|
||||
|
||||
// Draw sail directions
|
||||
if (!Application.isPlaying) // Calculate directions if out of play mode
|
||||
{
|
||||
SailForward = CalculateSailForward();
|
||||
SailUp = CalculateSailUp();
|
||||
SailRight = CalculateSailRight(SailUp, SailForward);
|
||||
}
|
||||
|
||||
Gizmos.color = Color.blue;
|
||||
Gizmos.DrawRay(center, SailForward);
|
||||
|
||||
Gizmos.color = Color.green;
|
||||
Gizmos.DrawRay(center, SailUp);
|
||||
|
||||
Gizmos.color = Color.red;
|
||||
Gizmos.DrawRay(center, SailRight);
|
||||
|
||||
// Draw sail shape
|
||||
if (a != null && b != null && c != null && d != null)
|
||||
{
|
||||
Gizmos.color = Color.white;
|
||||
|
||||
Gizmos.DrawLine(_pA, _pB);
|
||||
Gizmos.DrawLine(_pB, _pC);
|
||||
Gizmos.DrawLine(_pC, _pD);
|
||||
Gizmos.DrawLine(_pD, _pA);
|
||||
Gizmos.DrawLine(_pA, _pC);
|
||||
Gizmos.DrawLine(_pB, _pD);
|
||||
|
||||
Gizmos.DrawSphere(_pA, 0.1f);
|
||||
Gizmos.DrawSphere(_pB, 0.1f);
|
||||
Gizmos.DrawSphere(_pC, 0.1f);
|
||||
Gizmos.DrawSphere(_pD, 0.1f);
|
||||
|
||||
Handles.Label(_pA, "A (bottom left)");
|
||||
Handles.Label(_pB, "B (top left)");
|
||||
Handles.Label(_pC, "C (top right)");
|
||||
Handles.Label(_pD, "D (bottom right");
|
||||
}
|
||||
|
||||
// RUNTIME ONLY FROM THIS POINT FORWARD
|
||||
if (!Application.isPlaying)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Draw force point (center)
|
||||
Gizmos.color = Color.red;
|
||||
Gizmos.DrawSphere(center, 0.05f);
|
||||
|
||||
// Draw wind
|
||||
Gizmos.color = Color.green;
|
||||
Gizmos.DrawRay(center, TrueWind * 0.2f);
|
||||
Handles.Label(center + TrueWind * 0.2f, $"True Wind ({TrueWind.magnitude} m/s)");
|
||||
|
||||
Gizmos.color = Color.yellow;
|
||||
Gizmos.DrawRay(center, ApparentWind * 0.2f);
|
||||
Handles.Label(center + ApparentWind * 0.2f, $"Apparent Wind ({ApparentWind.magnitude} m/s)");
|
||||
|
||||
Gizmos.color = Color.red;
|
||||
Gizmos.DrawRay(center, SailForce * 0.001f);
|
||||
Handles.Label(center + SailForce * 0.001f, $"Force ({SailForce.magnitude} N)");
|
||||
|
||||
Gizmos.color = Color.white;
|
||||
Gizmos.DrawRay(center, _liftForceDirection);
|
||||
Handles.Label(center + _liftForceDirection, "Lift Force Dir.");
|
||||
|
||||
Gizmos.color = Color.gray;
|
||||
Gizmos.DrawRay(center, _dragForceDirection);
|
||||
Handles.Label(center + _dragForceDirection, "Drag Force Dir.");
|
||||
|
||||
Gizmos.color = Color.magenta;
|
||||
Gizmos.DrawRay(center - Vector3.up * 0.1f, ShipVelocity * 0.1f);
|
||||
Handles.Label(center - Vector3.up * 0.1f + ShipVelocity * 0.1f,
|
||||
$"Ship Velocity ({ShipVelocity.magnitude} m/s)");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
private void UpdateCachedPositions()
|
||||
{
|
||||
_pA = a.position;
|
||||
_pB = b.position;
|
||||
_pC = c.position;
|
||||
_pD = d.position;
|
||||
}
|
||||
|
||||
|
||||
private Vector3 CalculateSurfaceWeightedCenter()
|
||||
{
|
||||
return GeometryUtils.CalculateSurfaceWeightedCenter(_pA, _pB, _pC, _pD);
|
||||
}
|
||||
|
||||
|
||||
private float CalculateSailArea()
|
||||
{
|
||||
return GeometryUtils.CalculateQuadrilateralArea(_pA, _pB, _pC, _pD);
|
||||
}
|
||||
|
||||
|
||||
private Vector3 CalculateSailForward()
|
||||
{
|
||||
return (_pA - _pD).normalized;
|
||||
}
|
||||
|
||||
|
||||
private Vector3 CalculateSailUp()
|
||||
{
|
||||
return ((_pB + _pC) * 0.5f - (_pA + _pD) * 0.5f).normalized;
|
||||
}
|
||||
|
||||
|
||||
private Vector3 CalculateSailRight(Vector3 sailUp, Vector3 sailForward)
|
||||
{
|
||||
return Vector3.Cross(sailUp, sailForward).normalized;
|
||||
}
|
||||
|
||||
|
||||
private Vector3 CalculateApparentWind(Vector3 boatVelocity, Vector3 trueWind)
|
||||
{
|
||||
return trueWind - boatVelocity;
|
||||
}
|
||||
|
||||
|
||||
private float CalculateAngleOfAttack(Vector3 apparentWind, Vector3 sailForward)
|
||||
{
|
||||
return Vector3.SignedAngle(sailForward, apparentWind, Vector3.up);
|
||||
}
|
||||
|
||||
|
||||
private Vector3 CalculateSailForce()
|
||||
{
|
||||
float apparentWindSpeed = ApparentWind.magnitude;
|
||||
float dynamicPressure = 0.5f * airDensity * apparentWindSpeed * apparentWindSpeed;
|
||||
|
||||
float liftCoefficient = sailPreset.liftCoefficientVsAoACurve.Evaluate(AngleOfAttack) * sailPreset.liftScale;
|
||||
_liftForce = liftCoefficient * dynamicPressure * SailArea;
|
||||
|
||||
float dragCoefficient = sailPreset.dragCoefficientVsAoACurve.Evaluate(AngleOfAttack) * sailPreset.dragScale;
|
||||
_dragForce = dragCoefficient * dynamicPressure * SailArea;
|
||||
|
||||
_liftForceDirection = SailRight * Mathf.Sign(Vector3.Dot(ApparentWind, SailRight));
|
||||
_dragForceDirection = ApparentWind.normalized;
|
||||
|
||||
Vector3 totalForce = _liftForceDirection * _liftForce + _dragForceDirection * _dragForce;
|
||||
|
||||
// Compensate for the lean
|
||||
totalForce *= Vector3.Dot(SailUp, Vector3.up);
|
||||
|
||||
return totalForce;
|
||||
}
|
||||
|
||||
|
||||
#region UserSettings
|
||||
|
||||
/// <summary>
|
||||
/// Bottom left corner of the sail for square sails, or bottom front corner for triangular sails.
|
||||
/// First point in the clockwise corner definition pattern.
|
||||
/// Can be attached to any parent transform to enable dynamic sail configurations.
|
||||
/// </summary>
|
||||
[Tooltip("Bottom left sail corner if square sail.\r\nOtherwise bottom front.")]
|
||||
public Transform a;
|
||||
|
||||
/// <summary>
|
||||
/// Top left corner of the sail for square sails, or top front corner for triangular sails.
|
||||
/// Second point in the clockwise corner definition pattern.
|
||||
/// Can be attached to any parent transform to enable dynamic sail configurations.
|
||||
/// </summary>
|
||||
[Tooltip("Top left sail corner if square sail.\r\nOtherwise top front.")]
|
||||
public Transform b;
|
||||
|
||||
/// <summary>
|
||||
/// Top right corner of the sail for square sails, or top rear corner for triangular sails.
|
||||
/// Third point in the clockwise corner definition pattern.
|
||||
/// Can be attached to any parent transform to enable dynamic sail configurations.
|
||||
/// </summary>
|
||||
[Tooltip("Top right sail corner if square sail.\r\nOtherwise top rear.")]
|
||||
public Transform c;
|
||||
|
||||
/// <summary>
|
||||
/// Bottom right corner of the sail for square sails, or bottom rear corner for triangular sails.
|
||||
/// Fourth point in the clockwise corner definition pattern.
|
||||
/// Can be attached to any parent transform to enable dynamic sail configurations.
|
||||
/// </summary>
|
||||
[Tooltip("Bottom right sail corner if square sail.\r\nOtherwise bottom rear.")]
|
||||
public Transform d;
|
||||
|
||||
/// <summary>
|
||||
/// Defines the aerodynamic characteristics of the sail through lift and drag coefficient curves.
|
||||
/// Contains the relationship between angle of attack and force coefficients.
|
||||
/// </summary>
|
||||
public SailPreset sailPreset;
|
||||
|
||||
/// <summary>
|
||||
/// Air density in kg/m³ used in aerodynamic force calculations.
|
||||
/// Standard sea level value is 1.225 kg/m³.
|
||||
/// Can be adjusted as a multiplier to scale all sail forces uniformly without modifying the preset.
|
||||
/// </summary>
|
||||
[Tooltip(
|
||||
"The air density. Can also be used\r\nas a force coefficient as this affects both lift and drag forces equally.")]
|
||||
public float airDensity = 1.225f;
|
||||
|
||||
#endregion
|
||||
|
||||
#region SailCalculated
|
||||
|
||||
/// <summary>
|
||||
/// Surface-weighted center point of the sail in world space.
|
||||
/// All aerodynamic forces are applied at this position.
|
||||
/// Calculated from the quadrilateral formed by corner points a, b, c, and d.
|
||||
/// </summary>
|
||||
public Vector3 SailCenter { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Total surface area of the sail in square meters.
|
||||
/// Calculated from the quadrilateral formed by corner points a, b, c, and d.
|
||||
/// Used in aerodynamic force calculations along with dynamic pressure.
|
||||
/// </summary>
|
||||
public float SailArea { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Forward direction vector of the sail in world space.
|
||||
/// Determined by the vector from corner point d to corner point a.
|
||||
/// Used to calculate the angle of attack relative to the apparent wind.
|
||||
/// </summary>
|
||||
public Vector3 SailForward { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Up direction vector of the sail in world space.
|
||||
/// Calculated from the average of the top edge to the average of the bottom edge.
|
||||
/// Used to determine sail orientation and compensate for heel angle.
|
||||
/// </summary>
|
||||
public Vector3 SailUp { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Right direction vector of the sail in world space.
|
||||
/// Derived from the cross product of SailUp and SailForward.
|
||||
/// Defines the direction of lift force generation perpendicular to the sail plane.
|
||||
/// </summary>
|
||||
public Vector3 SailRight { get; private set; }
|
||||
|
||||
private Vector3 _liftForceDirection;
|
||||
|
||||
private Vector3 _dragForceDirection;
|
||||
|
||||
private float _liftForce;
|
||||
|
||||
private float _dragForce;
|
||||
|
||||
#endregion
|
||||
|
||||
#region WindCalculated
|
||||
|
||||
/// <summary>
|
||||
/// True wind vector from the WindGenerator, representing the actual environmental wind.
|
||||
/// Does not account for vessel motion.
|
||||
/// Measured in meters per second.
|
||||
/// </summary>
|
||||
public Vector3 TrueWind { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Current velocity of the vessel's Rigidbody in world space.
|
||||
/// Used to calculate apparent wind by combining with true wind.
|
||||
/// Measured in meters per second.
|
||||
/// </summary>
|
||||
public Vector3 ShipVelocity { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Total aerodynamic force vector applied to the vessel at the sail center point.
|
||||
/// Combines lift and drag forces based on sail geometry, apparent wind, and aerodynamic coefficients.
|
||||
/// Measured in Newtons.
|
||||
/// </summary>
|
||||
public Vector3 SailForce { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Wind experienced by the sail relative to the moving vessel.
|
||||
/// Calculated as the vector difference between true wind and vessel velocity.
|
||||
/// This is the effective wind that generates aerodynamic forces on the sail.
|
||||
/// Measured in meters per second.
|
||||
/// </summary>
|
||||
public Vector3 ApparentWind { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Angle in degrees between the sail's forward direction and the apparent wind direction.
|
||||
/// Measured on the horizontal plane using Vector3.up as the reference axis.
|
||||
/// Used to determine lift and drag coefficients from the SailPreset curves.
|
||||
/// Positive values indicate wind from the starboard side, negative values from port side.
|
||||
/// </summary>
|
||||
public float AngleOfAttack { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Cached
|
||||
|
||||
private Vector3 _pA;
|
||||
private Vector3 _pB;
|
||||
private Vector3 _pC;
|
||||
private Vector3 _pD;
|
||||
private Rigidbody _rb;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
namespace NWH.DWP2.SailController
|
||||
{
|
||||
[CustomEditor(typeof(SailController))]
|
||||
[CanEditMultipleObjects]
|
||||
public class SailControllerEditor : DWP2NUIEditor
|
||||
{
|
||||
public override bool OnInspectorNUI()
|
||||
{
|
||||
if (!base.OnInspectorNUI())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
SailController sailController = (SailController)target;
|
||||
|
||||
if (Application.isPlaying)
|
||||
{
|
||||
drawer.BeginSubsection("Debug Info");
|
||||
drawer.Label($"AoA: {sailController.AngleOfAttack}");
|
||||
drawer.Label($"Force Mag.: {sailController.SailForce.magnitude}");
|
||||
drawer.Label($"Force: {sailController.SailForce}");
|
||||
drawer.EndSubsection();
|
||||
}
|
||||
|
||||
drawer.BeginSubsection("Geometry");
|
||||
drawer.Field("a");
|
||||
drawer.Field("b");
|
||||
drawer.Field("c");
|
||||
drawer.Field("d");
|
||||
drawer.EndSubsection();
|
||||
|
||||
drawer.BeginSubsection("Physics");
|
||||
drawer.Field("sailPreset");
|
||||
drawer.Field("airDensity");
|
||||
drawer.EndSubsection();
|
||||
|
||||
drawer.EndEditor(this);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d69fd51567bf2794e97417e3614dc5bc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,140 @@
|
||||
// ╔════════════════════════════════════════════════════════════════╗
|
||||
// ║ 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 UnityEngine;
|
||||
#if UNITY_EDITOR
|
||||
using NWH.NUI;
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
|
||||
namespace NWH.DWP2.SailController
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the aerodynamic characteristics of a sail through lift and drag coefficient curves.
|
||||
/// Contains the relationship between angle of attack and force generation.
|
||||
/// Different sail types (square, lateen, bermuda, etc.) can be represented by different presets.
|
||||
/// Can be created via Assets > Create > NWH > DWP2 > SailPreset.
|
||||
/// </summary>
|
||||
[CreateAssetMenu(fileName = "SailPreset", menuName = "NWH/DWP2/SailPreset", order = 1)]
|
||||
public class SailPreset : ScriptableObject
|
||||
{
|
||||
/// <summary>
|
||||
/// Optional description of the sail type and its characteristics.
|
||||
/// </summary>
|
||||
public string description;
|
||||
|
||||
/// <summary>
|
||||
/// Defines how drag coefficient varies with angle of attack.
|
||||
/// X-axis represents angle of attack in degrees (-180 to 180).
|
||||
/// Y-axis represents the drag coefficient multiplier.
|
||||
/// Drag acts in the direction of the apparent wind.
|
||||
/// </summary>
|
||||
public AnimationCurve dragCoefficientVsAoACurve = new();
|
||||
|
||||
/// <summary>
|
||||
/// Global multiplier for all drag forces.
|
||||
/// Values greater than 1 increase drag, values less than 1 reduce it.
|
||||
/// Does not affect the shape of the drag curve.
|
||||
/// </summary>
|
||||
public float dragScale = 1f;
|
||||
|
||||
/// <summary>
|
||||
/// Defines how lift coefficient varies with angle of attack.
|
||||
/// X-axis represents angle of attack in degrees (-180 to 180).
|
||||
/// Y-axis represents the lift coefficient multiplier.
|
||||
/// Lift acts perpendicular to the sail plane.
|
||||
/// </summary>
|
||||
public AnimationCurve liftCoefficientVsAoACurve = new();
|
||||
|
||||
/// <summary>
|
||||
/// Global multiplier for all lift forces.
|
||||
/// Values greater than 1 increase lift, values less than 1 reduce it.
|
||||
/// Does not affect the shape of the lift curve.
|
||||
/// </summary>
|
||||
public float liftScale = 1f;
|
||||
|
||||
|
||||
private void Reset()
|
||||
{
|
||||
liftCoefficientVsAoACurve = GetDefaultLiftCurve();
|
||||
dragCoefficientVsAoACurve = GetDefaultDragCurve();
|
||||
}
|
||||
|
||||
|
||||
private AnimationCurve GetDefaultDragCurve()
|
||||
{
|
||||
AnimationCurve dragCurve = new();
|
||||
|
||||
for (float angle = -180f; angle <= 180f; angle += 20f)
|
||||
{
|
||||
float angleRadians = angle * Mathf.Deg2Rad;
|
||||
float forceCoefficient = Mathf.Sin(angleRadians);
|
||||
dragCurve.AddKey(angle, forceCoefficient);
|
||||
}
|
||||
|
||||
return dragCurve;
|
||||
}
|
||||
|
||||
|
||||
private AnimationCurve GetDefaultLiftCurve()
|
||||
{
|
||||
AnimationCurve liftCurve = new();
|
||||
|
||||
for (float angle = -180f; angle <= 180f; angle += 20f)
|
||||
{
|
||||
float angleRadians = angle * Mathf.Deg2Rad;
|
||||
float forceCoefficient = Mathf.Cos(angleRadians * 2f);
|
||||
liftCurve.AddKey(angle, forceCoefficient);
|
||||
}
|
||||
|
||||
return liftCurve;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
namespace NWH.DWP2.SailController
|
||||
{
|
||||
[CustomEditor(typeof(SailPreset))]
|
||||
[CanEditMultipleObjects]
|
||||
public class SailPresetEditor : DWP2NUIEditor
|
||||
{
|
||||
public override bool OnInspectorNUI()
|
||||
{
|
||||
if (!base.OnInspectorNUI())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
SailPreset sailPreset = (SailPreset)target;
|
||||
|
||||
EditorGUILayout.Space(30f);
|
||||
EditorGUILayout.LabelField("Description:");
|
||||
sailPreset.description = EditorGUILayout.TextArea(sailPreset.description, GUILayout.Height(60f));
|
||||
drawer.Space(100f);
|
||||
|
||||
drawer.BeginSubsection("Drag");
|
||||
drawer.Field("dragScale", true, "x100%");
|
||||
drawer.Field("dragCoefficientVsAoACurve", true, null, "Drag Coeff. vs AoA");
|
||||
drawer.EndSubsection();
|
||||
|
||||
drawer.BeginSubsection("Lift");
|
||||
drawer.Field("liftScale", true, "x100%");
|
||||
drawer.Field("liftCoefficientVsAoACurve", true, null, "Lift Coeff. vs AoA");
|
||||
drawer.EndSubsection();
|
||||
|
||||
drawer.EndEditor(this);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 209974261ad72654ea8ea4f529e3c63b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5aa4e2dd15926d244bff2f0697e73f06
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,160 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 209974261ad72654ea8ea4f529e3c63b, type: 3}
|
||||
m_Name: BermudaSailPreset
|
||||
m_EditorClassIdentifier:
|
||||
description: 'Typical sail configuration for a modern sailboat.
|
||||
|
||||
Generates
|
||||
force mostly from lift.
|
||||
|
||||
'
|
||||
liftScale: 1
|
||||
liftCoefficientVsAoACurve:
|
||||
serializedVersion: 2
|
||||
m_Curve:
|
||||
- serializedVersion: 3
|
||||
time: -180
|
||||
value: 0
|
||||
inSlope: 0
|
||||
outSlope: 0
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0
|
||||
outWeight: 0
|
||||
- serializedVersion: 3
|
||||
time: -150
|
||||
value: 1
|
||||
inSlope: 0
|
||||
outSlope: 0
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0
|
||||
outWeight: 0
|
||||
- serializedVersion: 3
|
||||
time: -100
|
||||
value: 0.2
|
||||
inSlope: 0
|
||||
outSlope: 0
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0
|
||||
outWeight: 0.33333334
|
||||
- serializedVersion: 3
|
||||
time: -30
|
||||
value: 1.5
|
||||
inSlope: 0
|
||||
outSlope: 0
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0.3697629
|
||||
outWeight: 0.33333334
|
||||
- serializedVersion: 3
|
||||
time: 0
|
||||
value: 0
|
||||
inSlope: 0
|
||||
outSlope: 0
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0.33333334
|
||||
outWeight: 0.33333334
|
||||
- serializedVersion: 3
|
||||
time: 30
|
||||
value: 1.5
|
||||
inSlope: 0
|
||||
outSlope: 0
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0.15104294
|
||||
outWeight: 0
|
||||
- serializedVersion: 3
|
||||
time: 100
|
||||
value: 0.2
|
||||
inSlope: 0
|
||||
outSlope: 0
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0
|
||||
outWeight: 0
|
||||
- serializedVersion: 3
|
||||
time: 150
|
||||
value: 1
|
||||
inSlope: 0
|
||||
outSlope: 0
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0
|
||||
outWeight: 0
|
||||
- serializedVersion: 3
|
||||
time: 180
|
||||
value: 0
|
||||
inSlope: 0
|
||||
outSlope: 0
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0
|
||||
outWeight: 0
|
||||
m_PreInfinity: 2
|
||||
m_PostInfinity: 2
|
||||
m_RotationOrder: 4
|
||||
dragScale: 1
|
||||
dragCoefficientVsAoACurve:
|
||||
serializedVersion: 2
|
||||
m_Curve:
|
||||
- serializedVersion: 3
|
||||
time: -190
|
||||
value: 0.15
|
||||
inSlope: 0
|
||||
outSlope: 0
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0
|
||||
outWeight: 0
|
||||
- serializedVersion: 3
|
||||
time: -90
|
||||
value: 1.3
|
||||
inSlope: 0
|
||||
outSlope: 0
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0.19646317
|
||||
outWeight: 0.09987462
|
||||
- serializedVersion: 3
|
||||
time: 0
|
||||
value: 0.15
|
||||
inSlope: 0.00037129602
|
||||
outSlope: 0.00037129602
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0
|
||||
outWeight: 0.14262822
|
||||
- serializedVersion: 3
|
||||
time: 90
|
||||
value: 1.3
|
||||
inSlope: 0
|
||||
outSlope: 0
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0.06089728
|
||||
outWeight: 0.09204839
|
||||
- serializedVersion: 3
|
||||
time: 180
|
||||
value: 0.15
|
||||
inSlope: 0
|
||||
outSlope: 0
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0
|
||||
outWeight: 0
|
||||
m_PreInfinity: 2
|
||||
m_PostInfinity: 2
|
||||
m_RotationOrder: 4
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a129b9f6ea8d1744da29b5f0f89022f2
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,371 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 209974261ad72654ea8ea4f529e3c63b, type: 3}
|
||||
m_Name: SquareSailPreset
|
||||
m_EditorClassIdentifier:
|
||||
description: 123
|
||||
liftScale: 0
|
||||
liftCoefficientVsAoACurve:
|
||||
serializedVersion: 2
|
||||
m_Curve:
|
||||
- serializedVersion: 3
|
||||
time: -180
|
||||
value: 1
|
||||
inSlope: -0.011697784
|
||||
outSlope: -0.011697784
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0.33333334
|
||||
outWeight: 0.33333334
|
||||
- serializedVersion: 3
|
||||
time: -160
|
||||
value: 0.7660443
|
||||
inSlope: -0.020658795
|
||||
outSlope: -0.020658795
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0.33333334
|
||||
outWeight: 0.33333334
|
||||
- serializedVersion: 3
|
||||
time: -140
|
||||
value: 0.17364815
|
||||
inSlope: -0.031651106
|
||||
outSlope: -0.031651106
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0.33333334
|
||||
outWeight: 0.33333334
|
||||
- serializedVersion: 3
|
||||
time: -120
|
||||
value: -0.4999999
|
||||
inSlope: -0.027833518
|
||||
outSlope: -0.027833518
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0.33333334
|
||||
outWeight: 0.33333334
|
||||
- serializedVersion: 3
|
||||
time: -100
|
||||
value: -0.9396926
|
||||
inSlope: -0.010992317
|
||||
outSlope: -0.010992317
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0.33333334
|
||||
outWeight: 0.33333334
|
||||
- serializedVersion: 3
|
||||
time: -80
|
||||
value: -0.9396926
|
||||
inSlope: 0.010992314
|
||||
outSlope: 0.010992314
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0.33333334
|
||||
outWeight: 0.33333334
|
||||
- serializedVersion: 3
|
||||
time: -60
|
||||
value: -0.50000006
|
||||
inSlope: 0.027833521
|
||||
outSlope: 0.027833521
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0.33333334
|
||||
outWeight: 0.33333334
|
||||
- serializedVersion: 3
|
||||
time: -40
|
||||
value: 0.17364822
|
||||
inSlope: 0.03165111
|
||||
outSlope: 0.03165111
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0.33333334
|
||||
outWeight: 0.33333334
|
||||
- serializedVersion: 3
|
||||
time: -20
|
||||
value: 0.76604444
|
||||
inSlope: 0.020658795
|
||||
outSlope: 0.020658795
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0.33333334
|
||||
outWeight: 0.33333334
|
||||
- serializedVersion: 3
|
||||
time: 0
|
||||
value: 1
|
||||
inSlope: 0
|
||||
outSlope: 0
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0.33333334
|
||||
outWeight: 0.33333334
|
||||
- serializedVersion: 3
|
||||
time: 20
|
||||
value: 0.76604444
|
||||
inSlope: -0.020658795
|
||||
outSlope: -0.020658795
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0.33333334
|
||||
outWeight: 0.33333334
|
||||
- serializedVersion: 3
|
||||
time: 40
|
||||
value: 0.17364822
|
||||
inSlope: -0.03165111
|
||||
outSlope: -0.03165111
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0.33333334
|
||||
outWeight: 0.33333334
|
||||
- serializedVersion: 3
|
||||
time: 60
|
||||
value: -0.50000006
|
||||
inSlope: -0.027833521
|
||||
outSlope: -0.027833521
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0.33333334
|
||||
outWeight: 0.33333334
|
||||
- serializedVersion: 3
|
||||
time: 80
|
||||
value: -0.9396926
|
||||
inSlope: -0.010992314
|
||||
outSlope: -0.010992314
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0.33333334
|
||||
outWeight: 0.33333334
|
||||
- serializedVersion: 3
|
||||
time: 100
|
||||
value: -0.9396926
|
||||
inSlope: 0.010992317
|
||||
outSlope: 0.010992317
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0.33333334
|
||||
outWeight: 0.33333334
|
||||
- serializedVersion: 3
|
||||
time: 120
|
||||
value: -0.4999999
|
||||
inSlope: 0.027833518
|
||||
outSlope: 0.027833518
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0.33333334
|
||||
outWeight: 0.33333334
|
||||
- serializedVersion: 3
|
||||
time: 140
|
||||
value: 0.17364815
|
||||
inSlope: 0.031651106
|
||||
outSlope: 0.031651106
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0.33333334
|
||||
outWeight: 0.33333334
|
||||
- serializedVersion: 3
|
||||
time: 160
|
||||
value: 0.7660443
|
||||
inSlope: 0.020658795
|
||||
outSlope: 0.020658795
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0.33333334
|
||||
outWeight: 0.33333334
|
||||
- serializedVersion: 3
|
||||
time: 180
|
||||
value: 1
|
||||
inSlope: 0.011697784
|
||||
outSlope: 0.011697784
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0.33333334
|
||||
outWeight: 0.33333334
|
||||
m_PreInfinity: 2
|
||||
m_PostInfinity: 2
|
||||
m_RotationOrder: 4
|
||||
dragScale: 1
|
||||
dragCoefficientVsAoACurve:
|
||||
serializedVersion: 2
|
||||
m_Curve:
|
||||
- serializedVersion: 3
|
||||
time: -180
|
||||
value: 0.00000008742278
|
||||
inSlope: -0.017101016
|
||||
outSlope: -0.017101016
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0.33333334
|
||||
outWeight: 0.33333334
|
||||
- serializedVersion: 3
|
||||
time: -160
|
||||
value: -0.3420202
|
||||
inSlope: -0.016069693
|
||||
outSlope: -0.016069693
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0.33333334
|
||||
outWeight: 0.33333334
|
||||
- serializedVersion: 3
|
||||
time: -140
|
||||
value: -0.64278764
|
||||
inSlope: -0.01310013
|
||||
outSlope: -0.01310013
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0.33333334
|
||||
outWeight: 0.33333334
|
||||
- serializedVersion: 3
|
||||
time: -120
|
||||
value: -0.8660254
|
||||
inSlope: -0.008550502
|
||||
outSlope: -0.008550502
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0.33333334
|
||||
outWeight: 0.33333334
|
||||
- serializedVersion: 3
|
||||
time: -100
|
||||
value: -0.9848077
|
||||
inSlope: -0.0029695586
|
||||
outSlope: -0.0029695586
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0.33333334
|
||||
outWeight: 0.33333334
|
||||
- serializedVersion: 3
|
||||
time: -80
|
||||
value: -0.9848077
|
||||
inSlope: 0.002969557
|
||||
outSlope: 0.002969557
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0.33333334
|
||||
outWeight: 0.33333334
|
||||
- serializedVersion: 3
|
||||
time: -60
|
||||
value: -0.86602545
|
||||
inSlope: 0.008550504
|
||||
outSlope: 0.008550504
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0.33333334
|
||||
outWeight: 0.33333334
|
||||
- serializedVersion: 3
|
||||
time: -40
|
||||
value: -0.6427876
|
||||
inSlope: 0.013100133
|
||||
outSlope: 0.013100133
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0.33333334
|
||||
outWeight: 0.33333334
|
||||
- serializedVersion: 3
|
||||
time: -20
|
||||
value: -0.34202012
|
||||
inSlope: 0.01606969
|
||||
outSlope: 0.01606969
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0.33333334
|
||||
outWeight: 0.33333334
|
||||
- serializedVersion: 3
|
||||
time: 0
|
||||
value: 0
|
||||
inSlope: 0.017101007
|
||||
outSlope: 0.017101007
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0.33333334
|
||||
outWeight: 0.33333334
|
||||
- serializedVersion: 3
|
||||
time: 20
|
||||
value: 0.34202012
|
||||
inSlope: 0.01606969
|
||||
outSlope: 0.01606969
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0.33333334
|
||||
outWeight: 0.33333334
|
||||
- serializedVersion: 3
|
||||
time: 40
|
||||
value: 0.6427876
|
||||
inSlope: 0.013100133
|
||||
outSlope: 0.013100133
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0.33333334
|
||||
outWeight: 0.33333334
|
||||
- serializedVersion: 3
|
||||
time: 60
|
||||
value: 0.86602545
|
||||
inSlope: 0.008550504
|
||||
outSlope: 0.008550504
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0.33333334
|
||||
outWeight: 0.33333334
|
||||
- serializedVersion: 3
|
||||
time: 80
|
||||
value: 0.9848077
|
||||
inSlope: 0.002969557
|
||||
outSlope: 0.002969557
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0.33333334
|
||||
outWeight: 0.33333334
|
||||
- serializedVersion: 3
|
||||
time: 100
|
||||
value: 0.9848077
|
||||
inSlope: -0.0029695586
|
||||
outSlope: -0.0029695586
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0.33333334
|
||||
outWeight: 0.33333334
|
||||
- serializedVersion: 3
|
||||
time: 120
|
||||
value: 0.8660254
|
||||
inSlope: -0.008550502
|
||||
outSlope: -0.008550502
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0.33333334
|
||||
outWeight: 0.33333334
|
||||
- serializedVersion: 3
|
||||
time: 140
|
||||
value: 0.64278764
|
||||
inSlope: -0.01310013
|
||||
outSlope: -0.01310013
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0.33333334
|
||||
outWeight: 0.33333334
|
||||
- serializedVersion: 3
|
||||
time: 160
|
||||
value: 0.3420202
|
||||
inSlope: -0.016069693
|
||||
outSlope: -0.016069693
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0.33333334
|
||||
outWeight: 0.33333334
|
||||
- serializedVersion: 3
|
||||
time: 180
|
||||
value: -0.00000008742278
|
||||
inSlope: -0.017101016
|
||||
outSlope: -0.017101016
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0.33333334
|
||||
outWeight: 0.33333334
|
||||
m_PreInfinity: 2
|
||||
m_PostInfinity: 2
|
||||
m_RotationOrder: 4
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8c52d46290a38a24b9e5b3645fbd472e
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,89 @@
|
||||
// ╔════════════════════════════════════════════════════════════════╗
|
||||
// ║ 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.DWP2.ShipController;
|
||||
using UnityEngine;
|
||||
#if UNITY_EDITOR
|
||||
using NWH.NUI;
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
|
||||
namespace NWH.DWP2.SailController
|
||||
{
|
||||
/// <summary>
|
||||
/// Rotates a transform based on player input from the AdvancedShipController.
|
||||
/// Used to control sail orientation in response to the RotateSail input axis.
|
||||
/// Typically attached to a sail boom or mast that rotates horizontally.
|
||||
/// Requires an AdvancedShipController component on a parent GameObject.
|
||||
/// </summary>
|
||||
public class SailRotator : MonoBehaviour
|
||||
{
|
||||
/// <summary>
|
||||
/// Local axis around which the transform rotates.
|
||||
/// Default (0, 1, 0) rotates around the Y-axis.
|
||||
/// Use negative values to reverse rotation direction.
|
||||
/// Magnitude is ignored - only direction matters.
|
||||
/// </summary>
|
||||
[Tooltip("Rotation axis of this transform.\r\nUse -1 to reverse the rotation.")]
|
||||
public Vector3 rotationAxis = new(0, 1, 0);
|
||||
|
||||
/// <summary>
|
||||
/// Maximum rotation speed in degrees per second when input is at full deflection.
|
||||
/// Higher values allow faster sail adjustment.
|
||||
/// Combined with rotationAxis and player input to determine final rotation.
|
||||
/// </summary>
|
||||
[Tooltip(
|
||||
"Rotation speed of this transform in deg/s.\r\nMultiplied by the rotationAxis to get the final rotation.")]
|
||||
public float rotationSpeed = 50f;
|
||||
|
||||
private AdvancedShipController _shipController;
|
||||
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_shipController = GetComponentInParent<AdvancedShipController>();
|
||||
Debug.Assert(_shipController != null, "SailController requires the AdvancedShipController to" +
|
||||
" be attached to one of the parents (does not have to be direct parent).");
|
||||
}
|
||||
|
||||
|
||||
private void Update()
|
||||
{
|
||||
float rotationAngle = _shipController.input.RotateSail * rotationSpeed * Time.deltaTime;
|
||||
transform.Rotate(rotationAngle * rotationAxis);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
namespace NWH.DWP2.SailController
|
||||
{
|
||||
[CustomEditor(typeof(SailRotator))]
|
||||
[CanEditMultipleObjects]
|
||||
public class SailRotatorEditor : DWP2NUIEditor
|
||||
{
|
||||
public override bool OnInspectorNUI()
|
||||
{
|
||||
if (!base.OnInspectorNUI())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
drawer.Field("rotationSpeed");
|
||||
drawer.Field("rotationAxis");
|
||||
|
||||
drawer.EndEditor(this);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b646a61e9c8466649a608978fa150813
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,197 @@
|
||||
// ╔════════════════════════════════════════════════════════════════╗
|
||||
// ║ 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 System.Collections;
|
||||
using UnityEngine;
|
||||
using Random = UnityEngine.Random;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using NWH.NUI;
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
|
||||
namespace NWH.DWP2.SailController
|
||||
{
|
||||
/// <summary>
|
||||
/// Generates procedural wind with randomized gusts for sail simulation.
|
||||
/// Creates realistic wind conditions by varying speed and direction over time.
|
||||
/// Provides a singleton instance accessible to all sail and wind-related components.
|
||||
/// Wind direction uses world coordinates where 0 degrees points along the positive Z-axis.
|
||||
/// Only one WindGenerator should exist per scene.
|
||||
/// </summary>
|
||||
public class WindGenerator : MonoBehaviour
|
||||
{
|
||||
/// <summary>
|
||||
/// Singleton instance accessible globally.
|
||||
/// Used by SailController and HullWindApplicator to get current wind conditions.
|
||||
/// </summary>
|
||||
public static WindGenerator Instance;
|
||||
|
||||
/// <summary>
|
||||
/// Primary wind direction in degrees on the horizontal plane.
|
||||
/// 0 degrees corresponds to positive Z-axis (north in Unity coordinates).
|
||||
/// Rotation follows the right-hand rule around the Y-axis.
|
||||
/// Wind will vary around this base direction within the limits of maxDirectionVariation.
|
||||
/// </summary>
|
||||
[Tooltip("Base wind direction in degrees with 0 degrees indicating Z-forward ('north').")]
|
||||
public float baseDirection;
|
||||
|
||||
/// <summary>
|
||||
/// Average wind speed in meters per second.
|
||||
/// Wind will vary around this base speed within the limits of maxSpeedVariation.
|
||||
/// Typical sailing winds range from 5 m/s (light breeze) to 20 m/s (strong wind).
|
||||
/// </summary>
|
||||
[Tooltip("Base wind speed in m/s.")]
|
||||
public float baseSpeed = 10.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum angular deviation from baseDirection during wind gusts.
|
||||
/// Measured in degrees.
|
||||
/// Higher values create more unpredictable wind shifts.
|
||||
/// Realistic values range from 15 to 45 degrees.
|
||||
/// </summary>
|
||||
[Tooltip("Maximum possible variation of the direction in degrees from the\r\nbaseDirection.")]
|
||||
public float maxDirectionVariation = 30f;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum speed deviation from baseSpeed during wind gusts.
|
||||
/// Measured in meters per second.
|
||||
/// Higher values create stronger variations between lulls and gusts.
|
||||
/// Can be positive or negative relative to base speed.
|
||||
/// </summary>
|
||||
[Tooltip("Maximum possible variation/deviation of the wind from the baseSpeed.")]
|
||||
public float maxSpeedVariation = 5f;
|
||||
|
||||
/// <summary>
|
||||
/// Longest possible duration between wind condition changes.
|
||||
/// Measured in seconds.
|
||||
/// New wind conditions are selected randomly between minVariationInterval and this value.
|
||||
/// </summary>
|
||||
[Tooltip("Maximum interval between the wind variations / changes.")]
|
||||
public float maxVariationInterval = 6.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Shortest possible duration between wind condition changes.
|
||||
/// Measured in seconds.
|
||||
/// New wind conditions are selected randomly between this value and maxVariationInterval.
|
||||
/// </summary>
|
||||
[Tooltip("Minimum interval between the wind variations / changes.")]
|
||||
public float minVariationInterval = 2.0f;
|
||||
|
||||
private float _currentInterval = 1f;
|
||||
private float _smoothingDirectionVelocity;
|
||||
private float _smoothingSpeedVelocity;
|
||||
private float _targetDirection;
|
||||
private float _targetSpeed;
|
||||
|
||||
/// <summary>
|
||||
/// Current wind vector in world space.
|
||||
/// Combines CurrentDirection and CurrentSpeed into a directional velocity vector.
|
||||
/// Updated every FixedUpdate with smooth damping between gust transitions.
|
||||
/// Measured in meters per second.
|
||||
/// </summary>
|
||||
public Vector3 CurrentWind { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Current wind direction in degrees on the horizontal plane.
|
||||
/// Smoothly interpolates between randomized target directions.
|
||||
/// 0 degrees corresponds to positive Z-axis.
|
||||
/// </summary>
|
||||
public float CurrentDirection { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Current wind speed magnitude in meters per second.
|
||||
/// Smoothly interpolates between randomized target speeds.
|
||||
/// </summary>
|
||||
public float CurrentSpeed { get; private set; }
|
||||
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (Instance != null)
|
||||
{
|
||||
Debug.LogWarning("The scene has more than one WindGenerator. The previous one(s) will be ignored.");
|
||||
}
|
||||
|
||||
Instance = this;
|
||||
}
|
||||
|
||||
|
||||
private void Start()
|
||||
{
|
||||
StartCoroutine(GustCoroutine());
|
||||
}
|
||||
|
||||
|
||||
private void FixedUpdate()
|
||||
{
|
||||
CurrentDirection = Mathf.SmoothDamp(CurrentDirection, _targetDirection,
|
||||
ref _smoothingDirectionVelocity, _currentInterval);
|
||||
CurrentSpeed = Mathf.SmoothDamp(CurrentSpeed, _targetSpeed,
|
||||
ref _smoothingSpeedVelocity, _currentInterval);
|
||||
|
||||
CurrentWind = Quaternion.AngleAxis(CurrentDirection, Vector3.up) * Vector3.forward * CurrentSpeed;
|
||||
}
|
||||
|
||||
|
||||
private IEnumerator GustCoroutine()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
_targetSpeed = baseSpeed + Random.Range(-maxSpeedVariation, maxSpeedVariation);
|
||||
_targetDirection = baseDirection + Random.Range(-maxDirectionVariation, maxDirectionVariation);
|
||||
_currentInterval = Random.Range(minVariationInterval, maxVariationInterval);
|
||||
yield return new WaitForSeconds(_currentInterval);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
namespace NWH.DWP2.SailController
|
||||
{
|
||||
[CustomEditor(typeof(WindGenerator))]
|
||||
[CanEditMultipleObjects]
|
||||
public class WindGeneratorEditor : DWP2NUIEditor
|
||||
{
|
||||
public override bool OnInspectorNUI()
|
||||
{
|
||||
if (!base.OnInspectorNUI())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
WindGenerator windGenerator = (WindGenerator)target;
|
||||
|
||||
if (Application.isPlaying)
|
||||
{
|
||||
drawer.Label($"Current Wind: {windGenerator.CurrentSpeed:0.0} from {windGenerator.CurrentDirection:0}");
|
||||
}
|
||||
|
||||
drawer.BeginSubsection("Base");
|
||||
drawer.Field("baseSpeed");
|
||||
drawer.Field("baseDirection");
|
||||
drawer.EndSubsection();
|
||||
|
||||
drawer.BeginSubsection("Variation");
|
||||
drawer.Field("maxSpeedVariation");
|
||||
drawer.Field("maxDirectionVariation");
|
||||
drawer.Field("minVariationInterval");
|
||||
drawer.Field("maxVariationInterval");
|
||||
drawer.EndSubsection();
|
||||
|
||||
drawer.EndEditor(this);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 18e23c96d90933845ab460c7c434709f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,79 @@
|
||||
// ╔════════════════════════════════════════════════════════════════╗
|
||||
// ║ 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 UnityEngine;
|
||||
#if UNITY_EDITOR
|
||||
using NWH.NUI;
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
|
||||
namespace NWH.DWP2.SailController
|
||||
{
|
||||
/// <summary>
|
||||
/// Visual indicator that rotates to show wind direction.
|
||||
/// Displays apparent wind when attached as a child of a SailController.
|
||||
/// Displays true wind from the WindGenerator when not under a SailController.
|
||||
/// Useful for debugging sail setup and helping players understand wind conditions.
|
||||
/// The transform's forward direction will point into the wind.
|
||||
/// </summary>
|
||||
public class WindIndicator : MonoBehaviour
|
||||
{
|
||||
private SailController _sailController;
|
||||
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_sailController = GetComponentInParent<SailController>();
|
||||
}
|
||||
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (_sailController != null)
|
||||
{
|
||||
// Show apparent wind.
|
||||
transform.rotation = Quaternion.LookRotation(_sailController.ApparentWind.normalized,
|
||||
transform.parent.up);
|
||||
}
|
||||
else if (WindGenerator.Instance != null)
|
||||
{
|
||||
// Show true wind.
|
||||
transform.rotation = Quaternion.LookRotation(WindGenerator.Instance.CurrentWind.normalized,
|
||||
transform.parent.up);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
namespace NWH.DWP2.SailController
|
||||
{
|
||||
[CustomEditor(typeof(WindIndicator))]
|
||||
[CanEditMultipleObjects]
|
||||
public class WindIndicatorEditor : DWP2NUIEditor
|
||||
{
|
||||
public override bool OnInspectorNUI()
|
||||
{
|
||||
if (!base.OnInspectorNUI())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
drawer.Info("Shows apparent wind if placed as a child of the SailController, " +
|
||||
"or true wind otherwise.");
|
||||
|
||||
drawer.EndEditor(this);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2b9bf5ab830c0d2468cb153262ad1d61
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user