< Summary

Class:Agent
Assembly:bamlab.micromissiles
File(s):/github/workspace/Assets/Scripts/Agents/Agent.cs
Covered lines:131
Uncovered lines:125
Coverable lines:256
Total lines:460
Line coverage:51.1% (131 of 256)
Covered branches:0
Total branches:0
Covered methods:26
Total methods:45
Method coverage:57.7% (26 of 45)

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
Agent()0%110100%
SetFlightPhase(...)0%20400%
GetFlightPhase()0%2100%
IsInitialized()0%2100%
IsTerminated()0%110100%
SetAgentConfig(...)0%110100%
SetStaticConfig(...)0%330100%
IsAssignable()0%2100%
AssignTarget(...)0%110100%
GetAssignedTarget()0%2100%
HasAssignedTarget()0%110100%
GetTargetModel()0%2100%
UnassignTarget()0%220100%
AddInterceptor(...)0%110100%
RemoveInterceptor(...)0%110100%
TerminateAgent()0%330100%
HandleInterceptHit(...)0%20400%
HandleInterceptMiss()0%20400%
HandleTargetIntercepted()0%6200%
HandleThreatHit()0%12300%
HandleHitGround()0%42600%
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%2401500%
AlignWithVelocity()0%6200%
CalculateAcceleration(...)0%2100%
CalculateMaxForwardAcceleration()0%330100%
CalculateMaxNormalAcceleration()0%550100%
CalculateDrag()0%56700%
CalculateLiftInducedDrag(...)0%12300%

File(s)

/github/workspace/Assets/Scripts/Agents/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  // TODO(titan): Replace this enumeration with the Protobuf enumeration.
 9  public enum FlightPhase { INITIALIZED, READY, BOOST, MIDCOURSE, TERMINAL, TERMINATED }
 10
 11  [SerializeField]
 13912  private FlightPhase _flightPhase = FlightPhase.INITIALIZED;
 13
 14  [SerializeField]
 15  protected Vector3 _velocity;
 16
 17  [SerializeField]
 18  protected Vector3 _acceleration;
 19
 20  [SerializeField]
 21  protected Vector3 _dragAcceleration;
 22
 23  [SerializeField]
 24  protected float _speed;
 25
 26  [SerializeField]
 13927  protected List<Agent> _interceptors = new List<Agent>();
 28  [SerializeField]
 29  protected Agent _target;
 30  [SerializeField]
 31  protected Agent _targetModel;
 32
 33  [SerializeField]
 13934  protected double _timeSinceBoost = 0;
 35  [SerializeField]
 13936  protected double _timeInPhase = 0;
 37
 38  public Configs.StaticConfig staticConfig;
 39  public Configs.AgentConfig agentConfig;
 40
 41  // Define delegates.
 42  public delegate void TerminatedEventHandler(Agent agent);
 43  public event TerminatedEventHandler OnTerminated;
 44
 45  public delegate void InterceptHitEventHandler(Interceptor interceptor, Threat target);
 46  public delegate void InterceptMissEventHandler(Interceptor interceptor, Threat target);
 47  public delegate void ThreatHitEventHandler(Threat threat);
 48  public delegate void ThreatMissEventHandler(Threat threat);
 49
 50  // Define events.
 51  public event InterceptHitEventHandler OnInterceptHit;
 52  public event InterceptMissEventHandler OnInterceptMiss;
 53  public event ThreatHitEventHandler OnThreatHit;
 54  public event ThreatMissEventHandler OnThreatMiss;
 55
 56  private Vector3 _initialVelocity;
 57
 058  public void SetFlightPhase(FlightPhase flightPhase) {
 059    _timeInPhase = 0;
 060    _flightPhase = flightPhase;
 061    if (flightPhase == FlightPhase.INITIALIZED || flightPhase == FlightPhase.TERMINATED) {
 062      GetComponent<Rigidbody>().constraints = RigidbodyConstraints.FreezeAll;
 063    } else {
 064      GetComponent<Rigidbody>().constraints = RigidbodyConstraints.None;
 065    }
 066    if (flightPhase == FlightPhase.BOOST) {
 067      SetVelocity(GetVelocity() + _initialVelocity);
 068    }
 069  }
 70
 071  public FlightPhase GetFlightPhase() {
 072    return _flightPhase;
 073  }
 74
 075  public bool IsInitialized() {
 076    return _flightPhase != FlightPhase.INITIALIZED;
 077  }
 78
 28579  public bool IsTerminated() {
 28580    return _flightPhase == FlightPhase.TERMINATED;
 28581  }
 82
 1683  public virtual void SetAgentConfig(Configs.AgentConfig config) {
 1684    agentConfig = config;
 1685  }
 86
 1687  public virtual void SetStaticConfig(Configs.StaticConfig config) {
 1688    staticConfig = config;
 1689    GetComponent<Rigidbody>().mass = staticConfig.BodyConfig?.Mass ?? 1;
 1690  }
 91
 092  public virtual bool IsAssignable() {
 093    return true;
 094  }
 95
 196  public virtual void AssignTarget(Agent target) {
 197    _target = target;
 198    _target.AddInterceptor(this);
 199    _targetModel = SimManager.Instance.CreateDummyAgent(target.GetPosition(), target.GetVelocity());
 1100  }
 101
 0102  public Agent GetAssignedTarget() {
 0103    return _target;
 0104  }
 105
 17106  public bool HasAssignedTarget() {
 17107    return _target != null;
 17108  }
 109
 0110  public Agent GetTargetModel() {
 0111    return _targetModel;
 0112  }
 113
 2114  public virtual void UnassignTarget() {
 4115    if (HasAssignedTarget()) {
 2116      _target.RemoveInterceptor(this);
 2117    }
 2118    _target = null;
 2119    _targetModel = null;
 2120  }
 121
 2122  public void AddInterceptor(Agent interceptor) {
 2123    _interceptors.Add(interceptor);
 2124  }
 125
 3126  public void RemoveInterceptor(Agent interceptor) {
 3127    _interceptors.Remove(interceptor);
 3128  }
 129
 130  public IReadOnlyList<Agent> AssignedInterceptors {
 15131    get { return _interceptors; }
 132  }
 133
 1134  public virtual void TerminateAgent() {
 1135    UnassignTarget();
 2136    if (_flightPhase != FlightPhase.TERMINATED) {
 1137      OnTerminated?.Invoke(this);
 1138    }
 1139    _flightPhase = FlightPhase.TERMINATED;
 1140    SetPosition(Vector3.zero);
 1141    gameObject.SetActive(false);
 1142  }
 143
 0144  public void HandleInterceptHit(Agent agent) {
 0145    if (this is Interceptor interceptor && agent is Threat threat) {
 0146      OnInterceptHit?.Invoke(interceptor, threat);
 0147      TerminateAgent();
 0148    }
 0149  }
 150
 0151  public void HandleInterceptMiss() {
 0152    if (this is Interceptor interceptor && _target is Threat threat) {
 0153      OnInterceptMiss?.Invoke(interceptor, threat);
 0154    }
 0155  }
 156
 0157  public void HandleTargetIntercepted() {
 0158    if (this is Threat threat) {
 0159      TerminateAgent();
 0160    }
 0161  }
 162
 0163  public void HandleThreatHit() {
 0164    if (this is Threat threat) {
 0165      OnThreatHit?.Invoke(threat);
 0166      TerminateAgent();
 0167    }
 0168  }
 169
 0170  public void HandleHitGround() {
 0171    if (this is Interceptor interceptor && _target is Threat target) {
 0172      OnInterceptMiss?.Invoke(interceptor, target);
 0173    }
 0174    if (this is Threat threat) {
 0175      OnThreatMiss?.Invoke(threat);
 0176    }
 0177    TerminateAgent();
 0178  }
 179
 16180  public void SetInitialVelocity(Vector3 velocity) {
 16181    _initialVelocity = velocity;
 16182  }
 183
 324184  public Vector3 GetPosition() {
 324185    return transform.position;
 324186  }
 187
 6188  public void SetPosition(Vector3 position) {
 6189    transform.position = position;
 6190  }
 191
 30192  public double GetSpeed() {
 30193    return GetComponent<Rigidbody>().linearVelocity.magnitude;
 30194  }
 195
 70196  public Vector3 GetVelocity() {
 70197    return GetComponent<Rigidbody>().linearVelocity;
 70198  }
 199
 2200  public void SetVelocity(Vector3 velocity) {
 2201    GetComponent<Rigidbody>().linearVelocity = velocity;
 2202  }
 203
 12204  public Vector3 GetAcceleration() {
 12205    return _acceleration;
 12206  }
 207
 0208  public void SetAcceleration(Vector3 acceleration) {
 0209    _acceleration = acceleration;
 0210  }
 211
 3212  public Transformation GetRelativeTransformation(Agent target) {
 3213    Transformation transformation = new Transformation();
 214
 215    // Get the relative position transformation.
 3216    transformation.position =
 217        GetRelativePositionTransformation(target.GetPosition() - GetPosition());
 218
 219    // Get the relative velocity transformation.
 3220    transformation.velocity = GetRelativeVelocityTransformation(
 221        target.GetPosition() - GetPosition(), target.GetVelocity() - GetVelocity());
 222
 223    // Get the relative acceleration transformation.
 3224    transformation.acceleration = GetRelativeAccelerationTransformation(target);
 3225    return transformation;
 3226  }
 227
 5228  public Transformation GetRelativeTransformationToWaypoint(Vector3 waypoint) {
 5229    Transformation transformation = new Transformation();
 230
 231    // Get the relative position transformation.
 5232    transformation.position = GetRelativePositionTransformation(waypoint - GetPosition());
 233
 234    // Get the relative velocity transformation.
 5235    transformation.velocity =
 236        GetRelativeVelocityTransformation(waypoint - GetPosition(), -GetVelocity());
 237
 5238    return transformation;
 5239  }
 240
 8241  private PositionTransformation GetRelativePositionTransformation(Vector3 relativePosition) {
 8242    PositionTransformation positionTransformation = new PositionTransformation();
 243
 244    // Set the relative position in Cartesian coordinates.
 8245    positionTransformation.cartesian = relativePosition;
 246
 247    // Calculate the distance (range) to the target.
 8248    positionTransformation.range = relativePosition.magnitude;
 249
 8250    Vector3 flatRelativePosition = Vector3.ProjectOnPlane(relativePosition, transform.up);
 8251    Vector3 verticalRelativePosition = relativePosition - flatRelativePosition;
 252
 253    // Calculate the elevation (vertical angle relative to forward).
 8254    positionTransformation.elevation =
 255        Mathf.Atan(verticalRelativePosition.magnitude / flatRelativePosition.magnitude);
 256
 257    // Calculate the azimuth (horizontal angle relative to forward).
 9258    if (flatRelativePosition.magnitude == 0) {
 1259      positionTransformation.azimuth = 0;
 8260    } else {
 7261      positionTransformation.azimuth =
 262          Vector3.SignedAngle(transform.forward, flatRelativePosition, transform.up) * Mathf.PI /
 263          180;
 7264    }
 265
 8266    return positionTransformation;
 8267  }
 268
 269  private VelocityTransformation GetRelativeVelocityTransformation(Vector3 relativePosition,
 8270                                                                   Vector3 relativeVelocity) {
 8271    VelocityTransformation velocityTransformation = new VelocityTransformation();
 272
 273    // Set the relative velocity in Cartesian coordinates.
 8274    velocityTransformation.cartesian = relativeVelocity;
 275
 276    // Calculate range rate (radial velocity).
 8277    velocityTransformation.range = Vector3.Dot(relativeVelocity, relativePosition.normalized);
 278
 279    // Project relative velocity onto the sphere passing through the target.
 8280    Vector3 tangentialVelocity = Vector3.ProjectOnPlane(relativeVelocity, relativePosition);
 281
 282    // The target azimuth vector is orthogonal to the relative position vector and
 283    // points to the starboard of the target along the azimuth-elevation sphere.
 8284    Vector3 targetAzimuth = Vector3.Cross(transform.up, relativePosition);
 285    // The target elevation vector is orthogonal to the relative position vector
 286    // and points upwards from the target along the azimuth-elevation sphere.
 8287    Vector3 targetElevation = Vector3.Cross(relativePosition, transform.right);
 288    // If the relative position vector is parallel to the yaw or pitch axis, the
 289    // target azimuth vector or the target elevation vector will be undefined.
 9290    if (targetAzimuth.magnitude == 0) {
 1291      targetAzimuth = Vector3.Cross(targetElevation, relativePosition);
 10292    } else if (targetElevation.magnitude == 0) {
 2293      targetElevation = Vector3.Cross(relativePosition, targetAzimuth);
 2294    }
 295
 296    // Project the relative velocity vector on the azimuth-elevation sphere onto
 297    // the target azimuth vector.
 8298    Vector3 tangentialVelocityOnAzimuth = Vector3.Project(tangentialVelocity, targetAzimuth);
 299
 300    // Calculate the time derivative of the azimuth to the target.
 8301    velocityTransformation.azimuth =
 302        tangentialVelocityOnAzimuth.magnitude / relativePosition.magnitude;
 9303    if (Vector3.Dot(tangentialVelocityOnAzimuth, targetAzimuth) < 0) {
 1304      velocityTransformation.azimuth *= -1;
 1305    }
 306
 307    // Project the velocity vector on the azimuth-elevation sphere onto the target
 308    // elevation vector.
 8309    Vector3 tangentialVelocityOnElevation = Vector3.Project(tangentialVelocity, targetElevation);
 310
 311    // Calculate the time derivative of the elevation to the target.
 8312    velocityTransformation.elevation =
 313        tangentialVelocityOnElevation.magnitude / relativePosition.magnitude;
 9314    if (Vector3.Dot(tangentialVelocityOnElevation, targetElevation) < 0) {
 1315      velocityTransformation.elevation *= -1;
 1316    }
 317
 8318    return velocityTransformation;
 8319  }
 320
 3321  private AccelerationTransformation GetRelativeAccelerationTransformation(Agent agent) {
 322    // Since the agent's acceleration is an input, the relative acceleration is just the agent's
 323    // acceleration.
 3324    AccelerationTransformation accelerationTransformation = new AccelerationTransformation();
 3325    accelerationTransformation.cartesian = agent.GetAcceleration();
 3326    return accelerationTransformation;
 3327  }
 328
 0329  public double GetDynamicPressure() {
 0330    var airDensity = Constants.CalculateAirDensityAtAltitude(transform.position.y);
 0331    var flowSpeed = GetSpeed();
 0332    return 0.5 * airDensity * (flowSpeed * flowSpeed);
 0333  }
 334
 335  protected abstract void UpdateReady(double deltaTime);
 336  protected abstract void UpdateBoost(double deltaTime);
 337  protected abstract void UpdateMidCourse(double deltaTime);
 338
 0339  protected virtual void Awake() {}
 340
 40341  protected virtual void Start() {}
 342
 0343  protected virtual void FixedUpdate() {
 0344    _speed = (float)GetSpeed();
 0345    if (_flightPhase != FlightPhase.INITIALIZED && _flightPhase != FlightPhase.READY) {
 0346      _timeSinceBoost += Time.fixedDeltaTime;
 0347    }
 0348    _timeInPhase += Time.fixedDeltaTime;
 349
 0350    var boostTime = staticConfig.BoostConfig?.BoostTime ?? 0;
 0351    double elapsedSimulationTime = SimManager.Instance.GetElapsedSimulationTime();
 352
 0353    if (_flightPhase == FlightPhase.TERMINATED) {
 0354      return;
 355    }
 0356    if (_flightPhase == FlightPhase.INITIALIZED || _flightPhase == FlightPhase.READY) {
 0357      SetFlightPhase(FlightPhase.BOOST);
 0358    }
 0359    if (_timeSinceBoost > boostTime && _flightPhase == FlightPhase.BOOST) {
 0360      SetFlightPhase(FlightPhase.MIDCOURSE);
 0361    }
 0362    AlignWithVelocity();
 0363    switch (_flightPhase) {
 364      case FlightPhase.INITIALIZED:
 0365        break;
 366      case FlightPhase.READY:
 0367        UpdateReady(Time.fixedDeltaTime);
 0368        break;
 369      case FlightPhase.BOOST:
 0370        UpdateBoost(Time.fixedDeltaTime);
 0371        break;
 372      case FlightPhase.MIDCOURSE:
 373      case FlightPhase.TERMINAL:
 0374        UpdateMidCourse(Time.fixedDeltaTime);
 0375        break;
 376      case FlightPhase.TERMINATED:
 0377        break;
 378    }
 379
 0380    _velocity = GetVelocity();
 381    // Store the acceleration because it is set to zero after each simulation step
 0382    _acceleration =
 383        GetComponent<Rigidbody>().GetAccumulatedForce() / GetComponent<Rigidbody>().mass;
 0384  }
 385
 0386  protected virtual void AlignWithVelocity() {
 0387    Vector3 velocity = GetVelocity();
 388    // Only align if we have significant velocity.
 0389    if (velocity.magnitude > 0.1f) {
 390      // Create a rotation with forward along velocity and up along world up.
 0391      Quaternion targetRotation = Quaternion.LookRotation(velocity, Vector3.up);
 392
 393      // Smoothly rotate towards the target rotation.
 0394      transform.rotation = Quaternion.RotateTowards(transform.rotation, targetRotation,
 395                                                    10000f * Time.fixedDeltaTime);
 0396    }
 0397  }
 398
 0399  protected Vector3 CalculateAcceleration(Vector3 accelerationInput) {
 0400    Vector3 gravity = Physics.gravity;
 0401    float airDrag = CalculateDrag();
 0402    float liftInducedDrag = CalculateLiftInducedDrag(accelerationInput + gravity);
 0403    float dragAcceleration = -(airDrag + liftInducedDrag);
 404
 405    // Project the drag acceleration onto the forward direction.
 0406    Vector3 dragAccelerationAlongRoll = dragAcceleration * transform.forward;
 0407    _dragAcceleration = dragAccelerationAlongRoll;
 408
 0409    return accelerationInput + gravity + dragAccelerationAlongRoll;
 0410  }
 411
 5412  public float CalculateMaxForwardAcceleration() {
 5413    return staticConfig.AccelerationConfig?.MaxForwardAcceleration ?? 0;
 5414  }
 415
 15416  public float CalculateMaxNormalAcceleration() {
 15417    float maxReferenceNormalAcceleration =
 418        (float)((staticConfig.AccelerationConfig?.MaxReferenceNormalAcceleration ?? 0) *
 419                Constants.kGravity);
 15420    float referenceSpeed = staticConfig.AccelerationConfig?.ReferenceSpeed ?? 1;
 15421    return Mathf.Pow((float)GetSpeed() / referenceSpeed, 2) * maxReferenceNormalAcceleration;
 15422  }
 423
 0424  private float CalculateDrag() {
 0425    float dragCoefficient = staticConfig.LiftDragConfig?.DragCoefficient ?? 0;
 0426    float crossSectionalArea = staticConfig.BodyConfig?.CrossSectionalArea ?? 0;
 0427    float mass = staticConfig.BodyConfig?.Mass ?? 1;
 0428    float dynamicPressure = (float)GetDynamicPressure();
 0429    float dragForce = dragCoefficient * dynamicPressure * crossSectionalArea;
 0430    return dragForce / mass;
 0431  }
 432
 0433  private float CalculateLiftInducedDrag(Vector3 accelerationInput) {
 0434    float liftAcceleration = Vector3.ProjectOnPlane(accelerationInput, transform.forward).magnitude;
 0435    float liftDragRatio = staticConfig.LiftDragConfig?.LiftDragRatio ?? 1;
 0436    return Mathf.Abs(liftAcceleration / liftDragRatio);
 0437  }
 438}
 439
 440public class DummyAgent : Agent {
 441  protected override void Start() {
 442    base.Start();
 443  }
 444
 445  protected override void FixedUpdate() {
 446    GetComponent<Rigidbody>().AddForce(_acceleration, ForceMode.Acceleration);
 447  }
 448
 449  protected override void UpdateReady(double deltaTime) {
 450    // Do nothing.
 451  }
 452
 453  protected override void UpdateBoost(double deltaTime) {
 454    // Do nothing.
 455  }
 456
 457  protected override void UpdateMidCourse(double deltaTime) {
 458    // Do nothing.
 459  }
 460}