@rpgjs/physic
Version:
A deterministic 2D top-down physics library for RPG, sandbox and MMO games
151 lines (150 loc) • 4.81 kB
JavaScript
import { Vector2 } from "./index2.js";
var IntegrationMethod = /* @__PURE__ */ ((IntegrationMethod2) => {
IntegrationMethod2["Euler"] = "euler";
IntegrationMethod2["Verlet"] = "verlet";
return IntegrationMethod2;
})(IntegrationMethod || {});
class Integrator {
/**
* Creates a new integrator
*
* @param config - Integrator configuration
*/
constructor(config) {
this.config = config;
this.gravity = config.gravity?.clone() ?? new Vector2(0, 0);
}
/**
* Integrates an entity's motion
*
* Updates position, velocity, and rotation based on forces and torques.
*
* @param entity - Entity to integrate
*/
integrate(entity) {
if (entity.isStatic() || entity.isSleeping()) {
return;
}
switch (this.config.method ?? "euler") {
case "euler":
this.integrateEuler(entity);
break;
case "verlet":
this.integrateVerlet(entity);
break;
}
}
/**
* Semi-implicit Euler integration
*
* Updates velocity first, then position. More stable than explicit Euler.
*
* @param entity - Entity to integrate
*/
integrateEuler(entity) {
const dt = this.config.deltaTime;
const invMass = entity.invMass;
if (invMass > 0) {
entity.force.addInPlace(this.gravity.mul(entity.mass));
}
const acceleration = entity.force.mul(invMass);
entity.velocity.addInPlace(acceleration.mul(dt));
const linearDampingFactor = 1 - entity.linearDamping;
entity.velocity.mulInPlace(linearDampingFactor);
entity.clampVelocities();
entity.notifyMovementChange();
const oldPosition = entity.position.clone();
entity.position.addInPlace(entity.velocity.mul(dt));
const delta = entity.position.sub(oldPosition);
if (delta.lengthSquared() > 1e-6) {
entity.notifyPositionChange();
}
const momentOfInertia = entity.mass * entity.radius * entity.radius;
if (momentOfInertia > 0) {
const angularAcceleration = entity.torque / momentOfInertia;
entity.angularVelocity += angularAcceleration * dt;
}
const angularDampingFactor = 1 - entity.angularDamping;
entity.angularVelocity *= angularDampingFactor;
if (Math.abs(entity.angularVelocity) > entity.maxAngularVelocity) {
entity.angularVelocity = Math.sign(entity.angularVelocity) * entity.maxAngularVelocity;
}
entity.rotation += entity.angularVelocity * dt;
while (entity.rotation > Math.PI) {
entity.rotation -= 2 * Math.PI;
}
while (entity.rotation < -Math.PI) {
entity.rotation += 2 * Math.PI;
}
entity.clearForces();
}
/**
* Verlet integration
*
* More stable than Euler, better energy conservation.
* Requires storing previous position.
*
* @param entity - Entity to integrate
*/
integrateVerlet(entity) {
const dt = this.config.deltaTime;
const dt2 = dt * dt;
const invMass = entity.invMass;
if (invMass > 0) {
entity.force.addInPlace(this.gravity.mul(entity.mass));
}
const acceleration = entity.force.mul(invMass);
const newPosition = entity.position.add(entity.velocity.mul(dt)).add(acceleration.mul(0.5 * dt2));
const newVelocity = newPosition.sub(entity.position).div(dt);
const linearDampingFactor = 1 - entity.linearDamping;
newVelocity.mulInPlace(linearDampingFactor);
const oldPosition = entity.position.clone();
entity.position = newPosition;
entity.velocity = newVelocity;
entity.clampVelocities();
entity.notifyMovementChange();
const delta = entity.position.sub(oldPosition);
if (delta.lengthSquared() > 1e-6) {
entity.notifyPositionChange();
}
const momentOfInertia = entity.mass * entity.radius * entity.radius;
if (momentOfInertia > 0) {
const angularAcceleration = entity.torque / momentOfInertia;
entity.angularVelocity += angularAcceleration * dt;
}
const angularDampingFactor = 1 - entity.angularDamping;
entity.angularVelocity *= angularDampingFactor;
if (Math.abs(entity.angularVelocity) > entity.maxAngularVelocity) {
entity.angularVelocity = Math.sign(entity.angularVelocity) * entity.maxAngularVelocity;
}
entity.rotation += entity.angularVelocity * dt;
while (entity.rotation > Math.PI) {
entity.rotation -= 2 * Math.PI;
}
while (entity.rotation < -Math.PI) {
entity.rotation += 2 * Math.PI;
}
entity.clearForces();
}
/**
* Updates the gravity vector
*
* @param gravity - New gravity vector
*/
setGravity(gravity) {
this.gravity.copyFrom(gravity);
}
/**
* Gets the current gravity vector
*
* @returns Gravity vector
*/
getGravity() {
return this.gravity.clone();
}
}
export {
IntegrationMethod,
Integrator
};
//# sourceMappingURL=index8.js.map