public Coords(CoordsType newType, Vector v) { this._type = newType; if (newType == CoordsType.Tile) { _X = (Int32)(v.X / Constants.TileSize); _Y = (Int32)(v.Y / Constants.TileSize); return; } _X = (Int32)Math.Floor(v.X); _Y = (Int32)Math.Floor(v.Y); }
public double DistanceTo(Vector somewhere) { return Math.Sqrt(Math.Pow(this.X - somewhere.X, 2) + Math.Pow(this.Y - somewhere.Y, 2)); }
public bool RayTracerVisibilityCheckPixel(Vector c1, Vector c2) { double x0 = c1.X; double y0 = c1.Y; double x1 = c2.X; double y1 = c2.Y; double dx = Math.Abs(x1 - x0); double dy = Math.Abs(y1 - y0); int x = (int)(Math.Floor(x0)); int y = (int)(Math.Floor(y0)); int n = 1; int x_inc, y_inc; double error; if (dx == 0) { x_inc = 0; error = Double.PositiveInfinity; } else if (x1 > x0) { x_inc = 1; n += (int)(Math.Floor(x1)) - x; error = (Math.Floor(x0) + 1 - x0) * dy; } else { x_inc = -1; n += x - (int)(Math.Floor(x1)); error = (x0 - Math.Floor(x0)) * dy; } if (dy == 0) { y_inc = 0; error -= Double.PositiveInfinity; } else if (y1 > y0) { y_inc = 1; n += (int)(Math.Floor(y1)) - y; error -= (Math.Floor(y0) + 1 - y0) * dx; } else { y_inc = -1; n += y - (int)(Math.Floor(y1)); error -= (y0 - Math.Floor(y0)) * dx; } Coords c2Tile = new Coords(CoordsType.Tile, c2); for (; n > 0; --n) { Coords currentCoords = new Coords(CoordsType.Tile, x, y); // We ignore accrued visibility for now. Can add it later. if ((this._visibilityMap[currentCoords.X, currentCoords.Y] == 0) && (currentCoords != c2Tile)) { return false; } if (error > 0) { y += y_inc; error -= dx; } else { x += x_inc; error += dy; } } return true; }
/// <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>(); }
/// <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; }
/* 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> /// Finds a route to the target. /// </summary> private void AcquireRoute() { // Line of sight check to avoid needless A* calls //Vector here = new Vector(_traveler.PositionPixel.X , _traveler.PositionPixel.Y); Vector there = new Vector(_goalPixel.X, _goalPixel.Y); List<Creature> clipCheck = _traveler.InhabitedMap.RayTracerPassabilityCheckRough(_traveler, _traveler.PositionDouble, there, _traveler.RadiusX); if (clipCheck != null && clipCheck.Count == 0) { _visiblePixelGoal = _goalPixel; _putativeCurrentPosition = _goalTile; EstimateTimeOfTravel(); return; } //Coords hereCoords = _traveler.Position; this._routeCoarse = _traveler.MyPathfinder.PathfinderAStarCoarse(_traveler.PositionTile, _goalTile, StaticMathFunctions.DistanceBetweenTwoCoordsEucledean); this._putativeCurrentPosition = _traveler.PositionTile; AcquireVisiblePixelGoal(); }
// Acquires new visible pixel goal. Deletes the route entries up to that point. private void AcquireVisiblePixelGoal() { UInt16 i = 0; //Coords returnVal; Coords currentTile = _traveler.PositionTile; for (; i < _routeCoarse.Count; ++i) { Direction currentDir = _routeCoarse[_routeCoarse.Count - i - 1]; Coords newTile = currentTile.NeighborInDirection(currentDir); //Vector here = new Vector(_traveler.PositionPixel.X , _traveler.PositionPixel.Y); Coords newTileMiddle = _traveler.InhabitedMap.GetTile(newTile).PixelMiddle(); Vector there = new Vector(newTileMiddle.X, newTileMiddle.Y); List<Creature> clipCheck = _traveler.InhabitedMap.RayTracerPassabilityCheckRough(_traveler, _traveler.PositionDouble, there, _traveler.RadiusX); if (clipCheck == null || clipCheck.Count > 0) { // We've reached an tile, to which a direct line can't go. Set the target to the midpoint of previous tile. _visiblePixelGoal = _traveler.InhabitedMap.GetTile(currentTile).PixelMiddle(); EstimateTimeOfTravel(); // remove directions from route _routeCoarse.RemoveRange(_routeCoarse.Count - i, i); _putativeCurrentPosition = currentTile; return; } currentTile = newTile; } // we went through the loop without returning. that means the end-pixel is visible. _visiblePixelGoal = _goalPixel; EstimateTimeOfTravel(); _putativeCurrentPosition = _goalTile; _routeCoarse = null; }
/// <summary> /// Ask the agent to move aside if he can. /// </summary> public void StepAsideRequest(Vector direction) { if (_owner.MoveSpeedCurrent > 0) { //the agent is already in motion. ignore request. return; } // include other conditions for ignoring the request here. // try to step aside MoveRequest(direction, false, false); }
/// <summary> /// Returns an exact pixel-by-pixel collision check. /// 'null' - terrain collision. /// empty - no collision. /// non-empty - creature collision. /// </summary> public List<Creature> RayTracerPassabilityCheckPrecise(Creature client, Vector v1, Vector v2) { return RayTracerPassabilityCheckPrecise(client, new Coords(CoordsType.Pixel, v1), new Coords(CoordsType.Pixel, v2)); }
/// <summary> /// Handles collisions for an agent. /// </summary> public void HandleCollisions(Creature someGuy, Vector oldPosition) { this.UpdateCreatureBoxes(someGuy, oldPosition); List<Creature> collisions = this.PotentialCreatureToCreatureCollision(someGuy, new Coords(CoordsType.Pixel, someGuy.PositionDouble)); for (int i = 0; i < collisions.Count; ++i) { this.HandleCollisionBetweenTwoPlayers(someGuy, collisions[i]); } }
/// <summary> /// Updates the tiles of the grid which an agent covers. /// </summary> public void UpdateCreatureBoxes(Creature someGuy, Vector oldPosition) { Coords oldCoords = new Coords((Int32)(oldPosition.X / _pixelsPerBoxX), (Int32)(oldPosition.Y / _pixelsPerBoxY)); Coords newCoords = new Coords((Int32)(someGuy.PositionDouble.X / _pixelsPerBoxX), (Int32)(someGuy.PositionDouble.Y / _pixelsPerBoxY)); if (oldCoords == newCoords) { // nothing to do return; } LinkedList<Coords> steppedOn = TilesCoveredByEllipse(new Coords(CoordsType.Pixel, someGuy.PositionDouble), someGuy.RadiusX, someGuy.RadiusY); foreach (Coords c in _occupiedBoxes[someGuy]) { this.ClippingBoxesRemoveFrom(c, someGuy); } foreach (Coords c in steppedOn) { this.ClippingBoxesAddTo(c, someGuy); } _occupiedBoxes[someGuy] = steppedOn; }
private void HandlerMouseRightClick() { Coords clicked = PixelUnderMousecursor(); Vector averagePosition= new Vector(); if (_selectedCreatures.Count > 0) { averagePosition = SelectedCreaturesAveragePosition(); } Coords averagePositionCoords = new Coords(CoordsType.Pixel, averagePosition); foreach (Creature c in _selectedCreatures) { if (!c.Dead) { Coords delta = c.PositionPixel - averagePositionCoords; c.CreatureBrain.OrderMove(clicked + delta); } } }