@rpgjs/physic
Version:
A deterministic 2D top-down physics library for RPG, sandbox and MMO games
84 lines (83 loc) • 3.2 kB
JavaScript
import { AABB } from "./index4.js";
import { Vector2 } from "./index2.js";
class LinearRepulsion {
/**
* @param engine - Physics engine used for spatial queries
* @param targetProvider - Function returning the target position
* @param maxSpeed - Maximum speed in units per second
* @param repulseRadius - Radius used to sample nearby obstacles
* @param repulseWeight - Weight of the repulsion force
* @param ignoredEntity - Optional entity to exclude from avoidance (e.g. the target)
*/
constructor(engine, targetProvider, maxSpeed = 2.5, repulseRadius = 2, repulseWeight = 4, ignoredEntity) {
this.engine = engine;
this.targetProvider = targetProvider;
this.maxSpeed = maxSpeed;
this.repulseRadius = repulseRadius;
this.repulseWeight = repulseWeight;
this.ignoredEntity = ignoredEntity;
this.repulseRadiusSq = repulseRadius * repulseRadius;
}
update(body, _dt) {
const entity = body.getEntity?.();
if (!entity) {
throw new Error("LinearRepulsion requires a movement body backed by a physics entity.");
}
const targetPosition = this.targetProvider();
const toTarget = new Vector2(targetPosition.x - entity.position.x, targetPosition.y - entity.position.y);
const distance = toTarget.length();
if (distance > 0) {
toTarget.divInPlace(distance);
} else {
toTarget.set(0, 0);
}
const bounds = AABB.fromCenterSize(entity.position.x, entity.position.y, this.repulseRadius * 2, this.repulseRadius * 2);
const neighbors = this.engine.queryAABB(bounds);
const ignored = this.ignoredEntity?.();
const push = new Vector2(0, 0);
for (const other of neighbors) {
if (other === entity || other === ignored || other.isStatic()) {
continue;
}
const diff = new Vector2(entity.position.x - other.position.x, entity.position.y - other.position.y);
const d2 = diff.lengthSquared();
if (d2 > this.repulseRadiusSq || d2 === 0) {
continue;
}
const d = Math.sqrt(d2);
const weight = this.repulseWeight * (this.repulseRadius - Math.min(d, this.repulseRadius)) / this.repulseRadius;
push.addInPlace(diff.mul(weight / d));
}
const maxPush = this.maxSpeed * 3;
const pushLength = push.length();
if (pushLength > maxPush && pushLength > 0) {
push.mulInPlace(maxPush / pushLength);
}
const desired = toTarget.mul(this.maxSpeed).add(push);
const desiredLength = desired.length();
if (desiredLength > this.maxSpeed && desiredLength > 0) {
desired.mulInPlace(this.maxSpeed / desiredLength);
}
if (!Number.isFinite(desired.x) || !Number.isFinite(desired.y)) {
entity.setVelocity({ x: 0, y: 0 });
return;
}
body.setVelocity(desired);
}
setParameters(maxSpeed, repulseRadius, repulseWeight) {
if (maxSpeed !== void 0) {
this.maxSpeed = maxSpeed;
}
if (repulseRadius !== void 0) {
this.repulseRadius = repulseRadius;
this.repulseRadiusSq = repulseRadius * repulseRadius;
}
if (repulseWeight !== void 0) {
this.repulseWeight = repulseWeight;
}
}
}
export {
LinearRepulsion
};
//# sourceMappingURL=index37.js.map