com.badlogic.gdx.ai.steer.behaviors.Hide Maven / Gradle / Ivy
/*******************************************************************************
* Copyright 2014 See AUTHORS file.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.badlogic.gdx.ai.steer.behaviors;
import com.badlogic.gdx.ai.steer.Limiter;
import com.badlogic.gdx.ai.steer.Proximity;
import com.badlogic.gdx.ai.steer.Proximity.ProximityCallback;
import com.badlogic.gdx.ai.steer.Steerable;
import com.badlogic.gdx.ai.steer.SteeringAcceleration;
import com.badlogic.gdx.ai.steer.proximities.FieldOfViewProximity;
import com.badlogic.gdx.ai.utils.Location;
import org.mini2Dx.gdx.math.Vector;
/** This behavior attempts to position a owner so that an obstacle is always between itself and the agent (the hunter) it's trying
* to hide from. First the distance to each of these obstacles is determined. Then the owner uses the arrive behavior to steer
* toward the closest one. If no appropriate obstacles can be found, no steering is returned.
*
* You can use this behavior not only for situations where you require a non-player character (NPC) to hide from the player, like
* find cover when fired at, but also in situations where you would like an NPC to sneak up on a player. For example, you can
* create an NPC capable of stalking a player through a gloomy forest, darting from tree to tree.
*
* It's worth mentioning that since this behavior can produce no steering acceleration it is commonly used with
* {@link PrioritySteering}. For instance, to make the owner go away from the target if there are no obstacles nearby to hide
* behind, just use {@link Hide} and {@link Evade} behaviors with this priority order.
*
* There are a few interesting modifications you might want to make to this behavior:
*
* - With {@link FieldOfViewProximity} you can allow the owner to hide only if the target is within its field of view. This
* tends to produce unsatisfactory performance though, because the owner starts to act like a child hiding from monsters beneath
* the bed sheets, something like "if you can't see it, then it can't see you" effect making the owner look dumb. This can be
* countered slightly though by adding in a time effect so that the owner will hide if the target is visible or if it has seen the
* target within the last {@code N} seconds. This gives it a sort of memory and produces reasonable-looking behavior.
* - The same as above, but this time the owner only tries to hide if the owner can see the target and the target can see the
* owner.
*
- It might be desirable to produce a force that steers the owner so that it always favors hiding positions that are to the
* side or rear of the pursuer. This can be achieved easily using the dot product to bias the distances returned from the method
* {@link #getHidingPosition}.
* - At the beginning of any of the methods a check can be made to test if the target is within a "threat distance" before
* proceeding with any further calculations. If the target is not a threat, then the method can return immediately with zero
* steering.
*
*
* @param Type of vector, either 2D or 3D, implementing the {@link Vector} interface
*
* @author davebaol */
public class Hide> extends Arrive implements ProximityCallback {
/** The proximity to find nearby obstacles. */
protected Proximity proximity;
/** The distance from the boundary of the obstacle behind which to hide. */
protected float distanceFromBoundary;
private T toObstacle;
private T bestHidingSpot;
private float distance2ToClosest;
/** Creates an {@code Hide} behavior for the specified owner.
* @param owner the owner of this behavior */
public Hide (Steerable owner) {
this(owner, null);
}
/** Creates a {@code Hide} behavior for the specified owner and target.
* @param owner the owner of this behavior
* @param target the target of this behavior */
public Hide (Steerable owner, Location target) {
this(owner, target, null);
}
/** Creates a {@code Hide} behavior for the specified owner, target and proximity.
* @param owner the owner of this behavior
* @param target the target of this behavior
* @param proximity the proximity to find nearby obstacles */
public Hide (Steerable owner, Location target, Proximity proximity) {
super(owner, target);
this.proximity = proximity;
this.bestHidingSpot = newVector(owner);
this.toObstacle = null; // Set to null since we'll reuse steering.linear for this vector
}
@Override
protected SteeringAcceleration calculateRealSteering (SteeringAcceleration steering) {
// Initialize member variables used by the callback
this.distance2ToClosest = Float.POSITIVE_INFINITY;
this.toObstacle = steering.linear;
// Find neighbors (the obstacles) using this behavior as callback
int neighborsCount = proximity.findNeighbors(this);
// If no suitable obstacles found return no steering otherwise use Arrive on the hiding spot
return neighborsCount == 0 ? steering.setZero() : arrive(steering, bestHidingSpot);
}
@Override
public boolean reportNeighbor (Steerable neighbor) {
// Calculate the position of the hiding spot for this obstacle
T hidingSpot = getHidingPosition(neighbor.getPosition(), neighbor.getBoundingRadius(), target.getPosition());
// Work in distance-squared space to find the closest hiding
// spot to the owner
float distance2 = hidingSpot.dst2(owner.getPosition());
if (distance2 < distance2ToClosest) {
distance2ToClosest = distance2;
bestHidingSpot.set(hidingSpot);
return true;
}
return false;
}
/** Returns the proximity used to find nearby obstacles. */
public Proximity getProximity () {
return proximity;
}
/** Sets the proximity used to find nearby obstacles.
* @param proximity the proximity to set
* @return this behavior for chaining. */
public Hide setProximity (Proximity proximity) {
this.proximity = proximity;
return this;
}
/** Returns the distance from the boundary of the obstacle behind which to hide. */
public float getDistanceFromBoundary () {
return distanceFromBoundary;
}
/** Sets the distance from the boundary of the obstacle behind which to hide.
* @param distanceFromBoundary the distance to set
* @return this behavior for chaining. */
public Hide setDistanceFromBoundary (float distanceFromBoundary) {
this.distanceFromBoundary = distanceFromBoundary;
return this;
}
/** Given the position of a target and the position and radius of an obstacle, this method calculates a position
* {@code distanceFromBoundary} away from the object's bounding radius and directly opposite the target. It does this by scaling
* the normalized "to obstacle" vector by the required distance away from the center of the obstacle and then adding the result
* to the obstacle's position.
* @param obstaclePosition
* @param obstacleRadius
* @param targetPosition
* @return the hiding position behind the obstacle. */
protected T getHidingPosition (T obstaclePosition, float obstacleRadius, T targetPosition) {
// Calculate how far away the agent is to be from the chosen
// obstacle's bounding radius
float distanceAway = obstacleRadius + distanceFromBoundary;
// Calculate the normalized vector toward the obstacle from the target
toObstacle.set(obstaclePosition).sub(targetPosition).nor();
// Scale it to size and add to the obstacle's position to get
// the hiding spot.
return toObstacle.scl(distanceAway).add(obstaclePosition);
}
//
// Setters overridden in order to fix the correct return type for chaining
//
@Override
public Hide setOwner (Steerable owner) {
this.owner = owner;
return this;
}
@Override
public Hide setEnabled (boolean enabled) {
this.enabled = enabled;
return this;
}
@Override
public Hide setLimiter (Limiter limiter) {
this.limiter = limiter;
return this;
}
@Override
public Hide setTarget (Location target) {
this.target = target;
return this;
}
@Override
public Hide setArrivalTolerance (float arrivalTolerance) {
this.arrivalTolerance = arrivalTolerance;
return this;
}
@Override
public Hide setDecelerationRadius (float decelerationRadius) {
this.decelerationRadius = decelerationRadius;
return this;
}
@Override
public Hide setTimeToTarget (float timeToTarget) {
this.timeToTarget = timeToTarget;
return this;
}
}