@rpgjs/physic
Version:
A deterministic 2D top-down physics library for RPG, sandbox and MMO games
153 lines (152 loc) • 4.24 kB
JavaScript
class SpringConstraint {
/**
* Creates a new spring constraint
*
* @param entityA - First entity
* @param entityB - Second entity
* @param restLength - Rest length of the spring
* @param springConstant - Spring constant (stiffness)
* @param damping - Damping coefficient
*/
constructor(entityA, entityB, restLength, springConstant, damping) {
this.entityA = entityA;
this.entityB = entityB;
this.restLength = restLength;
this.springConstant = springConstant;
this.damping = damping;
}
/**
* @inheritdoc
*/
update(_deltaTime) {
const direction = this.entityB.position.sub(this.entityA.position);
const distance = direction.length();
if (distance < 1e-3) {
return;
}
const displacement = distance - this.restLength;
const springForce = this.springConstant * displacement;
direction.normalizeInPlace();
const force = direction.mul(springForce);
this.entityA.applyForce(force.mul(-1));
this.entityB.applyForce(force);
if (this.damping > 0) {
const relativeVelocity = this.entityB.velocity.sub(this.entityA.velocity);
const dampingForce = direction.mul(-this.damping * relativeVelocity.dot(direction));
this.entityA.applyForce(dampingForce.mul(-1));
this.entityB.applyForce(dampingForce);
}
}
/**
* Gets the current length of the spring
*
* @returns Current length
*/
getCurrentLength() {
return this.entityA.position.distanceTo(this.entityB.position);
}
}
class DistanceConstraint {
/**
* Creates a new distance constraint
*
* @param entityA - First entity
* @param entityB - Second entity
* @param targetDistance - Target distance to maintain
* @param stiffness - Constraint stiffness (0-1, higher = stiffer)
*/
constructor(entityA, entityB, targetDistance, stiffness = 0.9) {
this.entityA = entityA;
this.entityB = entityB;
this.targetDistance = targetDistance;
this.stiffness = stiffness;
}
/**
* @inheritdoc
*/
update(_deltaTime) {
const direction = this.entityB.position.sub(this.entityA.position);
const distance = direction.length();
if (distance < 1e-3) {
return;
}
const error = distance - this.targetDistance;
if (Math.abs(error) < 1e-3) {
return;
}
direction.normalizeInPlace();
const totalInvMass = this.entityA.invMass + this.entityB.invMass;
if (totalInvMass === 0) {
return;
}
const correction = error * this.stiffness / totalInvMass;
const correctionA = direction.mul(-correction * this.entityA.invMass);
const correctionB = direction.mul(correction * this.entityB.invMass);
if (!this.entityA.isStatic()) {
this.entityA.position.addInPlace(correctionA);
}
if (!this.entityB.isStatic()) {
this.entityB.position.addInPlace(correctionB);
}
}
/**
* Gets the current distance
*
* @returns Current distance
*/
getCurrentDistance() {
return this.entityA.position.distanceTo(this.entityB.position);
}
}
class AnchorConstraint {
/**
* Creates a new anchor constraint
*
* @param entity - Entity to anchor
* @param anchorPoint - Anchor point in world space
* @param stiffness - Constraint stiffness (0-1)
*/
constructor(entity, anchorPoint, stiffness = 0.9) {
this.entity = entity;
this.anchorPoint = anchorPoint.clone();
this.stiffness = stiffness;
}
/**
* @inheritdoc
*/
update(_deltaTime) {
if (this.entity.isStatic()) {
return;
}
const direction = this.anchorPoint.sub(this.entity.position);
const distance = direction.length();
if (distance < 1e-3) {
return;
}
direction.normalizeInPlace();
const correction = direction.mul(distance * this.stiffness);
this.entity.position.addInPlace(correction);
}
/**
* Sets the anchor point
*
* @param point - New anchor point
*/
setAnchorPoint(point) {
this.anchorPoint.copyFrom(point);
}
/**
* Gets the anchor point
*
* @returns Anchor point
*/
getAnchorPoint() {
return this.anchorPoint.clone();
}
}
export {
AnchorConstraint,
DistanceConstraint,
SpringConstraint
};
//# sourceMappingURL=index10.js.map