protected void UpdateMovement() { // check if there is no explicit move request if (!_moveRequested) { // obtain a move from active Navigators: if (_myNavigator != null) { // Navigator found. Accelerate. this.Accelerate(); Nullable <Vector> moveDir = _myNavigator.Navigate(); if (moveDir == null) { // we've hit an impediment. kill the navigator and stop the ship. _myNavigator = null; _moveDir = null; _owner.MoveSpeedCurrent = 0; } else if (moveDir.Value.X == Double.PositiveInfinity) { // goal attained. stop the navigator. _myNavigator = null; } else { // request move from self this.MoveRequest(moveDir.Value, true, true); } } } // Time to do the move. Check if there is an active vector: if (_moveDir != null) { // do move this.Move(); // if there is no move request, we need to decelerate and normalize the dir-vector. if (!_moveRequested && _myNavigator == null) { this.Decelerate(); // careful with the boxing if (_owner.MoveSpeedCurrent > 0) { Vector normalized = _moveDir.Value; normalized.ScaleToLength(_owner.MoveSpeedCurrent); this._moveDir = normalized; } else { this._moveDir = null; } } } // Flush flags. _moveRequested = false; _moveClipChecked = false; }
/* * private UInt16 _sideStepTimer = 0; * private Vector _sideStepDir; */ /// <summary> /// Requests a move from the agent. /// </summary> /// <param name="direction">Move direction.</param> /// <param name="clipChecked">Whether the move has been clip-checked.</param> /// <param name="normalizedToSpeed">Whether the vector is normalized.</param> public void MoveRequest(Vector direction, bool clipChecked, bool normalizedToSpeed) { if (!normalizedToSpeed) { // Accelerate and normalize the move. this.Accelerate(); direction.ScaleToLength(_owner.MoveSpeedCurrent); } _moveDir = direction; _moveClipChecked = clipChecked; _moveRequested = true; }
/// <summary> /// Handles a collision between two agents. /// </summary> private void HandleCollisionBetweenTwoPlayers(Creature first, Creature second) { double distance = first.PositionDouble.DistanceTo(second.PositionDouble); double preferredDistance = Math.Sqrt(Math.Pow(first.RadiusX + second.RadiusX, 2) + Math.Pow(first.RadiusY + second.RadiusY, 2)); double deltaOver2 = 0.5 * (preferredDistance - distance); if (deltaOver2 < 0) { return; } // push both actors in opposite direction, by a vector of length delta/2. Vector mover = first.PositionDouble - second.PositionDouble; mover.ScaleToLength(deltaOver2); first.PositionDouble += mover; second.PositionDouble -= mover; // this might have cause other collisions. the algo needs fixing. }
/// <summary> /// Performs a terrain passability check betwee two points by doing pixel validity checks at interval delta. /// </summary> public List <Creature> RayTracerPassabilityCheckRough(Creature client, Vector v1, Vector v2, double delta) { Vector difference = v2 - v1; Vector deltaV = difference; deltaV.ScaleToLength(delta); Vector currentPosition = v1; for (int i = 0; i < difference.Length() / deltaV.Length(); ++i) { Coords pixel = new Coords(CoordsType.Pixel, currentPosition); List <Creature> collision = _myCollider.CreatureClippingCheck(client, pixel, false); if (collision == null || collision.Count > 0) { return(collision); } currentPosition += deltaV; } return(new List <Creature>()); }
/* private UInt16 _sideStepTimer = 0; private Vector _sideStepDir; */ /// <summary> /// Requests a move from the agent. /// </summary> /// <param name="direction">Move direction.</param> /// <param name="clipChecked">Whether the move has been clip-checked.</param> /// <param name="normalizedToSpeed">Whether the vector is normalized.</param> public void MoveRequest(Vector direction, bool clipChecked, bool normalizedToSpeed) { if (!normalizedToSpeed) { // Accelerate and normalize the move. this.Accelerate(); direction.ScaleToLength(_owner.MoveSpeedCurrent); } _moveDir = direction; _moveClipChecked = clipChecked; _moveRequested = true; }
/// <summary> /// Obtains step towards goal. Returns null if path is blocked. /// Returns (infinity, infinity) if goal is reached. /// </summary> /// <returns></returns> public Nullable<Vector> Navigate() { ++_timeTraveled; // Check to see if we've arrived. In that case the navigator has finished its job. if (_traveler.MyCollider.CollisionCheckPixelInEllipse(_goalPixel, _traveler.PositionPixel, _traveler.RadiusX, _traveler.RadiusY)) { // goal attained. ++Constants._navigationSuccesses; return new Vector(Double.PositiveInfinity, Double.PositiveInfinity); } // Check to see if we have a visible goal or if we're taking too long to get to it if (_visiblePixelGoal == null || _timeTraveled >= _timeTraveledUpperBound) { // no goal set. check to see if a exists and whether we're on it: if (_routeCoarse == null || !this._putativeCurrentPosition.Equals(_traveler.PositionTile)) { // no route or we need a recalc AcquireRoute(); } } // Check to see if we've arrived to the current visible goal. If so, get new one. if (_traveler.MyCollider.CollisionCheckPixelInEllipse(_visiblePixelGoal.Value, _traveler.PositionPixel, _traveler.RadiusX, _traveler.RadiusY)) { AcquireVisiblePixelGoal(); } // We move towards current visible pixel goal. // Declare movement vector Vector principleMoveDir = new Vector(_visiblePixelGoal.Value) - _traveler.PositionDouble; principleMoveDir.ScaleToLength(_traveler.MoveSpeedCurrent); Vector startPoint = _traveler.PositionDouble; // Try to move in given direction. If the move fails, offset the direction angle and try again. // The offsets are under a full PI in either direction (so the agent doesn't get stuck in a loop). // Avoidance is first attempted in one direction. If it fails, the agent tries to other direction. // If both directions fail, the agent gives up. Int32 numberOfOffsetAttempts = (Int32)(Math.PI / Constants.CollisionAvoidanceRotation); // flags whether avoidance direction was flipped. bool directionFlipped = false; // list of creatures the agent has asked to step aside. List<Creature> askedToStepAside = new List<Creature>(); while (true) { for (sbyte i = 0; i < numberOfOffsetAttempts; ++i) { double offsetAngle = Math.Pow(-1, (sbyte)_avoidanceDirection) * i * Constants.CollisionAvoidanceRotation; Vector moveDir = principleMoveDir.Rotate(offsetAngle); List<Creature> checkForCollisions = _traveler.MyCollider.RayTracerPassabilityCheckPrecise(_traveler, startPoint, startPoint + moveDir); if (checkForCollisions != null) { if (checkForCollisions.Count > 0) { // ask agents in the way to move foreach (Creature impediment in checkForCollisions) { if (!askedToStepAside.Contains(impediment)) { Vector vectorToImpediment = impediment.PositionDouble - _traveler.PositionDouble; vectorToImpediment.ScaleToLength(1); Vector moveDirPerpendicular = directionFlipped ? moveDir.PerpendiculatRight() : moveDir.PerpendiculatLeft(); moveDirPerpendicular.ScaleToLength(1); impediment.CreatureBrain.StepAsideRequest(vectorToImpediment + moveDirPerpendicular); askedToStepAside.Add(impediment); } } } else { // success. update creature position. //_traveler.PositionDouble = startPoint + moveDir; _timeWaited = 0; return moveDir; } } } // we failed to move anywhere. the agent tries avoidance in the other direction. if (!directionFlipped) { _avoidanceDirection = (AvoidanceDir)(((sbyte)_avoidanceDirection + 1) % 2); directionFlipped = true; } else { // avoidance in either direction failed. the agent gives up. // NOTE: perhaps add a brief (1 second?) waiting time for the agent before giving up. ++_timeWaited; if (_timeWaited >= Constants.FailedCollisionAvoidanceWaitTime) { // give up _timeWaited = 0; break; } else { // pass back static moveVector return new Vector(0, 0); } } } ++Constants._navigationFailures; return null; }
/// <summary> /// Obtains step towards goal. Returns null if path is blocked. /// Returns (infinity, infinity) if goal is reached. /// </summary> /// <returns></returns> public Nullable <Vector> Navigate() { ++_timeTraveled; // Check to see if we've arrived. In that case the navigator has finished its job. if (_traveler.MyCollider.CollisionCheckPixelInEllipse(_goalPixel, _traveler.PositionPixel, _traveler.RadiusX, _traveler.RadiusY)) { // goal attained. ++Constants._navigationSuccesses; return(new Vector(Double.PositiveInfinity, Double.PositiveInfinity)); } // Check to see if we have a visible goal or if we're taking too long to get to it if (_visiblePixelGoal == null || _timeTraveled >= _timeTraveledUpperBound) { // no goal set. check to see if a exists and whether we're on it: if (_routeCoarse == null || !this._putativeCurrentPosition.Equals(_traveler.PositionTile)) { // no route or we need a recalc AcquireRoute(); } } // Check to see if we've arrived to the current visible goal. If so, get new one. if (_traveler.MyCollider.CollisionCheckPixelInEllipse(_visiblePixelGoal.Value, _traveler.PositionPixel, _traveler.RadiusX, _traveler.RadiusY)) { AcquireVisiblePixelGoal(); } // We move towards current visible pixel goal. // Declare movement vector Vector principleMoveDir = new Vector(_visiblePixelGoal.Value) - _traveler.PositionDouble; principleMoveDir.ScaleToLength(_traveler.MoveSpeedCurrent); Vector startPoint = _traveler.PositionDouble; // Try to move in given direction. If the move fails, offset the direction angle and try again. // The offsets are under a full PI in either direction (so the agent doesn't get stuck in a loop). // Avoidance is first attempted in one direction. If it fails, the agent tries to other direction. // If both directions fail, the agent gives up. Int32 numberOfOffsetAttempts = (Int32)(Math.PI / Constants.CollisionAvoidanceRotation); // flags whether avoidance direction was flipped. bool directionFlipped = false; // list of creatures the agent has asked to step aside. List <Creature> askedToStepAside = new List <Creature>(); while (true) { for (sbyte i = 0; i < numberOfOffsetAttempts; ++i) { double offsetAngle = Math.Pow(-1, (sbyte)_avoidanceDirection) * i * Constants.CollisionAvoidanceRotation; Vector moveDir = principleMoveDir.Rotate(offsetAngle); List <Creature> checkForCollisions = _traveler.MyCollider.RayTracerPassabilityCheckPrecise(_traveler, startPoint, startPoint + moveDir); if (checkForCollisions != null) { if (checkForCollisions.Count > 0) { // ask agents in the way to move foreach (Creature impediment in checkForCollisions) { if (!askedToStepAside.Contains(impediment)) { Vector vectorToImpediment = impediment.PositionDouble - _traveler.PositionDouble; vectorToImpediment.ScaleToLength(1); Vector moveDirPerpendicular = directionFlipped ? moveDir.PerpendiculatRight() : moveDir.PerpendiculatLeft(); moveDirPerpendicular.ScaleToLength(1); impediment.CreatureBrain.StepAsideRequest(vectorToImpediment + moveDirPerpendicular); askedToStepAside.Add(impediment); } } } else { // success. update creature position. //_traveler.PositionDouble = startPoint + moveDir; _timeWaited = 0; return(moveDir); } } } // we failed to move anywhere. the agent tries avoidance in the other direction. if (!directionFlipped) { _avoidanceDirection = (AvoidanceDir)(((sbyte)_avoidanceDirection + 1) % 2); directionFlipped = true; } else { // avoidance in either direction failed. the agent gives up. // NOTE: perhaps add a brief (1 second?) waiting time for the agent before giving up. ++_timeWaited; if (_timeWaited >= Constants.FailedCollisionAvoidanceWaitTime) { // give up _timeWaited = 0; break; } else { // pass back static moveVector return(new Vector(0, 0)); } } } ++Constants._navigationFailures; return(null); }