mirror of
https://github.com/Heretek-AI/GDevelop.git
synced 2026-08-25 12:23:58 -04:00
361f6b3a87
- This pathfinding works in 2D and 3D is more flexible than the "grid-based" existing pathfinding. - For 3D models, you can optionally choose, like for 3D physics, to follow the exact mesh of the 3D model for pathfinding - allowing characters and objects to move on or around the 3D model naturally. - It handles "crowds" of characters, which can all go to a destination avoiding each others.
217 lines
6.6 KiB
TypeScript
217 lines
6.6 KiB
TypeScript
/*
|
|
GDevelop - Pathfinding Behavior Extension
|
|
Copyright (c) 2013-2016 Florian Rival (Florian.Rival@gmail.com)
|
|
*/
|
|
|
|
namespace gdjs {
|
|
export interface RuntimeInstanceContainer {
|
|
pathfindingObstaclesManager: gdjs.PathfindingObstaclesManager;
|
|
}
|
|
declare var rbush: any;
|
|
|
|
/**
|
|
* PathfindingObstaclesManager manages the common objects shared by objects
|
|
* having a pathfinding behavior: In particular, the obstacles behaviors are
|
|
* required to declare themselves (see
|
|
* `PathfindingObstaclesManager.addObstacle`) to the manager of their
|
|
* associated container (see `PathfindingObstaclesManager.getManager`).
|
|
* @category Behaviors > 2D Pathfinding
|
|
*/
|
|
export class PathfindingObstaclesManager {
|
|
_obstaclesRBush: any;
|
|
|
|
constructor(instanceContainer: gdjs.RuntimeInstanceContainer) {
|
|
this._obstaclesRBush = new rbush();
|
|
}
|
|
|
|
/**
|
|
* Get the obstacles manager of an instance container.
|
|
*/
|
|
static getManager(instanceContainer: gdjs.RuntimeInstanceContainer) {
|
|
if (!instanceContainer.pathfindingObstaclesManager) {
|
|
//Create the shared manager if necessary.
|
|
instanceContainer.pathfindingObstaclesManager =
|
|
new gdjs.PathfindingObstaclesManager(instanceContainer);
|
|
}
|
|
return instanceContainer.pathfindingObstaclesManager;
|
|
}
|
|
|
|
/**
|
|
* Add a obstacle to the list of existing obstacles.
|
|
*/
|
|
addObstacle(
|
|
pathfindingObstacleBehavior: PathfindingObstacleRuntimeBehavior
|
|
) {
|
|
if (pathfindingObstacleBehavior.currentRBushAABB)
|
|
pathfindingObstacleBehavior.currentRBushAABB.updateAABBFromOwner();
|
|
else
|
|
pathfindingObstacleBehavior.currentRBushAABB =
|
|
new gdjs.BehaviorRBushAABB(pathfindingObstacleBehavior);
|
|
|
|
this._obstaclesRBush.insert(pathfindingObstacleBehavior.currentRBushAABB);
|
|
}
|
|
|
|
/**
|
|
* Remove a obstacle from the list of existing obstacles. Be sure that the obstacle was
|
|
* added before.
|
|
*/
|
|
removeObstacle(
|
|
pathfindingObstacleBehavior: PathfindingObstacleRuntimeBehavior
|
|
) {
|
|
this._obstaclesRBush.remove(pathfindingObstacleBehavior.currentRBushAABB);
|
|
}
|
|
|
|
/**
|
|
* Returns all the platforms around the specified object.
|
|
* @param x The center X of the area to search.
|
|
* @param y The center Y of the area to search.
|
|
* @param radius The maximum distance, in pixels, the object is going to do.
|
|
* @param result An array returned with all platforms near the object.
|
|
*/
|
|
getAllObstaclesAround(
|
|
x: float,
|
|
y: float,
|
|
radius: float,
|
|
result: gdjs.PathfindingObstacleRuntimeBehavior[]
|
|
): void {
|
|
const searchArea = gdjs.staticObject(
|
|
PathfindingObstaclesManager.prototype.getAllObstaclesAround
|
|
);
|
|
// @ts-ignore
|
|
searchArea.minX = x - radius;
|
|
// @ts-ignore
|
|
searchArea.minY = y - radius;
|
|
// @ts-ignore
|
|
searchArea.maxX = x + radius;
|
|
// @ts-ignore
|
|
searchArea.maxY = y + radius;
|
|
const nearbyObstacles: gdjs.BehaviorRBushAABB<gdjs.PathfindingObstacleRuntimeBehavior>[] =
|
|
this._obstaclesRBush.search(searchArea);
|
|
result.length = 0;
|
|
nearbyObstacles.forEach((nearbyObstacle) =>
|
|
result.push(nearbyObstacle.behavior)
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* PathfindingObstacleRuntimeBehavior represents a behavior allowing objects to be
|
|
* considered as a obstacle by objects having Pathfinding Behavior.
|
|
* @category Behaviors > 2D Pathfinding
|
|
*/
|
|
export class PathfindingObstacleRuntimeBehavior extends gdjs.RuntimeBehavior {
|
|
_impassable: boolean;
|
|
_cost: float;
|
|
_oldX: float = 0;
|
|
_oldY: float = 0;
|
|
_oldWidth: float = 0;
|
|
_oldHeight: float = 0;
|
|
_manager: PathfindingObstaclesManager;
|
|
_registeredInManager: boolean = false;
|
|
currentRBushAABB: gdjs.BehaviorRBushAABB<PathfindingObstacleRuntimeBehavior> | null =
|
|
null;
|
|
|
|
constructor(
|
|
instanceContainer: gdjs.RuntimeInstanceContainer,
|
|
behaviorData,
|
|
owner: gdjs.RuntimeObject
|
|
) {
|
|
super(instanceContainer, behaviorData, owner);
|
|
this._impassable = behaviorData.impassable;
|
|
this._cost = behaviorData.cost;
|
|
this._manager = PathfindingObstaclesManager.getManager(instanceContainer);
|
|
|
|
//Note that we can't use getX(), getWidth()... of owner here:
|
|
//The owner is not yet fully constructed.
|
|
}
|
|
|
|
override applyBehaviorOverriding(behaviorData): boolean {
|
|
if (behaviorData.impassable !== undefined) {
|
|
this.setImpassable(behaviorData.impassable);
|
|
}
|
|
if (behaviorData.cost !== undefined) {
|
|
this.setCost(behaviorData.cost);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
onDestroy() {
|
|
if (this._manager && this._registeredInManager) {
|
|
this._manager.removeObstacle(this);
|
|
}
|
|
}
|
|
|
|
doStepPreEvents(instanceContainer: gdjs.RuntimeInstanceContainer) {
|
|
//Make sure the obstacle is or is not in the obstacles manager.
|
|
if (!this.activated() && this._registeredInManager) {
|
|
this._manager.removeObstacle(this);
|
|
this._registeredInManager = false;
|
|
} else {
|
|
if (this.activated() && !this._registeredInManager) {
|
|
this._manager.addObstacle(this);
|
|
this._registeredInManager = true;
|
|
}
|
|
}
|
|
|
|
//Track changes in size or position
|
|
if (
|
|
this._oldX !== this.owner.getX() ||
|
|
this._oldY !== this.owner.getY() ||
|
|
this._oldWidth !== this.owner.getWidth() ||
|
|
this._oldHeight !== this.owner.getHeight()
|
|
) {
|
|
if (this._registeredInManager) {
|
|
this._manager.removeObstacle(this);
|
|
this._manager.addObstacle(this);
|
|
}
|
|
this._oldX = this.owner.getX();
|
|
this._oldY = this.owner.getY();
|
|
this._oldWidth = this.owner.getWidth();
|
|
this._oldHeight = this.owner.getHeight();
|
|
}
|
|
}
|
|
|
|
doStepPostEvents(instanceContainer: gdjs.RuntimeInstanceContainer) {}
|
|
|
|
getAABB() {
|
|
return this.owner.getAABB();
|
|
}
|
|
|
|
onActivate() {
|
|
if (this._registeredInManager) {
|
|
return;
|
|
}
|
|
this._manager.addObstacle(this);
|
|
this._registeredInManager = true;
|
|
}
|
|
|
|
onDeActivate() {
|
|
if (!this._registeredInManager) {
|
|
return;
|
|
}
|
|
this._manager.removeObstacle(this);
|
|
this._registeredInManager = false;
|
|
}
|
|
|
|
getCost() {
|
|
return this._cost;
|
|
}
|
|
|
|
setCost(cost: float): void {
|
|
this._cost = cost;
|
|
}
|
|
|
|
isImpassable(): boolean {
|
|
return this._impassable;
|
|
}
|
|
|
|
setImpassable(impassable: boolean): void {
|
|
this._impassable = impassable;
|
|
}
|
|
}
|
|
gdjs.registerBehavior(
|
|
'PathfindingBehavior::PathfindingObstacleBehavior',
|
|
gdjs.PathfindingObstacleRuntimeBehavior
|
|
);
|
|
}
|