< Summary

Class:DummyAgent
Assembly:bamlab.micromissiles
File(s):/github/workspace/Assets/Scripts/Agents/Agent.cs
Covered lines:3
Uncovered lines:9
Coverable lines:12
Total lines:460
Line coverage:25% (3 of 12)
Covered branches:0
Total branches:0
Covered methods:1
Total methods:5
Method coverage:20% (1 of 5)

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
Start()0%110100%
FixedUpdate()0%2100%
UpdateReady(...)0%2100%
UpdateBoost(...)0%2100%
UpdateMidCourse(...)0%2100%

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]
 12  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]
 27  protected List<Agent> _interceptors = new List<Agent>();
 28  [SerializeField]
 29  protected Agent _target;
 30  [SerializeField]
 31  protected Agent _targetModel;
 32
 33  [SerializeField]
 34  protected double _timeSinceBoost = 0;
 35  [SerializeField]
 36  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
 58  public void SetFlightPhase(FlightPhase flightPhase) {
 59    _timeInPhase = 0;
 60    _flightPhase = flightPhase;
 61    if (flightPhase == FlightPhase.INITIALIZED || flightPhase == FlightPhase.TERMINATED) {
 62      GetComponent<Rigidbody>().constraints = RigidbodyConstraints.FreezeAll;
 63    } else {
 64      GetComponent<Rigidbody>().constraints = RigidbodyConstraints.None;
 65    }
 66    if (flightPhase == FlightPhase.BOOST) {
 67      SetVelocity(GetVelocity() + _initialVelocity);
 68    }
 69  }
 70
 71  public FlightPhase GetFlightPhase() {
 72    return _flightPhase;
 73  }
 74
 75  public bool IsInitialized() {
 76    return _flightPhase != FlightPhase.INITIALIZED;
 77  }
 78
 79  public bool IsTerminated() {
 80    return _flightPhase == FlightPhase.TERMINATED;
 81  }
 82
 83  public virtual void SetAgentConfig(Configs.AgentConfig config) {
 84    agentConfig = config;
 85  }
 86
 87  public virtual void SetStaticConfig(Configs.StaticConfig config) {
 88    staticConfig = config;
 89    GetComponent<Rigidbody>().mass = staticConfig.BodyConfig?.Mass ?? 1;
 90  }
 91
 92  public virtual bool IsAssignable() {
 93    return true;
 94  }
 95
 96  public virtual void AssignTarget(Agent target) {
 97    _target = target;
 98    _target.AddInterceptor(this);
 99    _targetModel = SimManager.Instance.CreateDummyAgent(target.GetPosition(), target.GetVelocity());
 100  }
 101
 102  public Agent GetAssignedTarget() {
 103    return _target;
 104  }
 105
 106  public bool HasAssignedTarget() {
 107    return _target != null;
 108  }
 109
 110  public Agent GetTargetModel() {
 111    return _targetModel;
 112  }
 113
 114  public virtual void UnassignTarget() {
 115    if (HasAssignedTarget()) {
 116      _target.RemoveInterceptor(this);
 117    }
 118    _target = null;
 119    _targetModel = null;
 120  }
 121
 122  public void AddInterceptor(Agent interceptor) {
 123    _interceptors.Add(interceptor);
 124  }
 125
 126  public void RemoveInterceptor(Agent interceptor) {
 127    _interceptors.Remove(interceptor);
 128  }
 129
 130  public IReadOnlyList<Agent> AssignedInterceptors {
 131    get { return _interceptors; }
 132  }
 133
 134  public virtual void TerminateAgent() {
 135    UnassignTarget();
 136    if (_flightPhase != FlightPhase.TERMINATED) {
 137      OnTerminated?.Invoke(this);
 138    }
 139    _flightPhase = FlightPhase.TERMINATED;
 140    SetPosition(Vector3.zero);
 141    gameObject.SetActive(false);
 142  }
 143
 144  public void HandleInterceptHit(Agent agent) {
 145    if (this is Interceptor interceptor && agent is Threat threat) {
 146      OnInterceptHit?.Invoke(interceptor, threat);
 147      TerminateAgent();
 148    }
 149  }
 150
 151  public void HandleInterceptMiss() {
 152    if (this is Interceptor interceptor && _target is Threat threat) {
 153      OnInterceptMiss?.Invoke(interceptor, threat);
 154    }
 155  }
 156
 157  public void HandleTargetIntercepted() {
 158    if (this is Threat threat) {
 159      TerminateAgent();
 160    }
 161  }
 162
 163  public void HandleThreatHit() {
 164    if (this is Threat threat) {
 165      OnThreatHit?.Invoke(threat);
 166      TerminateAgent();
 167    }
 168  }
 169
 170  public void HandleHitGround() {
 171    if (this is Interceptor interceptor && _target is Threat target) {
 172      OnInterceptMiss?.Invoke(interceptor, target);
 173    }
 174    if (this is Threat threat) {
 175      OnThreatMiss?.Invoke(threat);
 176    }
 177    TerminateAgent();
 178  }
 179
 180  public void SetInitialVelocity(Vector3 velocity) {
 181    _initialVelocity = velocity;
 182  }
 183
 184  public Vector3 GetPosition() {
 185    return transform.position;
 186  }
 187
 188  public void SetPosition(Vector3 position) {
 189    transform.position = position;
 190  }
 191
 192  public double GetSpeed() {
 193    return GetComponent<Rigidbody>().linearVelocity.magnitude;
 194  }
 195
 196  public Vector3 GetVelocity() {
 197    return GetComponent<Rigidbody>().linearVelocity;
 198  }
 199
 200  public void SetVelocity(Vector3 velocity) {
 201    GetComponent<Rigidbody>().linearVelocity = velocity;
 202  }
 203
 204  public Vector3 GetAcceleration() {
 205    return _acceleration;
 206  }
 207
 208  public void SetAcceleration(Vector3 acceleration) {
 209    _acceleration = acceleration;
 210  }
 211
 212  public Transformation GetRelativeTransformation(Agent target) {
 213    Transformation transformation = new Transformation();
 214
 215    // Get the relative position transformation.
 216    transformation.position =
 217        GetRelativePositionTransformation(target.GetPosition() - GetPosition());
 218
 219    // Get the relative velocity transformation.
 220    transformation.velocity = GetRelativeVelocityTransformation(
 221        target.GetPosition() - GetPosition(), target.GetVelocity() - GetVelocity());
 222
 223    // Get the relative acceleration transformation.
 224    transformation.acceleration = GetRelativeAccelerationTransformation(target);
 225    return transformation;
 226  }
 227
 228  public Transformation GetRelativeTransformationToWaypoint(Vector3 waypoint) {
 229    Transformation transformation = new Transformation();
 230
 231    // Get the relative position transformation.
 232    transformation.position = GetRelativePositionTransformation(waypoint - GetPosition());
 233
 234    // Get the relative velocity transformation.
 235    transformation.velocity =
 236        GetRelativeVelocityTransformation(waypoint - GetPosition(), -GetVelocity());
 237
 238    return transformation;
 239  }
 240
 241  private PositionTransformation GetRelativePositionTransformation(Vector3 relativePosition) {
 242    PositionTransformation positionTransformation = new PositionTransformation();
 243
 244    // Set the relative position in Cartesian coordinates.
 245    positionTransformation.cartesian = relativePosition;
 246
 247    // Calculate the distance (range) to the target.
 248    positionTransformation.range = relativePosition.magnitude;
 249
 250    Vector3 flatRelativePosition = Vector3.ProjectOnPlane(relativePosition, transform.up);
 251    Vector3 verticalRelativePosition = relativePosition - flatRelativePosition;
 252
 253    // Calculate the elevation (vertical angle relative to forward).
 254    positionTransformation.elevation =
 255        Mathf.Atan(verticalRelativePosition.magnitude / flatRelativePosition.magnitude);
 256
 257    // Calculate the azimuth (horizontal angle relative to forward).
 258    if (flatRelativePosition.magnitude == 0) {
 259      positionTransformation.azimuth = 0;
 260    } else {
 261      positionTransformation.azimuth =
 262          Vector3.SignedAngle(transform.forward, flatRelativePosition, transform.up) * Mathf.PI /
 263          180;
 264    }
 265
 266    return positionTransformation;
 267  }
 268
 269  private VelocityTransformation GetRelativeVelocityTransformation(Vector3 relativePosition,
 270                                                                   Vector3 relativeVelocity) {
 271    VelocityTransformation velocityTransformation = new VelocityTransformation();
 272
 273    // Set the relative velocity in Cartesian coordinates.
 274    velocityTransformation.cartesian = relativeVelocity;
 275
 276    // Calculate range rate (radial velocity).
 277    velocityTransformation.range = Vector3.Dot(relativeVelocity, relativePosition.normalized);
 278
 279    // Project relative velocity onto the sphere passing through the target.
 280    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.
 284    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.
 287    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.
 290    if (targetAzimuth.magnitude == 0) {
 291      targetAzimuth = Vector3.Cross(targetElevation, relativePosition);
 292    } else if (targetElevation.magnitude == 0) {
 293      targetElevation = Vector3.Cross(relativePosition, targetAzimuth);
 294    }
 295
 296    // Project the relative velocity vector on the azimuth-elevation sphere onto
 297    // the target azimuth vector.
 298    Vector3 tangentialVelocityOnAzimuth = Vector3.Project(tangentialVelocity, targetAzimuth);
 299
 300    // Calculate the time derivative of the azimuth to the target.
 301    velocityTransformation.azimuth =
 302        tangentialVelocityOnAzimuth.magnitude / relativePosition.magnitude;
 303    if (Vector3.Dot(tangentialVelocityOnAzimuth, targetAzimuth) < 0) {
 304      velocityTransformation.azimuth *= -1;
 305    }
 306
 307    // Project the velocity vector on the azimuth-elevation sphere onto the target
 308    // elevation vector.
 309    Vector3 tangentialVelocityOnElevation = Vector3.Project(tangentialVelocity, targetElevation);
 310
 311    // Calculate the time derivative of the elevation to the target.
 312    velocityTransformation.elevation =
 313        tangentialVelocityOnElevation.magnitude / relativePosition.magnitude;
 314    if (Vector3.Dot(tangentialVelocityOnElevation, targetElevation) < 0) {
 315      velocityTransformation.elevation *= -1;
 316    }
 317
 318    return velocityTransformation;
 319  }
 320
 321  private AccelerationTransformation GetRelativeAccelerationTransformation(Agent agent) {
 322    // Since the agent's acceleration is an input, the relative acceleration is just the agent's
 323    // acceleration.
 324    AccelerationTransformation accelerationTransformation = new AccelerationTransformation();
 325    accelerationTransformation.cartesian = agent.GetAcceleration();
 326    return accelerationTransformation;
 327  }
 328
 329  public double GetDynamicPressure() {
 330    var airDensity = Constants.CalculateAirDensityAtAltitude(transform.position.y);
 331    var flowSpeed = GetSpeed();
 332    return 0.5 * airDensity * (flowSpeed * flowSpeed);
 333  }
 334
 335  protected abstract void UpdateReady(double deltaTime);
 336  protected abstract void UpdateBoost(double deltaTime);
 337  protected abstract void UpdateMidCourse(double deltaTime);
 338
 339  protected virtual void Awake() {}
 340
 341  protected virtual void Start() {}
 342
 343  protected virtual void FixedUpdate() {
 344    _speed = (float)GetSpeed();
 345    if (_flightPhase != FlightPhase.INITIALIZED && _flightPhase != FlightPhase.READY) {
 346      _timeSinceBoost += Time.fixedDeltaTime;
 347    }
 348    _timeInPhase += Time.fixedDeltaTime;
 349
 350    var boostTime = staticConfig.BoostConfig?.BoostTime ?? 0;
 351    double elapsedSimulationTime = SimManager.Instance.GetElapsedSimulationTime();
 352
 353    if (_flightPhase == FlightPhase.TERMINATED) {
 354      return;
 355    }
 356    if (_flightPhase == FlightPhase.INITIALIZED || _flightPhase == FlightPhase.READY) {
 357      SetFlightPhase(FlightPhase.BOOST);
 358    }
 359    if (_timeSinceBoost > boostTime && _flightPhase == FlightPhase.BOOST) {
 360      SetFlightPhase(FlightPhase.MIDCOURSE);
 361    }
 362    AlignWithVelocity();
 363    switch (_flightPhase) {
 364      case FlightPhase.INITIALIZED:
 365        break;
 366      case FlightPhase.READY:
 367        UpdateReady(Time.fixedDeltaTime);
 368        break;
 369      case FlightPhase.BOOST:
 370        UpdateBoost(Time.fixedDeltaTime);
 371        break;
 372      case FlightPhase.MIDCOURSE:
 373      case FlightPhase.TERMINAL:
 374        UpdateMidCourse(Time.fixedDeltaTime);
 375        break;
 376      case FlightPhase.TERMINATED:
 377        break;
 378    }
 379
 380    _velocity = GetVelocity();
 381    // Store the acceleration because it is set to zero after each simulation step
 382    _acceleration =
 383        GetComponent<Rigidbody>().GetAccumulatedForce() / GetComponent<Rigidbody>().mass;
 384  }
 385
 386  protected virtual void AlignWithVelocity() {
 387    Vector3 velocity = GetVelocity();
 388    // Only align if we have significant velocity.
 389    if (velocity.magnitude > 0.1f) {
 390      // Create a rotation with forward along velocity and up along world up.
 391      Quaternion targetRotation = Quaternion.LookRotation(velocity, Vector3.up);
 392
 393      // Smoothly rotate towards the target rotation.
 394      transform.rotation = Quaternion.RotateTowards(transform.rotation, targetRotation,
 395                                                    10000f * Time.fixedDeltaTime);
 396    }
 397  }
 398
 399  protected Vector3 CalculateAcceleration(Vector3 accelerationInput) {
 400    Vector3 gravity = Physics.gravity;
 401    float airDrag = CalculateDrag();
 402    float liftInducedDrag = CalculateLiftInducedDrag(accelerationInput + gravity);
 403    float dragAcceleration = -(airDrag + liftInducedDrag);
 404
 405    // Project the drag acceleration onto the forward direction.
 406    Vector3 dragAccelerationAlongRoll = dragAcceleration * transform.forward;
 407    _dragAcceleration = dragAccelerationAlongRoll;
 408
 409    return accelerationInput + gravity + dragAccelerationAlongRoll;
 410  }
 411
 412  public float CalculateMaxForwardAcceleration() {
 413    return staticConfig.AccelerationConfig?.MaxForwardAcceleration ?? 0;
 414  }
 415
 416  public float CalculateMaxNormalAcceleration() {
 417    float maxReferenceNormalAcceleration =
 418        (float)((staticConfig.AccelerationConfig?.MaxReferenceNormalAcceleration ?? 0) *
 419                Constants.kGravity);
 420    float referenceSpeed = staticConfig.AccelerationConfig?.ReferenceSpeed ?? 1;
 421    return Mathf.Pow((float)GetSpeed() / referenceSpeed, 2) * maxReferenceNormalAcceleration;
 422  }
 423
 424  private float CalculateDrag() {
 425    float dragCoefficient = staticConfig.LiftDragConfig?.DragCoefficient ?? 0;
 426    float crossSectionalArea = staticConfig.BodyConfig?.CrossSectionalArea ?? 0;
 427    float mass = staticConfig.BodyConfig?.Mass ?? 1;
 428    float dynamicPressure = (float)GetDynamicPressure();
 429    float dragForce = dragCoefficient * dynamicPressure * crossSectionalArea;
 430    return dragForce / mass;
 431  }
 432
 433  private float CalculateLiftInducedDrag(Vector3 accelerationInput) {
 434    float liftAcceleration = Vector3.ProjectOnPlane(accelerationInput, transform.forward).magnitude;
 435    float liftDragRatio = staticConfig.LiftDragConfig?.LiftDragRatio ?? 1;
 436    return Mathf.Abs(liftAcceleration / liftDragRatio);
 437  }
 438}
 439
 440public class DummyAgent : Agent {
 4441  protected override void Start() {
 4442    base.Start();
 4443  }
 444
 0445  protected override void FixedUpdate() {
 0446    GetComponent<Rigidbody>().AddForce(_acceleration, ForceMode.Acceleration);
 0447  }
 448
 0449  protected override void UpdateReady(double deltaTime) {
 450    // Do nothing.
 0451  }
 452
 0453  protected override void UpdateBoost(double deltaTime) {
 454    // Do nothing.
 0455  }
 456
 0457  protected override void UpdateMidCourse(double deltaTime) {
 458    // Do nothing.
 0459  }
 460}