< Summary

Class:DummyAgent
Assembly:bamlab.micromissiles
File(s):/github/workspace/Assets/Scripts/Agent.cs
Covered lines:6
Uncovered lines:6
Coverable lines:12
Total lines:481
Line coverage:50% (6 of 12)
Covered branches:0
Total branches:0
Covered methods:2
Total methods:5
Method coverage:40% (2 of 5)

Metrics

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