优化实体列表add/remove效率。性能大幅度提高

This commit is contained in:
yhh
2021-04-16 17:48:13 +08:00
parent 5a6d1c72fe
commit 17b36f01b0
14 changed files with 1494 additions and 211 deletions

View File

@@ -0,0 +1,30 @@
module es {
export class ComponentType {
public static INDEX = 0;
private index_ = 0;
private type_: Class;
constructor(type: Class, index?: number) {
if (index !== undefined) {
this.index_ = ComponentType.INDEX++;
} else {
this.index_ = index;
}
this.type_ = type;
}
public getName(): string {
return getClassName(this.type_);
}
public getIndex(): number {
return this.index_;
}
public toString(): string {
return "ComponentType[" + getClassName(ComponentType) + "] (" + this.index_ + ")";
}
}
}

View File

@@ -39,6 +39,7 @@ module es {
* 返回一个BitSet实例包含实体拥有的组件的位
*/
public componentBits: BitSet;
private systemBits_: BitSet;
constructor(name: string) {
this.components = new ComponentList(this);
@@ -46,6 +47,7 @@ module es {
this.name = name;
this.id = Entity._idGenerator++;
this.systemBits_ = new BitSet();
this.componentBits = new BitSet();
}
@@ -109,6 +111,10 @@ module es {
this.setUpdateOrder(value);
}
public getSystemBits(): BitSet {
return this.systemBits_;
}
public get parent(): Transform {
return this.transform.parent;
}

View File

@@ -1,16 +1,5 @@
///<reference path="../Math/Vector2.ts" />
module es {
export enum SceneResolutionPolicy {
/**
* 默认情况下RenderTarget与屏幕大小匹配。RenderTarget与屏幕大小相匹配
*/
none,
/**
* 该应用程序采用最适合设计分辨率的宽度和高度
*/
bestFit
}
/** 场景 */
export class Scene {
/**
@@ -208,14 +197,6 @@ module es {
public findEntityWithTag(tag: number): Entity {
return this.entities.entityWithTag(tag);
}
/**
* 返回类型为T的所有实体
* @param type
*/
public entitiesOfType<T extends Entity>(type): T[] {
return this.entities.entitiesOfType<T>(type);
}
/**
* 返回第一个启用加载的类型为T的组件

View File

@@ -1,12 +1,29 @@
///<reference path="../../Utils/Collections/HashMap.ts"/>
module es {
export class SystemIndexManager {
public static INDEX = 0;
private static indices: HashMap<Function, number> = new HashMap<Function, number>();
public static getIndexFor(es: Class): number {
let index: number = SystemIndexManager.indices.get(es);
if (index === undefined) {
index = SystemIndexManager.INDEX++;
SystemIndexManager.indices.put(es, index);
}
return index;
}
}
/**
* 追踪实体的子集,但不实现任何排序或迭代。
*/
export abstract class EntitySystem {
private _entities: Entity[] = [];
private systemIndex_: number;
constructor(matcher?: Matcher) {
this._matcher = matcher ? matcher : Matcher.empty();
this.systemIndex_ = SystemIndexManager.getIndexFor(this.constructor);
this.initialize();
}
@@ -43,7 +60,7 @@ module es {
}
public onChanged(entity: Entity) {
let contains = new es.List(this._entities).contains(entity);
let contains = entity.getSystemBits().get(this.systemIndex_);
let interest = this._matcher.isInterestedEntity(entity);
if (interest && !contains)
@@ -54,6 +71,7 @@ module es {
public add(entity: Entity) {
this._entities.push(entity);
entity.getSystemBits().set(this.systemIndex_);
this.onAdded(entity);
}
@@ -61,6 +79,7 @@ module es {
public remove(entity: Entity) {
new es.List(this._entities).remove(entity);
entity.getSystemBits().clear(this.systemIndex_);
this.onRemoved(entity);
}

View File

@@ -145,10 +145,10 @@ module es {
this._tempBufferList.length = 0;
}
if (this._isComponentListUnsorted) {
this._updatableComponents.sort(ComponentList.compareUpdatableOrder.compare);
this._isComponentListUnsorted = false;
}
// if (this._isComponentListUnsorted) {
// this._updatableComponents.sort(ComponentList.compareUpdatableOrder.compare);
// this._isComponentListUnsorted = false;
// }
}
public handleRemove(component: Component) {

View File

@@ -8,11 +8,12 @@ module es {
/**
* 本帧添加的实体列表。用于对实体进行分组,以便我们可以同时处理它们
*/
public _entitiesToAdded: HashSet<Entity> = new HashSet<Entity>();
// public _entitiesToAdded: Entity[] = [];
public _entitiesToAdded: {[index: number]: Entity} = {};
/**
* 本帧被标记为删除的实体列表。用于对实体进行分组,以便我们可以同时处理它们
*/
public _entitiesToRemove: HashSet<Entity> = new HashSet<Entity>();
public _entitiesToRemove: {[index: number]: Entity} = {};
/**
* 标志,用于确定我们是否需要在这一帧中对实体进行排序
*/
@@ -48,7 +49,7 @@ module es {
* @param entity
*/
public add(entity: Entity) {
this._entitiesToAdded.add(entity);
this._entitiesToAdded[entity.id] = entity;
}
/**
@@ -56,16 +57,14 @@ module es {
* @param entity
*/
public remove(entity: Entity) {
Debug.warnIf(this._entitiesToRemove.contains(entity), `您正在尝试删除已经删除的实体(${entity.name})`);
// 防止在同一帧中添加或删除实体
if (this._entitiesToAdded.contains(entity)) {
this._entitiesToAdded.remove(entity);
if (this._entitiesToAdded[entity.id]) {
delete this._entitiesToAdded[entity.id];
return;
}
if (!this._entitiesToRemove.contains(entity))
this._entitiesToRemove.add(entity);
if (!this._entitiesToRemove[entity.id])
this._entitiesToRemove[entity.id] = entity;
}
/**
@@ -73,7 +72,7 @@ module es {
*/
public removeAllEntities() {
this._unsortedTags.clear();
this._entitiesToAdded.clear();
this._entitiesToAdded = {};
this._isEntityListUnsorted = false;
// 为什么我们要在这里更新列表?主要是为了处理在场景切换前被分离的实体。
@@ -95,7 +94,7 @@ module es {
* @param entity
*/
public contains(entity: Entity): boolean {
return new es.List(this._entities).contains(entity) || this._entitiesToAdded.contains(entity);
return !!this._entitiesToAdded[entity.id];
}
public getTagList(tag: number) {
@@ -127,43 +126,40 @@ module es {
}
public updateLists() {
if (this._entitiesToRemove.getCount() > 0) {
this._entitiesToRemove.toArray().forEach(entity => {
// 处理标签列表
this.removeFromTagList(entity);
for (let i in this._entitiesToRemove) {
let entity = this._entitiesToRemove[i];
this.removeFromTagList(entity);
// 处理常规实体列表
new es.List(this._entities).remove(entity);
entity.onRemovedFromScene();
entity.scene = null;
// 处理常规实体列表
new es.List(this._entities).remove(entity);
entity.onRemovedFromScene();
entity.scene = null;
this.scene.entityProcessors.onEntityRemoved(entity);
});
this._entitiesToRemove.clear();
this.scene.entityProcessors.onEntityRemoved(entity);
}
this._entitiesToRemove = {};
for (let i in this._entitiesToAdded) {
let entity = this._entitiesToAdded[i];
this._entities.push(entity);
entity.scene = this.scene;
this.addToTagList(entity);
this.scene.entityProcessors.onEntityAdded(entity);
}
if (this._entitiesToAdded.getCount() > 0) {
this._entitiesToAdded.toArray().forEach(entity => {
this._entities.push(entity);
entity.scene = this.scene;
this.addToTagList(entity);
this.scene.entityProcessors.onEntityAdded(entity);
});
this._entitiesToAdded.toArray().forEach(entity => {
entity.onAddedToScene();
})
this._entitiesToAdded.clear();
this._isEntityListUnsorted = true;
for (let i in this._entitiesToAdded) {
this._entitiesToAdded[i].onAddedToScene();
}
if (this._isEntityListUnsorted) {
this._entities.sort(Entity.entityComparer.compare);
this._isEntityListUnsorted = false;
}
this._entitiesToAdded = {};
// this._isEntityListUnsorted = true;
// if (this._isEntityListUnsorted) {
// this._entities.sort(Entity.entityComparer.compare);
// this._isEntityListUnsorted = false;
// }
}
/**
@@ -176,8 +172,13 @@ module es {
return this._entities[i];
}
for (let i = 0; i < this._entitiesToAdded.getCount(); i++) {
let entity = this._entitiesToAdded.toArray()[i];
// for (let i = 0; i < this._entitiesToAdded.size; i++) {
// let entity = this._entitiesToAdded.values;
// if (entity.name == name)
// return entity;
// }
for (let i in this._entitiesToAdded) {
let entity = this._entitiesToAdded[i];
if (entity.name == name)
return entity;
}
@@ -216,28 +217,6 @@ module es {
return null;
}
/**
* 返回一个T类型的所有实体的列表。
* 返回的List可以通过ListPool.free放回池中。
* @param type
*/
public entitiesOfType<T extends Entity>(type): T[] {
let list = ListPool.obtain<T>();
for (let i = 0; i < this._entities.length; i++) {
if (this._entities[i] instanceof type)
list.push(this._entities[i] as T);
}
for (let i = 0; i < this._entitiesToAdded.getCount(); i++) {
let entity = this._entitiesToAdded.toArray()[i];
if (TypeUtils.getType(entity) instanceof type) {
list.push(entity as T);
}
}
return list;
}
/**
* 返回在场景中找到的第一个T类型的组件。
* @param type
@@ -251,8 +230,17 @@ module es {
}
}
for (let i = 0; i < this._entitiesToAdded.getCount(); i++) {
let entity: Entity = this._entitiesToAdded.toArray()[i];
// for (let i = 0; i < this._entitiesToAdded.getCount(); i++) {
// let entity: Entity = this._entitiesToAdded.toArray()[i];
// if (entity.enabled) {
// let comp = entity.getComponent<T>(type);
// if (comp)
// return comp;
// }
// }
for (let i in this._entitiesToAdded) {
let entity = this._entitiesToAdded[i];
if (entity.enabled) {
let comp = entity.getComponent<T>(type);
if (comp)
@@ -275,8 +263,14 @@ module es {
this._entities[i].getComponents(type, comps);
}
for (let i = 0; i < this._entitiesToAdded.getCount(); i++) {
let entity = this._entitiesToAdded.toArray()[i];
// for (let i = 0; i < this._entitiesToAdded.getCount(); i++) {
// let entity = this._entitiesToAdded.toArray()[i];
// if (entity.enabled)
// entity.getComponents(type, comps);
// }
for (let i in this._entitiesToAdded) {
let entity = this._entitiesToAdded[i];
if (entity.enabled)
entity.getComponents(type, comps);
}
@@ -308,8 +302,26 @@ module es {
}
}
for (let i = 0; i < this._entitiesToAdded.getCount(); i++) {
let entity: Entity = this._entitiesToAdded.toArray()[i];
// for (let i = 0; i < this._entitiesToAdded.getCount(); i++) {
// let entity: Entity = this._entitiesToAdded.toArray()[i];
// if (entity.enabled) {
// let meet = true;
// for (let type of types) {
// let hasComp = entity.hasComponent(type);
// if (!hasComp) {
// meet = false;
// break;
// }
// }
// if (meet) {
// entities.push(entity);
// }
// }
// }
for (let i in this._entitiesToAdded) {
let entity = this._entitiesToAdded[i];
if (entity.enabled) {
let meet = true;
for (let type of types) {

View File

@@ -0,0 +1,158 @@
module es {
export class Bag<E> implements ImmutableBag<E> {
public size_ = 0;
public length = 0;
private array: Array<E> = [];
constructor(capacity = 64) {
this.length = capacity;
}
removeAt(index: number): E {
const e: E = this.array[index];
this.array[index] = this.array[--this.size_];
this.array[this.size_] = null;
return e;
}
remove(e: E): boolean {
let i: number;
let e2: E;
const size = this.size_;
for (i = 0; i < size; i++) {
e2 = this.array[i];
if (e == e2) {
this.array[i] = this.array[--this.size_];
this.array[this.size_] = null;
return true;
}
}
return false;
}
removeLast(): E {
if (this.size_ > 0) {
const e: E = this.array[--this.size_];
this.array[this.size_] = null;
return e;
}
return null;
}
contains(e: E): boolean {
let i: number;
let size: number;
for (i = 0, size = this.size_; size > i; i++) {
if (e === this.array[i]) {
return true;
}
}
return false;
}
removeAll(bag: ImmutableBag<E>): boolean {
let modified = false;
let i: number;
let j: number;
let l: number;
let e1: E;
let e2: E;
for (i = 0, l = bag.size(); i < l; i++) {
e1 = bag[i];
for (j = 0; j < this.size_; j++) {
e2 = this.array[j];
if (e1 === e2) {
this.removeAt(j);
j--;
modified = true;
break;
}
}
}
return modified;
}
get(index: number): E {
if (index >= this.length) {
throw new Error("ArrayIndexOutOfBoundsException");
}
return this.array[index];
}
safeGet(index: number): E {
if (index >= this.length) {
this.grow((index * 7) / 4 + 1);
}
return this.array[index];
}
size(): number {
return this.size_;
}
getCapacity(): number {
return this.length;
}
isIndexWithinBounds(index: number): boolean {
return index < this.getCapacity();
}
isEmpty(): boolean {
return this.size_ == 0;
}
add(e: E): void {
if (this.size_ === this.length) {
this.grow();
}
this.array[this.size_++] = e;
}
set(index: number, e: E): void {
if (index >= this.length) {
this.grow(index * 2);
}
this.size_ = index + 1;
this.array[index] = e;
}
grow(newCapacity: number = ~~((this.length * 3) / 2) + 1): void {
this.length = ~~newCapacity;
}
ensureCapacity(index: number): void {
if (index >= this.length) {
this.grow(index * 2);
}
}
clear(): void {
let i: number;
let size: number;
for (i = 0, size = this.size_; i < size; i++) {
this.array[i] = null;
}
this.size_ = 0;
}
addAll(items: ImmutableBag<E>): void {
let i: number;
for (i = 0; items.size() > i; i++) {
this.add(items.get(i));
}
}
}
}

View File

@@ -0,0 +1,119 @@
module es {
interface Map<K, V> {
clear(): void;
containsKey(key): boolean;
containsValue(value): boolean;
get(key): V;
isEmpty(): boolean;
put(key, value): void;
remove(key): V;
size(): number;
values(): V[];
}
function decode(key): string {
switch (typeof key) {
case "boolean":
return "" + key;
case "number":
return "" + key;
case "string":
return "" + key;
case "function":
return getClassName(key);
default:
key.uuid = key.uuid ? key.uuid : UUID.randomUUID();
return key.uuid;
}
}
export class HashMap<K, V> implements Map<K, V> {
private map_;
private keys_;
constructor() {
this.clear();
}
clear(): void {
this.map_ = {};
this.keys_ = {};
}
values(): V[] {
const result = [];
const map = this.map_;
for (const key in map) {
result.push(map[key]);
}
return result;
}
contains(value): boolean {
const map = this.map_;
for (const key in map) {
if (value === map[key]) {
return true;
}
}
return false;
}
containsKey(key): boolean {
return decode(key) in this.map_;
}
containsValue(value): boolean {
const map = this.map_;
for (const key in map) {
if (value === map[key]) {
return true;
}
}
return false;
}
get(key: K): V {
return this.map_[decode(key)];
}
isEmpty(): boolean {
return Object.keys(this.map_).length === 0;
}
keys(): K[] {
const keys = this.map_;
const result = [];
for (const key in keys) {
result.push(keys[key]);
}
return result;
}
/**
* if key is a string, use as is, else use key.id_ or key.name
*/
put(key, value): void {
const k = decode(key);
this.map_[k] = value;
this.keys_[k] = key;
}
remove(key): V {
const map = this.map_;
const k = decode(key);
const value = map[k];
delete map[k];
delete this.keys_[k];
return value;
}
size(): number {
return Object.keys(this.map_).length;
}
}
}

View File

@@ -0,0 +1,11 @@
module es {
export interface ImmutableBag<E> {
get(index: number): E;
size(): number;
isEmpty(): boolean;
contains(e: E): boolean;
}
}

293
source/src/Utils/UUID.ts Normal file
View File

@@ -0,0 +1,293 @@
module es {
const hex = [
// hex identity values 0-255
"00",
"01",
"02",
"03",
"04",
"05",
"06",
"07",
"08",
"09",
"0a",
"0b",
"0c",
"0d",
"0e",
"0f",
"10",
"11",
"12",
"13",
"14",
"15",
"16",
"17",
"18",
"19",
"1a",
"1b",
"1c",
"1d",
"1e",
"1f",
"20",
"21",
"22",
"23",
"24",
"25",
"26",
"27",
"28",
"29",
"2a",
"2b",
"2c",
"2d",
"2e",
"2f",
"30",
"31",
"32",
"33",
"34",
"35",
"36",
"37",
"38",
"39",
"3a",
"3b",
"3c",
"3d",
"3e",
"3f",
"40",
"41",
"42",
"43",
"44",
"45",
"46",
"47",
"48",
"49",
"4a",
"4b",
"4c",
"4d",
"4e",
"4f",
"50",
"51",
"52",
"53",
"54",
"55",
"56",
"57",
"58",
"59",
"5a",
"5b",
"5c",
"5d",
"5e",
"5f",
"60",
"61",
"62",
"63",
"64",
"65",
"66",
"67",
"68",
"69",
"6a",
"6b",
"6c",
"6d",
"6e",
"6f",
"70",
"71",
"72",
"73",
"74",
"75",
"76",
"77",
"78",
"79",
"7a",
"7b",
"7c",
"7d",
"7e",
"7f",
"80",
"81",
"82",
"83",
"84",
"85",
"86",
"87",
"88",
"89",
"8a",
"8b",
"8c",
"8d",
"8e",
"8f",
"90",
"91",
"92",
"93",
"94",
"95",
"96",
"97",
"98",
"99",
"9a",
"9b",
"9c",
"9d",
"9e",
"9f",
"a0",
"a1",
"a2",
"a3",
"a4",
"a5",
"a6",
"a7",
"a8",
"a9",
"aa",
"ab",
"ac",
"ad",
"ae",
"af",
"b0",
"b1",
"b2",
"b3",
"b4",
"b5",
"b6",
"b7",
"b8",
"b9",
"ba",
"bb",
"bc",
"bd",
"be",
"bf",
"c0",
"c1",
"c2",
"c3",
"c4",
"c5",
"c6",
"c7",
"c8",
"c9",
"ca",
"cb",
"cc",
"cd",
"ce",
"cf",
"d0",
"d1",
"d2",
"d3",
"d4",
"d5",
"d6",
"d7",
"d8",
"d9",
"da",
"db",
"dc",
"dd",
"de",
"df",
"e0",
"e1",
"e2",
"e3",
"e4",
"e5",
"e6",
"e7",
"e8",
"e9",
"ea",
"eb",
"ec",
"ed",
"ee",
"ef",
"f0",
"f1",
"f2",
"f3",
"f4",
"f5",
"f6",
"f7",
"f8",
"f9",
"fa",
"fb",
"fc",
"fd",
"fe",
"ff",
];
export class UUID {
static randomUUID(): string {
const d0 = (Math.random() * 0xffffffff) | 0;
const d1 = (Math.random() * 0xffffffff) | 0;
const d2 = (Math.random() * 0xffffffff) | 0;
const d3 = (Math.random() * 0xffffffff) | 0;
return (
hex[d0 & 0xff] +
hex[(d0 >> 8) & 0xff] +
hex[(d0 >> 16) & 0xff] +
hex[(d0 >> 24) & 0xff] +
"-" +
hex[d1 & 0xff] +
hex[(d1 >> 8) & 0xff] +
"-" +
hex[((d1 >> 16) & 0x0f) | 0x40] +
hex[(d1 >> 24) & 0xff] +
"-" +
hex[(d2 & 0x3f) | 0x80] +
hex[(d2 >> 8) & 0xff] +
"-" +
hex[(d2 >> 16) & 0xff] +
hex[(d2 >> 24) & 0xff] +
hex[d3 & 0xff] +
hex[(d3 >> 8) & 0xff] +
hex[(d3 >> 16) & 0xff] +
hex[(d3 >> 24) & 0xff]
);
}
}
}

View File

@@ -0,0 +1,7 @@
module es {
export interface Class extends Function {}
export function getClassName(klass): string {
return klass.className || klass.name;
}
}