Certainly, Here is the serializable class, the main script, and a screenshot of the main script component in the inspector.
In the inspector screenshot, “Middle Right Wing” is the variable that gets deleted.
Note that this does not happen in the editor, it only happens when running on iOS devices/simulators.
The serializable class:
using UnityEngine;
using System.Collections;
[System.Serializable]
public class ThinAirfoil {
public string name;
public Transform airfoilObject;
public GameObject airfoilGameObject;
//-----------------------------------------------
private BirdPhysicsHandler birdPhysicsHandler;
private Rigidbody handlerRigidbody;
//-----------------------------------------------
//-----------------------------------------------
//DIMENSIONAL VARIABLES
public float wingspan = 1;
public float averageChord = 0.22f;
private float aspectRatio;
private float planformArea = 0.0f;
//-----------------------------------------------
//DIMENSIONAL FUNCTIONS
//UPDATE THE PLANFORM AREA
void UpdatePlanformArea () {
planformArea = wingspan*averageChord;
UpdateAspectRatio();
}
//UPDATE THE ASPECT RATIO
void UpdateAspectRatio () {
aspectRatio = (wingspan*wingspan)/planformArea;
}
//-----------------------------------------------
//-----------------------------------------------
//AWAKE EVENTS
public void AwakeEvents (BirdPhysicsHandler passedHandler) {
birdPhysicsHandler = passedHandler;
handlerRigidbody = birdPhysicsHandler.thisRigidbody;
UpdatePlanformArea();
}
//-----------------------------------------------
//-----------------------------------------------
//UPDATE EVENTS
public void UpdateEvents () {
UpdateLift();
UpdateDrag();
ApplyCombinedForces();
#if UNITY_EDITOR
DrawVectors();
#endif
}
//-----------------------------------------------
//-----------------------------------------------
//LIFT VARIABLES
private Vector3 relativeAirVelocity;
private float angleOfAttack;
private float airspeedVelocity;
private float dynamicPressure;
public bool isCambered = true;
public float zeroAOALiftCoefficient = 1.2f;
private float liftCoefficient;
private float lift;
//-----------------------------------------------
//-----------------------------------------------
//LIFT
//CALCULATE THE VALUE OF LIFT
void UpdateLift () {
//Update local-space (relative) velocity
//using global-space velocty of this object
UpdateRelativeAirVelocity();
//Angle of Attack
UpdateAngleOfAttack();
//Airspeed Velocity
//(3D velocity of local-space air moving past wing)
//(Non-directional, since AoA handles direction)
airspeedVelocity = relativeAirVelocity.magnitude;
//Dynamic Pressure
dynamicPressure = 0.5f*birdPhysicsHandler.airDensity*airspeedVelocity;
//Lift coefficient using Thin-Airfoil Theory approximation
liftCoefficient = 2.0f*Mathf.PI*(angleOfAttack*Mathf.Deg2Rad);
//Handle Airfoil Shape
if (isCambered) {
//Apply Cambering Effect
liftCoefficient += zeroAOALiftCoefficient;
}
//Relative Lift Value
lift = dynamicPressure*planformArea*liftCoefficient;
}
//-----------------------------------------------
//CALCULATE THE RELATIVE AIR VELOCITY
//(convert total air velocity vector to the object's local-space coordinates)
void UpdateRelativeAirVelocity () {
//Convert global-space velocity to local-space (relative-space)
relativeAirVelocity = airfoilObject.InverseTransformDirection(birdPhysicsHandler.totalGlobalAirVelocity);
}
//CALCULATE THE ANGLE OF ATTACK
//(get the angle of attack using the actual relative, local-space, air velocity vector)
void UpdateAngleOfAttack () {
//(Only use y and z because we don't care about side-to-side for AoA)
if (relativeAirVelocity.z >= 0) {
angleOfAttack = Vector2.Angle(Vector2.right, new Vector2(relativeAirVelocity.z, relativeAirVelocity.y));
} else {
//If the object is moving backwards,
//we change the AoA to act like the
//back of the airfoil is now the front.
angleOfAttack = -Vector2.Angle(-Vector2.right, new Vector2(relativeAirVelocity.z, relativeAirVelocity.y));
//!!! - This is where a stall would trigger in the gameplay
}
//If the air velocity is coming from above the airfoil,
//then make the AoA negative (pointing downward relative
//to the passing air)
if (relativeAirVelocity.y > 0) {
angleOfAttack *= -1;
}
}
//-----------------------------------------------
//-----------------------------------------------
//DRAG VARIABLES
public float oswaldEfficiencyNumber = 0.9f;
private float drag = 0.0f;
//-----------------------------------------------
//-----------------------------------------------
//DRAG
void UpdateDrag () {
drag = (lift*lift)/(dynamicPressure*planformArea*Mathf.PI*oswaldEfficiencyNumber*aspectRatio);
}
//-----------------------------------------------
//-----------------------------------------------
//USE FORCES
private Vector3 combinedForces;
//APPLY COMBINED FORCES
void ApplyCombinedForces () {
combinedForces = GetLiftVector();
combinedForces += GetDragVector();
//Apply force
//(converts it to world-space because
//AddForceAtPosition doesn't have a
//local-space variant)
handlerRigidbody.AddForceAtPosition(airfoilObject.TransformDirection(combinedForces), airfoilObject.position);
}
//-----------------------------------------------
//LIFT FORCE
Vector3 GetLiftVector () {
//Gets the lift in the direction of the
//cross product of the local airflow vector
//and the local "right" direction vector
//(this makes the lift perpendicular to the airflow)
return lift*Vector3.Cross(relativeAirVelocity, Vector3.right).normalized;
}
//DRAG FORCE
Vector3 GetDragVector () {
//Gets drag force in the opposite of the airflow vector
//(because drag is applied as a pushing force,
//not calculated as a velocity)
return drag*(-relativeAirVelocity.normalized);
}
#if UNITY_EDITOR
//-----------------------------------------------
//-----------------------------------------------
//DIAGNOSTICS
public bool drawVectors = false;
void DrawVectors () {
if (drawVectors) {
//Lift
Debug.DrawRay(airfoilObject.position, airfoilObject.TransformDirection(lift*Vector3.Cross(relativeAirVelocity, Vector3.right).normalized), Color.green);
//Drag
Debug.DrawRay(airfoilObject.position, airfoilObject.TransformDirection(drag*(-relativeAirVelocity.normalized)), Color.red);
//Forward
Debug.DrawRay(airfoilObject.position, airfoilObject.forward, Color.yellow);
}
}
#endif
}
The main script:
using UnityEngine;
using System.Collections;
public class BirdPhysicsHandler : MonoBehaviour {
public ThinAirfoil[] airfoilObjects;
public bool useWind = true;
public Vector3 initialLocalVelocity = Vector3.forward;
//-----------------------------------------------
//-----------------------------------------------
//EFFICIENCY VARIABLES
[HideInInspector]
public Rigidbody thisRigidbody;
private Transform thisTransform;
//-----------------------------------------------
//-----------------------------------------------
//AWAKE
void Awake () {
thisRigidbody = rigidbody;
thisTransform = transform;
//Set center of mass to align with the wings
//!!! might want to set to custom position based on wing position
rigidbody.centerOfMass = new Vector3(0, 0, 0);
//Set Initial Local Velocity
thisRigidbody.velocity = thisTransform.TransformDirection(initialLocalVelocity);
//Run Airfoil Objects' Awake Events
foreach (ThinAirfoil thinAirfoil in airfoilObjects) {
thinAirfoil.AwakeEvents(this);
}
}
//-----------------------------------------------
//-----------------------------------------------
//UPDATE
void FixedUpdate () {
//Update applied wind
//(the motion of the wind relative to the object in global space)
UpdateTotalGlobalAirVelocity();
//Run Airfoil Objects' Update Events
foreach (ThinAirfoil thinAirfoil in airfoilObjects) {
thinAirfoil.UpdateEvents();
}
ApplyThrust();
#if UNITY_EDITOR
DrawVectors();
#endif
}
//-----------------------------------------------
//-----------------------------------------------
//LIFT VARIABLES
//Density of the air
[HideInInspector]
public float airDensity = 1.22521f; //Make this change with altitude
[HideInInspector]
public Vector3 totalGlobalAirVelocity;
//-----------------------------------------------
//LIFT CALCULATIONS
//CALCULATE TOTAL GLOBAL AIR VELOCITY
//(velocity vector of the air moving past the object in global-space coordinates)
void UpdateTotalGlobalAirVelocity () {
totalGlobalAirVelocity = thisRigidbody.velocity;
if (useWind) {
totalGlobalAirVelocity += WindGlobals.relativeGlobalWindVelocity;
}
}
//-----------------------------------------------
//-----------------------------------------------
//THRUST
private Vector3 combinedForces;
//APPLY COMBINED FORCES
void ApplyThrust () {
//Apply force
thisRigidbody.AddRelativeForce(GetThrustVector());
}
//-----------------------------------------------
//THRUST FORCE
public float forwardThrust = 1.0f;
public float upwardThrust = 0f;
Vector3 GetThrustVector () {
//Gets thrust as a relative vector
return new Vector3(0, upwardThrust, forwardThrust);
}
#if UNITY_EDITOR
//-----------------------------------------------
//-----------------------------------------------
//DIAGNOSTICS
public bool drawVectors = false;
void DrawVectors () {
if (drawVectors) {
//Thrust
Debug.DrawRay(thisTransform.position, thisTransform.TransformDirection(new Vector3(0, upwardThrust, forwardThrust)), Color.cyan);
//Gravity
Debug.DrawRay(thisTransform.position, Vector3.up*Physics.gravity.y, new Color(1, 0.5f, 0, 1));
//Global Air Velocity
Debug.DrawRay(thisTransform.position, totalGlobalAirVelocity, Color.white);
}
}
#endif
}
