优化triggerListener与ArrayUtils

This commit is contained in:
yhh
2020-11-26 17:26:49 +08:00
parent de3f7bff60
commit ae2cfdafdd
15 changed files with 418 additions and 261 deletions

View File

@@ -20,4 +20,26 @@ module es {
*/
onTriggerExit(other: Collider, local: Collider);
}
export class TriggerListenerHelper {
public static getITriggerListener(entity: Entity, components: ITriggerListener[]){
for (let i = 0; i < entity.components._components.length; i++) {
let component = entity.components._components.buffer[i];
if (isITriggerListener(component)) {
components.push(component);
}
}
for (let i = 0; i < entity.components._componentsToAdd.length; i++) {
let component = entity.components._componentsToAdd[i];
if (isITriggerListener(component)) {
components.push(component);
}
}
return components;
}
}
export var isITriggerListener = (props: any): props is ITriggerListener => typeof (props as ITriggerListener)['onTriggerEnter'] !== 'undefined';
}

View File

@@ -38,7 +38,7 @@ module es {
bounds.y += motion.y;
let neighbors = Physics.boxcastBroadphaseExcludingSelf(collider, bounds, collider.collidesWithLayers.value);
for (let j = 0; j < neighbors.length; j++) {
for (let j = 0; j < neighbors.size; j++) {
let neighbor = neighbors[j];
// 不检测触发器
if (neighbor.isTrigger)

View File

@@ -28,7 +28,8 @@ module es {
// 获取任何可能在新位置发生碰撞的东西
let neighbors = Physics.boxcastBroadphase(this._collider.bounds, this._collider.collidesWithLayers.value);
for (let neighbor of neighbors){
for (let i = 0; i < neighbors.size; i ++){
let neighbor = neighbors[i];
if (this._collider.overlaps(neighbor) && neighbor.enabled){
didCollide = true;
this.notifyTriggerListeners(this._collider, neighbor);
@@ -40,14 +41,14 @@ module es {
private notifyTriggerListeners(self: Collider, other: Collider) {
// 通知我们重叠的碰撞器实体上的任何侦听器
other.entity.getComponents("ITriggerListener", this._tempTriggerList);
TriggerListenerHelper.getITriggerListener(other.entity, this._tempTriggerList);
for (let i = 0; i < this._tempTriggerList.length; i++) {
this._tempTriggerList[i].onTriggerEnter(self, other);
}
this._tempTriggerList.length = 0;
// 通知此实体上的任何侦听器
this.entity.getComponents("ITriggerListener", this._tempTriggerList);
TriggerListenerHelper.getITriggerListener(this.entity, this._tempTriggerList);
for (let i = 0; i < this._tempTriggerList.length; i++) {
this._tempTriggerList[i].onTriggerEnter(other, self);
}

View File

@@ -204,7 +204,8 @@ module es {
}
protected async update() {
protected async update(currentTime?: number) {
if (currentTime != null) Time.update(currentTime);
if (this._scene != null) {
for (let i = this._globalManagers.length - 1; i >= 0; i--) {
if (this._globalManagers[i].enabled)

View File

@@ -335,7 +335,7 @@ module es {
* @param type
*/
public getOrCreateComponent<T extends Component>(type: T) {
let comp = this.components.getComponent<T>(type, true);
let comp = this.components.getComponent<T>(TypeUtils.getType(type), true);
if (!comp) {
comp = this.addComponent<T>(type);
}

View File

@@ -87,7 +87,7 @@ module es {
this._updatableComponents.remove(component);
if (Core.entitySystemsEnabled) {
this._entity.componentBits.set(ComponentTypeManager.getIndexFor(component["__proto__"]["constructor"]), false);
this._entity.componentBits.set(ComponentTypeManager.getIndexFor(TypeUtils.getType(component)), false);
this._entity.scene.entityProcessors.onComponentRemoved(this._entity);
}
}
@@ -101,7 +101,7 @@ module es {
this._updatableComponents.add(component);
if (Core.entitySystemsEnabled) {
this._entity.componentBits.set(ComponentTypeManager.getIndexFor(component["__proto__"]["constructor"]));
this._entity.componentBits.set(ComponentTypeManager.getIndexFor(TypeUtils.getType(component)));
this._entity.scene.entityProcessors.onComponentAdded(this._entity);
}
}
@@ -128,7 +128,7 @@ module es {
this._updatableComponents.add(component);
if (Core.entitySystemsEnabled) {
this._entity.componentBits.set(ComponentTypeManager.getIndexFor(component["__proto__"]["constructor"]));
this._entity.componentBits.set(ComponentTypeManager.getIndexFor(TypeUtils.getType(component)));
this._entity.scene.entityProcessors.onComponentAdded(this._entity);
}
@@ -167,7 +167,7 @@ module es {
this._updatableComponents.remove(component);
if (Core.entitySystemsEnabled) {
this._entity.componentBits.set(ComponentTypeManager.getIndexFor(component["__proto__"]["constructor"]), false);
this._entity.componentBits.set(ComponentTypeManager.getIndexFor(TypeUtils.getType(component)), false);
this._entity.scene.entityProcessors.onComponentRemoved(this._entity);
}

View File

@@ -4,10 +4,10 @@ module es {
*/
export class ColliderTriggerHelper {
private _entity: Entity;
/** 存储当前帧中发生的所有活动交对 */
private _activeTriggerIntersections: Pair<Collider>[] = [];
/** 存储前一帧的交对,以便我们可以在移动帧后检测出 */
private _previousTriggerIntersections: Pair<Collider>[] = [];
/** 存储当前帧中发生的所有活动交对 */
private _activeTriggerIntersections: Set<Pair<Collider>> = new Set();
/** 存储前一帧的交对,这样我们可以在移动这一帧后检测到退出 */
private _previousTriggerIntersections: Set<Pair<Collider>> = new Set();
private _tempTriggerList: ITriggerListener[] = [];
constructor(entity: Entity) {
@@ -15,32 +15,34 @@ module es {
}
/**
* 实体被移动后应该调用更新。它会处理碰撞器重叠的任何itriggerlistener。
* update应该在实体被移动后被调用它将处理任何与Colllider重叠的ITriggerListeners
* 它将处理任何与Collider重叠的ITriggerListeners。
*/
public update() {
// 对所有实体.colliders进行重叠检查这些实体.colliders是触发器与所有宽相碰撞器无论是否触发器。
// 任何重叠都会导致触发事件
let colliders = this._entity.getComponents(Collider);
for (let i = 0; i < colliders.length; i++) {
let collider = colliders[i];
let neighbors = Physics.boxcastBroadphase(collider.bounds, collider.collidesWithLayers);
for (let j = 0; j < neighbors.length; j++) {
for (let j = 0; j < neighbors.size; j++) {
let neighbor = neighbors[j];
// 我们至少需要一个碰撞器作为触发器
if (!collider.isTrigger && !neighbor.isTrigger)
continue;
if (collider.overlaps(neighbor)) {
let pair = new Pair<Collider>(collider, neighbor);
let shouldReportTriggerEvent = this._activeTriggerIntersections.findIndex(value => {
return value.first == pair.first && value.second == pair.second;
}) == -1 && this._previousTriggerIntersections.findIndex(value => {
return value.first == pair.first && value.second == pair.second;
}) == -1;
// 如果我们的某一个集合中已经有了这个对子(前一个或当前的触发交叉点),就不要调用输入事件了
let shouldReportTriggerEvent = !this._activeTriggerIntersections.has(pair) &&
!this._previousTriggerIntersections.has(pair);
if (shouldReportTriggerEvent)
this.notifyTriggerListeners(pair, true);
if (!this._activeTriggerIntersections.contains(pair))
this._activeTriggerIntersections.push(pair);
this._activeTriggerIntersections.add(pair);
}
}
}
@@ -51,31 +53,42 @@ module es {
}
private checkForExitedColliders() {
for (let i = 0; i < this._activeTriggerIntersections.length; i++) {
let index = this._previousTriggerIntersections.findIndex(value => {
if (value.first == this._activeTriggerIntersections[i].first && value.second == this._activeTriggerIntersections[i].second)
return true;
// 删除所有与此帧交互的触发器,留下我们退出的触发器
this.excepthWith(this._previousTriggerIntersections, this._activeTriggerIntersections);
return false;
});
if (index != -1)
this._previousTriggerIntersections.removeAt(index);
}
for (let i = 0; i < this._previousTriggerIntersections.length; i++) {
for (let i = 0; i < this._previousTriggerIntersections.size; i++) {
this.notifyTriggerListeners(this._previousTriggerIntersections[i], false)
}
this._previousTriggerIntersections.length = 0;
for (let i = 0; i < this._activeTriggerIntersections.length; i++) {
if (!this._previousTriggerIntersections.contains(this._activeTriggerIntersections[i])) {
this._previousTriggerIntersections.push(this._activeTriggerIntersections[i]);
this._previousTriggerIntersections.clear();
// 添加所有当前激活的触发器
this.unionWith(this._previousTriggerIntersections, this._activeTriggerIntersections);
// 清空活动集,为下一帧做准备
this._activeTriggerIntersections.clear();
}
private excepthWith(previous: Set<Pair<Collider>>, active: Set<Pair<Collider>>){
for (let i = 0; i < previous.size; i ++){
let previousDATA: Pair<Collider> = previous[i];
for (let j = 0; j < active.size; j ++){
let activeDATA: Pair<Collider> = active[j];
if (activeDATA.equals(previousDATA))
previous.delete(previousDATA);
}
}
this._activeTriggerIntersections.length = 0;
}
private unionWith(previous: Set<Pair<Collider>>, active: Set<Pair<Collider>>) {
for (let i = 0; i < this._activeTriggerIntersections.size; i ++) {
if (!this._previousTriggerIntersections.has(this._activeTriggerIntersections[i]))
this._previousTriggerIntersections.add(this._activeTriggerIntersections[i]);
}
}
private notifyTriggerListeners(collisionPair: Pair<Collider>, isEntering: boolean) {
collisionPair.first.entity.getComponents("ITriggerListener", this._tempTriggerList);
TriggerListenerHelper.getITriggerListener(collisionPair.first.entity, this._tempTriggerList);
for (let i = 0; i < this._tempTriggerList.length; i++) {
if (isEntering) {
this._tempTriggerList[i].onTriggerEnter(collisionPair.second, collisionPair.first);
@@ -86,7 +99,7 @@ module es {
this._tempTriggerList.length = 0;
if (collisionPair.second.entity) {
collisionPair.second.entity.getComponents("ITriggerListener", this._tempTriggerList);
TriggerListenerHelper.getITriggerListener(collisionPair.second.entity, this._tempTriggerList);
for (let i = 0; i < this._tempTriggerList.length; i++) {
if (isEntering) {
this._tempTriggerList[i].onTriggerEnter(collisionPair.first, collisionPair.second);

View File

@@ -1,11 +1,11 @@
///<reference path="./RaycastHit.ts" />
module es {
export class Physics {
public static _spatialHash: SpatialHash;
/** 调用reset并创建一个新的SpatialHash时使用的单元格大小 */
public static spatialHashCellSize = 100;
/** 接受layerMask的所有方法的默认值 */
public static readonly allLayers: number = -1;
private static _spatialHash: SpatialHash;
/**
* raycast是否检测配置为触发器的碰撞器
*/
@@ -120,13 +120,5 @@ module es {
return this._spatialHash.linecast(start, end, hits, layerMask);
}
/**
* debug绘制空间散列的内容
* @param secondsToDisplay
*/
public static debugDraw(secondsToDisplay) {
this._spatialHash.debugDraw(secondsToDisplay, 2);
}
}
}

View File

@@ -22,7 +22,7 @@ module es {
/**
* 用于返回冲突信息的共享HashSet
*/
public _tempHashSet: Collider[] = [];
public _tempHashSet: Set<Collider> = new Set<Collider>();
constructor(cellSize: number = 100) {
this._cellSize = cellSize;
@@ -53,8 +53,7 @@ module es {
for (let y = p1.y; y <= p2.y; y++) {
// 如果没有单元格,我们需要创建它
let c: Collider[] = this.cellAtPosition(x, y, true);
if (!c.firstOrDefault(c => c == collider))
c.push(collider);
c.push(collider);
}
}
}
@@ -92,29 +91,14 @@ module es {
this._cellDict.clear();
}
/**
* debug绘制空间散列的内容
* @param secondsToDisplay
* @param textScale
*/
public debugDraw(secondsToDisplay: number, textScale: number = 1) {
for (let x = this.gridBounds.x; x <= this.gridBounds.right; x++) {
for (let y = this.gridBounds.y; y <= this.gridBounds.bottom; y++) {
let cell = this.cellAtPosition(x, y);
if (cell && cell.length > 0)
this.debugDrawCellDetails(x, y, cell.length, secondsToDisplay, textScale);
}
}
}
/**
* 返回边框与单元格相交的所有对象
* @param bounds
* @param excludeCollider
* @param layerMask
*/
public aabbBroadphase(bounds: Rectangle, excludeCollider: Collider, layerMask: number): Collider[] {
this._tempHashSet.length = 0;
public aabbBroadphase(bounds: Rectangle, excludeCollider: Collider, layerMask: number): Set<Collider> {
this._tempHashSet.clear();
let p1 = this.cellCoords(bounds.x, bounds.y);
let p2 = this.cellCoords(bounds.right, bounds.bottom);
@@ -134,8 +118,7 @@ module es {
continue;
if (bounds.intersects(collider.bounds)) {
if (!this._tempHashSet.firstOrDefault(c => c == collider))
this._tempHashSet.push(collider);
this._tempHashSet.add(collider);
}
}
}
@@ -226,7 +209,7 @@ module es {
let resultCounter = 0;
let potentials = this.aabbBroadphase(bounds, null, layerMask);
for (let i = 0; i < potentials.length; i++) {
for (let i = 0; i < potentials.size; i++) {
let collider = potentials[i];
if (collider instanceof BoxCollider) {
results[resultCounter] = collider;
@@ -258,7 +241,7 @@ module es {
* @param x
* @param y
*/
private cellCoords(x: number, y: number): Vector2 {
public cellCoords(x: number, y: number): Vector2 {
return new Vector2(Math.floor(x * this._inverseCellSize), Math.floor(y * this._inverseCellSize));
}
@@ -269,7 +252,7 @@ module es {
* @param y
* @param createCellIfEmpty
*/
private cellAtPosition(x: number, y: number, createCellIfEmpty: boolean = false): Collider[] {
public cellAtPosition(x: number, y: number, createCellIfEmpty: boolean = false): Collider[] {
let cell: Collider[] = this._cellDict.tryGetValue(x, y);
if (!cell) {
if (createCellIfEmpty) {
@@ -279,10 +262,6 @@ module es {
}
return cell;
}
private debugDrawCellDetails(x: number, y: number, cellCount: number, secondsToDisplay = 0.5, textScale = 1) {
}
}
/**

View File

@@ -21,10 +21,9 @@ class ArrayUtils {
}
}
/**
* 执行插入排序
* @param ary
* @param ary
*/
public static insertionSort(ary: number[]): void {
let len: number = ary.length;
@@ -39,9 +38,9 @@ class ArrayUtils {
/**
* 执行二分搜索
* @param ary 搜索的数组(必须排序过)
* @param value 需要搜索的值
* @return 返回匹配结果的数组索引
* @param ary 搜索的数组(必须排序过)
* @param value 需要搜索的值
* @returns 返回匹配结果的数组索引
*/
public static binarySearch(ary: number[], value: number): number {
let startIndex: number = 0;
@@ -59,9 +58,8 @@ class ArrayUtils {
/**
* 返回匹配项的索引
* @param ary
* @param num
* @return 返回匹配项的索引
* @param ary
* @param num
*/
public static findElementIndex(ary: any[], num: any): any {
let len: number = ary.length;
@@ -74,8 +72,7 @@ class ArrayUtils {
/**
* 返回数组中最大值的索引
* @param ary
* @return 返回数组中最大值的索引
* @param ary
*/
public static getMaxElementIndex(ary: number[]): number {
let matchIndex: number = 0;
@@ -89,8 +86,7 @@ class ArrayUtils {
/**
* 返回数组中最小值的索引
* @param ary
* @return 返回数组中最小值的索引
* @param ary
*/
public static getMinElementIndex(ary: number[]): number {
let matchIndex: number = 0;
@@ -104,8 +100,10 @@ class ArrayUtils {
/**
* 返回一个"唯一性"数组
* @param ary 需要唯一性的数组
* @return 唯一性的数组
* @param ary 需要唯一性的数组
* @returns 唯一性的数组
*
* @tutorial
* 比如: [1, 2, 2, 3, 4]
* 返回: [1, 2, 3, 4]
*/
@@ -177,7 +175,7 @@ class ArrayUtils {
/**
* 清除列表
* @param ary 列表
* @param ary
*/
public static clearList(ary: any[]): void {
if (!ary) return;
@@ -197,12 +195,10 @@ class ArrayUtils {
return ary.slice(0, ary.length);
}
/**
* 判断2个数组是否相同
* @param ary1 数组1
* @param ary2 数组2
* @return 是否相同
* @param ary1 数组1
* @param ary2 数组2
*/
public static equals(ary1: number[], ary2: number[]): Boolean {
if (ary1 == ary2) return true;
@@ -215,12 +211,12 @@ class ArrayUtils {
return true;
}
/**
* 根据索引插入元素,索引和索引后的元素都向后移动一位
* @param index 插入索引
* @param value 插入的元素
* @return 插入的元素 未插入则返回空
* @param ary
* @param index 插入索引
* @param value 插入的元素
* @returns 插入的元素 未插入则返回空
*/
public static insert(ary: any[], index: number, value: any): any {
if (!ary) return null;
@@ -237,4 +233,66 @@ class ArrayUtils {
}
return value;
}
/**
* 打乱数组 FisherYates shuffle
* @param list
*/
public static shuffle<T>(list: T[]) {
let n = list.length;
while (n > 1) {
n--;
let k = RandomUtils.randint(0, n + 1);
let value: T = list[k];
list[k] = list[n];
list[n] = value;
}
}
/**
* 如果项目已经在列表中返回false如果成功添加返回true
* @param list
* @param item
*/
public static addIfNotPresent<T>(list: T[], item: T) {
if (list.contains(item))
return false;
list.push(item);
return true;
}
/**
* 返回列表中的最后一项。列表中至少应该有一个项目
* @param list
*/
public static lastItem<T>(list: T[]) {
return list[list.length - 1];
}
/**
* 从列表中随机获取一个项目。不清空检查列表!
* @param list
*/
public static randomItem<T>(list: T[]) {
return list[RandomUtils.randint(0, list.length)];
}
/**
* 从列表中随机获取物品。不清空检查列表也不验证列表数是否大于项目数。返回的List可以通过ListPool.free放回池中
* @param list
* @param itemCount 从列表中返回的随机项目的数量
*/
public static randomItems<T>(list: T[], itemCount: number){
let set = new Set<T>();
while (set.size != itemCount) {
let item = this.randomItem(list);
if (!set.has(item))
set.add(item);
}
let items = es.ListPool.obtain<T>();
set.forEach(value => items.push(value));
return items;
}
}

View File

@@ -25,7 +25,6 @@ module es {
*/
public static union(first: Rectangle, point: Vector2) {
let rect = new Rectangle(point.x, point.y, 0, 0);
// let rectResult = first.union(rect);
let result = new Rectangle();
result.x = Math.min(first.x, rect.x);
result.y = Math.min(first.y, rect.y);

View File

@@ -0,0 +1,7 @@
module es {
export class TypeUtils {
public static getType(obj: any){
return obj["__proto__"]["constructor"];
}
}
}