/// <summary> /// Method to set the path of an ai unit in order to dodge an object /// </summary> /// <param name="callingAI">ai to perform dodge</param> /// <param name="closestObstruction">closest obstruction</param> private void dodgeObject(DynamicObject callingAI, StaticObject closestObstruction) { if (!this.isObjectRegistered(callingAI)) { return; } PathInformation pathInfo = objectPaths[callingAI]; if (pathInfo.currentWaypoint == null) { return; } if (!dodgeInactiveCountDown.Keys.Contains(callingAI)) { dodgeInactiveCountDown.Add(callingAI, RETAIN_DODGE_PATH_TICKS); } else { return; } Vector3 dodgeWp = new Vector3(); bool bFlag = false; // Set the new path: float dodgeDistance = (callingAI.getBoundingSphere().Radius + closestObstruction.getBoundingSphere().Radius) * DODGE_DISTANCE_MULTIPLIER; float distanceToObject = (callingAI.Position - closestObstruction.Position).Length(); float distanceToCurrentWp = (callingAI.Position - pathInfo.currentWaypoint.Position).Length(); float dodgeAngle = (float)Math.Abs(Math.Atan2(distanceToObject, dodgeDistance)); for (int i = (int)Math.Ceiling(dodgeAngle); i *dodgeAngle < Math.PI; ++i) { for (int j = (int)Math.Ceiling(dodgeAngle); j *dodgeAngle < Math.PI; ++j) { if (isDodgeValid(callingAI, dodgeAngle, i, j, pathInfo, closestObstruction, dodgeDistance, ref dodgeWp)) { bFlag = true; break; } if (isDodgeValid(callingAI, -dodgeAngle, i, j, pathInfo, closestObstruction, dodgeDistance, ref dodgeWp)) { bFlag = true; break; } } if (bFlag) { break; } } List <Node> path = pathInfo.remainingPath; if (path.Count > 0) { path.Remove(path.Last()); } path.Add(new Node(dodgeWp, -1)); pathInfo.remainingPath = path; }
/// <summary> /// Method to test if a collision will occur on the current route of the calling ai (and which collision will occur first). If such /// a possible collision is detected the callingAI will attempt to dodge the object. /// </summary> /// <param name="callingAI">AI that the test is performed for.</param> /// <param name="obstructionsList">A list of obstructions as returned by PathIntersectTest</param> private void avoidCollisions(DynamicObject callingAI, List <StaticObject> obstructionsList) { //find the closest obstruction and dodge it: if (obstructionsList.Count == 0) { return; } PathInformation pathInfo = objectPaths[callingAI]; StaticObject closestObstruction = obstructionsList.First(); float closestObstructionDistance = (closestObstruction.Position - callingAI.Position).Length(); for (int i = 1; i < obstructionsList.Count; ++i) { StaticObject obstruction = obstructionsList.ElementAt(i); float distanceToObstruction = (obstruction.Position - callingAI.Position).Length(); if (distanceToObstruction < closestObstructionDistance) { closestObstruction = obstruction; closestObstructionDistance = distanceToObstruction; } } if (closestObstruction is DynamicObject) { if (isObjectRegistered(closestObstruction as DynamicObject)) { if (objectPaths[closestObstruction as DynamicObject].currentWaypoint != null) { if (objectPaths[closestObstruction as DynamicObject].currentWaypoint.connectedEdges.Count == 0 || closestObstruction is playerObject) { return; // that object is dodging already don't make it stop } } } } //Make the obstruction yield for the next few steps: if (closestObstruction is DynamicObject) { if (!movementYieldList.Keys.Contains(closestObstruction as DynamicObject)) { movementYieldList.Add(closestObstruction as DynamicObject, YIELD_TICKS); } } //Now dodge it: dodgeObject(callingAI, closestObstruction); }
/// <summary> /// Performs a dogfight move against the opponent /// </summary> /// <param name="ti">Team on which the ai is registered</param> /// <param name="ai">Character to perform move</param> /// <param name="target">Opponent of the ai</param> public void doDogfightMove(TeamInformation ti, DynamicObject ai, StaticObject target) { if (!this.isObjectRegistered(ai)) { return; } float radiusToGoBehindTarget = (target.getBoundingSphere().Radius + ai.getBoundingSphere().Radius) * RADIUS_MULTIPLIER_TO_GO_BEHIND_TARGET; Vector3 wpPosition = target.Position + Vector3.Normalize(Matrix.CreateFromQuaternion(target.rotation).Forward) * radiusToGoBehindTarget; Vector3 wpDPosition = target.Position + Vector3.Normalize(Matrix.CreateFromQuaternion(target.rotation).Up) * radiusToGoBehindTarget / RADIUS_FACTOR_TO_GO_ABOVE_TARGET; if (Vector3.Dot(Vector3.Normalize(wpPosition - target.Position), Vector3.Normalize(ai.Position - target.Position)) < DOT_ANGLE_TO_STOP_DOGFIGHT_MOVE || (ai.Position - target.Position).Length() > CHASE_WHEN_FURTHER) { PathInformation fighterPath = objectPaths[ai]; List <Node> waypointList = fighterPath.remainingPath; //we clear the waypoint list and add new waypoints: if (waypointList.Count > 0) { bool shouldAddTopWaypt = (Vector3.Dot(Vector3.Normalize(Matrix.CreateFromQuaternion(target.rotation).Forward), Vector3.Normalize(target.Position - ai.Position)) > 0); if ((wpPosition - waypointList.ElementAt(0).Position).Length() > TARGET_WP_BUFFER || shouldAddTopWaypt) { waypointList.Clear(); if (shouldAddTopWaypt) { waypointList.Insert(0, new Node(wpDPosition, -1)); } waypointList.Insert(0, new Node(wpPosition, -1)); } } else { if (Vector3.Dot(Vector3.Normalize(Matrix.CreateFromQuaternion(target.rotation).Forward), Vector3.Normalize(target.Position - ai.Position)) > 0) { waypointList.Insert(0, new Node(wpDPosition, -1)); } waypointList.Insert(0, new Node(wpPosition, -1)); } fighterPath.remainingPath = waypointList; } else //stop navigation (we are behind the target so we can start shooting it) { getPath(ai).Clear(); } }
/// <summary> /// Method to update the movement of all registered AI characters. The list of waypoints have to be in reverse order (as returned by the A*) /// </summary> /// <param name="gt">Game time as passed on by the game loop</param> public void updateAIMovement(GameTime gt) { //Clear the yield list at the beginning of the step: for (int i = AI_MOVEMENT_SLOT_COUNT * currentSlot; i < AI_MOVEMENT_SLOT_COUNT * (currentSlot + 1) && i < objectPaths.Keys.Count; ++i) { DynamicObject ai = objectPaths.Keys.ElementAt(i); //reset the speed: ai.ShipMovementInfo.speed = 0; //counts down the path retaining table: if (dodgeInactiveCountDown.Keys.Contains(ai)) { if (dodgeInactiveCountDown[ai]-- <= 0) { dodgeInactiveCountDown.Remove(ai); } } //if the object has to yield then do nothing if (movementYieldList.Keys.Contains(ai)) { if (movementYieldList[ai]-- > 0) { continue; } else { movementYieldList.Remove(ai); } } //get the current path and check for obsticles PathInformation pathInfo = objectPaths[ai]; float closeToWaypoint = ai.getMaxSpeed * DISTANCE_TO_WAYPOINT_IN_SECONDS_WHEN_CLOSE; float veryCloseToWaypoint = ai.getMaxSpeed * DISTANCE_TO_WAYPOINT_IN_SECONDS_WHEN_VERY_CLOSE; if (pathInfo.currentWaypoint != null) //if there is a path { float distToWayPoint = (pathInfo.currentWaypoint.Position - ai.Position).Length(); //List<StaticObject> obstructionsList = pathIntersectTest(ai); List <StaticObject> nearbyList = this.obstructionsInCloseProximity(ai); avoidCollisions(ai, nearbyList.ToList()); //if the object has to yield then do nothing if (movementYieldList.Keys.Contains(ai)) { if (movementYieldList[ai] > 0) { continue; } } //if very close to the next waypoint remove that waypoint so that we can go to the next: if (distToWayPoint <= veryCloseToWaypoint) { pathInfo.reachedWaypoint(); } else// if (nearbyList.Count == 0) { //We want our ship to slowly rotate towards the direction it has to move in: Vector3 vLookDir = Vector3.Zero, vWantDir = Vector3.Zero; turnAI(ref vWantDir, ref vLookDir, ai, pathInfo.currentWaypoint.Position, gt); //now set the speed: float compLookOntoWant = Vector3.Dot(vLookDir, vWantDir); if (Math.Abs(compLookOntoWant) > 1) { compLookOntoWant = 1; } ai.ShipMovementInfo.speed = ai.getMaxSpeed * (float)(Math.Pow(TURNING_SPEED_COEF, -Math.Abs(Math.Acos(compLookOntoWant) * 180 / Math.PI))); } } } if ((currentSlot + 1) * AI_MOVEMENT_SLOT_COUNT < objectPaths.Keys.Count) { currentSlot++; } else { currentSlot = 0; } }
/// <summary> /// Checks if a particular dodge is valid /// </summary> private bool isDodgeValid(DynamicObject callingAI, float dodgeAngle, int dodgeAngleMultiplierYaw, int dodgeAngleMultiplierPitch, PathInformation pathInfo, StaticObject closestObstruction, float dodgeDistance, ref Vector3 dodgeWp) { bool bFlag = false; //Define a conal area around the current path to choose another path from Quaternion qRot = Quaternion.CreateFromYawPitchRoll(dodgeAngle * dodgeAngleMultiplierYaw, dodgeAngle * dodgeAngleMultiplierPitch, 0); Vector3 choiceVector = Vector3.Normalize( Vector3.Transform(pathInfo.currentWaypoint.Position - callingAI.Position, Matrix.CreateFromQuaternion(qRot))); dodgeWp = callingAI.Position + choiceVector * dodgeDistance; if ((dodgeWp - closestObstruction.Position).Length() > dodgeDistance) { foreach (GridObjectInterface o in spatialGrid.checkNeighbouringBlocks(dodgeWp)) { if (o != callingAI) { if (o is StaticObject) { if ((o.Position - dodgeWp).Length() > (o.getBoundingSphere().Radius + callingAI.getBoundingSphere().Radius *(DODGE_DISTANCE_MULTIPLIER))) { bFlag = true; } } if (o is DynamicObject) { if (isObjectRegistered(o as DynamicObject)) { Node otherObjectsWaypoint = objectPaths[o as DynamicObject].currentWaypoint; if (otherObjectsWaypoint != null) { if ((otherObjectsWaypoint.Position - dodgeWp).Length() < (o.getBoundingSphere().Radius + callingAI.getBoundingSphere().Radius *(DODGE_DISTANCE_MULTIPLIER))) { bFlag = false; } } } } if (!spatialGrid.isInGrid(new Node(dodgeWp, -1))) { bFlag = false; } } } } return(bFlag); }
/// <summary> /// Checks if a particular dodge is valid /// </summary> private bool isDodgeValid(DynamicObject callingAI,float dodgeAngle,int dodgeAngleMultiplierYaw,int dodgeAngleMultiplierPitch,PathInformation pathInfo, StaticObject closestObstruction,float dodgeDistance, ref Vector3 dodgeWp) { bool bFlag = false; //Define a conal area around the current path to choose another path from Quaternion qRot = Quaternion.CreateFromYawPitchRoll(dodgeAngle * dodgeAngleMultiplierYaw, dodgeAngle * dodgeAngleMultiplierPitch, 0); Vector3 choiceVector = Vector3.Normalize( Vector3.Transform(pathInfo.currentWaypoint.Position - callingAI.Position, Matrix.CreateFromQuaternion(qRot))); dodgeWp = callingAI.Position + choiceVector * dodgeDistance; if ((dodgeWp - closestObstruction.Position).Length() > dodgeDistance) { foreach (GridObjectInterface o in spatialGrid.checkNeighbouringBlocks(dodgeWp)) { if (o != callingAI) { if (o is StaticObject) { if ((o.Position - dodgeWp).Length() > (o.getBoundingSphere().Radius + callingAI.getBoundingSphere().Radius * (DODGE_DISTANCE_MULTIPLIER))) bFlag = true; } if (o is DynamicObject) { if (isObjectRegistered(o as DynamicObject)) { Node otherObjectsWaypoint = objectPaths[o as DynamicObject].currentWaypoint; if (otherObjectsWaypoint != null) { if ((otherObjectsWaypoint.Position - dodgeWp).Length() < (o.getBoundingSphere().Radius + callingAI.getBoundingSphere().Radius * (DODGE_DISTANCE_MULTIPLIER))) bFlag = false; } } } if (!spatialGrid.isInGrid(new Node(dodgeWp,-1))) bFlag = false; } } } return bFlag; }