新增box复合体 修复vector2运算问题

This commit is contained in:
YHH
2020-06-11 00:03:26 +08:00
parent 74cbb4c9fd
commit 2eec9a82f9
28 changed files with 1154 additions and 200 deletions

View File

@@ -0,0 +1,19 @@
///<reference path="./Composite.ts"/>
class Box extends Composite{
constructor(center: Vector2, width: number, height: number, borderStiffness = 0.2, diagonalStiffness = 0.5){
super();
let tl = this.addParticle(new Particle(Vector2.add(center, new Vector2(-width / 2, -height / 2))));
let tr = this.addParticle(new Particle(Vector2.add(center, new Vector2(width / 2, -height / 2))));
let br = this.addParticle(new Particle(Vector2.add(center, new Vector2(width / 2, height / 2))));
let bl = this.addParticle(new Particle(Vector2.add(center, new Vector2(-width / 2, height / 2))));
this.addConstraint(new DistanceConstraint(tl, tr, borderStiffness));
this.addConstraint(new DistanceConstraint(tr, br, borderStiffness));
this.addConstraint(new DistanceConstraint(br, bl, borderStiffness));
this.addConstraint(new DistanceConstraint(bl, tl, borderStiffness));
this.addConstraint(new DistanceConstraint(tl, br, diagonalStiffness)).setCollidesWithColliders(false);
this.addConstraint(new DistanceConstraint(bl, tr, diagonalStiffness)).setCollidesWithColliders(false);
}
}

View File

@@ -0,0 +1,67 @@
class Composite {
private _constraints: Constraint[] = [];
public friction = new Vector2(0.98, 1);
public drawParticles: boolean = true;
public drawConstraints: boolean = true;
public particles: Particle[] = [];
/**
* 处理解决所有约束条件
*/
public solveConstraints(){
for (let i = this._constraints.length - 1; i >= 0; i --){
this._constraints[i].solve();
}
}
public addParticle(particle: Particle){
this.particles.push(particle);
return particle;
}
public addConstraint<T extends Constraint>(constraint: T): T{
this._constraints.push(constraint);
constraint.composite = this;
return constraint;
}
public removeConstraint(constraint: Constraint){
this._constraints.remove(constraint);
}
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, p.mass), gravity));
let vel = Vector2.multiply(Vector2.subtract(p.position, p.lastPosition), this.friction);
let nextPos = Vector2.add(Vector2.add(p.position, vel), Vector2.multiply(p.acceleration, new Vector2(0.5 * deltaTimeSquared, 0.5 * deltaTimeSquared)));
p.lastPosition = p.position;
p.position = nextPos;
p.acceleration.x = p.acceleration.y = 0;
}
}
public debugRender(graphics: egret.Graphics){
if (this.drawConstraints){
for (let i = 0; i < this._constraints.length; i ++){
this._constraints[i].debugRender(graphics);
}
}
if (this.drawParticles){
for (let i = 0; i < this.particles.length; i ++){
let size = this.particles[i].radius ? this.particles[i].radius : 4;
graphics.lineStyle(4, DebugDefaults.verletParticle);
graphics.drawRect(this.particles[i].position.x, this.particles[i].position.y, size, size);
graphics.endFill();
}
}
}
}

View File

@@ -0,0 +1,8 @@
abstract class Constraint {
public composite: Composite;
public collidesWithColliders = true;
public abstract solve();
public debugRender(graphics: egret.Graphics) {}
}

View File

@@ -0,0 +1,54 @@
class DistanceConstraint extends Constraint {
public stiffness: number = 0;
public restingDistance: number = 0;
public tearSensitivity = Number.POSITIVE_INFINITY;
private _particleOne: Particle;
private _particleTwo: Particle;
constructor(first: Particle, second: Particle, stiffness: number, distance = -1){
super();
this._particleOne = first;
this._particleTwo = second;
this.stiffness = stiffness;
if (distance > -1){
this.restingDistance = distance;
}else{
this.restingDistance = Vector2.distance(first.position, second.position);
}
}
public setCollidesWithColliders(collidesWithColliders: boolean){
this.collidesWithColliders = collidesWithColliders;
return this;
}
public solve() {
let diff = Vector2.subtract(this._particleOne.position, this._particleTwo.position);
let d = diff.length();
let difference = (this.restingDistance - d) / d;
if (d / this.restingDistance > this.tearSensitivity){
this.composite.removeConstraint(this);
return;
}
let im1 = 1 / this._particleOne.mass;
let im2 = 1 / this._particleTwo.mass;
let scalarP1 = (im1 / (im1 + im2)) * this.stiffness;
let scalarP2 = this.stiffness - scalarP1;
this._particleOne.position = Vector2.add(this._particleOne.position, Vector2.multiply(diff, new Vector2(scalarP1 * difference, scalarP1 * difference)));
this._particleTwo.position = Vector2.subtract(this._particleTwo.position, Vector2.multiply(diff, new Vector2(scalarP2 * difference, scalarP2 * difference)))
}
public debugRender(graphics: egret.Graphics){
graphics.lineStyle(1, DebugDefaults.verletConstraintEdge);
graphics.lineTo(this._particleOne.position.x, this._particleOne.position.y);
graphics.lineTo(this._particleTwo.position.x, this._particleTwo.position.y);
graphics.endFill();
}
}

View File

@@ -0,0 +1,19 @@
class Particle {
public position: Vector2 = new Vector2(0, 0);
public lastPosition: Vector2 = new Vector2(0, 0);
public isPinned: boolean;
public pinnedPosition;
public acceleration: Vector2 = new Vector2(0, 0);
public mass: number = 1;
public radius: number = 0;
public collidesWithColliders = true;
constructor(position: Vector2){
this.position = position;
this.lastPosition = position;
}
public applyForce(force: Vector2){
this.acceleration = Vector2.add(this.acceleration, new Vector2(force.x / this.mass, force.y / this.mass));
}
}

View File

@@ -0,0 +1,7 @@
class SpatialHash {
public overlapCircle(circleCenter: Vector2, radius: number, results: any[], layerMask){
let resultCounter = 0;
return resultCounter;
}
}

View File

@@ -0,0 +1,113 @@
/**
* 基于verlet 物理引擎进行改造的物理引擎 ts重写
* https://github.com/subprotocol/verlet-js
*/
class VerletWorld {
public gravity: Vector2 = new Vector2(0, 980);
public maximumStepIterations = 5;
public constraintIterations = 3;
public simulationBounds: Rectangle;
private _leftOverTime: number = 0;
private _iterationSteps: number = 0;
private _fixedDeltaTime = 1 / 60;
private _composites: Composite[] = [];
private _fixedDeltaTimeSq: number;
private static _colliders = new Array(4);
constructor(simulationBounds?: Rectangle){
this.simulationBounds = simulationBounds;
this._fixedDeltaTimeSq = Math.pow(this._fixedDeltaTime, 2);
}
public update(){
this.updateTiming();
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);
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, -1);
}
}
}
}
private handleCollisions(p: Particle, collidesWithLayers: number){
let collidedCount = Physics.overlapCircleAll(p.position, p.radius, VerletWorld._colliders, collidesWithLayers);
// handle
}
private 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 debugRender(displayObject: egret.DisplayObject){
if (!displayObject)
return;
displayObject.stage.removeChildren();
for (let i = 0; i < this._composites.length; i ++){
let shape = new egret.Shape();
this._composites[i].debugRender(shape.graphics);
displayObject.stage.addChild(shape);
}
}
public addComposite<T extends Composite>(composite: T): T{
this._composites.push(composite);
return composite;
}
private updateTiming(){
this._leftOverTime += Time.deltaTime;
this._iterationSteps = Math.trunc(this._leftOverTime / this._fixedDeltaTime);
this._leftOverTime -= this._iterationSteps * this._fixedDeltaTime;
this._iterationSteps = Math.min(this._iterationSteps, this.maximumStepIterations);
}
}