com.badlogic.gdx.ai.steer.proximities.FieldOfViewProximity 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.proximities;
import com.badlogic.gdx.ai.GdxAI;
import com.badlogic.gdx.ai.Timepiece;
import com.badlogic.gdx.ai.steer.Steerable;
import com.badlogic.gdx.math.Vector;
import com.badlogic.gdx.utils.Array;
/** {@code FieldOfViewProximity} emulates the peripheral vision of the owner as if it had eyes. Any agents contained in the
* specified list that are within the field of view of the owner are considered owner's neighbors. The field of view is determined
* by a radius and an angle in degrees.
*
* Note that this implementation checks the AI time of the current frame through the {@link Timepiece#getTime()
* GdxAI.getTimepiece().getTime()} method in order to calculate neighbors only once per frame (assuming delta time is always
* greater than 0, if time has changed the frame has changed too). This means that
*
* - if you forget to {@link Timepiece#update(float) update the timepiece} on each frame the proximity instance will be
* calculated only the very first time, which is not what you want of course.
* - ideally the timepiece should be updated before the proximity is updated by the {@link #findNeighbors(ProximityCallback)}
* method.
*
*
* @param Type of vector, either 2D or 3D, implementing the {@link Vector} interface
*
* @author davebaol */
public class FieldOfViewProximity> extends ProximityBase {
/** The radius of this proximity. */
protected float radius;
/** The angle in radians of this proximity. */
protected float angle;
private float coneThreshold;
private float lastTime;
private T ownerOrientation;
private T toAgent;
/** Creates a {@code FieldOfViewProximity} for the specified owner, agents and cone area defined by the given radius and angle
* in radians.
* @param owner the owner of this proximity
* @param agents the agents
* @param radius the radius of the cone area
* @param angle the angle in radians of the cone area */
public FieldOfViewProximity (Steerable owner, Array extends Steerable> agents, float radius, float angle) {
super(owner, agents);
this.radius = radius;
setAngle(angle);
this.lastTime = 0;
this.ownerOrientation = owner.getPosition().cpy().setZero();
this.toAgent = owner.getPosition().cpy().setZero();
}
/** Returns the radius of this proximity. */
public float getRadius () {
return radius;
}
/** Sets the radius of this proximity. */
public void setRadius (float radius) {
this.radius = radius;
}
/** Returns the angle of this proximity in radians. */
public float getAngle () {
return angle;
}
/** Sets the angle of this proximity in radians. */
public void setAngle (float angle) {
this.angle = angle;
this.coneThreshold = (float)Math.cos(angle * 0.5f);
}
@Override
public int findNeighbors (ProximityCallback callback) {
int neighborCount = 0;
int agentCount = agents.size;
// If the frame is new then avoid repeating calculations
// when this proximity is used by multiple group behaviors.
float currentTime = GdxAI.getTimepiece().getTime();
if (this.lastTime != currentTime) {
// Save the current time
this.lastTime = currentTime;
T ownerPosition = owner.getPosition();
// Transform owner orientation to a Vector
owner.angleToVector(ownerOrientation, owner.getOrientation());
// Scan the agents searching for neighbors
for (int i = 0; i < agentCount; i++) {
Steerable currentAgent = agents.get(i);
// Make sure the agent being examined isn't the owner
if (currentAgent != owner) {
toAgent.set(currentAgent.getPosition()).sub(ownerPosition);
// The bounding radius of the current agent is taken into account
// by adding it to the radius proximity
float range = radius + currentAgent.getBoundingRadius();
float toAgentLen2 = toAgent.len2();
// Make sure the current agent is within the range.
// Notice we're working in distance-squared space to avoid square root.
if (toAgentLen2 < range * range) {
// If the current agent is within the field of view of the owner,
// report it to the callback and tag it for further consideration.
if (ownerOrientation.dot(toAgent) > coneThreshold) {
if (callback.reportNeighbor(currentAgent)) {
currentAgent.setTagged(true);
neighborCount++;
continue;
}
}
}
}
// Clear the tag
currentAgent.setTagged(false);
}
} else {
// Scan the agents searching for tagged neighbors
for (int i = 0; i < agentCount; i++) {
Steerable currentAgent = agents.get(i);
// Make sure the agent being examined isn't the owner and that
// it's tagged.
if (currentAgent != owner && currentAgent.isTagged()) {
if (callback.reportNeighbor(currentAgent)) {
neighborCount++;
}
}
}
}
return neighborCount;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy