// XXX 4-23-03: Temporary work around (see comment above) // // Checks for intersection of the given spherical obstacle with a // volume of "likely future vehicle positions": a cylinder along the // current path, extending minTimeToCollision seconds along the // forward axis from current position. // // If they intersect, a collision is imminent and this function returns // a steering force pointing laterally away from the obstacle's center. // // Returns a zero vector if the obstacle is outside the cylinder // // xxx couldn't this be made more compact using localizePosition? Vector3 steerToAvoid(AbstractVehicle v, float minTimeToCollision) { // minimum distance to obstacle before avoidance is required float minDistanceToCollision = minTimeToCollision * v.speed(); float minDistanceToCenter = minDistanceToCollision + radius; // contact distance: sum of radii of obstacle and vehicle float totalRadius = radius + v.radius(); // obstacle center relative to vehicle position Vector3 localOffset = center - v.Position; // distance along vehicle's forward axis to obstacle's center float forwardComponent = localOffset.DotProduct(v.forward()); Vector3 forwardOffset = forwardComponent * v.forward(); // offset from forward axis to obstacle's center Vector3 offForwardOffset = localOffset - forwardOffset; // test to see if sphere overlaps with obstacle-free corridor bool inCylinder = offForwardOffset.Length < totalRadius; bool nearby = forwardComponent < minDistanceToCenter; bool inFront = forwardComponent > 0; // if all three conditions are met, steer away from sphere center if (inCylinder && nearby && inFront) { return(offForwardOffset * -1); } else { return(Vector3.ZERO); } }
// ---------------------------------------------------------------------------- // Alignment behavior: steer to head in same direction as neighbors public Vector3 steerForAlignment(float maxDistance, float cosMaxAngle, ArrayList flock) { // steering accumulator and count of neighbors, both initially zero Vector3 steering = Vector3.Zero; int neighbors = 0; // for each of the other vehicles... //for (AVIterator other = flock.begin(); other != flock.end(); other++) for (int i = 0; i < flock.Count; i++) { AbstractVehicle other = (AbstractVehicle)flock[i]; if (inBoidNeighborhood(other, radius() * 3, maxDistance, cosMaxAngle)) { // accumulate sum of neighbor's heading steering += other.forward(); // count neighbors neighbors++; } } // divide by neighbors, subtract off current heading to get error- // correcting direction, then normalize to pure direction if (neighbors > 0) { steering = ((steering / (float)neighbors) - forward()); steering.Normalize(); } return(steering); }
// Given the time until nearest approach (predictNearestApproachTime) // determine position of each vehicle at that time, and the distance // between them float computeNearestApproachPositions(AbstractVehicle other, float time) { Vector3 myTravel = forward() * speed() * time; Vector3 otherTravel = other.forward() * other.speed() * time; Vector3 myFinal = Position + myTravel; Vector3 otherFinal = other.Position + otherTravel; // xxx for annotation ourPositionAtNearestApproach = myFinal; hisPositionAtNearestApproach = otherFinal; return((myFinal - otherFinal).Length);//Vector3::distance (myFinal, otherFinal); }
// XXX 4-23-03: Temporary work around (see comment above) // // Checks for intersection of the given spherical obstacle with a // volume of "likely future vehicle positions": a cylinder along the // current path, extending minTimeToCollision seconds along the // forward axis from current position. // // If they intersect, a collision is imminent and this function returns // a steering force pointing laterally away from the obstacle's center. // // Returns a zero vector if the obstacle is outside the cylinder // // xxx couldn't this be made more compact using localizePosition? Vector3 steerToAvoid(AbstractVehicle v, float minTimeToCollision) { // minimum distance to obstacle before avoidance is required float minDistanceToCollision = minTimeToCollision * v.speed(); float minDistanceToCenter = minDistanceToCollision + radius; // contact distance: sum of radii of obstacle and vehicle float totalRadius = radius + v.radius(); // obstacle center relative to vehicle position Vector3 localOffset = center - v.Position; // distance along vehicle's forward axis to obstacle's center float forwardComponent = localOffset.DotProduct(v.forward()); Vector3 forwardOffset = forwardComponent * v.forward(); // offset from forward axis to obstacle's center Vector3 offForwardOffset = localOffset - forwardOffset; // test to see if sphere overlaps with obstacle-free corridor bool inCylinder = offForwardOffset.Length < totalRadius; bool nearby = forwardComponent < minDistanceToCenter; bool inFront = forwardComponent > 0; // if all three conditions are met, steer away from sphere center if (inCylinder && nearby && inFront) { return offForwardOffset * -1; } else { return Vector3.ZERO; } }
public Vector3 steerForPursuit(AbstractVehicle quarry, float maxPredictionTime) { // offset from this to quarry, that distance, unit vector toward quarry Vector3 offset = quarry.Position - Position; float distance = offset.Length; Vector3 unitOffset = offset / distance; // how parallel are the paths of "this" and the quarry // (1 means parallel, 0 is pependicular, -1 is anti-parallel) float parallelness = forward().Dot(quarry.forward()); // how "forward" is the direction to the quarry // (1 means dead ahead, 0 is directly to the side, -1 is straight back) float forwardness = forward().Dot(unitOffset); float directTravelTime = distance / speed(); int f = intervalComparison(forwardness, -0.707f, 0.707f); int p = intervalComparison(parallelness, -0.707f, 0.707f); float timeFactor = 0; // to be filled in below Vector3 color = OpenSteerColours.gBlack; // to be filled in below (xxx just for debugging) // Break the pursuit into nine cases, the cross product of the // quarry being [ahead, aside, or behind] us and heading // [parallel, perpendicular, or anti-parallel] to us. switch (f) { case +1: switch (p) { case +1: // ahead, parallel timeFactor = 4; color = OpenSteerColours.gBlack; break; case 0: // ahead, perpendicular timeFactor = 1.8f; color = OpenSteerColours.gGray50; break; case -1: // ahead, anti-parallel timeFactor = 0.85f; color = OpenSteerColours.gWhite; break; } break; case 0: switch (p) { case +1: // aside, parallel timeFactor = 1; color = OpenSteerColours.gRed; break; case 0: // aside, perpendicular timeFactor = 0.8f; color = OpenSteerColours.gYellow; break; case -1: // aside, anti-parallel timeFactor = 4; color = OpenSteerColours.gGreen; break; } break; case -1: switch (p) { case +1: // behind, parallel timeFactor = 0.5f; color = OpenSteerColours.gCyan; break; case 0: // behind, perpendicular timeFactor = 2; color = OpenSteerColours.gBlue; break; case -1: // behind, anti-parallel timeFactor = 2; color = OpenSteerColours.gMagenta; break; } break; } // estimated time until intercept of quarry float et = directTravelTime * timeFactor; // xxx experiment, if kept, this limit should be an argument float etl = (et > maxPredictionTime) ? maxPredictionTime : et; // estimated position of quarry at intercept Vector3 target = quarry.predictFuturePosition(etl); // annotation annotationLine(Position, target, gaudyPursuitAnnotation ? color : OpenSteerColours.gGray40); return(steerForSeek(target)); }
// ---------------------------------------------------------------------------- // Unaligned collision avoidance behavior: avoid colliding with other nearby // vehicles moving in unconstrained directions. Determine which (if any) // other other vehicle we would collide with first, then steers to avoid the // site of that potential collision. Returns a steering force vector, which // is zero length if there is no impending collision. public Vector3 steerToAvoidNeighbors(float minTimeToCollision, ArrayList others) { // first priority is to prevent immediate interpenetration Vector3 separation = steerToAvoidCloseNeighbors(0, others); if (separation != Vector3.Zero) { return(separation); } // otherwise, go on to consider potential future collisions float steer = 0; AbstractVehicle threat = null; // Time (in seconds) until the most immediate collision threat found // so far. Initial value is a threshold: don't look more than this // many frames into the future. float minTime = minTimeToCollision; // xxx solely for annotation Vector3 xxxThreatPositionAtNearestApproach = new Vector3(); Vector3 xxxOurPositionAtNearestApproach = new Vector3(); // for each of the other vehicles, determine which (if any) // pose the most immediate threat of collision. //for (AVIterator i = others.begin(); i != others.end(); i++) for (int i = 0; i < others.Count; i++) { AbstractVehicle other = (AbstractVehicle)others[i]; if (other != this) { // avoid when future positions are this close (or less) float collisionDangerThreshold = radius() * 2; // predicted time until nearest approach of "this" and "other" float time = predictNearestApproachTime(other); // If the time is in the future, sooner than any other // threatened collision... if ((time >= 0) && (time < minTime)) { // if the two will be close enough to collide, // make a note of it if (computeNearestApproachPositions(other, time) < collisionDangerThreshold) { minTime = time; threat = other; xxxThreatPositionAtNearestApproach = hisPositionAtNearestApproach; xxxOurPositionAtNearestApproach = ourPositionAtNearestApproach; } } } } // if a potential collision was found, compute steering to avoid if (threat != null) { // parallel: +1, perpendicular: 0, anti-parallel: -1 float parallelness = forward().Dot(threat.forward()); float angle = 0.707f; if (parallelness < -angle) { // anti-parallel "head on" paths: // steer away from future threat position Vector3 offset = xxxThreatPositionAtNearestApproach - Position; float sideDot = offset.Dot(side()); steer = (sideDot > 0) ? -1.0f : 1.0f; } else { if (parallelness > angle) { // parallel paths: steer away from threat Vector3 offset = threat.Position - Position; float sideDot = offset.Dot(side()); steer = (sideDot > 0) ? -1.0f : 1.0f; } else { // perpendicular paths: steer behind threat // (only the slower of the two does this) if (threat.speed() <= speed()) { float sideDot = side().Dot(threat.velocity()); steer = (sideDot > 0) ? -1.0f : 1.0f; } } } annotateAvoidNeighbor(threat, steer, xxxOurPositionAtNearestApproach, xxxThreatPositionAtNearestApproach); } return(side() * steer); }
// Given the time until nearest approach (predictNearestApproachTime) // determine position of each vehicle at that time, and the distance // between them float computeNearestApproachPositions(AbstractVehicle other, float time) { Vector3 myTravel = forward () * speed () * time; Vector3 otherTravel = other.forward () * other.speed () * time; Vector3 myFinal = Position + myTravel; Vector3 otherFinal = other.Position + otherTravel; // xxx for annotation ourPositionAtNearestApproach = myFinal; hisPositionAtNearestApproach = otherFinal; return (myFinal - otherFinal).Length;//Vector3::distance (myFinal, otherFinal); }
public Vector3 steerForPursuit( AbstractVehicle quarry, float maxPredictionTime) { // offset from this to quarry, that distance, unit vector toward quarry Vector3 offset = quarry.Position - Position; float distance = offset.Length; Vector3 unitOffset = offset / distance; // how parallel are the paths of "this" and the quarry // (1 means parallel, 0 is pependicular, -1 is anti-parallel) float parallelness = forward().DotProduct (quarry.forward()); // how "forward" is the direction to the quarry // (1 means dead ahead, 0 is directly to the side, -1 is straight back) float forwardness = forward().DotProduct (unitOffset); float directTravelTime = distance / speed (); int f = intervalComparison (forwardness, -0.707f, 0.707f); int p = intervalComparison (parallelness, -0.707f, 0.707f); float timeFactor = 0; // to be filled in below Vector3 color=OpenSteerColours.gBlack; // to be filled in below (xxx just for debugging) // Break the pursuit into nine cases, the cross product of the // quarry being [ahead, aside, or behind] us and heading // [parallel, perpendicular, or anti-parallel] to us. switch (f) { case +1: switch (p) { case +1: // ahead, parallel timeFactor = 4; color = OpenSteerColours.gBlack; break; case 0: // ahead, perpendicular timeFactor = 1.8f; color = OpenSteerColours.gGray50; break; case -1: // ahead, anti-parallel timeFactor = 0.85f; color = OpenSteerColours.gWhite; break; } break; case 0: switch (p) { case +1: // aside, parallel timeFactor = 1; color = OpenSteerColours.gRed; break; case 0: // aside, perpendicular timeFactor = 0.8f; color = OpenSteerColours.gYellow; break; case -1: // aside, anti-parallel timeFactor = 4; color = OpenSteerColours.gGreen; break; } break; case -1: switch (p) { case +1: // behind, parallel timeFactor = 0.5f; color = OpenSteerColours.gCyan; break; case 0: // behind, perpendicular timeFactor = 2; color = OpenSteerColours.gBlue; break; case -1: // behind, anti-parallel timeFactor = 2; color = OpenSteerColours.gMagenta; break; } break; } // estimated time until intercept of quarry float et = directTravelTime * timeFactor; // xxx experiment, if kept, this limit should be an argument float etl = (et > maxPredictionTime) ? maxPredictionTime : et; // estimated position of quarry at intercept Vector3 target = quarry.predictFuturePosition (etl); // annotation annotationLine (Position, target, gaudyPursuitAnnotation ? color : OpenSteerColours.gGray40); return steerForSeek (target); }