Added physical balloon (sphere with collider that expand when filled with gas, receive buoyancy force, and is linked to the vessel with a spring)
This commit is contained in:
parent
9e93806a99
commit
0306486fb0
@ -16,6 +16,8 @@ namespace Aerostats
|
||||
|
||||
// In this class, amount of gas Q is specified in m^3 at stp (standard pressure of 100kPa and temperature of 0°C = 273K). Ideal gas equation P.V = n.R.T gives us the equivalent number of moles: n = P.V/R/T = 100000.Q/8.314/273 = 44.Q mol
|
||||
private static readonly float R = 8.314f;
|
||||
private static readonly float ZeroCelsius = 273.15f;
|
||||
private static readonly float StandardPressure = 100000.0f;
|
||||
private static readonly float StpVolumeToMoles = 100000.0f / R / 273.15f;
|
||||
|
||||
/// <summary>
|
||||
@ -60,9 +62,34 @@ namespace Aerostats
|
||||
/// If the balloon can not store the target amount of gas (because the maximum volume has been reached), the system won't try to inject more gas, to avoid venting through the security valve of the balloon.
|
||||
/// </summary>
|
||||
[KSPField(isPersistant = true, guiActive = true)]
|
||||
[UI_FloatRange(minValue = 0.0f, maxValue = 20000.0f, stepIncrement = 1.0f)]
|
||||
[UI_FloatRange(minValue = 0, maxValue = 20000.0f, stepIncrement = 1.0f)]
|
||||
public float LiftingGasTargetQuantity;
|
||||
|
||||
/// <summary>
|
||||
/// Mass of the empty balloon, in kg
|
||||
/// </summary>
|
||||
[KSPField(isPersistant = false, guiActive = false)]
|
||||
public float BalloonEmptyMass = 50.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Minimum amount of gas that will be use to inflate the balloon at the beginning.
|
||||
/// This is needed if you want the balloon to lift itself right after staging.
|
||||
/// </summary>
|
||||
[KSPField(isPersistant = false, guiActive = false)]
|
||||
public float MinimumFillQuantity = 75.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Drag coefficient of the balloon
|
||||
/// </summary>
|
||||
[KSPField(isPersistant = false, guiActive = false)]
|
||||
public float BalloonDragCoeff = 0.3f;
|
||||
|
||||
/// <summary>
|
||||
/// Force applied by the spring between the balloon and the vessel, proportional to spring extension, in newton per meter
|
||||
/// </summary>
|
||||
[KSPField(isPersistant = false, guiActive = false)]
|
||||
public float SpringHardness = 20000.0f;
|
||||
|
||||
private bool Staged;
|
||||
|
||||
/// <summary>
|
||||
@ -76,6 +103,8 @@ namespace Aerostats
|
||||
/// </summary>
|
||||
private float Inflation = 0;
|
||||
|
||||
private GameObject Balloon;
|
||||
|
||||
private void PlayAnimation(string animationName, float animationSpeed)
|
||||
{
|
||||
foreach (Animation animation in part.FindModelAnimators(animationName))
|
||||
@ -117,12 +146,22 @@ namespace Aerostats
|
||||
{
|
||||
UnityEngine.Debug.Log("Aerostats: staged");
|
||||
ScreenMessages.PostScreenMessage("staged");
|
||||
PlayAnimation(DeployAnimation, 1.0f);
|
||||
//PlayAnimation(DeployAnimation, 1.0f);
|
||||
|
||||
Balloon = GameObject.CreatePrimitive(PrimitiveType.Sphere);
|
||||
Balloon.transform.position = part.Rigidbody.position + part.Rigidbody.transform.up;
|
||||
Balloon.AddComponent<Rigidbody>();
|
||||
Balloon.rigidbody.mass = BalloonEmptyMass;
|
||||
|
||||
Balloon.rigidbody.angularDrag = 10.0f;
|
||||
|
||||
LiftingGasQuantity = Math.Min(MinimumFillQuantity, RemainingCompressedGas);
|
||||
RemainingCompressedGas -= LiftingGasQuantity;
|
||||
}
|
||||
|
||||
private void FixedUpdate()
|
||||
{
|
||||
if (!Staged && part.inverseStage >= Staging.CurrentStage)
|
||||
if (!Staged && GameSettings.LAUNCH_STAGES.GetKeyDown() && vessel.isActiveVessel && (part.inverseStage == Staging.CurrentStage - 1 || Staging.CurrentStage == 0))
|
||||
{
|
||||
Staged = true;
|
||||
OnStaged();
|
||||
@ -130,21 +169,25 @@ namespace Aerostats
|
||||
|
||||
if (Staged)
|
||||
{
|
||||
if (!IsAnimationPlaying(DeployAnimation))
|
||||
//if (!IsAnimationPlaying(DeployAnimation))
|
||||
{
|
||||
float externalTemperature = (float)FlightGlobals.getExternalTemperature();
|
||||
float balloonInternalTemperature = externalTemperature;
|
||||
float externalPressure = Math.Max((float)FlightGlobals.getStaticPressure() * 1000, 0.00001f);
|
||||
float externalPressure = Math.Max((float)FlightGlobals.getStaticPressure() * 1000.0f, 0.00001f);
|
||||
Util.PostSingleScreenMessage("external atmo", "Temperature = " + externalTemperature + "K, Pressure=" + externalPressure + "Pa");
|
||||
|
||||
float currentMaxQuantity = MaxBalloonVolume / R / balloonInternalTemperature * externalPressure / StpVolumeToMoles;
|
||||
|
||||
LiftingGasTargetQuantity = Math.Max(MinimumFillQuantity, LiftingGasTargetQuantity);
|
||||
if(LiftingGasTargetQuantity > LiftingGasQuantity)
|
||||
{
|
||||
// infalting balloon
|
||||
float stepFinalQuantity = Math.Min(LiftingGasQuantity + MaxGasFillRate * Time.fixedDeltaTime, Math.Min(LiftingGasTargetQuantity, currentMaxQuantity));
|
||||
float step = Math.Max(stepFinalQuantity - LiftingGasQuantity, 0.0f);
|
||||
LiftingGasQuantity += step;
|
||||
RemainingCompressedGas -= step;
|
||||
|
||||
// gas exhausted
|
||||
if(RemainingCompressedGas < 0)
|
||||
{
|
||||
Util.PostSingleScreenMessage("out of gas", "Lifting gas reserve exhausted");
|
||||
@ -154,6 +197,7 @@ namespace Aerostats
|
||||
}
|
||||
else
|
||||
{
|
||||
// deflating balloon
|
||||
LiftingGasQuantity = Math.Max(LiftingGasQuantity - MaxGasVentRate * Time.fixedDeltaTime, LiftingGasTargetQuantity);
|
||||
}
|
||||
|
||||
@ -170,16 +214,56 @@ namespace Aerostats
|
||||
Inflation = currentGasVolume / MaxBalloonVolume;
|
||||
|
||||
Util.PostSingleScreenMessage("inflation", "Inflation = " + (Inflation * 100.0f).ToString("0.00") + "%");
|
||||
SetInflation(Inflation);
|
||||
//SetInflation(Inflation);
|
||||
|
||||
float airDensity = (float)FlightGlobals.getAtmDensity(externalPressure / 1000.0f, externalTemperature, vessel.mainBody);
|
||||
float balloonLift = currentGasVolume * (airDensity - GasDensity);
|
||||
Util.PostSingleScreenMessage("lift", "Air density = " + airDensity + "kg/m^3, Lift = " + balloonLift + "kg");
|
||||
float currentGasDensity = GasDensity * balloonInternalTemperature / ZeroCelsius / externalPressure * StandardPressure;
|
||||
float balloonLift = currentGasVolume * airDensity;
|
||||
float balloonGasMass = currentGasDensity * currentGasVolume;
|
||||
Util.PostSingleScreenMessage("lift", "Air density = " + airDensity + "kg/m^3, Lift = " + (balloonLift - balloonGasMass) + "kg");
|
||||
var gravityAccel = FlightGlobals.getGeeForceAtPosition(vessel.GetWorldPos3D());
|
||||
//Util.PostSingleScreenMessage("gravity", "Gravity accel = (" + gravityAccel.x + ", " + gravityAccel.y + ", " + gravityAccel.z + ")");
|
||||
part.Rigidbody.AddForce(-gravityAccel * balloonLift / 1000.0f, ForceMode.Force);
|
||||
|
||||
// V = 4/3*pi*r^3
|
||||
float radius = Mathf.Pow(currentGasVolume * 0.75f / Mathf.PI, 0.333f);
|
||||
float scale = radius + 0.1f;
|
||||
Balloon.transform.localScale = new Vector3(scale,scale,scale);
|
||||
Balloon.rigidbody.mass = (BalloonEmptyMass + balloonGasMass) * 0.001f;
|
||||
|
||||
Balloon.rigidbody.AddForce(-gravityAccel * balloonLift / 1000.0f, ForceMode.Force);
|
||||
|
||||
// balloon drag
|
||||
var airVelocity = Balloon.rigidbody.velocity + Krakensbane.GetFrameVelocity() /*- vessel.mainBody.getRFrmVel(vessel.GetWorldPos3D())*/;
|
||||
float sqVel = (float)airVelocity.magnitude;
|
||||
sqVel *= sqVel;
|
||||
float dragForce = 0.5f * airDensity * sqVel * BalloonDragCoeff * (Mathf.PI * radius * radius);
|
||||
Util.PostSingleScreenMessage("balloon drag", "Drag = " + dragForce + "N");
|
||||
Balloon.rigidbody.AddForce(-airVelocity.normalized * dragForce / 1000.0f, ForceMode.Force);
|
||||
|
||||
// spring between balloon and base
|
||||
float restLength = 2.0f + radius;
|
||||
var springVec = Balloon.rigidbody.position - part.rigidbody.position;
|
||||
float springLength = springVec.magnitude;
|
||||
float springForceMag = 0;
|
||||
if(springLength > restLength)
|
||||
{
|
||||
float tensingLength = springLength - restLength;
|
||||
springForceMag = tensingLength * SpringHardness;
|
||||
var springForce = springVec * (springForceMag / springLength * 0.001f);
|
||||
part.rigidbody.AddForce(springForce, ForceMode.Force);
|
||||
Balloon.rigidbody.AddForceAtPosition(-springForce, Balloon.rigidbody.position - Balloon.transform.up * radius, ForceMode.Force);
|
||||
}
|
||||
Util.PostSingleScreenMessage("spring force", "Spring force = " + springForceMag + "N");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
if (Balloon != null)
|
||||
{
|
||||
Gizmos.DrawLine(part.transform.position, Balloon.transform.position - Balloon.transform.up * Balloon.transform.localScale.x);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
BIN
doc/references/balloon_almost_empty.jpg
Normal file
BIN
doc/references/balloon_almost_empty.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 162 KiB |
BIN
doc/references/balloon_full.jpg
Normal file
BIN
doc/references/balloon_full.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 94 KiB |
Loading…
Reference in New Issue
Block a user