All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.badlogic.gdx.ai.pfa.PathSmoother Maven / Gradle / Ivy

There is a newer version: 1.8.2
Show newest version
/*******************************************************************************
 * 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.pfa;

import com.badlogic.gdx.ai.utils.Ray;
import com.badlogic.gdx.ai.utils.RaycastCollisionDetector;
import com.badlogic.gdx.math.Vector;
import com.badlogic.gdx.utils.TimeUtils;

/** A {@code PathSmoother} takes a {@link SmoothableGraphPath} and transforms it by linking directly the nodes that are in line of
 * sight. The smoothed path contains at most as many nodes as the original path. Also, the nodes in the smoothed path are unlikely
 * to have any connections between them (if they were connected in the graph, the pathfinder would have found the smoothed route
 * directly, unless their connections had dramatically large costs).
 * 

* Some world representations are more prone to rough paths than others. Fore example, tile-based graphs tend to be highly * erratic. The final appearance also depends on how characters act on the path. If they are using some kind of path following * steering behavior, then the path will be gently smoothed by the steering. It is worth testing your game before assuming the * path will need smoothing. *

* For some games, path smoothing is essential to get the AI looking smart. The path smoothing algorithm is rather simple but * involves raycast and can be somewhat time-consuming. *

* The algorithm assumes that there is a clear route between any two adjacent nodes in the given path. Although this algorithm * produces a smooth path, it doesn't search all possible smoothed paths to find the best one, but the final result is usually * much more satisfactory than the original path. * * @param Type of node * @param Type of vector, either 2D or 3D, implementing the {@link Vector} interface * * @author davebaol */ public class PathSmoother> { RaycastCollisionDetector raycastCollisionDetector; Ray ray; /** Creates a {@code PathSmoother} using the given {@link RaycastCollisionDetector} * @param raycastCollisionDetector the raycast collision detector */ public PathSmoother (RaycastCollisionDetector raycastCollisionDetector) { this.raycastCollisionDetector = raycastCollisionDetector; } /** Smoothes the given path in place. * @param path the path to smooth * @return the number of nodes removed from the path. */ public int smoothPath (SmoothableGraphPath path) { int inputPathLength = path.getCount(); // If the path is two nodes long or less, then we can't smooth it if (inputPathLength <= 2) return 0; // Make sure the ray is instantiated if (this.ray == null) { V vec = path.getNodePosition(0); this.ray = new Ray(vec.cpy(), vec.cpy()); } // Keep track of where we are in the smoothed path. // We start at 1, because we must always include the start node in the smoothed path. int outputIndex = 1; // Keep track of where we are in the input path // We start at 2, because we assume two adjacent // nodes will pass the ray cast int inputIndex = 2; boolean collided = false; // Loop until we find the last item in the input while (inputIndex < inputPathLength) { // Set the ray ray.start.set(path.getNodePosition(outputIndex - 1)); ray.end.set(path.getNodePosition(inputIndex)); // Do the ray cast collided = raycastCollisionDetector.collides(ray); if (collided) { // The ray test failed, swap nodes and consider the next output node path.swapNodes(outputIndex, inputIndex - 1); outputIndex++; } // Consider the next input node inputIndex++; } // Reached the last input node, always add it to the smoothed path. path.swapNodes(outputIndex, inputIndex - 1); path.truncatePath(outputIndex + 1); // Return the number of removed nodes return inputIndex - outputIndex - 1; } /** Smoothes in place the path specified by the given request, possibly over multiple consecutive frames. * @param request the path smoothing request * @param timeToRun the time in nanoseconds that this call can use on the current frame * @return {@code true} if this operation has completed; {@code false} if more time is needed to complete. */ public boolean smoothPath (PathSmootherRequest request, long timeToRun) { long lastTime = TimeUtils.nanoTime(); SmoothableGraphPath path = request.path; int inputPathLength = path.getCount(); // If the path is two nodes long or less, then we can't smooth it if (inputPathLength <= 2) return true; if (request.isNew) { request.isNew = false; // Make sure the ray is instantiated if (this.ray == null) { V vec = request.path.getNodePosition(0); this.ray = new Ray(vec.cpy(), vec.cpy()); } // Keep track of where we are in the smoothed path. // We start at 1, because we must always include the start node in the smoothed path. request.outputIndex = 1; // Keep track of where we are in the input path // We start at 2, because we assume two adjacent // nodes will pass the ray cast request.inputIndex = 2; } // Loop until we find the last item in the input while (request.inputIndex < inputPathLength) { // Check the available time long currentTime = TimeUtils.nanoTime(); timeToRun -= currentTime - lastTime; if (timeToRun <= PathFinderQueue.TIME_TOLERANCE) return false; // Set the ray ray.start.set(path.getNodePosition(request.outputIndex - 1)); ray.end.set(path.getNodePosition(request.inputIndex)); // Do the ray cast boolean collided = raycastCollisionDetector.collides(ray); if (collided) { // The ray test failed, swap nodes and consider the next output node path.swapNodes(request.outputIndex, request.inputIndex - 1); request.outputIndex++; } // Consider the next input node request.inputIndex++; // Store the current time lastTime = currentTime; } // Reached the last input node, always add it to the smoothed path path.swapNodes(request.outputIndex, request.inputIndex - 1); path.truncatePath(request.outputIndex + 1); // Smooth completed return true; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy