新增mover移动器组件 用于处理itriggerListener接口碰撞信息
This commit is contained in:
@@ -57,6 +57,10 @@ abstract class Component {
|
||||
|
||||
}
|
||||
|
||||
public debugRender(){
|
||||
|
||||
}
|
||||
|
||||
/** 内部使用 运行时不应该调用 */
|
||||
public registerComponent(){
|
||||
this.entity.componentBits.set(ComponentTypeManager.getIndexFor(this), false);
|
||||
|
||||
@@ -46,4 +46,17 @@ class BoxCollider extends Collider {
|
||||
this.shape = new Box(1, 1);
|
||||
this._colliderRequiresAutoSizing = true;
|
||||
}
|
||||
|
||||
public setSize(width: number, height: number){
|
||||
this._colliderRequiresAutoSizing = false;
|
||||
let box = this.shape as Box;
|
||||
if (width != box.width || height != box.height){
|
||||
box.updateBox(width, height);
|
||||
this._isPositionDirty = true;
|
||||
if (this.entity && this._isParentEntityAddedToScene)
|
||||
Physics.updateCollider(this);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -7,9 +7,9 @@ abstract class Collider extends Component{
|
||||
public collidesWithLayers = Physics.allLayers;
|
||||
|
||||
public _localOffsetLength: number;
|
||||
public _isPositionDirty = true;
|
||||
public _isRotationDirty = true;
|
||||
protected _isParentEntityAddedToScene;
|
||||
protected _isPositionDirty = true;
|
||||
protected _isRotationDirty = true;
|
||||
protected _colliderRequiresAutoSizing;
|
||||
protected _localOffset: Vector2;
|
||||
protected _isColliderRegisterd;
|
||||
@@ -59,6 +59,46 @@ abstract class Collider extends Component{
|
||||
return this.shape.overlaps(other.shape);
|
||||
}
|
||||
|
||||
public collidesWith(collider: Collider, motion: Vector2){
|
||||
let oldPosition = this.shape.position;
|
||||
this.shape.position = Vector2.add(this.shape.position, motion);
|
||||
|
||||
let result = this.shape.collidesWithShape(collider.shape);
|
||||
if (result)
|
||||
result.collider = collider;
|
||||
|
||||
this.shape.position = oldPosition;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public onAddedToEntity(){
|
||||
if (this._colliderRequiresAutoSizing){
|
||||
if (!(this instanceof BoxCollider)){
|
||||
console.error("Only box and circle colliders can be created automatically");
|
||||
}
|
||||
|
||||
let renderable = this.entity.getComponent<RenderableComponent>(RenderableComponent);
|
||||
if (!renderable){
|
||||
let renderbaleBounds = renderable.bounds;
|
||||
|
||||
let width = renderbaleBounds.width / this.entity.transform.scale.x;
|
||||
let height = renderbaleBounds.height / this.entity.transform.scale.y;
|
||||
|
||||
if (this instanceof BoxCollider){
|
||||
let boxCollider = this as BoxCollider;
|
||||
boxCollider.width = width;
|
||||
boxCollider.height = height;
|
||||
|
||||
this.localOffset = Vector2.subtract(renderbaleBounds.center, this.entity.transform.position);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this._isParentEntityAddedToScene = true;
|
||||
this.registerColliderWithPhysicsSystem();
|
||||
}
|
||||
|
||||
public onEntityTransformChanged(comp: ComponentTransform){
|
||||
switch (comp){
|
||||
case ComponentTransform.position:
|
||||
|
||||
62
source/src/ECS/Components/Physics/Mover.ts
Normal file
62
source/src/ECS/Components/Physics/Mover.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
class Mover extends Component {
|
||||
private _triggerHelper: ColliderTriggerHelper;
|
||||
|
||||
public onAddedToEntity(){
|
||||
this._triggerHelper = new ColliderTriggerHelper(this.entity);
|
||||
}
|
||||
|
||||
public calculateMovement(motion: Vector2){
|
||||
let collisionResult = new CollisionResult();
|
||||
|
||||
if (!this.entity.getComponent(Collider) || !this._triggerHelper){
|
||||
return null;
|
||||
}
|
||||
|
||||
let colliders: Collider[] = this.entity.getComponents(Collider);
|
||||
for (let i = 0; i < colliders.length; i ++){
|
||||
let collider = colliders[i];
|
||||
|
||||
if (collider.isTrigger)
|
||||
continue;
|
||||
|
||||
let bounds = collider.bounds;
|
||||
bounds.x += motion.x;
|
||||
bounds.y += motion.y;
|
||||
let neighbors = Physics.boxcastBroadphaseExcludingSelf(collider, bounds, collider.collidesWithLayers);
|
||||
|
||||
for (let j = 0; j < neighbors.length; j ++){
|
||||
let neighbor = neighbors[j];
|
||||
if (neighbor.isTrigger)
|
||||
continue;
|
||||
|
||||
let _internalcollisionResult = collider.collidesWith(neighbor, motion);
|
||||
if (_internalcollisionResult){
|
||||
motion = Vector2.subtract(motion, _internalcollisionResult.minimumTranslationVector);
|
||||
|
||||
if (_internalcollisionResult.collider){
|
||||
collisionResult = _internalcollisionResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ListPool.free(colliders);
|
||||
|
||||
return collisionResult;
|
||||
}
|
||||
|
||||
public applyMovement(motion: Vector2){
|
||||
this.entity.transform.position = Vector2.add(this.entity.transform.position, motion);
|
||||
|
||||
if (this._triggerHelper)
|
||||
this._triggerHelper.update();
|
||||
}
|
||||
|
||||
public move(motion: Vector2){
|
||||
let collisionResult = this.calculateMovement(motion);
|
||||
|
||||
this.applyMovement(motion);
|
||||
|
||||
return collisionResult;
|
||||
}
|
||||
}
|
||||
@@ -212,7 +212,7 @@ class Entity {
|
||||
return this.components.getComponent(type, false) as T;
|
||||
}
|
||||
|
||||
public getComponents(typeName: string, componentList?){
|
||||
public getComponents(typeName: string | any, componentList?){
|
||||
return this.components.getComponents(typeName, componentList);
|
||||
}
|
||||
|
||||
|
||||
@@ -120,20 +120,26 @@ class ComponentList {
|
||||
return null;
|
||||
}
|
||||
|
||||
public getComponents(typeName: string, components?){
|
||||
public getComponents(typeName: string | any, components?){
|
||||
if (!components)
|
||||
components = [];
|
||||
|
||||
for (let i = 0; i < this._components.length; i ++){
|
||||
let component = this._components[i];
|
||||
if (egret.is(component, typeName))
|
||||
if (typeof(typeName) == "string" && egret.is(component, typeName))
|
||||
components.push(component);
|
||||
else if (component instanceof typeName) {
|
||||
components.push(component);
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < this._componentsToAdd.length; i ++){
|
||||
let component = this._componentsToAdd[i];
|
||||
if (egret.is(component, typeName))
|
||||
if (typeof(typeName) == "string" && egret.is(component, typeName))
|
||||
components.push(component);
|
||||
else if (component instanceof typeName){
|
||||
components.push(component);
|
||||
}
|
||||
}
|
||||
|
||||
return components;
|
||||
|
||||
@@ -23,6 +23,10 @@ class Rectangle {
|
||||
return this.y + this.height;
|
||||
}
|
||||
|
||||
public get center(){
|
||||
return new Vector2(this.x + (this.width / 2), this.y + (this.height / 2));
|
||||
}
|
||||
|
||||
public get location() {
|
||||
return new Vector2(this.x, this.y);
|
||||
}
|
||||
@@ -140,4 +144,35 @@ class Rectangle {
|
||||
this.height = maxY - minY;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 给定多边形的点,计算边界
|
||||
* @param points
|
||||
*/
|
||||
public static rectEncompassingPoints(points: Vector2[]){
|
||||
let minX = Number.POSITIVE_INFINITY;
|
||||
let minY = Number.POSITIVE_INFINITY;
|
||||
let maxX = Number.NEGATIVE_INFINITY;
|
||||
let maxY = Number.NEGATIVE_INFINITY;
|
||||
|
||||
for (let i = 0; i < points.length; i ++){
|
||||
let pt = points[i];
|
||||
|
||||
if (pt.x < minX){
|
||||
minX = pt.x;
|
||||
}
|
||||
if (pt.x > maxX){
|
||||
maxX = pt.x;
|
||||
}
|
||||
|
||||
if (pt.y < minY){
|
||||
minY = pt.y;
|
||||
}
|
||||
if (pt.y > maxY){
|
||||
maxY = pt.y;
|
||||
}
|
||||
}
|
||||
|
||||
return this.fromMinMax(minX, minY, maxX, maxY);
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ class ColliderTriggerHelper {
|
||||
* 实体被移动后,应该调用更新。它会处理碰撞器重叠的任何itriggerlistener。
|
||||
*/
|
||||
public update() {
|
||||
let colliders = this._entity.getComponents("Collider");
|
||||
let colliders = this._entity.getComponents(Collider);
|
||||
for (let i = 0; i < colliders.length; i++) {
|
||||
let collider = colliders[i];
|
||||
|
||||
|
||||
@@ -11,6 +11,10 @@ class Physics {
|
||||
return this._spatialHash.aabbBroadphase(rect, null, layerMask);
|
||||
}
|
||||
|
||||
public static boxcastBroadphaseExcludingSelf(collider: Collider, rect: Rectangle, layerMask = this.allLayers){
|
||||
return this._spatialHash.aabbBroadphase(rect, collider, layerMask);
|
||||
}
|
||||
|
||||
public static addCollider(collider: Collider){
|
||||
Physics._spatialHash.register(collider);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
class CollisionResult {
|
||||
public collider: Collider;
|
||||
public minimumTranslationVector: Vector2;
|
||||
public normal: Vector2;
|
||||
public point: Vector2;
|
||||
|
||||
@@ -42,13 +42,29 @@ class Polygon extends Shape {
|
||||
this.points = points;
|
||||
this.recalculateCenterAndEdgeNormals();
|
||||
|
||||
this._originalPoints = new Array(points.length);
|
||||
this._originalPoints = points;
|
||||
this._originalPoints = new Array(this.points.length);
|
||||
this.points.forEach(point => this._originalPoints.push(point));
|
||||
}
|
||||
|
||||
public collidesWithShape(other: Shape){
|
||||
if (other instanceof Polygon)
|
||||
return ShapeCollisions.polygonToPolygon(this, other);
|
||||
let result = new CollisionResult();
|
||||
if (other instanceof Polygon){
|
||||
result = ShapeCollisions.polygonToPolygon(this, other);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
if (other instanceof Circle){
|
||||
result = ShapeCollisions.circleToPolygon(other, this);
|
||||
if (result){
|
||||
result.invertResult();
|
||||
return result;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
throw new Error(`overlaps of Polygon to ${other} are not supported`);
|
||||
}
|
||||
|
||||
public recalculateCenterAndEdgeNormals() {
|
||||
@@ -133,6 +149,11 @@ class Polygon extends Shape {
|
||||
return isInside;
|
||||
}
|
||||
|
||||
/**
|
||||
* 建立一个对称的多边形(六边形,八角形,n角形)并返回点
|
||||
* @param vertCount
|
||||
* @param radius
|
||||
*/
|
||||
public static buildSymmertricalPolygon(vertCount: number, radius: number) {
|
||||
let verts = new Array(vertCount);
|
||||
|
||||
@@ -146,5 +167,42 @@ class Polygon extends Shape {
|
||||
|
||||
public recalculateBounds(collider: Collider) {
|
||||
this.center = collider.localOffset;
|
||||
|
||||
if (collider.shouldColliderScaleAndRotationWithTransform){
|
||||
let hasUnitScale = true;
|
||||
let tempMat: Matrix2D;
|
||||
let combinedMatrix = Matrix2D.createTranslation(-this._polygonCenter.x, -this._polygonCenter.y);
|
||||
|
||||
if (collider.entity.transform.scale != Vector2.one){
|
||||
tempMat = Matrix2D.createScale(collider.entity.transform.scale.x, collider.entity.transform.scale.y);
|
||||
combinedMatrix = Matrix2D.multiply(combinedMatrix, tempMat);
|
||||
|
||||
hasUnitScale = false;
|
||||
let scaledOffset = Vector2.multiply(collider.localOffset, collider.entity.transform.scale);
|
||||
this.center = scaledOffset;
|
||||
}
|
||||
|
||||
if (collider.entity.transform.rotation != 0){
|
||||
tempMat = Matrix2D.createRotation(collider.entity.transform.rotation);
|
||||
combinedMatrix = Matrix2D.multiply(combinedMatrix, tempMat);
|
||||
|
||||
let offsetAngle = Math.atan2(collider.localOffset.y, collider.localOffset.x) * MathHelper.Rad2Deg;
|
||||
let offsetLength = hasUnitScale ? collider._localOffsetLength : (Vector2.multiply(collider.localOffset, collider.entity.transform.scale)).length();
|
||||
this.center = MathHelper.pointOnCirlce(Vector2.zero, offsetLength, collider.entity.transform.rotationDegrees + offsetAngle);
|
||||
}
|
||||
|
||||
tempMat = Matrix2D.createTranslation(this._polygonCenter.x, this._polygonCenter.y);
|
||||
combinedMatrix = Matrix2D.multiply(combinedMatrix, tempMat);
|
||||
|
||||
Vector2Ext.transform(this._originalPoints, combinedMatrix, this.points);
|
||||
this.isUnrotated = collider.entity.transform.rotation == 0;
|
||||
|
||||
if (collider._isRotationDirty)
|
||||
this._areEdgeNormalsDirty = true;
|
||||
}
|
||||
|
||||
this.position = Vector2.add(collider.entity.transform.position, this.center);
|
||||
this.bounds = Rectangle.rectEncompassingPoints(this.points);
|
||||
this.bounds.location = Vector2.add(this.bounds.location, this.position);
|
||||
}
|
||||
}
|
||||
@@ -6,4 +6,5 @@ abstract class Shape {
|
||||
public abstract recalculateBounds(collider: Collider);
|
||||
public abstract pointCollidesWithShape(point: Vector2): CollisionResult;
|
||||
public abstract overlaps(other: Shape);
|
||||
public abstract collidesWithShape(other: Shape): CollisionResult;
|
||||
}
|
||||
@@ -5,7 +5,7 @@ class Vector2Ext {
|
||||
* @param center
|
||||
* @param c
|
||||
*/
|
||||
public static isTriangleCCW(a: Vector2, center: Vector2, c: Vector2){
|
||||
public static isTriangleCCW(a: Vector2, center: Vector2, c: Vector2) {
|
||||
return this.cross(Vector2.subtract(center, a), Vector2.subtract(c, center)) < 0;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ class Vector2Ext {
|
||||
* @param u
|
||||
* @param v
|
||||
*/
|
||||
public static cross(u: Vector2, v: Vector2){
|
||||
public static cross(u: Vector2, v: Vector2) {
|
||||
return u.y * v.x - u.x * v.y;
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ class Vector2Ext {
|
||||
* @param first
|
||||
* @param second
|
||||
*/
|
||||
public static perpendicular(first: Vector2, second: Vector2){
|
||||
public static perpendicular(first: Vector2, second: Vector2) {
|
||||
return new Vector2(-1 * (second.y - first.y), second.x - first.x);
|
||||
}
|
||||
|
||||
@@ -32,9 +32,9 @@ class Vector2Ext {
|
||||
* 标准化把向量弄乱了
|
||||
* @param vec
|
||||
*/
|
||||
public static normalize(vec: Vector2){
|
||||
public static normalize(vec: Vector2) {
|
||||
let magnitude = Math.sqrt((vec.x * vec.x) + (vec.y * vec.y));
|
||||
if (magnitude > MathHelper.Epsilon){
|
||||
if (magnitude > MathHelper.Epsilon) {
|
||||
vec = Vector2.divide(vec, new Vector2(magnitude));
|
||||
} else {
|
||||
vec.x = vec.y = 0;
|
||||
@@ -42,4 +42,19 @@ class Vector2Ext {
|
||||
|
||||
return vec;
|
||||
}
|
||||
|
||||
public static transformA(sourceArray: Vector2[], sourceIndex: number, matrix: Matrix2D,
|
||||
destinationArray: Vector2[], destinationIndex: number, length: number) {
|
||||
for (let i = 0; i < length; i ++){
|
||||
let position = sourceArray[sourceIndex + i];
|
||||
let destination = destinationArray[destinationIndex + 1];
|
||||
destination.x = (position.x * matrix.m11) + (position.y * matrix.m21) + matrix.m31;
|
||||
destination.y = (position.x * matrix.m12) + (position.y * matrix.m22) + matrix.m32;
|
||||
destinationArray[destinationIndex + i] = destination;
|
||||
}
|
||||
}
|
||||
|
||||
public static transform(sourceArray: Vector2[], matrix: Matrix2D, destinationArray: Vector2[]) {
|
||||
this.transformA(sourceArray, 0, matrix, destinationArray, 0, sourceArray.length);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user