Vector3 SteeringBehaviour :: escort (const WorldInterface& world, const PhysicsObjectId& id_target, const Vector3& offset) { assert(id_target != PhysicsObjectId::ID_NOTHING); // no initialization needed m_steering_behaviour = ESCORT; // // This is a variation on the arrive steering behaviour. // First we work out the desired position from the // offset. Then we arrive at that position. Finally we // add on the velocity of the target and truncate to our // maximum speed. // // A more sophisticated implementation would use a form of // arrive based on pursue instead of seek. Or maybe this // already happens; I'm not sure. In either case, // slowing down for arrival messes up time predictions. // if(!world.isAlive(m_id_agent) || !world.isAlive( id_target)) { assert(invariant()); return Vector3::ZERO; } assert(world.isAlive(m_id_agent )); assert(world.isAlive( id_target)); Vector3 agent_position = world.getPosition(m_id_agent); Vector3 target_velocity = world.getVelocity( id_target); Vector3 escort_position = calculateEscortPosition(world, id_target, offset); if(agent_position == escort_position) { // we are in the correct position, so just match speeds assert(invariant()); return target_velocity; } double agent_speed_max = world.getShipSpeedMax(m_id_agent); assert(agent_speed_max >= 0.0); double agent_acceleration = world.getShipSpeedMax(m_id_agent); assert(agent_acceleration >= 0.0); double current_distance = agent_position.getDistance(escort_position); double desired_relative_speed = calculateMaxSafeSpeed(current_distance, agent_acceleration); Vector3 desired_relative_velocity = (escort_position - agent_position).getCopyWithNorm(desired_relative_speed); Vector3 desired_agent_velocity = desired_relative_velocity + target_velocity; // limit desired velocity to maximum speed assert(invariant()); return desired_agent_velocity.getTruncated(agent_speed_max); }
Vector3 SteeringBehaviour :: evade (const WorldInterface& world, const PhysicsObjectId& id_target) { assert(id_target != PhysicsObjectId::ID_NOTHING); // no initialization needed m_steering_behaviour = EVADE; if(!world.isAlive(m_id_agent) || !world.isAlive( id_target)) { assert(invariant()); return Vector3::ZERO; } Vector3 agent_position = world.getPosition(m_id_agent); Vector3 target_position = world.getPosition( id_target); if(agent_position == target_position) { assert(invariant()); return Vector3::ZERO; } assert(world.isAlive(m_id_agent )); assert(world.isAlive( id_target)); Vector3 target_velocity = world.getVelocity(id_target); double agent_speed_max = world.getShipSpeedMax(m_id_agent); assert(agent_speed_max >= 0.0); Vector3 aim_direction = getAimDirection(agent_position, agent_speed_max, target_position, target_velocity); if(aim_direction.isZero()) { assert(invariant()); return (agent_position - target_position).getCopyWithNorm(agent_speed_max); } else { assert(invariant()); return aim_direction * -agent_speed_max; } }
void UnitAiMoonGuard::steerToTargetAndShoot(const WorldInterface& world) { assert(m_id_target != PhysicsObjectId::ID_NOTHING); assert(world.isAlive(m_id_target)); Vector3 agent = getShip().getPosition(); Vector3 agent_velocity = world.getVelocity(getShip().getId()); Vector3 target = world.getPosition(m_id_target); Vector3 target_velocity = world.getVelocity(m_id_target); Vector3 desired_velocity; double agent_max_speed = world.getShipSpeedMax(getShip().getId()); double fromAgentToTarget = agent.getAngle(target); double fromAgentToTargetFront = agent.getAngle(target + target_velocity); if (fromAgentToTargetFront < fromAgentToTarget) { desired_velocity = m_steering_behaviour.seek(world, m_id_target); } else { desired_velocity = m_steering_behaviour.getAimDirection(agent, Bullet::SPEED, target, target_velocity); if (desired_velocity == Vector3::ZERO) { desired_velocity = m_steering_behaviour.pursue(world, m_id_target); } else { desired_velocity.setNorm(agent_max_speed); } } Vector3 avoid_velocity = avoidPlanets(world, desired_velocity); avoid_velocity = avoidParticles(world, avoid_velocity); avoid_velocity = avoidShips(world, avoid_velocity); getShipAi().setDesiredVelocity(avoid_velocity); double angle = agent_velocity.getAngle(desired_velocity); double distance = target.getDistanceSquared(agent); if (angle <= SHOOT_ANGLE_RADIANS_MAX && distance < FIRE_RANGE) { getShipAi().markFireBulletDesired(); } }
Vector3 SteeringBehaviour :: explore (const WorldInterface& world, double distance_new_position_min, double distance_new_position_max) { assert(distance_new_position_min >= 0.0); assert(distance_new_position_min <= distance_new_position_max); assert(distance_new_position_min > EXPLORE_DISTANCE_NEW_POSITION); double position_distance = (distance_new_position_max + distance_new_position_min) * 0.5; double position_distance_tolerance = (distance_new_position_max - distance_new_position_min) * 0.5; bool is_new_position_needed = false; if(m_steering_behaviour != EXPLORE || m_desired_distance != position_distance || m_desired_distance_tolerance != position_distance_tolerance) { m_steering_behaviour = EXPLORE; m_desired_distance = position_distance; m_desired_distance_tolerance = position_distance_tolerance; is_new_position_needed = true; } if(!world.isAlive(m_id_agent)) { // the explore position doesn't matter at this point assert(invariant()); return Vector3::ZERO; } Vector3 agent_position = world.getPosition(m_id_agent); double agent_speed_max = world.getShipSpeedMax(m_id_agent); assert(agent_speed_max >= 0.0); if(agent_position.isDistanceLessThan(m_explore_position, EXPLORE_DISTANCE_NEW_POSITION) || is_new_position_needed) { m_explore_position = calculateExplorePosition(agent_position); } return (m_explore_position - agent_position).getCopyWithNorm(agent_speed_max); }
Vector3 SteeringBehaviour :: arrive (const WorldInterface& world, const Vector3& target_position) { // no initialization needed m_steering_behaviour = ARRIVE; PhysicsObjectId id_agent = m_id_agent; if(!world.isAlive(id_agent)) { assert(invariant()); return Vector3::ZERO; } Vector3 agent_position = world.getPosition(id_agent); if(agent_position == target_position) { assert(invariant()); return Vector3::ZERO; } double agent_speed = world.getShipSpeedMax(id_agent); assert(agent_speed >= 0.0); double agent_acceleration = world.getShipAcceleration(id_agent); assert(agent_acceleration >= 0.0); double distance = agent_position.getDistance(target_position); double max_safe_speed = calculateMaxSafeSpeed(distance, agent_acceleration); if(max_safe_speed < agent_speed) agent_speed = max_safe_speed; assert(agent_speed >= 0.0); assert(agent_speed <= max_safe_speed); assert(invariant()); return (target_position - agent_position).getCopyWithNorm(agent_speed); }
Vector3 SteeringBehaviour :: aim (const WorldInterface& world, const PhysicsObjectId& id_target, double shot_speed) { assert(id_target != PhysicsObjectId::ID_NOTHING); assert(shot_speed >= 0.0); // no initialization needed m_steering_behaviour = AIM; if(!world.isAlive(m_id_agent) || !world.isAlive( id_target)) { assert(invariant()); return Vector3::ZERO; } Vector3 agent_position = world.getPosition(m_id_agent); Vector3 target_position = world.getPosition( id_target); if(agent_position == target_position) { assert(invariant()); return Vector3::ZERO; } assert(world.isAlive(m_id_agent )); assert(world.isAlive( id_target)); double agent_speed_max = world.getShipSpeedMax(m_id_agent); assert(agent_speed_max >= 0.0); Vector3 target_velocity = world.getVelocity(id_target); Vector3 aim_direction = getAimDirection(agent_position, shot_speed, target_position, target_velocity); assert(invariant()); return aim_direction * agent_speed_max; }
Vector3 SteeringBehaviour :: flee (const WorldInterface& world, const Vector3& target_position) { // no initialization needed m_steering_behaviour = FLEE; if(!world.isAlive(m_id_agent)) { assert(invariant()); return Vector3::ZERO; } Vector3 agent_position = world.getPosition(m_id_agent); if(agent_position == target_position) { assert(invariant()); return Vector3::ZERO; } double agent_speed_max = world.getShipSpeedMax(m_id_agent); assert(agent_speed_max >= 0.0); return (agent_position - target_position).getCopyWithNorm(agent_speed_max); }
// avoid only when going to collide Vector3 SteeringBehaviour :: avoid (const WorldInterface& world, const Vector3& original_velocity, const Vector3& sphere_center, double sphere_radius, double clearance, double avoid_distance) const { assert(sphere_radius >= 0.0); assert(clearance > 0.0); assert(clearance <= avoid_distance); if(!world.isAlive(m_id_agent) || original_velocity.isZero()) { assert(invariant()); return Vector3::ZERO; } Vector3 agent_position = world.getPosition(m_id_agent); double agent_radius = world.getRadius(m_id_agent); double desired_speed = world.getShipSpeedMax(m_id_agent); Vector3 relative_position = sphere_center - agent_position; double radius_sum = sphere_radius + agent_radius; if(relative_position.isNormGreaterThan(radius_sum + avoid_distance)) { if(DEBUGGING_AVOID) cout << "Avoid: Outside avoid distance" << endl; assert(invariant()); return original_velocity.getTruncated(desired_speed); // too far away to worry about } Vector3 agent_forward = world.getForward(m_id_agent); if(relative_position.dotProduct(agent_forward) < 0.0) { // past center of object; no cylinder if(DEBUGGING_AVOID) cout << "Avoid: Departing from object" << endl; if(relative_position.isNormLessThan(radius_sum + clearance)) { // we are too close, so flee and slow down double distance_fraction = (relative_position.getNorm() - radius_sum) / clearance; if(DEBUGGING_AVOID) cout << "\tInside panic distance: fraction = " << distance_fraction << endl; if(distance_fraction < 0.0) distance_fraction = 0.0; Vector3 interpolated = original_velocity.getNormalized() * distance_fraction + -relative_position.getNormalized() * (1.0 - distance_fraction); if(distance_fraction > AVOID_SPEED_FACTOR_MIN) desired_speed *= distance_fraction; else desired_speed *= AVOID_SPEED_FACTOR_MIN; if(original_velocity.isNormLessThan(desired_speed)) desired_speed = original_velocity.getNorm(); assert(invariant()); return interpolated.getCopyWithNorm(desired_speed); } else { if(DEBUGGING_AVOID) cout << "\tPast object" << endl; assert(invariant()); return original_velocity.getTruncated(desired_speed); // far enough past object } } else { // have not reached center of object; check against cylinder if(DEBUGGING_AVOID) cout << "Avoid: Approaching object" << endl; double distance_from_cylinder_center = relative_position.getAntiProjection(agent_forward).getNorm(); double clearance_fraction = (distance_from_cylinder_center - radius_sum) / clearance; if(DEBUGGING_AVOID) { cout << "\tTo sphere: " << relative_position << endl; cout << "\tDistance_from_cylinder_center: " << distance_from_cylinder_center << endl; cout << "\tRadius_sum: " << radius_sum << endl; cout << "\tClearance: " << clearance << endl; cout << "\tFraction: " << clearance_fraction << endl; } if(clearance_fraction < 0.0) { clearance_fraction = 0.0; if(DEBUGGING_AVOID) cout << "\tLined up at sphere" << endl; } if(clearance_fraction > 1.0) { if(DEBUGGING_AVOID) cout << "\tOutside cylinder" << endl; assert(invariant()); return original_velocity; // outside of danger cylinder } if(DEBUGGING_AVOID) cout << "\tModified fraction: " << clearance_fraction << endl; assert(!original_velocity.isZero()); assert(!agent_forward.isZero()); Vector3 sideways_vector = -relative_position.getAntiProjection(agent_forward); while(sideways_vector.isNormLessThan(AVOID_SIDEWAYS_NORM_MIN)) { // we have almost no sideways, so use a random value sideways_vector = Vector3::getRandomUnitVector().getAntiProjection(agent_forward); // keep trying until we get a good one } assert(!original_velocity.isZero()); assert(!sideways_vector.isZero()); assert(sideways_vector.isOrthogonal(agent_forward)); if(DEBUGGING_AVOID) cout << "\tSideways: " << sideways_vector << endl; Vector3 interpolated = original_velocity.getNormalized() * clearance_fraction + sideways_vector .getNormalized() * (1.0 - clearance_fraction); if(original_velocity.isNormLessThan(desired_speed)) desired_speed = original_velocity.getNorm(); if(DEBUGGING_AVOID) cout << "\tDodging out of cylinder: " << interpolated.getCopyWithNorm(desired_speed) << endl; assert(invariant()); return interpolated.getCopyWithNorm(desired_speed); } }
Vector3 SteeringBehaviour :: patrolSphere (const WorldInterface& world, const Vector3& sphere_center, double patrol_radius, double patrol_radius_tolerance) { assert(patrol_radius >= 0.0); assert(patrol_radius_tolerance >= 0.0); bool is_new_position_needed = false; if(m_steering_behaviour != PATROL_SPHERE || m_sphere_center != sphere_center || m_desired_distance != patrol_radius || m_desired_distance_tolerance != patrol_radius_tolerance) { m_steering_behaviour = PATROL_SPHERE; m_sphere_center = sphere_center; m_desired_distance = patrol_radius; m_desired_distance_tolerance = patrol_radius_tolerance; is_new_position_needed = true; } if(!world.isAlive(m_id_agent)) { // the explore position doesn't matter at this point assert(invariant()); return Vector3::ZERO; } Vector3 agent_position = world.getPosition(m_id_agent); if(isNearEnoughPatrolSpherePoint(agent_position) || is_new_position_needed) { m_explore_position = calculatePatrolSpherePosition(agent_position); } double agent_speed_max = world.getShipSpeedMax(m_id_agent); assert(agent_speed_max >= 0.0); if(DEBUGGING_PATROL_SPHERE) { cout << "Explore:" << endl; cout << "\tAgent position: " << agent_position << endl; cout << "\tSphere position: " << m_sphere_center << endl; cout << "\tExplore position: " << m_explore_position << endl; } double distance_from_sphere = agent_position.getDistance(m_sphere_center); double weight_parallel = 1.0; if(m_desired_distance_tolerance > 0.0) { double distance_outside_optimum = distance_from_sphere - m_desired_distance; weight_parallel = distance_outside_optimum / m_desired_distance_tolerance; if(weight_parallel > 1.0) weight_parallel = 1.0; if(weight_parallel < -1.0) weight_parallel = -1.0; if(DEBUGGING_PATROL_SPHERE) { cout << "\tSphere distance: " << distance_from_sphere << endl; cout << "\tDesired distance: " << m_desired_distance << endl; cout << "\tTolerance: " << m_desired_distance_tolerance << endl; cout << "\tweight_parallel: " << weight_parallel << endl; } if(weight_parallel >= 0.0) weight_parallel = weight_parallel * weight_parallel; else weight_parallel = -(weight_parallel * weight_parallel); if(DEBUGGING_PATROL_SPHERE) cout << "\t => " << weight_parallel << endl; assert(weight_parallel <= 1.0); assert(weight_parallel >= -1.0); } assert(weight_parallel <= 1.0); assert(weight_parallel >= -1.0); double weight_perpendicular = 1.0 - fabs(weight_parallel); assert(weight_perpendicular <= 1.0); assert(weight_perpendicular >= -1.0); if(DEBUGGING_PATROL_SPHERE) cout << "\tweight_perpendicular: " << weight_perpendicular << endl; Vector3 to_sphere = m_sphere_center - agent_position; Vector3 to_destination = m_explore_position - agent_position; Vector3 parallel_to_normal = to_destination.getProjection(to_sphere); Vector3 perpendicular_to_normal = to_destination - parallel_to_normal; Vector3 desired_vector = parallel_to_normal * weight_parallel + perpendicular_to_normal * weight_perpendicular; assert(invariant()); return desired_vector.getCopyWithNormSafe(agent_speed_max); }