diff --git a/Assets/Scripts/AffectedProjectile.cs b/Assets/Scripts/AffectedProjectile.cs
index 2ae7497..59784db 100644
--- a/Assets/Scripts/AffectedProjectile.cs
+++ b/Assets/Scripts/AffectedProjectile.cs
@@ -21,6 +21,7 @@ public class AffectedProjectile : ICloneable {
public TerminalBallisticDetector terminalDetector = new TerminalBallisticDetector();
public float aliveVelocity = 0.01f; //When below this velocity, this projectile is dead
+ public float minimumAltitude = -10f; //Kill projectile below this altitude
public bool Active { get; set; } = false;
@@ -32,7 +33,7 @@ public class AffectedProjectile : ICloneable {
public Affector_Gravity affGravity = new Affector_Gravity();
public Affector_Coriolis affCoriolis = new Affector_Coriolis();
public Affector_SpinTwist affSpinTwist = new Affector_SpinTwist();
- public Affector_WindDrag affWindDrag = new Affector_WindDrag();
+ public Affector_WindDragCubed affWindDrag = new Affector_WindDragCubed();
public Default_TerminalCalculator termCalc = new Default_TerminalCalculator();
@@ -103,7 +104,8 @@ public virtual void Tick(float deltaTime) {
}
public virtual bool IsDead() {
- return physicsTransform.Velocity.magnitude < aliveVelocity || physicsTransform.Position.y < -10;
+ return physicsTransform.Velocity.sqrMagnitude < aliveVelocity * aliveVelocity
+ || physicsTransform.Position.y < minimumAltitude;
}
public virtual object Clone() {
@@ -115,7 +117,7 @@ public virtual object Clone() {
newProj.affGravity = affGravity.Clone() as Affector_Gravity;
newProj.affCoriolis = affCoriolis.Clone() as Affector_Coriolis;
newProj.affSpinTwist = affSpinTwist.Clone() as Affector_SpinTwist;
- newProj.affWindDrag = affWindDrag.Clone() as Affector_WindDrag;
+ newProj.affWindDrag = affWindDrag.Clone() as Affector_WindDragCubed;
newProj.termCalc = termCalc.Clone() as Default_TerminalCalculator;
diff --git a/Assets/Scripts/Environment/ProjectileEnvironmentData.cs b/Assets/Scripts/Environment/ProjectileEnvironmentData.cs
index f97b5fd..3bde08f 100644
--- a/Assets/Scripts/Environment/ProjectileEnvironmentData.cs
+++ b/Assets/Scripts/Environment/ProjectileEnvironmentData.cs
@@ -71,4 +71,14 @@ public Vector3 GetGravity(float altitude)
public float planetaryMass = 5.972E24f;
const float gravitationalConstant = 6.67408E-11f;
+
+ ///
+ /// Speed of sound in air at a given altitude, derived from temperature.
+ /// Uses the approximation c = sqrt(gamma * R * T) for dry air.
+ ///
+ public float GetSpeedOfSound(float altitude) {
+ float temperature = ProjectileAffectors.AltitudeAirTemperature(altitude);
+ // gamma (ratio of specific heats for air) = 1.4, R (specific gas constant for dry air) = 287.05
+ return Mathf.Sqrt(1.4f * 287.05f * temperature);
+ }
}
diff --git a/Assets/Scripts/ProjLauncher/InternalBallistics.cs b/Assets/Scripts/ProjLauncher/InternalBallistics.cs
index 8c935da..2cfd5f6 100644
--- a/Assets/Scripts/ProjLauncher/InternalBallistics.cs
+++ b/Assets/Scripts/ProjLauncher/InternalBallistics.cs
@@ -11,7 +11,8 @@ public float
///
- /// Gets the recoil velocity.
+ /// Gets the recoil velocity using conservation of momentum.
+ /// Propellant gas velocity is approximated as half of muzzle velocity.
///
/// The recoil velocity.
/// Projectile mass in kg.
@@ -24,12 +25,11 @@ public static float GetRecoilVelocity(
float launcherMass,
float muzzleVelocity
) {
+ // Conservation of momentum: launcher_mass * recoil_vel = proj_mass * muzzle_vel + prop_mass * gas_vel
+ // Gas velocity is typically approximated as ~0.5 * muzzle velocity
return
- Mathf.Sqrt(
- ((projectileMass + propellantMass)
- * muzzleVelocity * muzzleVelocity)
- / launcherMass
- );
+ (projectileMass * muzzleVelocity + 0.5f * propellantMass * muzzleVelocity)
+ / launcherMass;
}
diff --git a/Assets/Scripts/ProjectileAffectors/Affectors/Affector_Coriolis.cs b/Assets/Scripts/ProjectileAffectors/Affectors/Affector_Coriolis.cs
index 6569c39..9fa8e67 100644
--- a/Assets/Scripts/ProjectileAffectors/Affectors/Affector_Coriolis.cs
+++ b/Assets/Scripts/ProjectileAffectors/Affectors/Affector_Coriolis.cs
@@ -9,12 +9,11 @@ public class Affector_Coriolis : AffectorBase
//public Affector_Coriolis() : base()
//{ }
- public override void Tick_PostPhysics(float deltaTime)
+ public override void Tick_PrePhysics(float deltaTime)
{
proj.physicsTransform.AddForce(
GetCoriolisAcceleration(proj.physicsTransform.Velocity),
- ForceMode.Acceleration,
- deltaTime
+ ForceMode.Acceleration
);
}
diff --git a/Assets/Scripts/ProjectileAffectors/Affectors/Affector_Gravity.cs b/Assets/Scripts/ProjectileAffectors/Affectors/Affector_Gravity.cs
index 156e5a8..1daad67 100644
--- a/Assets/Scripts/ProjectileAffectors/Affectors/Affector_Gravity.cs
+++ b/Assets/Scripts/ProjectileAffectors/Affectors/Affector_Gravity.cs
@@ -16,12 +16,10 @@ public class Affector_Gravity : AffectorBase {
public Vector3 Gravity => useAdvancedGravity ? GetGravity(proj.physicsTransform.Position.y) : gravity;
- public override void Tick_PostPhysics(float deltaTime) {
- //Debug.Log($"Gravity is running {gravity * deltaTime}");
+ public override void Tick_PrePhysics(float deltaTime) {
proj.physicsTransform.AddForce(
Gravity,
- ForceMode.Acceleration,
- deltaTime
+ ForceMode.Acceleration
);
}
diff --git a/Assets/Scripts/ProjectileAffectors/Affectors/Affector_SpinTwist.cs b/Assets/Scripts/ProjectileAffectors/Affectors/Affector_SpinTwist.cs
index 458fa40..d08190c 100644
--- a/Assets/Scripts/ProjectileAffectors/Affectors/Affector_SpinTwist.cs
+++ b/Assets/Scripts/ProjectileAffectors/Affectors/Affector_SpinTwist.cs
@@ -1,63 +1,59 @@
-using System.Collections;
+using System.Collections;
using System.Collections.Generic;
using UnityEngine;
-//[CreateAssetMenu(fileName = "SpinTwistAffector", menuName = "AProjectiles/Affectors/SpinTwistAffector")]
[System.Serializable]
public class Affector_SpinTwist : AffectorBase
{
- //public Affector_SpinTwist() : base()
- //{
- //}
+ // Cumulative time of flight, needed for the empirical spin drift formula
+ float totalTOF = 0.0f;
- public override void Tick_PostPhysics(float deltaTime)
+ public override void Tick_PrePhysics(float deltaTime)
{
- Vector3 pos = proj.physicsTransform.Position;
-
- pos +=
+ float stability = GetMillerStability(
+ proj.projectileData.bulletMass,
+ proj.projectileData.bulletDiameter,
+ proj.projectileData.bulletLength,
+ proj.physicsTransform.SpinVelocity
+ );
+
+ float twist = proj.physicsTransform.SpinVelocity;
+ float lateralAccel = GetSpinDriftAcceleration(stability, totalTOF, deltaTime, twist);
+
+ // Apply spin drift as a lateral acceleration perpendicular to velocity
+ Vector3 driftDirection =
Quaternion.FromToRotation(Vector3.forward, proj.physicsTransform.Velocity.normalized) *
- new Vector3(
- GetSpinDrift(
- GetMillerStability(
- proj.projectileData.bulletMass,
- proj.projectileData.bulletDiameter,
- proj.projectileData.bulletLength,
- proj.physicsTransform.SpinVelocity
- ),
- deltaTime,
- proj.physicsTransform.SpinVelocity),
- 0, 0);
- //proj.physicsTransform.Position = pos;
+ Vector3.right;
+
+ proj.physicsTransform.AddForce(
+ driftDirection * lateralAccel,
+ ForceMode.Acceleration
+ );
+
+ totalTOF += deltaTime;
}
#region Related Methods
- public float GetSpinDrift(float millerStability, float deltaTime, float twist)
+ ///
+ /// Computes the lateral acceleration due to spin drift by differentiating
+ /// the empirical drift formula: D(t) = 0.03175 * (SG + 1.2) * t^1.83
+ /// Derivative: a(t) = 0.03175 * (SG + 1.2) * 1.83 * t^0.83
+ ///
+ public float GetSpinDriftAcceleration(float millerStability, float tof, float deltaTime, float twist)
{
- // For the source of this equation please visit
- // https://loadoutroom.com/13415/reaper-tips-spin-drift-coriolis-effect/
-
- //Please note that, when viewed from the rear:
- //The bullet will drift to the right if spinning clockwise
- //The bullet will drift to the left if spinning anticlockwise
- //If this is being calculated on a bullet each physics tick,
- //TOF can be replaced by the time delta between those ticks
-
- //The 'SG' value can be calculated using the 'MillerStability()' function
- //(Multiplied by 0.0254 to convert from inches to metres)
-
- return 0.03175f * (millerStability + 1.2f) * Mathf.Pow(deltaTime, 1.83f) * Mathf.Sign(twist);
+ if (tof < 0.0001f)
+ return 0.0f;
+ // Analytical derivative of the empirical drift displacement formula
+ // (multiplied by 0.0254 to convert from inches to metres, then divided out by the
+ // empirical constant which was already adjusted — the 0.03175 already includes this)
+ return 0.03175f * (millerStability + 1.2f) * 1.83f * Mathf.Pow(tof, 0.83f) * Mathf.Sign(twist);
}
- float l, t; //Only initialize these once
-
- //Add in multiple stability methods
-
///
- /// Millers the stability.
+ /// Computes gyroscopic stability using the Miller twist rule.
///
- /// The gyrospoic stability of the bullet.
/// Bullet mass (kg).
/// Bullet diameter (meters).
/// Bullet length (meters).
@@ -67,18 +63,14 @@ public float GetMillerStability(float bulletMass, float bulletDiameter, float bu
// Equation data taken from:
// https://en.wikipedia.org/wiki/Miller_twist_rule#References
-
- //Firstly, convert the inputted SI values into their imperial counterparts (For the equation)
+ // Convert SI values into their imperial counterparts (for the equation)
bulletMass *= 15432.4f; //kg to grains
bulletDiameter *= 39.3701f; //metres to inches
bulletLength *= 39.3701f; //metres to inches
twist *= 39.3701f; //metres to inches
- //BulletLength(calibers) = BulletLength(inches) / BulletDiameter(inches)
- //Twist(calibers ber turn) = Twist(inches per turn) / BulletDiameter(inches)
-
- l = bulletLength / bulletDiameter;
- t = twist / bulletDiameter;
+ float l = bulletLength / bulletDiameter;
+ float t = twist / bulletDiameter;
return
(30 * bulletMass) /
@@ -90,7 +82,6 @@ public float GetMillerStability(float bulletMass, float bulletDiameter, float bu
);
}
-
#endregion
}
diff --git a/Assets/Scripts/ProjectileAffectors/Affectors/Affector_WindDrag - Copy.cs b/Assets/Scripts/ProjectileAffectors/Affectors/Affector_WindDrag - Copy.cs
deleted file mode 100644
index 45ece5c..0000000
--- a/Assets/Scripts/ProjectileAffectors/Affectors/Affector_WindDrag - Copy.cs
+++ /dev/null
@@ -1,89 +0,0 @@
-using System.Collections;
-using System.Collections.Generic;
-using UnityEngine;
-
-//[CreateAssetMenu(fileName = "DragWindAffector", menuName = "AProjectiles/Affectors/DragWindAffector")]
-[System.Serializable]
-public class Affector_WindDrag : AffectorBase
-{
- //public Affector_WindDrag(AffectedProjectile projectile) : base(projectile)
- //{
- //}
-
- #region Settings
-
- ///
- /// Whether or not to use the variables from the global 'ProjectileEnvironment' component.
- /// It's recommended that this be left on
- ///
- public bool useEnvironmentSettings = true;
-
- #region If Not Using Environment Settings
-
- ///
- /// The air density in kg/m3.
- ///
- public float airDensity;
-
- ///
- /// The velocity of the wind in m/s
- ///
- public Vector3 windVelocity = Vector3.zero;
-
- public Vector3 GetwindVelocity()
- {
- return useEnvironmentSettings ?
- (Application.isPlaying ? ProjectileEnvironment.instance.environment.windVelocity : Vector3.positiveInfinity)
- :
- this.windVelocity;
-
- }
-
- #endregion
-
- #endregion
-
- //{TODO} Fix and update
- //public override void Tick(float deltaTime)
- //{
- // proj.AddForce(
- // GetDragAndWindAcceleration(
- // proj.velocity, proj.position.y,
- // proj.projectileData.dragCoefficient,
- // proj.projectileData.crossSectionalArea,
- // proj.projectileData.bulletMass),
- // ForceMode.Acceleration,
- // deltaTime
- // );
- //}
-
- #region Related Functions
-
- public Vector3 GetDragAndWindAcceleration(
- Vector3 velocity, float altitude,
- float dragCoefficient, float crossSectionalArea, float projectileMass)
- {
- Vector3 velDiff = windVelocity - velocity;
-
- return
- GetAirDensity(altitude) * dragCoefficient * crossSectionalArea *
- new Vector3(
- Mathf.Sign(velDiff.x) * velDiff.x * velDiff.x,
- Mathf.Sign(velDiff.y) * velDiff.y * velDiff.y,
- Mathf.Sign(velDiff.z) * velDiff.z * velDiff.z
- )
- / (2 * projectileMass);
-
- }
-
- float GetAirDensity(float altitude)
- {
- return
- useEnvironmentSettings ?
- ProjectileEnvironment.instance.environment.GetAirDensity(altitude) :
- airDensity;
-
- }
-
- #endregion
-}
diff --git a/Assets/Scripts/ProjectileAffectors/Affectors/Affector_WindDrag - Copy.cs.meta b/Assets/Scripts/ProjectileAffectors/Affectors/Affector_WindDrag - Copy.cs.meta
deleted file mode 100644
index 7ff063b..0000000
--- a/Assets/Scripts/ProjectileAffectors/Affectors/Affector_WindDrag - Copy.cs.meta
+++ /dev/null
@@ -1,11 +0,0 @@
-fileFormatVersion: 2
-guid: 177c2336db4326c4380372c5595fd4bc
-MonoImporter:
- externalObjects: {}
- serializedVersion: 2
- defaultReferences: []
- executionOrder: 0
- icon: {instanceID: 0}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Scripts/ProjectileAffectors/Affectors/Affector_WindDragCubed.cs b/Assets/Scripts/ProjectileAffectors/Affectors/Affector_WindDragCubed.cs
index 34a2af2..b43bc30 100644
--- a/Assets/Scripts/ProjectileAffectors/Affectors/Affector_WindDragCubed.cs
+++ b/Assets/Scripts/ProjectileAffectors/Affectors/Affector_WindDragCubed.cs
@@ -1,12 +1,9 @@
-using System.Collections;
+using System.Collections;
using System.Collections.Generic;
using UnityEngine;
-//[CreateAssetMenu(fileName = "DragWindCubedAffector", menuName = "AProjectiles/Affectors/CubedDragWindAffector")]
[System.Serializable]
public class Affector_WindDragCubed : AffectorBase {
- //public Affector_WindDragCubed() : base()
- //{ }
#region Settings
@@ -33,7 +30,6 @@ public Vector3 GetwindVelocity() {
(Application.isPlaying ? ProjectileEnvironment.instance.environment.windVelocity : Vector3.positiveInfinity)
:
this.windVelocity;
-
}
#endregion
@@ -42,16 +38,29 @@ public Vector3 GetwindVelocity() {
public DirectionalDragCube directionDragCube = new DirectionalDragCube();
- //{TODO}Update this to use the DirectionalDragCube
- public override void Tick_PostPhysics(float deltaTime) {
- proj.physicsTransform.AddForce(
+ public override void Tick_PrePhysics(float deltaTime) {
+ ProjectileData projData = proj.projectileData;
+ VirtualPhysicsTransform pt = proj.physicsTransform;
+
+ float altitude = pt.Position.y;
+
+ // Use Mach-dependent drag coefficient when advanced drag model is available
+ float cd;
+ if (projData.useAdvancedDragModel && projData.isGraphGenerated) {
+ float speedOfSound = GetSpeedOfSound(altitude);
+ float mach = pt.Velocity.magnitude / speedOfSound;
+ cd = projData.GetDragCoefficient(mach);
+ } else {
+ cd = projData.dragCoefficient;
+ }
+
+ pt.AddForce(
GetDragAndWindAcceleration(
- proj.physicsTransform.Velocity, proj.physicsTransform.Position.y,
- proj.projectileData.dragCoefficient,
- proj.projectileData.CrossSectionalArea,
- proj.projectileData.bulletMass),
- ForceMode.Acceleration,
- deltaTime
+ pt.Velocity, altitude,
+ cd,
+ projData.CrossSectionalArea,
+ projData.bulletMass),
+ ForceMode.Acceleration
);
}
@@ -60,17 +69,21 @@ public override void Tick_PostPhysics(float deltaTime) {
public Vector3 GetDragAndWindAcceleration(
Vector3 velocity, float altitude,
float dragCoefficient, float crossSectionalArea, float projectileMass) {
- Vector3 velDiff = windVelocity - velocity;
+ Vector3 velRelative = GetwindVelocity() - velocity;
- return
- GetAirDensity(altitude) * dragCoefficient * crossSectionalArea *
- new Vector3(
- Mathf.Sign(velDiff.x) * velDiff.x * velDiff.x,
- Mathf.Sign(velDiff.y) * velDiff.y * velDiff.y,
- Mathf.Sign(velDiff.z) * velDiff.z * velDiff.z
- )
- / (2 * projectileMass);
+ // Drag acts along the relative velocity vector using its magnitude squared
+ float speed = velRelative.magnitude;
+ if (speed < 0.0001f)
+ return Vector3.zero;
+ // F_drag = 0.5 * rho * Cd * A * |v_rel|^2 * direction(v_rel)
+ // Since velRelative already has the direction and one factor of speed,
+ // we multiply by speed (not speed^2) and by velRelative (not normalized)
+ // a_drag = F_drag / m
+ return
+ (GetAirDensity(altitude) * dragCoefficient * crossSectionalArea * speed)
+ / (2 * projectileMass)
+ * velRelative;
}
float GetAirDensity(float altitude) {
@@ -78,7 +91,13 @@ float GetAirDensity(float altitude) {
useEnvironmentSettings ?
ProjectileEnvironment.instance.environment.GetAirDensity(altitude) :
airDensity;
+ }
+ float GetSpeedOfSound(float altitude) {
+ return
+ useEnvironmentSettings ?
+ ProjectileEnvironment.instance.environment.GetSpeedOfSound(altitude) :
+ 343.0f;
}
#endregion
diff --git a/Assets/Scripts/ProjectileAffectors/TerminalBallistics/Default_TerminalCalculator.cs b/Assets/Scripts/ProjectileAffectors/TerminalBallistics/Default_TerminalCalculator.cs
index e588a6c..936331a 100644
--- a/Assets/Scripts/ProjectileAffectors/TerminalBallistics/Default_TerminalCalculator.cs
+++ b/Assets/Scripts/ProjectileAffectors/TerminalBallistics/Default_TerminalCalculator.cs
@@ -44,11 +44,14 @@ protected virtual Tuple WillPen(TerminalBallisticsData penData) {
//$$stoppingDist=\sqrt[3]{\frac{projMass * initVel^2}{targetDensity*projCrossArea * projDragCoefficient}} $$
//Heavily simplified model
+ float speedOfSound = ProjectileEnvironment.instance.environment.GetSpeedOfSound(physicsTransform.Position.y);
+ float mach = physicsTransform.Velocity.magnitude / speedOfSound;
+
float stoppingDist =
Mathf.Pow( //3rd root
- (projData.bulletMass * physicsTransform.VelocityMagnitude * physicsTransform.VelocityMagnitude) //{TODO} Make velocity relative to target
+ (projData.bulletMass * physicsTransform.VelocityMagnitude * physicsTransform.VelocityMagnitude)
/
- (targetDensity * projData.CrossSectionalArea * projData.GetDragCoefficient(physicsTransform.Velocity.magnitude / 343.0f)) //{TODO} Rewrite correctly!
+ (targetDensity * projData.CrossSectionalArea * projData.GetDragCoefficient(mach))
, (1.0f / 3.0f)
);
@@ -60,8 +63,7 @@ protected virtual void Penetrate(TerminalBallisticsData penData, float stoppingD
ProjectileData projData = penData.projectile.projectileData;
VirtualPhysicsTransform physicsTransform = penData.projectile.physicsTransform;
float targetDensity = 1000.0f; //{TODO} Hook this up
-
- //343.0f - Temporary hardcoded value - 1 mach in air ~= 343 m/s
+ float speedOfSound = ProjectileEnvironment.instance.environment.GetSpeedOfSound(physicsTransform.Position.y);
ThicknessData objectThickness =
ProjectileHelper.FindThickness(
@@ -93,7 +95,7 @@ protected virtual void Penetrate(TerminalBallisticsData penData, float stoppingD
(
(
projData.CrossSectionalArea *
- projData.GetDragCoefficient(physicsTransform.Velocity.magnitude / 343.0f) *
+ projData.GetDragCoefficient(physicsTransform.Velocity.magnitude / speedOfSound) *
targetDensity *
objectThickness.thickness * objectThickness.thickness * objectThickness.thickness
)
@@ -127,8 +129,7 @@ protected virtual void Richochet(TerminalBallisticsData penData) {
penData.projectile.physicsTransform.Position = penData.hitInfo.point + (penData.hitInfo.normal * 0.01f);
//{TODO} Tie this up together with how much energy we lose to the rigidbody
//Restitution dependant richochet
- PhysicMaterial physMat =
- penData.hitInfo.collider.gameObject.GetComponent().material;
+ PhysicMaterial physMat = penData.hitInfo.collider.sharedMaterial;
penData.projectile.physicsTransform.Velocity =
(physMat == null)
? //If the hit object does not have a physics material, then just do a normal reflect
diff --git a/Assets/Scripts/ProjectileStuff/ProjectileAffectors.cs b/Assets/Scripts/ProjectileStuff/ProjectileAffectors.cs
index 36ffc7b..bfe4969 100644
--- a/Assets/Scripts/ProjectileStuff/ProjectileAffectors.cs
+++ b/Assets/Scripts/ProjectileStuff/ProjectileAffectors.cs
@@ -124,15 +124,15 @@ public static float WaterVapourPressure(float dewPointTemperature)
// https://www.brisbanehotairballooning.com.au/calculate-air-density/
return (0.99999683f + dewPointTemperature * //0
- (-0.90826951f * (10 ^ -2) + dewPointTemperature * //1
- (0.78736169f * (10 ^ -4) + dewPointTemperature * //2
- (-0.61117958f * (10 ^ -6) + dewPointTemperature * //3
- (0.43884187f * (10 ^ -8) + dewPointTemperature * //4
- (-0.29883885f * (10 ^ -10) + dewPointTemperature * //5
- (0.21874425f * (10 ^ -12) + dewPointTemperature * //6
- (-0.17892321f * (10 ^ -14) + dewPointTemperature * //7
- (0.11112018f * (10 ^ -16) + dewPointTemperature * //8
- (-0.30994571f * (10 ^ -19)))))))))));
+ (-0.90826951e-2f + dewPointTemperature * //1
+ (0.78736169e-4f + dewPointTemperature * //2
+ (-0.61117958e-6f + dewPointTemperature * //3
+ (0.43884187e-8f + dewPointTemperature * //4
+ (-0.29883885e-10f + dewPointTemperature * //5
+ (0.21874425e-12f + dewPointTemperature * //6
+ (-0.17892321e-14f + dewPointTemperature * //7
+ (0.11112018e-16f + dewPointTemperature * //8
+ (-0.30994571e-19f))))))))));
}
#endregion
diff --git a/Assets/Scripts/VirtualPhysicsTransform.cs b/Assets/Scripts/VirtualPhysicsTransform.cs
index 4f11a9e..c181152 100644
--- a/Assets/Scripts/VirtualPhysicsTransform.cs
+++ b/Assets/Scripts/VirtualPhysicsTransform.cs
@@ -37,6 +37,7 @@ public void Initialise(VirtualPhysicsTransform fromData) {
prevRotVel = fromData.prevRotVel;
mass = fromData.mass;
+ accel = fromData.accel;
}
#region Private Vars
@@ -59,6 +60,10 @@ public void Initialise(VirtualPhysicsTransform fromData) {
float
mass = 1.0f;
+ // Accumulated acceleration for Velocity Verlet integration.
+ // Forces are accumulated here during PrePhysics, then applied in Tick.
+ Vector3 accel = Vector3.zero;
+
#endregion
#region Properties
@@ -137,7 +142,12 @@ public virtual void Tick(float deltaTime) {
prevRot = rot;
prevRotVel = rotVel;
- pos += vel * deltaTime;
+ // Velocity Verlet integration:
+ // pos uses second-order correction for better trajectory accuracy
+ pos += vel * deltaTime + 0.5f * accel * deltaTime * deltaTime;
+ vel += accel * deltaTime;
+ accel = Vector3.zero;
+
rot *= Quaternion.LerpUnclamped(Quaternion.identity, RotationalVelocity, deltaTime);
}
@@ -156,17 +166,20 @@ public virtual void Tick(float deltaTime) {
#region Physics
+ ///
+ /// Adds a force to this physics body.
+ /// For Acceleration/Force modes, the force is accumulated and applied during Tick()
+ /// using Velocity Verlet integration. The deltaTime parameter is ignored for these
+ /// modes as dt is applied during integration.
+ /// Impulse/VelocityChange are applied immediately.
+ ///
public void AddForce(Vector3 force, ForceMode mode = ForceMode.Force, float deltaTime = float.NaN) {
- if (float.IsNaN(deltaTime))
- deltaTime = Time.deltaTime;
-
- //Debug.Log($"deltaTime: {deltaTime}, force: {force}");
switch (mode) {
case ForceMode.Acceleration:
- vel += force * deltaTime;
+ accel += force;
return;
case ForceMode.Force:
- vel += (force / mass) * deltaTime;
+ accel += force / mass;
return;
case ForceMode.Impulse:
vel += force / mass;