UNPKG

@rpgjs/physic

Version:

A deterministic 2D top-down physics library for RPG, sandbox and MMO games

84 lines (83 loc) 3.2 kB
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