예제 #1
0
 /// <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();
 }
예제 #2
0
 /// <summary>
 /// Checks for an obstacle en-route to the provided <c>destination</c>. Returns true if one
 /// is found that requires immediate action and provides the detour to avoid it, false otherwise.
 /// </summary>
 /// <param name="destProxy">The current destination. May be the AutoPilotTarget or an obstacle detour.</param>
 /// <param name="castingDistanceSubtractor">The distance to subtract from the casted Ray length to avoid 
 /// detecting any ObstacleZoneCollider around the destination.</param>
 /// <param name="detourProxy">The obstacle detour.</param>
 /// <param name="destinationOffset">The offset from destination.Position that is our destinationPoint.</param>
 /// <returns>
 ///   <c>true</c> if an obstacle was found and a detour generated, false if the way is effectively clear.
 /// </returns>
 private bool TryCheckForObstacleEnrouteTo(AutoPilotDestinationProxy destProxy, out AutoPilotDestinationProxy detourProxy) {
     D.AssertNotNull(destProxy, DebugName);  // 12.15.16 Got null ref in TryCheckForObstacleEnrouteTo()
     Profiler.BeginSample("Ship TryCheckForObstacleEnrouteTo Execution", _ship);
     int iterationCount = Constants.Zero;
     bool hasDetour = TryCheckForObstacleEnrouteTo(destProxy, out detourProxy, ref iterationCount);
     Profiler.EndSample();
     return hasDetour;
 }
예제 #3
0
        private bool TryCheckForObstacleEnrouteTo(AutoPilotDestinationProxy destProxy, out AutoPilotDestinationProxy detourProxy, ref int iterationCount) {
            D.AssertNotNull(destProxy, DebugName);  // 12.15.16 Got null ref
            D.AssertException(iterationCount++ < 10);
            detourProxy = null;
            Vector3 destBearing = (destProxy.Position - Position).normalized;
            float rayLength = destProxy.GetObstacleCheckRayLength(Position);
            Ray ray = new Ray(Position, destBearing);

            bool isDetourGenerated = false;
            RaycastHit hitInfo;
            if (Physics.Raycast(ray, out hitInfo, rayLength, AvoidableObstacleZoneOnlyLayerMask.value)) {
                // there is an AvoidableObstacleZone in the way. Warning: hitInfo.transform returns the rigidbody parent since 
                // the obstacleZone trigger collider is static. UNCLEAR if this means it forms a compound collider as this is a raycast
                var obstacleZoneGo = hitInfo.collider.gameObject;
                var obstacleZoneHitDistance = hitInfo.distance;
                IAvoidableObstacle obstacle = obstacleZoneGo.GetSafeFirstInterfaceInParents<IAvoidableObstacle>(excludeSelf: true);

                if (obstacle == destProxy.Destination) {
                    D.LogBold(ShowDebugLog, "{0} encountered obstacle {1} which is the destination. \nRay length = {2:0.00}, DistanceToHit = {3:0.00}.",
                        DebugName, obstacle.DebugName, rayLength, obstacleZoneHitDistance);
                    HandleObstacleFoundIsTarget(obstacle);
                }
                else {
                    D.Log(ShowDebugLog, "{0} encountered obstacle {1} at {2} when checking approach to {3}. \nRay length = {4:0.#}, DistanceToHit = {5:0.#}.",
                        DebugName, obstacle.DebugName, obstacle.Position, destProxy.DebugName, rayLength, obstacleZoneHitDistance);
                    if (TryGenerateDetourAroundObstacle(obstacle, hitInfo, out detourProxy)) {
                        AutoPilotDestinationProxy newDetourProxy;
                        if (TryCheckForObstacleEnrouteTo(detourProxy, out newDetourProxy, ref iterationCount)) {
                            D.Log(ShowDebugLog, "{0} found another obstacle on the way to detour {1}.", DebugName, detourProxy.DebugName);
                            detourProxy = newDetourProxy;
                        }
                        isDetourGenerated = true;
                    }
                }
            }
            return isDetourGenerated;
        }
예제 #4
0
        private void InitiateObstacleCheckingEnrouteTo(AutoPilotDestinationProxy destProxy, CourseRefreshMode courseRefreshMode) {
            D.AssertNotNull(destProxy, DebugName);  // 12.15.16 Got null ref in TryCheckForObstacleEnrouteTo()
            D.AssertNull(_apObstacleCheckJob, DebugName);
            _apObstacleCheckPeriod = __GenerateObstacleCheckPeriod();
            AutoPilotDestinationProxy detourProxy;
            string jobName = "{0}.ApObstacleCheckJob".Inject(DebugName);
            _apObstacleCheckJob = _jobMgr.RecurringWaitForHours(new Reference<GameTimeDuration>(() => _apObstacleCheckPeriod), jobName, waitMilestone: () => {

                Profiler.BeginSample("Ship ApObstacleCheckJob Execution", _ship);
                if (TryCheckForObstacleEnrouteTo(destProxy, out detourProxy)) {
                    KillApObstacleCheckJob();
                    RefreshCourse(courseRefreshMode, detourProxy);
                    Profiler.EndSample();
                    ContinueCourseToTargetVia(detourProxy);
                    return;
                }
                if (_doesApObstacleCheckPeriodNeedRefresh) {
                    _apObstacleCheckPeriod = __GenerateObstacleCheckPeriod();
                    _doesApObstacleCheckPeriodNeedRefresh = false;
                }
                Profiler.EndSample();

            });
        }
예제 #5
0
 /// <summary>
 /// Tries to generate a detour around the provided obstacle. Returns <c>true</c> if a detour
 /// was generated, <c>false</c> otherwise. 
 /// <remarks>A detour can always be generated around an obstacle. However, this algorithm considers other factors
 /// before initiating a heading change to redirect to a detour. E.g. moving obstacles that are far away 
 /// and/or require only a small change in heading may not necessitate a diversion to a detour yet.
 /// </remarks>
 /// </summary>
 /// <param name="obstacle">The obstacle.</param>
 /// <param name="zoneHitInfo">The zone hit information.</param>
 /// <param name="detourProxy">The resulting detour.</param>
 /// <returns></returns>
 private bool TryGenerateDetourAroundObstacle(IAvoidableObstacle obstacle, RaycastHit zoneHitInfo, out AutoPilotDestinationProxy detourProxy) {
     detourProxy = GenerateDetourAroundObstacle(obstacle, zoneHitInfo, _ship.Command.UnitMaxFormationRadius);
     bool useDetour = true;
     Vector3 detourBearing = (detourProxy.Position - Position).normalized;
     float reqdTurnAngleToDetour = Vector3.Angle(_ship.CurrentHeading, detourBearing);
     if (obstacle.IsMobile) {
         if (reqdTurnAngleToDetour < DetourTurnAngleThreshold) {
             useDetour = false;
             // angle is still shallow but short remaining distance might require use of a detour
             float maxDistanceTraveledBeforeNextObstacleCheck = _engineRoom.IntendedCurrentSpeedValue * _apObstacleCheckPeriod.TotalInHours;
             float obstacleDistanceThresholdRequiringDetour = maxDistanceTraveledBeforeNextObstacleCheck * 2F;   // HACK
             float distanceToObstacleZone = zoneHitInfo.distance;
             if (distanceToObstacleZone <= obstacleDistanceThresholdRequiringDetour) {
                 useDetour = true;
             }
         }
     }
     if (useDetour) {
         D.Log(ShowDebugLog, "{0} has generated detour {1} to get by obstacle {2}. Reqd Turn = {3:0.#} degrees.", DebugName, detourProxy.DebugName, obstacle.DebugName, reqdTurnAngleToDetour);
     }
     else {
         D.Log(ShowDebugLog, "{0} has declined to generate a detour to get by mobile obstacle {1}. Reqd Turn = {2:0.#} degrees.", DebugName, obstacle.DebugName, reqdTurnAngleToDetour);
     }
     return useDetour;
 }
예제 #6
0
        private void InitiateNavigationTo(AutoPilotDestinationProxy destProxy, Action hasArrived = null) {
            if (!_engineRoom.IsPropulsionEngaged) {
                D.Error("{0}.InitiateNavigationTo({1}) called without propulsion engaged. AutoPilotSpeed: {2}", DebugName, destProxy.DebugName, ApSpeed.GetValueName());
            }
            D.AssertNull(_apNavJob, DebugName);

            bool isDestinationADetour = destProxy != ApTargetProxy;
            bool isDestFastMover = destProxy.IsFastMover;
            bool isIncreaseAboveApSpeedAllowed = isDestinationADetour || isDestFastMover;
            GameTimeDuration progressCheckPeriod = default(GameTimeDuration);
            Speed correctedSpeed;

            float distanceToArrival;
            Vector3 directionToArrival;
#pragma warning disable 0219
            bool isArrived = false;
#pragma warning restore 0219
            if (isArrived = !destProxy.TryGetArrivalDistanceAndDirection(Position, out directionToArrival, out distanceToArrival)) {
                // arrived
                if (hasArrived != null) {
                    hasArrived();
                }
                return;
            }
            else {
                //D.Log(ShowDebugLog, "{0} powering up. Distance to arrival at {1} = {2:0.0}.", DebugName, destination.DebugName, distanceToArrival);
                progressCheckPeriod = GenerateProgressCheckPeriod(distanceToArrival, out correctedSpeed);
                if (correctedSpeed != default(Speed)) {
                    //D.Log(ShowDebugLog, "{0} is correcting its speed to {1} to get a minimum of 5 progress checks.", DebugName, correctedSpeed.GetValueName());
                    ChangeSpeed_Internal(correctedSpeed, _isApCurrentSpeedFleetwide);
                }
                //D.Log(ShowDebugLog, "{0} initial progress check period set to {1}.", DebugName, progressCheckPeriod);
            }

            int minFrameWaitBetweenAttemptedCourseCorrectionChecks = 0;
            int previousFrameCourseWasCorrected = 0;

            float halfArrivalWindowDepth = destProxy.ArrivalWindowDepth / 2F;

            string jobName = "{0}.ApNavJob".Inject(DebugName);
            _apNavJob = _jobMgr.RecurringWaitForHours(new Reference<GameTimeDuration>(() => progressCheckPeriod), jobName, waitMilestone: () => {
                //D.Log(ShowDebugLog, "{0} making ApNav progress check on Date: {1}, Frame: {2}. CheckPeriod = {3}.", DebugName, _gameTime.CurrentDate, Time.frameCount, progressCheckPeriod);

                Profiler.BeginSample("Ship ApNav Job Execution", _ship);
                if (isArrived = !destProxy.TryGetArrivalDistanceAndDirection(Position, out directionToArrival, out distanceToArrival)) {
                    KillApNavJob();
                    if (hasArrived != null) {
                        hasArrived();
                    }
                    Profiler.EndSample();
                    return;
                }

                //D.Log(ShowDebugLog, "{0} beginning progress check on Date: {1}.", DebugName, _gameTime.CurrentDate);
                if (CheckForCourseCorrection(directionToArrival, ref previousFrameCourseWasCorrected, ref minFrameWaitBetweenAttemptedCourseCorrectionChecks)) {
                    //D.Log(ShowDebugLog, "{0} is making a mid course correction of {1:0.00} degrees. Frame = {2}.",
                    //DebugName, Vector3.Angle(directionToArrival, _ship.Data.IntendedHeading), Time.frameCount);
                    Profiler.BeginSample("ChangeHeading_Internal", _ship);
                    ChangeHeading_Internal(directionToArrival);
                    _ship.UpdateDebugCoursePlot();  // 5.7.16 added to keep plots current with moving targets
                    Profiler.EndSample();
                }

                Profiler.BeginSample("TryCheckForPeriodOrSpeedCorrection", _ship);
                GameTimeDuration correctedPeriod;
                if (TryCheckForPeriodOrSpeedCorrection(distanceToArrival, isIncreaseAboveApSpeedAllowed, halfArrivalWindowDepth, progressCheckPeriod, out correctedPeriod, out correctedSpeed)) {
                    if (correctedPeriod != default(GameTimeDuration)) {
                        D.AssertDefault((int)correctedSpeed);
                        //D.Log(ShowDebugLog, "{0} is correcting progress check period from {1} to {2} en-route to {3}, Distance to arrival = {4:0.0}.",
                        //Name, progressCheckPeriod, correctedPeriod, destination.DebugName, distanceToArrival);
                        progressCheckPeriod = correctedPeriod;
                    }
                    else {
                        D.AssertNotDefault((int)correctedSpeed);
                        //D.Log(ShowDebugLog, "{0} is correcting speed from {1} to {2} en-route to {3}, Distance to arrival = {4:0.0}.",
                        //Name, CurrentSpeed.GetValueName(), correctedSpeed.GetValueName(), destination.DebugName, distanceToArrival);
                        Profiler.BeginSample("ChangeSpeed_Internal", _ship);
                        ChangeSpeed_Internal(correctedSpeed, _isApCurrentSpeedFleetwide);
                        Profiler.EndSample();
                    }
                }
                Profiler.EndSample();
                //D.Log(ShowDebugLog, "{0} completed progress check on Date: {1}, NextProgressCheckPeriod: {2}.", DebugName, _gameTime.CurrentDate, progressCheckPeriod);
                //D.Log(ShowDebugLog, "{0} not yet arrived. DistanceToArrival = {1:0.0}.", DebugName, distanceToArrival);
                Profiler.EndSample();
            });
        }
예제 #7
0
        /// <summary>
        /// Continues the course to target via the provided obstacleDetour. Called while underway upon encountering an obstacle.
        /// </summary>
        /// <param name="obstacleDetour">The obstacle detour's proxy.</param>
        private void ContinueCourseToTargetVia(AutoPilotDestinationProxy obstacleDetour) {
            CleanupAnyRemainingJobs();   // always called while already engaged
            //D.Log(ShowDebugLog, "{0} continuing course to target {1} via obstacle detour {2}. Distance to detour = {3:0.0}.",
            //    DebugName, ApTargetFullName, obstacleDetour.DebugName, Vector3.Distance(Position, obstacleDetour.Position));

            ResumeApSpeed(); // Uses ShipSpeed to catchup as we must go through this detour
            Vector3 newHeading = (obstacleDetour.Position - Position).normalized;
            ChangeHeading_Internal(newHeading, headingConfirmed: () => {
                //D.Log(ShowDebugLog, "{0} is now on heading to reach obstacle detour {1}.", DebugName, obstacleDetour.DebugName);
                InitiateNavigationTo(obstacleDetour, hasArrived: () => {
                    // even if this is an obstacle that has appeared on the way to another obstacle detour, go around it, then direct to target
                    RefreshCourse(CourseRefreshMode.RemoveWaypoint, obstacleDetour);
                    ResumeDirectCourseToTarget();
                });
                InitiateObstacleCheckingEnrouteTo(obstacleDetour, CourseRefreshMode.ReplaceObstacleDetour);
            });
        }
예제 #8
0
        /// <summary>
        /// Initiates a course to the target after first going to <c>obstacleDetour</c>. This 'Initiate' version includes 2 responsibilities
        /// not present in the 'Continue' version. 1) It waits for the fleet to align before departure, and 2) engages the engines.
        /// </summary>
        /// <param name="obstacleDetour">The proxy for the obstacle detour.</param>
        private void InitiateCourseToTargetVia(AutoPilotDestinationProxy obstacleDetour) {
            D.AssertNull(_apNavJob);
            D.AssertNull(_apObstacleCheckJob);
            D.AssertNull(_apActionToExecuteWhenFleetIsAligned);
            //D.Log(ShowDebugLog, "{0} initiating course to target {1} at {2} via obstacle detour {3}. Distance to detour = {4:0.0}.",
            //Name, TargetFullName, ApTargetProxy.Position, obstacleDetour.DebugName, Vector3.Distance(Position, obstacleDetour.Position));

            Vector3 newHeading = (obstacleDetour.Position - Position).normalized;
            if (newHeading.IsSameAs(Vector3.zero)) {
                D.Error("{0}: ObstacleDetour and current location shouldn't be able to be the same.", DebugName);
            }
            if (_isApFleetwideMove) {
                ChangeHeading_Internal(newHeading);

                _apActionToExecuteWhenFleetIsAligned = () => {
                    //D.Log(ShowDebugLog, "{0} reports fleet {1} is aligned. Initiating departure for detour {2}.",
                    //Name, _ship.Command.DisplayName, obstacleDetour.DebugName);
                    _apActionToExecuteWhenFleetIsAligned = null;
                    EngageEnginesAtApSpeed(isFleetSpeed: false);   // this is a detour so catch up
                                                                   // even if this is an obstacle that has appeared on the way to another obstacle detour, go around it, then try direct to target

                    InitiateNavigationTo(obstacleDetour, hasArrived: () => {
                        RefreshCourse(CourseRefreshMode.RemoveWaypoint, obstacleDetour);
                        ResumeDirectCourseToTarget();
                    });
                    InitiateObstacleCheckingEnrouteTo(obstacleDetour, CourseRefreshMode.ReplaceObstacleDetour);
                };
                //D.Log(ShowDebugLog, "{0} starting wait for fleet to align, actual speed = {1:0.##}.", DebugName, ActualSpeedValue);
                _ship.Command.WaitForFleetToAlign(_apActionToExecuteWhenFleetIsAligned, _ship);
            }
            else {
                ChangeHeading_Internal(newHeading, headingConfirmed: () => {
                    EngageEnginesAtApSpeed(isFleetSpeed: false);   // this is a detour so catch up
                                                                   // even if this is an obstacle that has appeared on the way to another obstacle detour, go around it, then try direct to target
                    InitiateNavigationTo(obstacleDetour, hasArrived: () => {
                        RefreshCourse(CourseRefreshMode.RemoveWaypoint, obstacleDetour);
                        ResumeDirectCourseToTarget();
                    });
                    InitiateObstacleCheckingEnrouteTo(obstacleDetour, CourseRefreshMode.ReplaceObstacleDetour);
                });
            }
        }
예제 #9
0
 /// <summary>
 /// Engages the pilot to pursue the target using the provided proxy. "Pursuit" here
 /// entails continuously adjusting speed and heading to stay within the arrival window
 /// provided by the proxy. There is no 'notification' to the ship as the pursuit never
 /// terminates until the pilot is disengaged by the ship.
 /// </summary>
 /// <param name="apTgtProxy">The proxy for the target this Pilot is being engaged to pursue.</param>
 /// <param name="apSpeed">The initial speed used by the pilot.</param>
 internal void EngagePilotToPursue(AutoPilotDestinationProxy apTgtProxy, Speed apSpeed) {
     Utility.ValidateNotNull(apTgtProxy);
     ApTargetProxy = apTgtProxy;
     ApSpeed = apSpeed;
     _isApFleetwideMove = false;
     _isApCurrentSpeedFleetwide = false;
     _isApInPursuit = true;
     RefreshCourse(CourseRefreshMode.NewCourse);
     EngagePilot();
 }
예제 #10
0
 /// <summary>
 /// Engages the pilot to move to the target using the provided proxy. It will notify the ship
 /// when it arrives via Ship.HandleTargetReached.
 /// </summary>
 /// <param name="apTgtProxy">The proxy for the target this Pilot is being engaged to reach.</param>
 /// <param name="speed">The initial speed the pilot should travel at.</param>
 /// <param name="isFleetwideMove">if set to <c>true</c> [is fleetwide move].</param>
 internal void EngagePilotToMoveTo(AutoPilotDestinationProxy apTgtProxy, Speed speed, bool isFleetwideMove) {
     Utility.ValidateNotNull(apTgtProxy);
     D.Assert(!InvalidApSpeeds.Contains(speed), speed.GetValueName());
     ApTargetProxy = apTgtProxy;
     ApSpeed = speed;
     _isApFleetwideMove = isFleetwideMove;
     _isApCurrentSpeedFleetwide = isFleetwideMove;
     _isApInPursuit = false;
     RefreshCourse(CourseRefreshMode.NewCourse);
     EngagePilot();
 }