/// <summary> /// Generates a detour that avoids the obstacle that was found by the provided entryRay and hit. /// </summary> /// <param name="entryRay">The ray used to find the entryPt.</param> /// <param name="entryHit">The info for the entryHit.</param> /// <returns></returns> private INavigableTarget GenerateDetourAroundObstacle(Ray entryRay, RaycastHit entryHit) { INavigableTarget detour = null; Transform obstacle = entryHit.transform; string obstacleName = obstacle.parent.name + "." + obstacle.name; Vector3 rayEntryPoint = entryHit.point; SphereCollider obstacleCollider = entryHit.collider as SphereCollider; float obstacleRadius = obstacleCollider.radius; float rayLength = (2F * obstacleRadius) + 1F; Vector3 pointBeyondKeepoutZone = entryRay.GetPoint(entryHit.distance + rayLength); Vector3 rayExitPoint = FindRayExitPoint(entryRay, entryHit, pointBeyondKeepoutZone, 0); //D.Log("{0} found RayExitPoint. EntryPt to exitPt distance = {1}.", Name, Vector3.Distance(rayEntryPoint, rayExitPoint)); Vector3 obstacleCenter = obstacle.position; var ptOnSphere = UnityUtility.FindClosestPointOnSphereOrthogonalToIntersectingLine(rayEntryPoint, rayExitPoint, obstacleCenter, obstacleRadius); float obstacleClearanceLeeway = 2F; // HACK var detourWorldSpaceLocation = ptOnSphere + (ptOnSphere - obstacleCenter).normalized * obstacleClearanceLeeway; INavigableTarget obstacleParent = obstacle.gameObject.GetSafeFirstInterfaceInParents<INavigableTarget>(); D.Assert(obstacleParent != null, "Obstacle {0} does not have a {1} parent.".Inject(obstacleName, typeof(INavigableTarget).Name)); if (obstacleParent.IsMobile) { var detourRelativeToObstacleCenter = detourWorldSpaceLocation - obstacleCenter; var detourRef = new Reference<Vector3>(() => obstacle.position + detourRelativeToObstacleCenter); detour = new MovingLocation(detourRef); } else { detour = new StationaryLocation(detourWorldSpaceLocation); } //D.Log("{0} found detour {1} to avoid obstacle {2} at {3}. \nDistance to detour = {4:0.#}. Obstacle keepout radius = {5:0.##}. Detour is {6:0.#} from obstacle center.", //Name, detour.FullName, obstacleName, obstacleCenter, Vector3.Distance(Position, detour.Position), obstacleRadius, Vector3.Distance(obstacleCenter, detour.Position)); return detour; }
/// <summary> /// Generates a detour around the provided obstacle. /// </summary> /// <param name="obstacle">The obstacle.</param> /// <param name="hitInfo">The hit information.</param> /// <param name="fleetRadius">The fleet radius.</param> /// <returns></returns> private AutoPilotDestinationProxy GenerateDetourAroundObstacle(IAvoidableObstacle obstacle, RaycastHit hitInfo, float fleetRadius) { Vector3 detourPosition = obstacle.GetDetour(Position, hitInfo, fleetRadius); StationaryLocation detour = new StationaryLocation(detourPosition); Vector3 detourOffset = CalcDetourOffset(detour); float tgtStandoffDistance = _ship.CollisionDetectionZoneRadius; return detour.GetApMoveTgtProxy(detourOffset, tgtStandoffDistance, Position); }
/// <summary> /// Refreshes the course. /// </summary> /// <param name="mode">The mode.</param> /// <param name="wayPtProxy">The optional waypoint. When not null, this is always a StationaryLocation detour to avoid an obstacle.</param> /// <exception cref="System.NotImplementedException"></exception> private void RefreshCourse(CourseRefreshMode mode, AutoPilotDestinationProxy wayPtProxy = null) { //D.Log(ShowDebugLog, "{0}.RefreshCourse() called. Mode = {1}. CourseCountBefore = {2}.", DebugName, mode.GetValueName(), AutoPilotCourse.Count); switch (mode) { case CourseRefreshMode.NewCourse: D.AssertNull(wayPtProxy); ApCourse.Clear(); ApCourse.Add(_ship); IShipNavigable courseTgt; if (ApTargetProxy.IsMobile) { courseTgt = new MobileLocation(new Reference<Vector3>(() => ApTargetProxy.Position)); } else { courseTgt = new StationaryLocation(ApTargetProxy.Position); } ApCourse.Add(courseTgt); // includes fstOffset break; case CourseRefreshMode.AddWaypoint: ApCourse.Insert(ApCourse.Count - 1, new StationaryLocation(wayPtProxy.Position)); // changes Course.Count break; case CourseRefreshMode.ReplaceObstacleDetour: D.AssertEqual(3, ApCourse.Count); ApCourse.RemoveAt(ApCourse.Count - 2); // changes Course.Count ApCourse.Insert(ApCourse.Count - 1, new StationaryLocation(wayPtProxy.Position)); // changes Course.Count break; case CourseRefreshMode.RemoveWaypoint: D.AssertEqual(3, ApCourse.Count); bool isRemoved = ApCourse.Remove(new StationaryLocation(wayPtProxy.Position)); // Course.RemoveAt(Course.Count - 2); // changes Course.Count D.Assert(isRemoved); break; case CourseRefreshMode.ClearCourse: D.AssertNull(wayPtProxy); ApCourse.Clear(); break; default: throw new NotImplementedException(ErrorMessages.UnanticipatedSwitchValue.Inject(mode)); } //D.Log(ShowDebugLog, "CourseCountAfter = {0}.", Course.Count); HandleCourseChanged(); }
/// <summary> /// Calculates and returns the world space offset to the provided detour that when combined with the /// detour's position, represents the actual location in world space this ship is trying to reach, /// aka DetourPoint. Used to keep ships from bunching up at the detour when many ships in a fleet encounter the same obstacle. /// </summary> /// <param name="detour">The detour.</param> /// <returns></returns> private Vector3 CalcDetourOffset(StationaryLocation detour) { if (_isApFleetwideMove) { // make separate detour offsets as there may be a lot of ships encountering this detour Quaternion shipCurrentRotation = _ship.transform.rotation; Vector3 shipToDetourDirection = (detour.Position - _ship.Position).normalized; Quaternion shipRotationChgReqdToFaceDetour = Quaternion.FromToRotation(_ship.CurrentHeading, shipToDetourDirection); Quaternion shipRotationThatFacesDetour = Math3D.AddRotation(shipCurrentRotation, shipRotationChgReqdToFaceDetour); Vector3 shipLocalFormationOffset = _ship.FormationStation.LocalOffset; Vector3 detourWorldSpaceOffset = Math3D.TransformDirectionMath(shipRotationThatFacesDetour, shipLocalFormationOffset); return detourWorldSpaceOffset; } return Vector3.zero; }
public TargetInfo(StationaryLocation sl, Vector3 fstOffset, float fullSpeed) { Target = sl; Destination = sl.Position + fstOffset; CloseEnoughDistance = fullSpeed * 0.5F; CloseEnoughDistanceSqrd = CloseEnoughDistance * CloseEnoughDistance; }
/// <summary> /// Plots a course to a stationary location and notifies the requester of the /// outcome via the onCoursePlotCompleted event. /// </summary> /// <param name="location">The stationary location.</param> /// <param name="speed">The speed.</param> /// <param name="isFleetMove">if set to <c>true</c> this navigator will only move when the fleet is ready.</param> private void PlotCourse(StationaryLocation location, Speed speed, bool isFleetMove) { // a formationOffset is required if this is a fleet move Vector3 destinationOffset = isFleetMove ? _data.FormationStation.StationOffset : Vector3.zero; _targetInfo = new TargetInfo(location, destinationOffset, _data.FullStlSpeed); Speed = speed; _isFleetMove = isFleetMove; PlotCourse(); }
private void AssessNeedForRepair() { if (Data.Health < 0.30F) { if (CurrentOrder == null || CurrentOrder.Directive != ShipDirective.Repair) { var repairLoc = Data.Position - _transform.forward * 10F; INavigableTarget repairDestination = new StationaryLocation(repairLoc); OverrideCurrentOrder(ShipDirective.Repair, retainSuperiorsOrder: true, target: repairDestination); } } }
public ShipDestinationInfo(StationaryLocation location, Vector3 fstOffset, Reference<float> distanceTraveledInOneHour) { Target = location; _fstOffset = fstOffset; _closeEnoughDistanceRef = distanceTraveledInOneHour; _progressCheckDistanceRef = distanceTraveledInOneHour; }