< Summary

Class:Agent
Assembly:bamlab.micromissiles
File(s):/github/workspace/Assets/Scripts/Agent.cs
Covered lines:111
Uncovered lines:160
Coverable lines:271
Total lines:481
Line coverage:40.9% (111 of 271)
Covered branches:0
Total branches:0
Covered methods:20
Total methods:45
Method coverage:44.4% (20 of 45)

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
Agent()0%110100%
SetFlightPhase(...)0%5.024060%
GetFlightPhase()0%2100%
HasLaunched()0%2100%
HasTerminated()0%2100%
SetDynamicAgentConfig(...)0%110100%
SetStaticAgentConfig(...)0%110100%
IsAssignable()0%2100%
AssignTarget(...)0%2100%
GetAssignedTarget()0%2100%
HasAssignedTarget()0%110100%
GetTargetModel()0%2100%
CheckTargetHit()0%12300%
UnassignTarget()0%2100%
IsHit()0%2100%
AddInterceptor(...)0%2100%
RemoveInterceptor(...)0%2100%
TerminateAgent()0%12300%
HandleInterceptHit(...)0%56700%
HandleInterceptMiss()0%72800%
HandleThreatHit()0%12300%
HandleThreatMiss()0%12300%
SetInitialVelocity(...)0%110100%
GetPosition()0%110100%
SetPosition(...)0%110100%
GetSpeed()0%110100%
GetVelocity()0%110100%
SetVelocity(...)0%110100%
GetAcceleration()0%110100%
SetAcceleration(...)0%2100%
GetRelativeTransformation(...)0%110100%
GetRelativeTransformationToWaypoint(...)0%110100%
GetRelativePositionTransformation(...)0%220100%
GetRelativeVelocityTransformation(...)0%550100%
GetRelativeAccelerationTransformation(...)0%110100%
GetDynamicPressure()0%2100%
Awake()0%2100%
Start()0%110100%
FixedUpdate()0%2101400%
AlignWithVelocity()0%6200%
CalculateAcceleration(...)0%2100%
CalculateMaxForwardAcceleration()0%110100%
CalculateMaxNormalAcceleration()0%110100%
CalculateDrag()0%2100%
CalculateLiftInducedDrag(...)0%2100%

File(s)

/github/workspace/Assets/Scripts/Agent.cs

#LineLine coverage
 1using System.Collections;
 2using System.Collections.Generic;
 3using UnityEngine;
 4
 5public abstract class Agent : MonoBehaviour {
 6  // In the initialized phase, the agent is subject to no forces.
 7  // In the ready phase, the agent is subject to gravity and drag with zero input acceleration.
 8  public enum FlightPhase { INITIALIZED, READY, BOOST, MIDCOURSE, TERMINAL, TERMINATED }
 9
 10  [SerializeField]
 4111  private FlightPhase _flightPhase = FlightPhase.INITIALIZED;
 12
 13  [SerializeField]
 14  protected Vector3 _velocity;
 15
 16  [SerializeField]
 17  protected Vector3 _acceleration;
 18
 19  [SerializeField]
 20  protected Vector3 _dragAcceleration;
 21
 22  [SerializeField]
 23  // Only for debugging (viewing in editor)
 24  // Don't bother setting this it won't be used
 25  protected float _speed;
 26
 27  [SerializeField]
 4128  protected List<Agent> _interceptors = new List<Agent>();
 29  [SerializeField]
 30  protected Agent _target;
 31  [SerializeField]
 32  protected Agent _targetModel;
 4133  protected bool _isHit = false;
 4134  protected bool _isMiss = false;
 35
 36  [SerializeField]
 4137  protected double _timeSinceBoost = 0;
 38  [SerializeField]
 4139  protected double _timeInPhase = 0;
 40
 41  public StaticAgentConfig staticAgentConfig;
 42  public DynamicAgentConfig dynamicAgentConfig;
 43
 44  // Define delegates
 45  // MarkDestroyed event handler
 46  public delegate void TerminatedEventHandler(Agent agent);
 47  public event TerminatedEventHandler OnTerminated;
 48
 49  public delegate void InterceptHitEventHandler(Interceptor interceptor, Threat target);
 50  public delegate void InterceptMissEventHandler(Interceptor interceptor, Threat target);
 51  public delegate void ThreatHitEventHandler(Threat threat);
 52  public delegate void ThreatMissEventHandler(Threat threat);
 53
 54  // Define events
 55  public event InterceptHitEventHandler OnInterceptHit;
 56  public event InterceptMissEventHandler OnInterceptMiss;
 57  public event ThreatHitEventHandler OnThreatHit;
 58  public event ThreatMissEventHandler OnThreatMiss;
 59
 60  private Vector3 _initialVelocity;
 61
 1562  public void SetFlightPhase(FlightPhase flightPhase) {
 1563    _timeInPhase = 0;
 1564    _flightPhase = flightPhase;
 1565    if (flightPhase == FlightPhase.INITIALIZED || flightPhase == FlightPhase.TERMINATED) {
 066      GetComponent<Rigidbody>().constraints = RigidbodyConstraints.FreezeAll;
 1567    } else {
 1568      GetComponent<Rigidbody>().constraints = RigidbodyConstraints.None;
 1569    }
 1570    if (flightPhase == FlightPhase.BOOST) {
 071      SetVelocity(GetVelocity() + _initialVelocity);
 072    }
 1573  }
 74
 075  public FlightPhase GetFlightPhase() {
 076    return _flightPhase;
 077  }
 78
 079  public bool HasLaunched() {
 080    return _flightPhase != FlightPhase.INITIALIZED;
 081  }
 82
 083  public bool HasTerminated() {
 084    return _flightPhase == FlightPhase.TERMINATED;
 085  }
 86
 1587  public virtual void SetDynamicAgentConfig(DynamicAgentConfig config) {
 1588    dynamicAgentConfig = config;
 1589  }
 90
 1591  public virtual void SetStaticAgentConfig(StaticAgentConfig config) {
 1592    staticAgentConfig = config;
 1593    GetComponent<Rigidbody>().mass = staticAgentConfig.bodyConfig.mass;
 1594  }
 95
 096  public virtual bool IsAssignable() {
 097    return true;
 098  }
 99
 0100  public virtual void AssignTarget(Agent target) {
 0101    _target = target;
 0102    _target.AddInterceptor(this);
 0103    _targetModel = SimManager.Instance.CreateDummyAgent(target.GetPosition(), target.GetVelocity());
 0104  }
 105
 0106  public Agent GetAssignedTarget() {
 0107    return _target;
 0108  }
 109
 6110  public bool HasAssignedTarget() {
 6111    return _target != null;
 6112  }
 113
 0114  public Agent GetTargetModel() {
 0115    return _targetModel;
 0116  }
 117
 0118  public void CheckTargetHit() {
 0119    if (HasAssignedTarget() && _target.IsHit()) {
 0120      UnassignTarget();
 0121    }
 0122  }
 123
 0124  public virtual void UnassignTarget() {
 0125    _target.RemoveInterceptor(this);
 0126    _target = null;
 0127    _targetModel = null;
 0128  }
 129
 130  // Return whether the agent has hit or been hit.
 0131  public bool IsHit() {
 0132    return _isHit;
 0133  }
 134
 0135  public void AddInterceptor(Agent interceptor) {
 0136    _interceptors.Add(interceptor);
 0137  }
 138
 0139  public void RemoveInterceptor(Agent interceptor) {
 0140    _interceptors.Remove(interceptor);
 0141  }
 142
 0143  public virtual void TerminateAgent() {
 0144    if (_flightPhase != FlightPhase.TERMINATED) {
 0145      OnTerminated?.Invoke(this);
 0146    }
 0147    _flightPhase = FlightPhase.TERMINATED;
 0148    SetPosition(new Vector3(0, 0, 0));
 0149    gameObject.SetActive(false);
 0150  }
 151
 152  // Mark the agent as having hit the target or been hit.
 0153  public void HandleInterceptHit(Agent otherAgent) {
 0154    _isHit = true;
 0155    if (this is Interceptor interceptor && otherAgent is Threat threat) {
 0156      OnInterceptHit?.Invoke(interceptor, threat);
 0157    } else if (this is Threat threatAgent && otherAgent is Interceptor interceptorTarget) {
 0158      OnInterceptHit?.Invoke(interceptorTarget, threatAgent);
 0159    }
 0160    TerminateAgent();
 0161  }
 162
 0163  public void HandleInterceptMiss() {
 0164    if (_target != null) {
 0165      if (this is Interceptor interceptor && _target is Threat threat) {
 0166        OnInterceptMiss?.Invoke(interceptor, threat);
 0167      } else if (this is Threat threatAgent && _target is Interceptor interceptorTarget) {
 0168        OnInterceptMiss?.Invoke(interceptorTarget, threatAgent);
 0169      }
 0170      UnassignTarget();
 0171    }
 0172    TerminateAgent();
 0173  }
 174
 175  // This happens if we, e.g., hit the carrier
 0176  public void HandleThreatHit() {
 0177    _isHit = true;
 0178    if (this is Threat threat) {
 0179      OnThreatHit?.Invoke(threat);
 0180    }
 0181    TerminateAgent();
 0182  }
 183
 184  // This happens if we, e.g., hit the floor
 0185  public void HandleThreatMiss() {
 0186    if (this is Threat threat) {
 0187      OnThreatMiss?.Invoke(threat);
 0188    }
 0189    TerminateAgent();
 0190  }
 191
 15192  public void SetInitialVelocity(Vector3 velocity) {
 15193    _initialVelocity = velocity;
 15194  }
 195
 14196  public Vector3 GetPosition() {
 14197    return transform.position;
 14198  }
 199
 4200  public void SetPosition(Vector3 position) {
 4201    transform.position = position;
 4202  }
 203
 8204  public double GetSpeed() {
 8205    return GetComponent<Rigidbody>().linearVelocity.magnitude;
 8206  }
 207
 34208  public Vector3 GetVelocity() {
 34209    return GetComponent<Rigidbody>().linearVelocity;
 34210  }
 211
 1212  public void SetVelocity(Vector3 velocity) {
 1213    GetComponent<Rigidbody>().linearVelocity = velocity;
 1214  }
 215
 3216  public Vector3 GetAcceleration() {
 3217    return _acceleration;
 3218  }
 219
 0220  public void SetAcceleration(Vector3 acceleration) {
 0221    _acceleration = acceleration;
 0222  }
 223
 3224  public Transformation GetRelativeTransformation(Agent target) {
 3225    Transformation transformation = new Transformation();
 226
 227    // Get the relative position transformation.
 3228    transformation.position = GetRelativePositionTransformation(target.GetPosition());
 229
 230    // Get the relative velocity transformation.
 3231    transformation.velocity = GetRelativeVelocityTransformation(
 232        target.GetPosition() - GetPosition(), target.GetVelocity() - GetVelocity());
 233
 234    // Get the relative acceleration transformation.
 3235    transformation.acceleration = GetRelativeAccelerationTransformation(target);
 3236    return transformation;
 3237  }
 238
 5239  public Transformation GetRelativeTransformationToWaypoint(Vector3 waypoint) {
 5240    Transformation transformation = new Transformation();
 241
 242    // Get the relative position transformation.
 5243    transformation.position = GetRelativePositionTransformation(waypoint);
 244
 245    // Get the relative velocity transformation.
 5246    transformation.velocity =
 247        GetRelativeVelocityTransformation(waypoint - GetPosition(), -GetVelocity());
 248
 5249    return transformation;
 5250  }
 251
 8252  private PositionTransformation GetRelativePositionTransformation(Vector3 relativePosition) {
 8253    PositionTransformation positionTransformation = new PositionTransformation();
 254
 255    // Set the relative position in Cartesian coordinates
 8256    positionTransformation.cartesian = relativePosition;
 257
 258    // Calculate the distance (range) to the target
 8259    positionTransformation.range = relativePosition.magnitude;
 260
 8261    Vector3 flatRelativePosition = Vector3.ProjectOnPlane(relativePosition, transform.up);
 8262    Vector3 verticalRelativePosition = relativePosition - flatRelativePosition;
 263
 264    // Calculate elevation (vertical angle relative to forward)
 8265    positionTransformation.elevation =
 266        Mathf.Atan(verticalRelativePosition.magnitude / flatRelativePosition.magnitude);
 267
 268    // Calculate azimuth (horizontal angle relative to forward)
 9269    if (flatRelativePosition.magnitude == 0) {
 1270      positionTransformation.azimuth = 0;
 8271    } else {
 7272      positionTransformation.azimuth =
 273          Vector3.SignedAngle(transform.forward, flatRelativePosition, transform.up) * Mathf.PI /
 274          180;
 7275    }
 276
 8277    return positionTransformation;
 8278  }
 279
 280  private VelocityTransformation GetRelativeVelocityTransformation(Vector3 relativePosition,
 8281                                                                   Vector3 relativeVelocity) {
 8282    VelocityTransformation velocityTransformation = new VelocityTransformation();
 283
 284    // Set the relative velocity in Cartesian coordinates.
 8285    velocityTransformation.cartesian = relativeVelocity;
 286
 287    // Calculate range rate (radial velocity).
 8288    velocityTransformation.range = Vector3.Dot(relativeVelocity, relativePosition.normalized);
 289
 290    // Project relative velocity onto the sphere passing through the target.
 8291    Vector3 tangentialVelocity = Vector3.ProjectOnPlane(relativeVelocity, relativePosition);
 292
 293    // The target azimuth vector is orthogonal to the relative position vector and
 294    // points to the starboard of the target along the azimuth-elevation sphere.
 8295    Vector3 targetAzimuth = Vector3.Cross(transform.up, relativePosition);
 296    // The target elevation vector is orthogonal to the relative position vector
 297    // and points upwards from the target along the azimuth-elevation sphere.
 8298    Vector3 targetElevation = Vector3.Cross(relativePosition, transform.right);
 299    // If the relative position vector is parallel to the yaw or pitch axis, the
 300    // target azimuth vector or the target elevation vector will be undefined.
 9301    if (targetAzimuth.magnitude == 0) {
 1302      targetAzimuth = Vector3.Cross(targetElevation, relativePosition);
 10303    } else if (targetElevation.magnitude == 0) {
 2304      targetElevation = Vector3.Cross(relativePosition, targetAzimuth);
 2305    }
 306
 307    // Project the relative velocity vector on the azimuth-elevation sphere onto
 308    // the target azimuth vector.
 8309    Vector3 tangentialVelocityOnAzimuth = Vector3.Project(tangentialVelocity, targetAzimuth);
 310
 311    // Calculate the time derivative of the azimuth to the target.
 8312    velocityTransformation.azimuth =
 313        tangentialVelocityOnAzimuth.magnitude / relativePosition.magnitude;
 13314    if (Vector3.Dot(tangentialVelocityOnAzimuth, targetAzimuth) < 0) {
 5315      velocityTransformation.azimuth *= -1;
 5316    }
 317
 318    // Project the velocity vector on the azimuth-elevation sphere onto the target
 319    // elevation vector.
 8320    Vector3 tangentialVelocityOnElevation = Vector3.Project(tangentialVelocity, targetElevation);
 321
 322    // Calculate the time derivative of the elevation to the target.
 8323    velocityTransformation.elevation =
 324        tangentialVelocityOnElevation.magnitude / relativePosition.magnitude;
 9325    if (Vector3.Dot(tangentialVelocityOnElevation, targetElevation) < 0) {
 1326      velocityTransformation.elevation *= -1;
 1327    }
 328
 8329    return velocityTransformation;
 8330  }
 331
 3332  private AccelerationTransformation GetRelativeAccelerationTransformation(Agent agent) {
 333    // Since the agent's acceleration is an input, the relative acceleration is just the agent's
 334    // acceleration.
 3335    AccelerationTransformation accelerationTransformation = new AccelerationTransformation();
 3336    accelerationTransformation.cartesian = agent.GetAcceleration();
 3337    return accelerationTransformation;
 3338  }
 339
 0340  public double GetDynamicPressure() {
 0341    var airDensity = Constants.CalculateAirDensityAtAltitude(transform.position.y);
 0342    var flowSpeed = GetSpeed();
 0343    return 0.5 * airDensity * (flowSpeed * flowSpeed);
 0344  }
 345
 346  protected abstract void UpdateReady(double deltaTime);
 347  protected abstract void UpdateBoost(double deltaTime);
 348  protected abstract void UpdateMidCourse(double deltaTime);
 349
 0350  protected virtual void Awake() {}
 351
 352  // Start is called before the first frame update
 38353  protected virtual void Start() {}
 354
 355  // Update is called once per frame
 0356  protected virtual void FixedUpdate() {
 0357    _speed = (float)GetSpeed();
 0358    if (_flightPhase != FlightPhase.INITIALIZED && _flightPhase != FlightPhase.READY) {
 0359      _timeSinceBoost += Time.fixedDeltaTime;
 0360    }
 0361    _timeInPhase += Time.fixedDeltaTime;
 362
 0363    var launch_time = dynamicAgentConfig.dynamic_config.launch_config.launch_time;
 0364    var boost_time = staticAgentConfig.boostConfig.boostTime;
 0365    double elapsedSimulationTime = SimManager.Instance.GetElapsedSimulationTime();
 366
 0367    if (_flightPhase == FlightPhase.TERMINATED) {
 0368      return;
 369    }
 370
 0371    if (_flightPhase == FlightPhase.INITIALIZED || _flightPhase == FlightPhase.READY) {
 0372      float launchTimeVariance = 0.5f;
 0373      float launchTimeNoise = Random.Range(-launchTimeVariance, launchTimeVariance);
 0374      launch_time += launchTimeNoise;
 375
 0376      if (elapsedSimulationTime >= launch_time) {
 0377        SetFlightPhase(FlightPhase.BOOST);
 0378      }
 0379    }
 0380    if (_timeSinceBoost > boost_time && _flightPhase == FlightPhase.BOOST) {
 0381      SetFlightPhase(FlightPhase.MIDCOURSE);
 0382    }
 0383    AlignWithVelocity();
 0384    switch (_flightPhase) {
 385      case FlightPhase.INITIALIZED:
 0386        break;
 387      case FlightPhase.READY:
 0388        UpdateReady(Time.fixedDeltaTime);
 0389        break;
 390      case FlightPhase.BOOST:
 0391        UpdateBoost(Time.fixedDeltaTime);
 0392        break;
 393      case FlightPhase.MIDCOURSE:
 394      case FlightPhase.TERMINAL:
 0395        UpdateMidCourse(Time.fixedDeltaTime);
 0396        break;
 397      case FlightPhase.TERMINATED:
 0398        break;
 399    }
 400
 0401    _velocity = GetVelocity();
 402    // Store the acceleration because it is set to zero after each simulation step
 0403    _acceleration =
 404        GetComponent<Rigidbody>().GetAccumulatedForce() / GetComponent<Rigidbody>().mass;
 0405  }
 406
 0407  protected virtual void AlignWithVelocity() {
 0408    Vector3 velocity = GetVelocity();
 0409    if (velocity.magnitude > 0.1f)  // Only align if we have significant velocity
 0410    {
 411      // Create a rotation with forward along velocity and up along world up
 0412      Quaternion targetRotation = Quaternion.LookRotation(velocity, Vector3.up);
 413
 414      // Smoothly rotate towards the target rotation
 0415      transform.rotation = Quaternion.RotateTowards(transform.rotation, targetRotation,
 416                                                    10000f * Time.fixedDeltaTime);
 0417    }
 0418  }
 419
 0420  protected Vector3 CalculateAcceleration(Vector3 accelerationInput) {
 0421    Vector3 gravity = Physics.gravity;
 0422    float airDrag = CalculateDrag();
 0423    float liftInducedDrag = CalculateLiftInducedDrag(accelerationInput + gravity);
 0424    float dragAcceleration = -(airDrag + liftInducedDrag);
 425
 426    // Project the drag acceleration onto the forward direction
 0427    Vector3 dragAccelerationAlongRoll = dragAcceleration * transform.forward;
 0428    _dragAcceleration = dragAccelerationAlongRoll;
 429
 0430    return accelerationInput + gravity + dragAccelerationAlongRoll;
 0431  }
 432
 6433  protected float CalculateMaxForwardAcceleration() {
 6434    return staticAgentConfig.accelerationConfig.maxForwardAcceleration;
 6435  }
 436
 8437  protected float CalculateMaxNormalAcceleration() {
 8438    float maxReferenceNormalAcceleration =
 439        (float)(staticAgentConfig.accelerationConfig.maxReferenceNormalAcceleration *
 440                Constants.kGravity);
 8441    float referenceSpeed = staticAgentConfig.accelerationConfig.referenceSpeed;
 8442    return Mathf.Pow((float)GetSpeed() / referenceSpeed, 2) * maxReferenceNormalAcceleration;
 8443  }
 444
 0445  private float CalculateDrag() {
 0446    float dragCoefficient = staticAgentConfig.liftDragConfig.dragCoefficient;
 0447    float crossSectionalArea = staticAgentConfig.bodyConfig.crossSectionalArea;
 0448    float mass = staticAgentConfig.bodyConfig.mass;
 0449    float dynamicPressure = (float)GetDynamicPressure();
 0450    float dragForce = dragCoefficient * dynamicPressure * crossSectionalArea;
 0451    return dragForce / mass;
 0452  }
 453
 0454  private float CalculateLiftInducedDrag(Vector3 accelerationInput) {
 0455    float liftAcceleration = Vector3.ProjectOnPlane(accelerationInput, transform.up).magnitude;
 0456    float liftDragRatio = staticAgentConfig.liftDragConfig.liftDragRatio;
 0457    return Mathf.Abs(liftAcceleration / liftDragRatio);
 0458  }
 459}
 460
 461public class DummyAgent : Agent {
 462  protected override void Start() {
 463    base.Start();
 464  }
 465
 466  protected override void FixedUpdate() {
 467    GetComponent<Rigidbody>().AddForce(_acceleration, ForceMode.Acceleration);
 468  }
 469
 470  protected override void UpdateReady(double deltaTime) {
 471    // Do nothing
 472  }
 473
 474  protected override void UpdateBoost(double deltaTime) {
 475    // Do nothing
 476  }
 477
 478  protected override void UpdateMidCourse(double deltaTime) {
 479    // Do nothing
 480  }
 481}