UNPKG

@rpgjs/physic

Version:

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

151 lines (150 loc) 4.81 kB
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