优化spriteRenderer渲染方法

This commit is contained in:
yhh
2020-07-08 18:12:17 +08:00
parent 299c1b8e7d
commit aea50926a9
25 changed files with 607 additions and 567 deletions

View File

@@ -2,13 +2,16 @@ class Physics {
private static _spatialHash: SpatialHash;
/** 调用reset并创建一个新的SpatialHash时使用的单元格大小 */
public static spatialHashCellSize = 100;
/** 接受layerMask的所有方法的默认值 */
public static readonly allLayers: number = -1;
public static reset(){
this._spatialHash = new SpatialHash(this.spatialHashCellSize);
}
/**
* 从SpatialHash中移除所有碰撞器
*/
public static clear(){
this._spatialHash.clear();
}
@@ -26,14 +29,26 @@ class Physics {
return this._spatialHash.aabbBroadphase(rect, collider, layerMask);
}
/**
* 将对撞机添加到物理系统中
* @param collider
*/
public static addCollider(collider: Collider){
Physics._spatialHash.register(collider);
}
/**
* 从物理系统中移除对撞机
* @param collider
*/
public static removeCollider(collider: Collider){
Physics._spatialHash.remove(collider);
}
/**
* 更新物理系统中对撞机的位置。这实际上只是移除然后重新添加带有新边界的碰撞器
* @param collider
*/
public static updateCollider(collider: Collider){
this._spatialHash.remove(collider);
this._spatialHash.register(collider);

View File

@@ -1,4 +1,7 @@
///<reference path="./Polygon.ts" />
/**
* 多边形的特殊情况。在进行SAT碰撞检查时我们只需要检查2个轴而不是8个轴
*/
class Box extends Polygon {
public width: number;
public height: number;
@@ -9,7 +12,13 @@ class Box extends Polygon {
this.height = height;
}
/**
* 在一个盒子的形状中建立多边形需要的点的帮助方法
* @param width
* @param height
*/
private static buildBox(width: number, height: number): Vector2[]{
// 我们在(0,0)的中心周围创建点
let halfWidth = width / 2;
let halfHeight = height / 2;
let verts = new Array(4);
@@ -21,11 +30,8 @@ class Box extends Polygon {
return verts;
}
/**
*
* @param other
*/
public overlaps(other: Shape){
// 特殊情况这一个高性能方式实现其他情况则使用polygon方法检测
if (this.isUnrotated){
if (other instanceof Box && other.isUnrotated)
return this.bounds.intersects(other.bounds);
@@ -37,11 +43,8 @@ class Box extends Polygon {
return super.overlaps(other);
}
/**
*
* @param other
*/
public collidesWithShape(other: Shape){
// 特殊情况这一个高性能方式实现其他情况则使用polygon方法检测
if (this.isUnrotated && other instanceof Box && other.isUnrotated){
return ShapeCollisions.boxToBox(this, other);
}
@@ -51,10 +54,16 @@ class Box extends Polygon {
return super.collidesWithShape(other);
}
/**
* 更新框点,重新计算中心,设置宽度/高度
* @param width
* @param height
*/
public updateBox(width: number, height: number){
this.width = width;
this.height = height;
// 我们在(0,0)的中心周围创建点
let halfWidth = width / 2;
let halfHeight = height / 2;
@@ -69,7 +78,7 @@ class Box extends Polygon {
public containsPoint(point: Vector2){
if (this.isUnrotated)
return this.bounds.contains(point);
return this.bounds.containsInVec(point);
return super.containsPoint(point);
}

View File

@@ -32,7 +32,7 @@ class Circle extends Shape {
public recalculateBounds(collider: Collider) {
this.center = collider.localOffset;
if (collider.shouldColliderScaleAndRotationWithTransform) {
if (collider.shouldColliderScaleAndRotateWithTransform) {
let scale = collider.entity.scale;
let hasUnitScale = scale.x == 1 && scale.y == 1;
let maxScale = Math.max(scale.x, scale.y);

View File

@@ -103,6 +103,13 @@ class Polygon extends Shape {
return new Vector2(x / points.length, y / points.length);
}
/**
* 迭代多边形的所有边,并得到任意边上离点最近的点。
* 通过最近点的平方距离和它所在的边的法线返回。
* 点应该在多边形的空间中(点-多边形.位置)
* @param points
* @param point
*/
public static getClosestPointOnPolygonToPoint(points: Vector2[], point: Vector2): { closestPoint, distanceSquared, edgeNormal } {
let distanceSquared = Number.MAX_VALUE;
let edgeNormal = new Vector2(0, 0);
@@ -121,6 +128,7 @@ class Polygon extends Shape {
distanceSquared = tempDistanceSquared;
closestPoint = closest;
// 求直线的法线
let line = Vector2.subtract(points[j], points[i]);
edgeNormal.x = -line.y;
edgeNormal.y = line.x;
@@ -136,7 +144,13 @@ class Polygon extends Shape {
return ShapeCollisions.pointToPoly(point, this);
}
/**
* 本质上,这个算法所做的就是从一个点发射一条射线。
* 如果它与奇数条多边形边相交,我们就知道它在多边形内部。
* @param point
*/
public containsPoint(point: Vector2) {
// 将点归一化到多边形坐标空间中
point = Vector2.subtract(point, this.position);
let isInside = false;
@@ -171,7 +185,7 @@ class Polygon extends Shape {
// 如果我们没有旋转或不关心TRS我们使用localOffset作为中心我们会从那开始
this.center = collider.localOffset;
if (collider.shouldColliderScaleAndRotationWithTransform){
if (collider.shouldColliderScaleAndRotateWithTransform){
let hasUnitScale = true;
let tempMat: Matrix2D;
let combinedMatrix = Matrix2D.createTranslation(-this._polygonCenter.x, -this._polygonCenter.y);
@@ -188,7 +202,7 @@ class Polygon extends Shape {
}
if (collider.entity.rotation != 0){
tempMat = Matrix2D.createRotation(collider.entity.rotation);
tempMat = Matrix2D.createRotation(collider.entity.rotation, tempMat);
combinedMatrix = Matrix2D.multiply(combinedMatrix, tempMat);
// 为了处理偏移原点的旋转我们只需要将圆心在(0,0)附近移动我们的偏移使角度为0
@@ -204,10 +218,6 @@ class Polygon extends Shape {
// 最后变换原始点
Vector2Ext.transform(this._originalPoints, combinedMatrix, this.points);
this.isUnrotated = collider.entity.rotation == 0;
// 如果旋转的话,我们只需要重建边的法线
if (collider._isRotationDirty)
this._areEdgeNormalsDirty = true;
}
this.position = Vector2.add(collider.entity.position, this.center);

View File

@@ -274,7 +274,7 @@ class ShapeCollisions {
let result = new CollisionResult();
let minkowskiDiff = this.minkowskiDifference(first, second);
if (minkowskiDiff.contains(new Vector2(0, 0))){
if (minkowskiDiff.containsInVec(new Vector2(0, 0))){
// 计算MTV。如果它是零我们就可以称它为非碰撞
result.minimumTranslationVector = minkowskiDiff.getClosestPointOnBoundsToOrigin();

View File

@@ -2,10 +2,15 @@ class SpatialHash {
public gridBounds: Rectangle = new Rectangle();
private _raycastParser: RaycastResultParser;
/** 散列中每个单元格的大小 */
private _cellSize: number;
/** 1除以单元格大小。缓存结果因为它被大量使用。 */
private _inverseCellSize: number;
private _overlapTestCircle: Circle;
/** 缓存的循环用于重叠检查 */
private _overlapTestCircle: Circle = new Circle(0);
/** 用于返回冲突信息的共享HashSet */
private _tempHashSet: Collider[] = [];
/** 保存所有数据的字典 */
private _cellDict: NumberDictionary = new NumberDictionary();
constructor(cellSize: number = 100) {
@@ -14,6 +19,10 @@ class SpatialHash {
this._raycastParser = new RaycastResultParser();
}
/**
* 从SpatialHash中删除对象
* @param collider
*/
public remove(collider: Collider) {
let bounds = collider.registeredPhysicsBounds;
let p1 = this.cellCoords(bounds.x, bounds.y);
@@ -21,6 +30,7 @@ class SpatialHash {
for (let x = p1.x; x <= p2.x; x++) {
for (let y = p1.y; y <= p2.y; y++) {
// 单元格应该始终存在,因为这个碰撞器应该在所有查询的单元格中
let cell = this.cellAtPosition(x, y);
if (!cell)
console.error(`removing Collider [${collider}] from a cell that it is not present in`);
@@ -30,22 +40,28 @@ class SpatialHash {
}
}
/**
* 将对象添加到SpatialHash
* @param collider
*/
public register(collider: Collider) {
let bounds = collider.bounds;
collider.registeredPhysicsBounds = bounds;
let p1 = this.cellCoords(bounds.x, bounds.y);
let p2 = this.cellCoords(bounds.right, bounds.bottom);
if (!this.gridBounds.contains(new Vector2(p1.x, p1.y))) {
// 更新边界以跟踪网格大小
if (!this.gridBounds.containsInVec(new Vector2(p1.x, p1.y))) {
this.gridBounds = RectangleExt.union(this.gridBounds, p1);
}
if (!this.gridBounds.contains(new Vector2(p2.x, p2.y))) {
if (!this.gridBounds.containsInVec(new Vector2(p2.x, p2.y))) {
this.gridBounds = RectangleExt.union(this.gridBounds, p2);
}
for (let x = p1.x; x <= p2.x; x++) {
for (let y = p1.y; y <= p2.y; y++) {
// 如果没有单元格,我们需要创建它
let c = this.cellAtPosition(x, y, true);
c.push(collider);
}
@@ -56,6 +72,13 @@ class SpatialHash {
this._cellDict.clear();
}
/**
* 获取位于指定圆内的所有碰撞器
* @param circleCenter
* @param radius
* @param results
* @param layerMask
*/
public overlapCircle(circleCenter: Vector2, radius: number, results: Collider[], layerMask) {
let bounds = new Rectangle(circleCenter.x - radius, circleCenter.y - radius, radius * 2, radius * 2);
@@ -82,6 +105,12 @@ class SpatialHash {
return resultCounter;
}
/**
* 返回边框与单元格相交的所有对象
* @param bounds
* @param excludeCollider
* @param layerMask
*/
public aabbBroadphase(bounds: Rectangle, excludeCollider: Collider, layerMask: number) {
this._tempHashSet.length = 0;
@@ -94,9 +123,11 @@ class SpatialHash {
if (!cell)
continue;
// 当cell不为空。循环并取回所有碰撞器
for (let i = 0; i < cell.length; i++) {
let collider = cell[i];
// 如果它是自身或者如果它不匹配我们的层掩码 跳过这个碰撞器
if (collider == excludeCollider || !Flags.isFlagSet(layerMask, collider.physicsLayer))
continue;
@@ -111,6 +142,13 @@ class SpatialHash {
return {tempHashSet: this._tempHashSet, bounds: bounds};
}
/**
* 获取世界空间x,y值的单元格。
* 如果单元格为空且createCellIfEmpty为true则会创建一个新的单元格
* @param x
* @param y
* @param createCellIfEmpty
*/
private cellAtPosition(x: number, y: number, createCellIfEmpty: boolean = false) {
let cell: Collider[] = this._cellDict.tryGetValue(x, y);
if (!cell) {
@@ -122,6 +160,11 @@ class SpatialHash {
return cell;
}
/**
* 获取单元格的x,y值作为世界空间的x,y值
* @param x
* @param y
*/
private cellCoords(x: number, y: number): Vector2 {
return new Vector2(Math.floor(x * this._inverseCellSize), Math.floor(y * this._inverseCellSize));
}
@@ -131,6 +174,10 @@ class RaycastResultParser {
}
/**
* 包装一个Unit32列表碰撞器字典
* 它的主要目的是将int、int x、y坐标散列到单个Uint32键中使用O(1)查找。
*/
class NumberDictionary {
private _store: Map<number, Collider[]> = new Map<number, Collider[]>();
@@ -154,6 +201,10 @@ class NumberDictionary {
this._store.set(this.getKey(x, y), list);
}
/**
* 使用蛮力方法从字典存储列表中移除碰撞器
* @param obj
*/
public remove(obj: Collider) {
this._store.forEach(list => {
if (list.contains(obj))
@@ -165,6 +216,9 @@ class NumberDictionary {
return this._store.get(this.getKey(x, y));
}
/**
* 清除字典数据
*/
public clear() {
this._store.clear();
}