准备新增verlet物理引擎

This commit is contained in:
YHH
2020-08-31 09:28:53 +08:00
parent 358e899e8b
commit 8db028db1c
6 changed files with 276 additions and 9 deletions

View File

@@ -12,14 +12,5 @@ module samples {
this.camera.entity.addComponent(new es.FollowCamera(moonEntity)); this.camera.entity.addComponent(new es.FollowCamera(moonEntity));
}); });
} }
public update(){
super.update();
let moonEntity = this.findEntity("moon");
if (!moonEntity)
return;
let spriteRenderer = moonEntity.getComponent<es.SpriteRenderer>(es.SpriteRenderer);
console.log(spriteRenderer.bounds, this.camera.bounds);
}
} }
} }

View File

@@ -0,0 +1,64 @@
module es {
/**
* 代表了Verlet世界中的一个对象。由粒子和约束组成并处理更新它们
*/
export class Composite {
/**
* 摩擦作用于所有粒子运动以使其阻尼。值应该非常接近1。
*/
public friction: Vector2 = new Vector2(0.98, 1);
/**
* 当实体的时候,碰撞器应该碰撞的所有层的图层蒙版。使用了移动方法。默认为所有层。
*/
public collidesWithLayers = Physics.allLayers;
public particles: Particle[] = [];
public _constraints: Constraint[] = [];
/**
* 处理解决所有约束条件
*/
public solveConstraints(){
for (let i = this._constraints.length - 1; i >= 0; i --){
this._constraints[i].solve();
}
}
/**
* 对每个粒子应用重力并做Velet积分
* @param deltaTimeSquared
* @param gravity
*/
public updateParticles(deltaTimeSquared: number, gravity: Vector2){
for (let j = 0; j < this.particles.length; j ++){
let p = this.particles[j];
if (p.isPinned){
p.position = p.pinnedPosition;
continue;
}
p.applyForce(Vector2.multiply(new Vector2(p.mass), gravity));
// 计算速度并用摩擦阻尼
let vel = Vector2.subtract(p.position, p.lastPosition).multiply(this.friction);
// 使用verlet积分计算下一个位置
let nextPos = Vector2.add(p.position, vel).add(Vector2.multiply(new Vector2(0.5 * deltaTimeSquared), p.acceleration));
p.lastPosition = p.position;
p.position = nextPos;
p.acceleration.x = p.acceleration.y = 0;
}
}
public handleConstraintCollisions(){
for (let i = this._constraints.length - 1; i >= 0; i --){
if (this._constraints[i].collidesWithColliders)
this._constraints[i].handleCollisions(this.collidesWithLayers);
}
}
public debugRender(camera: Camera){
}
}
}

View File

@@ -0,0 +1,18 @@
module es {
export abstract class Constraint {
/**
* 如果为true约束将用标准碰撞器检查碰撞。内部约束不需要将此设置为true。
*/
public collidesWithColliders: boolean = true;
/**
* 解决约束
*/
public abstract solve();
/**
* 如果collidesWithColliders为true这个会被调用
* @param collidesWithLayers
*/
public handleCollisions(collidesWithLayers: number){}
}
}

View File

@@ -0,0 +1,36 @@
module es {
export class Particle {
/**
* 粒子的当前位置
*/
public position: Vector2;
/**
* 粒子最近移动之前的位置
*/
public lastPosition: Vector2;
/**
* 粒子的质量。考虑到所有的力量和限制
*/
public mass: number = 1;
/**
* 粒子的半径
*/
public radius: number = 0;
/**
* 如果为true粒子将与标准对撞机相撞
*/
public collidesWithColliders: boolean = true;
public isPinned: boolean;
public acceleration: Vector2;
public pinnedPosition: Vector2;
/**
* 对质点施加一个考虑质量的力
* @param force
*/
public applyForce(force: Vector2){
this.acceleration.add(Vector2.divide(force, new Vector2(this.mass)));
}
}
}

View File

@@ -0,0 +1,158 @@
module es {
/**
* Verlet模拟的根。创建一个世界并调用它的更新方法。
*/
export class VerletWorld {
/**
* 用于模拟的重力
*/
public gravity: Vector2 = new Vector2(0, 980);
/**
* 整个模拟的最大迭代次数
*/
public constraintIterations = 3;
/**
* 整个模拟的最大迭代次数
*/
public maximumStepIterations = 5;
/**
* 世界的边界。粒子将被限制在这个空间中。
*/
public simulationBounds?: Rectangle;
/**
* 粒子是否允许被拖拽
*/
public allowDragging: boolean = true;
public _composites: Composite[] = [];
public static _colliders: Collider[] = new Array(4);
public _tempCircle: Circle = new Circle(1);
public _leftOverTime: number = 0;
public _fixedDeltaTime = 1 / 60;
public _iterationSteps: number = 0;
public _fixedDeltaTimeSq: number = 0;
constructor(simulationBounds: Rectangle = null) {
this.simulationBounds = simulationBounds;
this._fixedDeltaTimeSq = Math.pow(this._fixedDeltaTimeSq, 2);
}
public update(){
this.updateTiming();
if (this.allowDragging)
this.handleDragging();
for (let iteration = 1; iteration <= this._iterationSteps; iteration++){
for (let i = this._composites.length - 1; i >= 0; i --){
let composite = this._composites[i];
for (let s = 0; s < this.constraintIterations; s++)
composite.solveConstraints();
composite.updateParticles(this._fixedDeltaTimeSq, this.gravity);
composite.handleConstraintCollisions();
for (let j = 0; j < composite.particles.length; j ++){
let p = composite.particles[j];
if (this.simulationBounds){
this.constrainParticleToBounds(p);
}
if (p.collidesWithColliders)
this.handleCollisions(p, composite.collidesWithLayers);
}
}
}
}
public handleCollisions(p: Particle, collidesWithLayers: number){
let collidedCount = Physics.overlapCircleAll(p.position, p.radius, VerletWorld._colliders, collidesWithLayers);
for (let i = 0; i < collidedCount; i ++){
let collider = VerletWorld._colliders[i];
if (collider.isTrigger)
continue;
let collisionResult = new CollisionResult();
// 如果我们有一个足够大的粒子半径使用一个圆来检查碰撞,否则回落到一个点
if (p.radius < 2){
if (collider.shape.pointCollidesWithShape(p.position, collisionResult)){
// TODO: 添加一个碰撞器字典让碰撞器设置为力的体积。然后number可以在这里乘以mtv。它应该是非常小的值比如0.002。
p.position.subtract(collisionResult.minimumTranslationVector);
}
}else{
this._tempCircle.radius = p.radius;
this._tempCircle.position = p.position;
if (this._tempCircle.collidesWithShape(collider.shape, collisionResult)){
p.position.subtract(collisionResult.minimumTranslationVector);
}
}
}
}
public constrainParticleToBounds(p: Particle){
let tempPos = p.position;
let bounds = this.simulationBounds;
if (p.radius == 0){
if (tempPos.y > bounds.height)
tempPos.y = bounds.height;
else if(tempPos.y < bounds.y)
tempPos.y = bounds.y;
if (tempPos.x < bounds.x)
tempPos.x = bounds.x;
else if (tempPos.x > bounds.width)
tempPos.x = bounds.width;
}else{
if (tempPos.y < bounds.y + p.radius)
tempPos.y = 2 * (bounds.y + p.radius) - tempPos.y;
if (tempPos.y > bounds.height - p.radius)
tempPos.y = 2 * (bounds.height - p.radius) - tempPos.y;
if (tempPos.x > bounds.width - p.radius)
tempPos.x = 2 * (bounds.width - p.radius) - tempPos.x;
if (tempPos.x < bounds.x + p.radius)
tempPos.x = 2 * (bounds.x + p.radius) - tempPos.x;
}
p.position = tempPos;
}
public updateTiming(){
this._leftOverTime += Time.deltaTime;
this._iterationSteps = Math.floor(Math.trunc(this._leftOverTime / this._fixedDeltaTime));
this._leftOverTime -= this._iterationSteps * this._fixedDeltaTime;
this._iterationSteps = Math.min(this._iterationSteps, this.maximumStepIterations);
}
/**
* 向模拟添加composite
* @param composite
*/
public addComposite<T extends Composite>(composite: T): T{
this._composites.push(composite);
return composite;
}
/**
* 从模拟中删除一个composite
* @param composite
*/
public removeComposite(composite: Composite){
this._composites.remove(composite);
}
public handleDragging(){
}
public debugRender(camera: Camera){
for (let i = 0; i < this._composites.length; i ++)
this._composites[i].debugRender(camera);
}
}
}