Files
esengine/source/src/ECS/Components/Physics/ArcadeRigidbody.ts

241 lines
9.9 KiB
TypeScript
Raw Normal View History

module es {
/**
*
*
*/
export class ArcadeRigidbody extends Component implements IUpdatable {
/** 这个刚体的质量。质量为0则是一个不可移动的物体 */
public get mass(){
return this._mass;
}
public set mass(value: number){
this.setMass(value);
}
/**
* 0-101
*/
public get elasticity() {
return this._elasticity;
}
public set elasticiy(value: number){
this.setElasticity(value);
}
/**
* 0 - 101
*/
public get friction(){
return this._friction;
}
public set friction(value: number){
this.setFriction(value);
}
/**
* 0-9沿线glue摩擦力
*/
public get glue() {
return this._glue;
}
public set glue(value: number){
this.setGlue(value);
}
/**
* Physics.gravity
*/
public shouldUseGravity: boolean = true;
/**
*
*/
public velocity: Vector2;
/**
* 0
*/
public get isImmovable(){
return this._mass < 0.0001;
}
public _mass = 10;
public _elasticity = 0.5;
public _friction = 0.5;
public _glue = 0.01;
public _inverseMass;
public _collider: Collider;
constructor(){
super();
this._inverseMass = 1 / this._mass;
}
/**
* 0
* @param mass
*/
public setMass(mass: number): ArcadeRigidbody {
this._mass = MathHelper.clamp(mass, 0, Number.MAX_VALUE);
if (this._mass > 0.0001)
this._inverseMass = 1 / this._mass;
else
this._inverseMass = 0;
return this;
}
/**
* 0-101
* @param value
*/
public setElasticity(value: number): ArcadeRigidbody {
this._elasticity = MathHelper.clamp01(value);
return this;
}
/**
* 0 - 101
* @param value
*/
public setFriction(value: number): ArcadeRigidbody {
this._friction = MathHelper.clamp01(value);
return this;
}
/**
* 0-9沿线glue摩擦力
* @param value
*/
public setGlue(value: number): ArcadeRigidbody {
this._glue = MathHelper.clamp(value, 0, 10);
return this;
}
/**
* 100000使使
* @param force
*/
public addImpulse(force: Vector2) {
if (!this.isImmovable) {
this.velocity.add(new Vector2(force.x * 100000, force.y * 100000).multiply(new Vector2(this._inverseMass * Time.deltaTime)));
}
}
public onAddedToEntity(){
this._collider = this.entity.getComponent<es.Collider>(es.Collider);
if (this._collider == null) {
console.warn("ArcadeRigidbody 没有 Collider。ArcadeRigidbody需要一个Collider!");
}
}
public update(){
if (this.isImmovable || this._collider == null) {
this.velocity = Vector2.zero;
return;
}
if (this.shouldUseGravity)
this.velocity.add(Vector2.multiply(Physics.gravity, new Vector2(Time.deltaTime)));
this.entity.transform.position.add(Vector2.multiply(this.velocity, new Vector2(Time.deltaTime)));
let collisionResult = new CollisionResult();
// 捞取我们在新的位置上可能会碰撞到的任何东西
let neighbors = Physics.boxcastBroadphaseExcludingSelfNonRect(this._collider, this._collider.collidesWithLayers.value);
for (let neighbor of neighbors) {
// 如果邻近的对撞机是同一个实体,则忽略它
if (neighbor.entity == this.entity) {
continue;
}
if (this._collider.collidesWithNonMotion(neighbor, collisionResult)) {
// 如果附近有一个ArcadeRigidbody我们就会处理完整的碰撞响应。如果没有我们会根据附近是不可移动的来计算事情
let neighborRigidbody = neighbor.entity.getComponent<ArcadeRigidbody>(ArcadeRigidbody);
if (neighborRigidbody != null) {
this.processOverlap(neighborRigidbody, collisionResult.minimumTranslationVector);
this.processCollision(neighborRigidbody, collisionResult.minimumTranslationVector);
} else {
// 没有ArcadeRigidbody所以我们假设它是不动的只移动我们自己的
this.entity.transform.position.subtract(collisionResult.minimumTranslationVector);
let relativeVelocity = this.velocity.clone();
this.calculateResponseVelocity(relativeVelocity, collisionResult.minimumTranslationVector, relativeVelocity);
this.velocity.add(relativeVelocity);
}
}
}
}
/**
*
* @param other
* @param minimumTranslationVector
*/
public processOverlap(other: ArcadeRigidbody, minimumTranslationVector: Vector2) {
if (this.isImmovable) {
other.entity.transform.position.add(minimumTranslationVector);
}else if(other.isImmovable) {
this.entity.transform.position.subtract(minimumTranslationVector);
} else {
this.entity.transform.position.subtract(Vector2.multiply(minimumTranslationVector, Vector2Ext.halfVector()));
other.entity.transform.position.add(Vector2.multiply(minimumTranslationVector, Vector2Ext.halfVector()));
}
}
/**
*
* @param other
* @param minimumTranslationVector
*/
public processCollision(other: ArcadeRigidbody, minimumTranslationVector: Vector2) {
// 我们计算两个相撞物体的响应。
// 计算的基础是沿碰撞表面法线反射的物体的相对速度。
// 然后,响应的一部分会根据质量加到每个物体上
let relativeVelocity = Vector2.subtract(this.velocity, other.velocity);
this.calculateResponseVelocity(relativeVelocity, minimumTranslationVector, relativeVelocity);
// 现在,我们使用质量来线性缩放两个刚体上的响应
let totalinverseMass = this._inverseMass + other._inverseMass;
let ourResponseFraction = this._inverseMass / totalinverseMass;
let otherResponseFraction = other._inverseMass / totalinverseMass;
this.velocity.add(Vector2.multiply(relativeVelocity, new Vector2(ourResponseFraction)));
other.velocity.subtract(Vector2.multiply(relativeVelocity, new Vector2(otherResponseFraction)));
}
/**
* MTV之间的相对速度使
* @param relativeVelocity
* @param minimumTranslationVector
* @param responseVelocity
*/
public calculateResponseVelocity(relativeVelocity: Vector2, minimumTranslationVector: Vector2, responseVelocity: Vector2 = new Vector2()){
// 首先我们得到反方向的归一化MTV表面法线
let inverseMTV = Vector2.multiply(minimumTranslationVector, new Vector2(-1));
let normal = Vector2.normalize(inverseMTV);
// 速度是沿碰撞法线和碰撞平面分解的。
// 弹性将影响沿法线的响应(法线速度分量),摩擦力将影响速度的切向分量(切向速度分量)
let n = Vector2.dot(relativeVelocity, normal);
let normalVelocityComponent = Vector2.multiply(normal, new Vector2(n));
let tangentialVelocityComponent = Vector2.subtract(relativeVelocity, normalVelocityComponent);
if (n > 0)
normalVelocityComponent = Vector2.zero;
// 如果切向分量的平方幅度小于glue那么我们就把摩擦力提升到最大
let coefficientOfFriction = this._friction;
if (tangentialVelocityComponent.lengthSquared() < this._glue)
coefficientOfFriction = 1.01;
// 弹性影响速度的法向分量,摩擦力影响速度的切向分量
responseVelocity = Vector2.multiply(new Vector2(-(1 + this._elasticity)), normalVelocityComponent).subtract(Vector2.multiply(new Vector2(coefficientOfFriction), tangentialVelocityComponent));
}
}
}