diff --git a/demo/libs/framework/framework.d.ts b/demo/libs/framework/framework.d.ts index d99afe4c..1f0f31c7 100644 --- a/demo/libs/framework/framework.d.ts +++ b/demo/libs/framework/framework.d.ts @@ -26,6 +26,7 @@ declare abstract class Component { abstract initialize(): any; update(): void; bind(displayRender: egret.DisplayObject): this; + registerComponent(): void; } declare class Entity { name: string; @@ -34,6 +35,7 @@ declare class Entity { readonly components: Component[]; private _updateOrder; private _enabled; + componentBits: BitSet; enabled: boolean; setEnabled(isEnabled: boolean): this; constructor(name: string); @@ -47,7 +49,7 @@ declare class Entity { } declare class Scene extends egret.DisplayObjectContainer { camera: Camera; - entities: Entity[]; + readonly entities: EntityList; private _projectionMatrix; private _transformMatrix; private _matrixTransformMatrix; @@ -55,7 +57,7 @@ declare class Scene extends egret.DisplayObjectContainer { constructor(displayObject: egret.DisplayObject); createEntity(name: string): Entity; addEntity(entity: Entity): Entity; - destoryAllEntities(): void; + destroyAllEntities(): void; findEntity(name: string): Entity; addEntityProcessor(processor: EntitySystem): EntitySystem; removeEntityProcessor(processor: EntitySystem): void; @@ -139,6 +141,11 @@ declare class EntitySystem { scene: Scene; constructor(matcher?: Matcher); initialize(): void; + onChanged(entity: Entity): void; + add(entity: Entity): void; + onAdded(entity: Entity): void; + remove(entity: Entity): void; + onRemoved(entity: Entity): void; update(): void; lateUpdate(): void; protected begin(): void; @@ -153,8 +160,48 @@ declare abstract class EntityProcessingSystem extends EntitySystem { protected process(entities: Entity[]): void; protected lateProcess(entities: Entity[]): void; } +declare class BitSet { + private static LONG_MASK; + private _bits; + constructor(nbits?: number); + and(bs: BitSet): void; + andNot(bs: BitSet): void; + cardinality(): number; + clear(pos?: number): void; + private ensure; + get(pos: number): boolean; + intersects(set: BitSet): boolean; + isEmpty(): boolean; + nextSetBit(from: number): number; + set(pos: number): void; +} +declare class ComponentTypeManager { + private static _componentTypesMask; + static add(type: any): void; + static getIndexFor(type: any): number; +} +declare class EntityList { + scene: Scene; + private _entitiesToRemove; + private _entitiesToAdded; + private _tempEntityList; + private _entities; + constructor(scene: Scene); + readonly count: number; + readonly buffer: Entity[]; + add(entity: Entity): void; + remove(entity: Entity): void; + findEntity(name: string): Entity; + update(): void; + removeAllEntities(): void; + updateLists(): void; +} declare class Matcher { + protected allSet: BitSet; + protected exclusionSet: BitSet; + protected oneSet: BitSet; static empty(): Matcher; + IsIntersted(e: Entity): boolean; } declare class MathHelper { static toDegrees(radians: number): number; diff --git a/demo/libs/framework/framework.js b/demo/libs/framework/framework.js index 3886dae2..76051810 100644 --- a/demo/libs/framework/framework.js +++ b/demo/libs/framework/framework.js @@ -264,6 +264,11 @@ var Component = (function () { this.displayRender = displayRender; return this; }; + Component.prototype.registerComponent = function () { + var _this = this; + this.entity.componentBits.set(ComponentTypeManager.getIndexFor(this)); + this.entity.scene.entityProcessors.forEach(function (processor) { return processor.onChanged(_this.entity); }); + }; return Component; }()); var Entity = (function () { @@ -273,6 +278,7 @@ var Entity = (function () { this.name = name; this.transform = new Transform(this); this.components = []; + this.componentBits = new BitSet(); } Object.defineProperty(Entity.prototype, "enabled", { get: function () { @@ -310,7 +316,8 @@ var Entity = (function () { }; Entity.prototype.attachToScene = function (newScene) { this.scene = newScene; - newScene.entities.push(this); + newScene.entities.add(this); + this.components.forEach(function (component) { return component.registerComponent(); }); for (var i = 0; i < this.transform.childCount; i++) { this.transform.getChild(i).entity.attachToScene(newScene); } @@ -342,10 +349,10 @@ var Scene = (function (_super) { __extends(Scene, _super); function Scene(displayObject) { var _this = _super.call(this) || this; - _this.entities = []; displayObject.stage.addChild(_this); _this._projectionMatrix = new Matrix2D(0, 0, 0, 0, 0, 0); _this.entityProcessors = []; + _this.entities = new EntityList(_this); _this.addEventListener(egret.Event.ACTIVATE, _this.onActive, _this); _this.addEventListener(egret.Event.DEACTIVATE, _this.onDeactive, _this); _this.addEventListener(egret.Event.ENTER_FRAME, _this.update, _this); @@ -357,15 +364,19 @@ var Scene = (function (_super) { return this.addEntity(entity); }; Scene.prototype.addEntity = function (entity) { - this.entities.push(entity); + this.entities.add(entity); entity.scene = this; + for (var i = 0; i < entity.transform.childCount; i++) + this.addEntity(entity.transform.getChild(i).entity); return entity; }; - Scene.prototype.destoryAllEntities = function () { - this.entities.forEach(function (entity) { return entity.destory(); }); + Scene.prototype.destroyAllEntities = function () { + for (var i = 0; i < this.entities.count; i++) { + this.entities.buffer[i].destory(); + } }; Scene.prototype.findEntity = function (name) { - return this.entities.firstOrDefault(function (entity) { return entity.name == name; }); + return this.entities.findEntity(name); }; Scene.prototype.addEntityProcessor = function (processor) { processor.scene = this; @@ -391,8 +402,9 @@ var Scene = (function (_super) { Scene.prototype.onDeactive = function () { }; Scene.prototype.update = function () { + this.entities.updateLists(); this.entityProcessors.forEach(function (processor) { return processor.update(); }); - this.entities.forEach(function (entity) { return entity.update(); }); + this.entities.update(); this.entityProcessors.forEach(function (processor) { return processor.lateUpdate(); }); }; Scene.prototype.prepRenderState = function () { @@ -406,8 +418,7 @@ var Scene = (function (_super) { this.removeEventListener(egret.Event.ACTIVATE, this.onActive, this); this.camera.destory(); this.camera = null; - this.entities.forEach(function (entity) { return entity.destory(); }); - this.entities.length = 0; + this.entities.removeAllEntities(); }; return Scene; }(egret.DisplayObjectContainer)); @@ -599,7 +610,7 @@ var Camera = (function (_super) { }; Camera.prototype.update = function () { var _this = this; - SceneManager.getActiveScene().entities.forEach(function (entity) { return entity.components.forEach(function (component) { + SceneManager.getActiveScene().entities.buffer.forEach(function (entity) { return entity.components.forEach(function (component) { if (component.displayRender) { var has = _this.entity.scene.$children.indexOf(component.displayRender); if (has == -1) { @@ -644,6 +655,26 @@ var EntitySystem = (function () { }); EntitySystem.prototype.initialize = function () { }; + EntitySystem.prototype.onChanged = function (entity) { + var contains = this._entities.contains(entity); + var interest = this._matcher.IsIntersted(entity); + if (interest && !contains) + this.add(entity); + else if (!interest && contains) + this.remove(entity); + }; + EntitySystem.prototype.add = function (entity) { + this._entities.push(entity); + this.onAdded(entity); + }; + EntitySystem.prototype.onAdded = function (entity) { + }; + EntitySystem.prototype.remove = function (entity) { + this._entities.remove(entity); + this.onRemoved(entity); + }; + EntitySystem.prototype.onRemoved = function (entity) { + }; EntitySystem.prototype.update = function () { this.begin(); this.process(this._entities); @@ -679,12 +710,231 @@ var EntityProcessingSystem = (function (_super) { }; return EntityProcessingSystem; }(EntitySystem)); +var BitSet = (function () { + function BitSet(nbits) { + if (nbits === void 0) { nbits = 64; } + var length = nbits >> 6; + if ((nbits & BitSet.LONG_MASK) != 0) + length++; + this._bits = new Array(length); + } + BitSet.prototype.and = function (bs) { + var max = Math.min(this._bits.length, bs._bits.length); + var i; + for (var i_1 = 0; i_1 < max; ++i_1) + this._bits[i_1] &= bs._bits[i_1]; + while (i < this._bits.length) + this._bits[i++] = 0; + }; + BitSet.prototype.andNot = function (bs) { + var i = Math.min(this._bits.length, bs._bits.length); + while (--i >= 0) + this._bits[i] &= ~bs._bits[i]; + }; + BitSet.prototype.cardinality = function () { + var card = 0; + for (var i = this._bits.length - 1; i >= 0; i--) { + var a = this._bits[i]; + if (a == 0) + continue; + if (a == -1) { + card += 64; + continue; + } + a = ((a >> 1) & 0x5555555555555555) + (a & 0x5555555555555555); + a = ((a >> 2) & 0x3333333333333333) + (a & 0x3333333333333333); + var b = ((a >> 32) + a); + b = ((b >> 4) & 0x0f0f0f0f) + (b & 0x0f0f0f0f); + b = ((b >> 8) & 0x00ff00ff) + (b & 0x00ff00ff); + card += ((b >> 16) & 0x0000ffff) + (b & 0x0000ffff); + } + return card; + }; + BitSet.prototype.clear = function (pos) { + if (pos != undefined) { + var offset = pos >> 6; + this.ensure(offset); + this._bits[offset] &= ~(1 << pos); + } + else { + for (var i = 0; i < this._bits.length; i++) + this._bits[i] = 0; + } + }; + BitSet.prototype.ensure = function (lastElt) { + if (lastElt >= this._bits.length) { + var nd = new Number[lastElt + 1]; + nd = this._bits.copyWithin(0, 0, this._bits.length); + this._bits = nd; + } + }; + BitSet.prototype.get = function (pos) { + var offset = pos >> 6; + if (offset >= this._bits.length) + return false; + return (this._bits[offset] & (1 << pos)) != 0; + }; + BitSet.prototype.intersects = function (set) { + var i = Math.min(this._bits.length, set._bits.length); + while (--i >= 0) { + if ((this._bits[i] & set._bits[i]) != 0) + return true; + } + return false; + }; + BitSet.prototype.isEmpty = function () { + for (var i = this._bits.length - 1; i >= 0; i--) { + if (this._bits[i] != 0) + return false; + } + return true; + }; + BitSet.prototype.nextSetBit = function (from) { + var offset = from >> 6; + var mask = 1 << from; + while (offset < this._bits.length) { + var h = this._bits[offset]; + do { + if ((h & mask) != 0) + return from; + mask <<= 1; + from++; + } while (mask != 0); + mask = 1; + offset++; + } + return -1; + }; + BitSet.prototype.set = function (pos) { + var offset = pos >> 6; + this.ensure(offset); + this._bits[offset] |= 1 << pos; + }; + BitSet.LONG_MASK = 0x3f; + return BitSet; +}()); +var ComponentTypeManager = (function () { + function ComponentTypeManager() { + } + ComponentTypeManager.add = function (type) { + if (!this._componentTypesMask.has(type)) + this._componentTypesMask[type] = this._componentTypesMask.size; + }; + ComponentTypeManager.getIndexFor = function (type) { + var v = -1; + if (!this._componentTypesMask.has(type)) { + this.add(type); + v = this._componentTypesMask.get(type); + } + return v; + }; + ComponentTypeManager._componentTypesMask = new Map(); + return ComponentTypeManager; +}()); +var EntityList = (function () { + function EntityList(scene) { + this._entitiesToRemove = []; + this._entitiesToAdded = []; + this._tempEntityList = []; + this._entities = []; + this.scene = scene; + } + Object.defineProperty(EntityList.prototype, "count", { + get: function () { + return this._entities.length; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(EntityList.prototype, "buffer", { + get: function () { + return this._entities; + }, + enumerable: true, + configurable: true + }); + EntityList.prototype.add = function (entity) { + this._entitiesToAdded.push(entity); + }; + EntityList.prototype.remove = function (entity) { + if (this._entitiesToAdded.contains(entity)) { + this._entitiesToAdded.remove(entity); + return; + } + if (!this._entitiesToRemove.contains(entity)) + this._entitiesToRemove.push(entity); + }; + EntityList.prototype.findEntity = function (name) { + for (var i = 0; i < this._entities.length; i++) { + if (this._entities[i].name == name) + return this._entities[i]; + } + return this._entitiesToAdded.firstOrDefault(function (entity) { return entity.name == name; }); + }; + EntityList.prototype.update = function () { + for (var i = 0; i < this._entities.length; i++) { + var entity = this._entities[i]; + if (entity.enabled) + entity.update(); + } + }; + EntityList.prototype.removeAllEntities = function () { + this._entitiesToAdded.length = 0; + this.updateLists(); + for (var i = 0; i < this._entities.length; i++) { + this._entities[i].scene = null; + } + this._entities.length = 0; + }; + EntityList.prototype.updateLists = function () { + var _this = this; + if (this._entitiesToRemove.length > 0) { + var temp = this._entitiesToRemove; + this._entitiesToRemove = this._tempEntityList; + this._tempEntityList = temp; + this._tempEntityList.forEach(function (entity) { + _this._entities.remove(entity); + entity.scene = null; + _this.scene.entityProcessors.forEach(function (processor) { return processor.remove(entity); }); + }); + this._tempEntityList.length = 0; + } + if (this._entitiesToAdded.length > 0) { + var temp = this._entitiesToAdded; + this._entitiesToAdded = this._tempEntityList; + this._tempEntityList = temp; + this._tempEntityList.forEach(function (entity) { + _this._entities.push(entity); + entity.scene = _this.scene; + _this.scene.entityProcessors.forEach(function (processor) { return processor.onChanged(entity); }); + }); + this._tempEntityList.length = 0; + } + }; + return EntityList; +}()); var Matcher = (function () { function Matcher() { + this.allSet = new BitSet(); + this.exclusionSet = new BitSet(); + this.oneSet = new BitSet(); } Matcher.empty = function () { return new Matcher(); }; + Matcher.prototype.IsIntersted = function (e) { + if (!this.allSet.isEmpty()) { + for (var i = this.allSet.nextSetBit(0); i >= 0; i = this.allSet.nextSetBit(i + 1)) { + if (!e.componentBits.get(i)) + return false; + } + } + if (!this.exclusionSet.isEmpty() && this.exclusionSet.intersects(e.componentBits)) + return false; + if (!this.oneSet.isEmpty() && !this.oneSet.intersects(e.componentBits)) + return false; + return true; + }; return Matcher; }()); var MathHelper = (function () { diff --git a/demo/libs/framework/framework.min.js b/demo/libs/framework/framework.min.js index 43fd6dcb..b9104ab8 100644 --- a/demo/libs/framework/framework.min.js +++ b/demo/libs/framework/framework.min.js @@ -1 +1 @@ -window.framework={},window.__extends=this&&this.__extends||function(){var t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n])};return function(e,n){function i(){this.constructor=e}t(e,n),e.prototype=null===n?Object.create(n):(i.prototype=n.prototype,new i)}}(),Array.prototype.findIndex=function(t){return function(t,e){for(var n=0,i=t.length;n-1}(this,t)},Array.prototype.firstOrDefault=function(t){return function(t,e){var n=t.findIndex(e);return-1==n?null:t[n]}(this,t)},Array.prototype.find=function(t){return function(t,e){return t.firstOrDefault(e)}(this,t)},Array.prototype.where=function(t){return function(t,e){if("function"==typeof t.reduce)return t.reduce(function(n,i,r){return e.call(arguments[2],i,r,t)&&n.push(i),n},[]);for(var n=[],i=0,r=t.length;i=0&&t.splice(n,1)}while(n>=0)}(this,t)},Array.prototype.remove=function(t){return function(t,e){var n=t.findIndex(function(t){return t===e});return n>=0&&(t.splice(n,1),!0)}(this,t)},Array.prototype.removeAt=function(t){return function(t,e){t.splice(e,1)}(this,t)},Array.prototype.removeRange=function(t,e){return function(t,e,n){t.splice(e,n)}(this,t,e)},Array.prototype.select=function(t){return function(t,e){if("function"==typeof t.reduce)return t.reduce(function(n,i,r){return n.push(e.call(arguments[2],i,r,t)),n},[]);for(var n=[],i=0,r=t.length;io?1:-1}),t}(this,t,e)},Array.prototype.orderByDescending=function(t,e){return function(t,e,n){return t.sort(function(t,i){var r=e(t),o=e(i);return n?-n(r,o):r=0;t--){this.transform.getChild(t).entity.destory()}},t}(),Scene=function(t){function e(e){var n=t.call(this)||this;return n.entities=[],e.stage.addChild(n),n._projectionMatrix=new Matrix2D(0,0,0,0,0,0),n.entityProcessors=[],n.addEventListener(egret.Event.ACTIVATE,n.onActive,n),n.addEventListener(egret.Event.DEACTIVATE,n.onDeactive,n),n.addEventListener(egret.Event.ENTER_FRAME,n.update,n),n}return __extends(e,t),e.prototype.createEntity=function(t){var e=new Entity(t);return e.transform.position=new Vector2(0,0),this.addEntity(e)},e.prototype.addEntity=function(t){return this.entities.push(t),t.scene=this,t},e.prototype.destoryAllEntities=function(){this.entities.forEach(function(t){return t.destory()})},e.prototype.findEntity=function(t){return this.entities.firstOrDefault(function(e){return e.name==t})},e.prototype.addEntityProcessor=function(t){return t.scene=this,this.entityProcessors.push(t),t},e.prototype.removeEntityProcessor=function(t){this.entityProcessors.remove(t)},e.prototype.getEntityProcessor=function(){return this.entityProcessors.firstOrDefault(function(t){return t instanceof EntitySystem})},e.prototype.setActive=function(){return SceneManager.setActiveScene(this),this},e.prototype.initialize=function(){this.camera=this.createEntity("camera").addComponent(new Camera),this.entityProcessors.forEach(function(t){return t.initialize()})},e.prototype.onActive=function(){},e.prototype.onDeactive=function(){},e.prototype.update=function(){this.entityProcessors.forEach(function(t){return t.update()}),this.entities.forEach(function(t){return t.update()}),this.entityProcessors.forEach(function(t){return t.lateUpdate()})},e.prototype.prepRenderState=function(){this._projectionMatrix.m11=2/this.stage.width,this._projectionMatrix.m22=-2/this.stage.height,this._transformMatrix=this.camera.transformMatrix,this._matrixTransformMatrix=Matrix2D.multiply(this._transformMatrix,this._projectionMatrix)},e.prototype.destory=function(){this.removeEventListener(egret.Event.DEACTIVATE,this.onDeactive,this),this.removeEventListener(egret.Event.ACTIVATE,this.onActive,this),this.camera.destory(),this.camera=null,this.entities.forEach(function(t){return t.destory()}),this.entities.length=0},e}(egret.DisplayObjectContainer),SceneManager=function(){function t(){}return t.createScene=function(t,e){return e.name=t,this._loadedScenes.set(t,e),e},t.setActiveScene=function(t){if(this._activeScene){if(this._activeScene==t)return;this._lastScene=this._activeScene,this._activeScene.destory()}return this._activeScene=t,this._activeScene.initialize(),t},t.getActiveScene=function(){return this._activeScene},t._loadedScenes=new Map,t}();!function(t){t[t.clean=0]="clean",t[t.positionDirty=1]="positionDirty",t[t.scaleDirty=2]="scaleDirty",t[t.rotationDirty=3]="rotationDirty"}(DirtyType||(DirtyType={}));var Transform=function(){function t(t){this._localRotation=0,this._worldTransform=Matrix2D.identity,this._worldToLocalTransform=Matrix2D.identity,this._worldInverseTransform=Matrix2D.identity,this._rotation=0,this.entity=t,this._scale=this._localScale=Vector2.One,this._children=[]}return Object.defineProperty(t.prototype,"childCount",{get:function(){return this._children.length},enumerable:!0,configurable:!0}),t.prototype.getChild=function(t){return this._children[t]},Object.defineProperty(t.prototype,"parent",{get:function(){return this._parent},set:function(t){this.setParent(t)},enumerable:!0,configurable:!0}),t.prototype.setParent=function(t){return this._parent==t?this:(this._parent&&this._parent._children.remove(this),t&&t._children.push(this),this._parent=t,this)},Object.defineProperty(t.prototype,"position",{get:function(){return this.updateTransform(),this.parent?(this.parent.updateTransform(),this._position=Vector2.transform(this._localPosition,this.parent._worldTransform)):this._position=this._localPosition,this._position},set:function(t){this.setPosition(t)},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"localPosition",{get:function(){return this.updateTransform(),this._localPosition},set:function(t){this.setLocalPosition(t)},enumerable:!0,configurable:!0}),t.prototype.setLocalPosition=function(t){return t==this._localPosition?this:(this._localPosition=t,this._localDirty=this._positionDirty=this._localPositionDirty=this._localRotationDirty=this._localScaleDirty=!0,this)},t.prototype.setPosition=function(t){if(t==this._position)return this;this._position=t,this.parent?this.localPosition=Vector2.transform(this._position,this._worldToLocalTransform):this.localPosition=t;for(var e=0;e-1}(this,t)},Array.prototype.firstOrDefault=function(t){return function(t,e){var n=t.findIndex(e);return-1==n?null:t[n]}(this,t)},Array.prototype.find=function(t){return function(t,e){return t.firstOrDefault(e)}(this,t)},Array.prototype.where=function(t){return function(t,e){if("function"==typeof t.reduce)return t.reduce(function(n,i,r){return e.call(arguments[2],i,r,t)&&n.push(i),n},[]);for(var n=[],i=0,r=t.length;i=0&&t.splice(n,1)}while(n>=0)}(this,t)},Array.prototype.remove=function(t){return function(t,e){var n=t.findIndex(function(t){return t===e});return n>=0&&(t.splice(n,1),!0)}(this,t)},Array.prototype.removeAt=function(t){return function(t,e){t.splice(e,1)}(this,t)},Array.prototype.removeRange=function(t,e){return function(t,e,n){t.splice(e,n)}(this,t,e)},Array.prototype.select=function(t){return function(t,e){if("function"==typeof t.reduce)return t.reduce(function(n,i,r){return n.push(e.call(arguments[2],i,r,t)),n},[]);for(var n=[],i=0,r=t.length;io?1:-1}),t}(this,t,e)},Array.prototype.orderByDescending=function(t,e){return function(t,e,n){return t.sort(function(t,i){var r=e(t),o=e(i);return n?-n(r,o):r=0;t--){this.transform.getChild(t).entity.destory()}},t}(),Scene=function(t){function e(e){var n=t.call(this)||this;return e.stage.addChild(n),n._projectionMatrix=new Matrix2D(0,0,0,0,0,0),n.entityProcessors=[],n.entities=new EntityList(n),n.addEventListener(egret.Event.ACTIVATE,n.onActive,n),n.addEventListener(egret.Event.DEACTIVATE,n.onDeactive,n),n.addEventListener(egret.Event.ENTER_FRAME,n.update,n),n}return __extends(e,t),e.prototype.createEntity=function(t){var e=new Entity(t);return e.transform.position=new Vector2(0,0),this.addEntity(e)},e.prototype.addEntity=function(t){this.entities.add(t),t.scene=this;for(var e=0;e>6;0!=(e&t.LONG_MASK)&&n++,this._bits=new Array(n)}return t.prototype.and=function(t){for(var e,n=Math.min(this._bits.length,t._bits.length),i=0;i=0;)this._bits[e]&=~t._bits[e]},t.prototype.cardinality=function(){for(var t=0,e=this._bits.length-1;e>=0;e--){var n=this._bits[e];if(0!=n)if(-1!=n){var i=((n=((n=(n>>1&0x5555555555555400)+(0x5555555555555400&n))>>2&0x3333333333333400)+(0x3333333333333400&n))>>32)+n;t+=((i=((i=(i>>4&252645135)+(252645135&i))>>8&16711935)+(16711935&i))>>16&65535)+(65535&i)}else t+=64}return t},t.prototype.clear=function(t){if(null!=t){var e=t>>6;this.ensure(e),this._bits[e]&=~(1<=this._bits.length){var e=new Number[t+1];e=this._bits.copyWithin(0,0,this._bits.length),this._bits=e}},t.prototype.get=function(t){var e=t>>6;return!(e>=this._bits.length)&&0!=(this._bits[e]&1<=0;)if(0!=(this._bits[e]&t._bits[e]))return!0;return!1},t.prototype.isEmpty=function(){for(var t=this._bits.length-1;t>=0;t--)if(0!=this._bits[t])return!1;return!0},t.prototype.nextSetBit=function(t){for(var e=t>>6,n=1<>6;this.ensure(e),this._bits[e]|=1<0){var e=this._entitiesToRemove;this._entitiesToRemove=this._tempEntityList,this._tempEntityList=e,this._tempEntityList.forEach(function(e){t._entities.remove(e),e.scene=null,t.scene.entityProcessors.forEach(function(t){return t.remove(e)})}),this._tempEntityList.length=0}if(this._entitiesToAdded.length>0){e=this._entitiesToAdded;this._entitiesToAdded=this._tempEntityList,this._tempEntityList=e,this._tempEntityList.forEach(function(e){t._entities.push(e),e.scene=t.scene,t.scene.entityProcessors.forEach(function(t){return t.onChanged(e)})}),this._tempEntityList.length=0}},t}(),Matcher=function(){function t(){this.allSet=new BitSet,this.exclusionSet=new BitSet,this.oneSet=new BitSet}return t.empty=function(){return new t},t.prototype.IsIntersted=function(t){if(!this.allSet.isEmpty())for(var e=this.allSet.nextSetBit(0);e>=0;e=this.allSet.nextSetBit(e+1))if(!t.componentBits.get(e))return!1;return!(!this.exclusionSet.isEmpty()&&this.exclusionSet.intersects(t.componentBits))&&!(!this.oneSet.isEmpty()&&!this.oneSet.intersects(t.componentBits))},t}(),MathHelper=function(){function t(){}return t.toDegrees=function(t){return 57.29577951308232*t},t.toRadians=function(t){return.017453292519943295*t},t}(),Matrix2D=function(){function t(t,e,n,i,r,o){this.m11=0,this.m12=0,this.m21=0,this.m22=0,this.m31=0,this.m32=0,this.m11=t,this.m12=e,this.m21=n,this.m22=i,this.m31=r,this.m32=o}return Object.defineProperty(t,"identity",{get:function(){return t._identity},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"translation",{get:function(){return new Vector2(this.m31,this.m32)},set:function(t){this.m31=t.x,this.m32=t.y},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"rotation",{get:function(){return Math.atan2(this.m21,this.m11)},set:function(t){var e=Math.cos(t),n=Math.sin(t);this.m11=e,this.m12=n,this.m21=-n,this.m22=e},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"rotationDegrees",{get:function(){return MathHelper.toDegrees(this.rotation)},set:function(t){this.rotation=MathHelper.toRadians(t)},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"scale",{get:function(){return new Vector2(this.m11,this.m22)},set:function(t){this.m11=t.x,this.m12=t.y},enumerable:!0,configurable:!0}),t.add=function(t,e){return t.m11+=e.m11,t.m12+=e.m12,t.m21+=e.m21,t.m22+=e.m22,t.m31+=e.m31,t.m32+=e.m32,t},t.divide=function(t,e){return t.m11/=e.m11,t.m12/=e.m12,t.m21/=e.m21,t.m22/=e.m22,t.m31/=e.m31,t.m32/=e.m32,t},t.multiply=function(t,e){var n=t.m11*e.m11+t.m12*e.m21,i=t.m11*e.m12+t.m12*e.m22,r=t.m21*e.m11+t.m22*e.m21,o=t.m21*e.m12+t.m22*e.m22,s=t.m31*e.m11+t.m32*e.m21+e.m31,a=t.m31*e.m12+t.m32*e.m22+e.m32;return t.m11=n,t.m12=i,t.m21=r,t.m22=o,t.m31=s,t.m32=a,t},t.multiplyTranslation=function(e,n,i){var r=t.createTranslation(n,i);return t.multiply(e,r)},t.prototype.determinant=function(){return this.m11*this.m22-this.m12*this.m21},t.invert=function(t,e){var n=1/t.determinant();return e.m11=t.m22*n,e.m12=-t.m12*n,e.m21=-t.m21*n,e.m22=t.m11*n,e.m31=(t.m32*t.m21-t.m31*t.m22)*n,e.m32=-(t.m32*t.m11-t.m31*t.m12)*n,e},t.createTranslation=function(e,n,i){return void 0===i&&(i=t.identity),i.m11=1,i.m12=0,i.m21=0,i.m22=1,i.m31=e,i.m32=n,i},t.createRotation=function(e,n){n=t.identity;var i=Math.cos(e),r=Math.sin(e);return n.m11=i,n.m12=r,n.m21=-r,n.m22=i,n},t.createScale=function(e,n,i){return void 0===i&&(i=t.identity),i.m11=e,i.m12=0,i.m21=0,i.m22=n,i.m31=0,i.m32=0,i},t._identity=new t(1,0,0,1,0,0),t}(),Vector2=function(){function t(t,e){this.x=0,this.y=0,this.x=t,this.y=e}return Object.defineProperty(t,"One",{get:function(){return this.unitVector2},enumerable:!0,configurable:!0}),t.add=function(t,e){return t.x+=e.x,t.y+=e.y,t},t.divide=function(t,e){return t.x/=e.x,t.y/=e.y,t},t.multiply=function(t,e){return t.x*=e.x,t.y*=e.y,t},t.subtract=function(t,e){return t.x-=e.x,t.y-=e.y,t},t.prototype.normalize=function(){var t=1/Math.sqrt(this.x*this.x+this.y*this.y);this.x*=t,this.y*=t},t.transform=function(e,n){return new t(e.x*n.m11+e.y*n.m21,e.x*n.m12+e.y*n.m22)},t.unitVector2=new t(1,1),t}(); \ No newline at end of file diff --git a/source/bin/framework.d.ts b/source/bin/framework.d.ts index d99afe4c..1f0f31c7 100644 --- a/source/bin/framework.d.ts +++ b/source/bin/framework.d.ts @@ -26,6 +26,7 @@ declare abstract class Component { abstract initialize(): any; update(): void; bind(displayRender: egret.DisplayObject): this; + registerComponent(): void; } declare class Entity { name: string; @@ -34,6 +35,7 @@ declare class Entity { readonly components: Component[]; private _updateOrder; private _enabled; + componentBits: BitSet; enabled: boolean; setEnabled(isEnabled: boolean): this; constructor(name: string); @@ -47,7 +49,7 @@ declare class Entity { } declare class Scene extends egret.DisplayObjectContainer { camera: Camera; - entities: Entity[]; + readonly entities: EntityList; private _projectionMatrix; private _transformMatrix; private _matrixTransformMatrix; @@ -55,7 +57,7 @@ declare class Scene extends egret.DisplayObjectContainer { constructor(displayObject: egret.DisplayObject); createEntity(name: string): Entity; addEntity(entity: Entity): Entity; - destoryAllEntities(): void; + destroyAllEntities(): void; findEntity(name: string): Entity; addEntityProcessor(processor: EntitySystem): EntitySystem; removeEntityProcessor(processor: EntitySystem): void; @@ -139,6 +141,11 @@ declare class EntitySystem { scene: Scene; constructor(matcher?: Matcher); initialize(): void; + onChanged(entity: Entity): void; + add(entity: Entity): void; + onAdded(entity: Entity): void; + remove(entity: Entity): void; + onRemoved(entity: Entity): void; update(): void; lateUpdate(): void; protected begin(): void; @@ -153,8 +160,48 @@ declare abstract class EntityProcessingSystem extends EntitySystem { protected process(entities: Entity[]): void; protected lateProcess(entities: Entity[]): void; } +declare class BitSet { + private static LONG_MASK; + private _bits; + constructor(nbits?: number); + and(bs: BitSet): void; + andNot(bs: BitSet): void; + cardinality(): number; + clear(pos?: number): void; + private ensure; + get(pos: number): boolean; + intersects(set: BitSet): boolean; + isEmpty(): boolean; + nextSetBit(from: number): number; + set(pos: number): void; +} +declare class ComponentTypeManager { + private static _componentTypesMask; + static add(type: any): void; + static getIndexFor(type: any): number; +} +declare class EntityList { + scene: Scene; + private _entitiesToRemove; + private _entitiesToAdded; + private _tempEntityList; + private _entities; + constructor(scene: Scene); + readonly count: number; + readonly buffer: Entity[]; + add(entity: Entity): void; + remove(entity: Entity): void; + findEntity(name: string): Entity; + update(): void; + removeAllEntities(): void; + updateLists(): void; +} declare class Matcher { + protected allSet: BitSet; + protected exclusionSet: BitSet; + protected oneSet: BitSet; static empty(): Matcher; + IsIntersted(e: Entity): boolean; } declare class MathHelper { static toDegrees(radians: number): number; diff --git a/source/bin/framework.js b/source/bin/framework.js index 3886dae2..76051810 100644 --- a/source/bin/framework.js +++ b/source/bin/framework.js @@ -264,6 +264,11 @@ var Component = (function () { this.displayRender = displayRender; return this; }; + Component.prototype.registerComponent = function () { + var _this = this; + this.entity.componentBits.set(ComponentTypeManager.getIndexFor(this)); + this.entity.scene.entityProcessors.forEach(function (processor) { return processor.onChanged(_this.entity); }); + }; return Component; }()); var Entity = (function () { @@ -273,6 +278,7 @@ var Entity = (function () { this.name = name; this.transform = new Transform(this); this.components = []; + this.componentBits = new BitSet(); } Object.defineProperty(Entity.prototype, "enabled", { get: function () { @@ -310,7 +316,8 @@ var Entity = (function () { }; Entity.prototype.attachToScene = function (newScene) { this.scene = newScene; - newScene.entities.push(this); + newScene.entities.add(this); + this.components.forEach(function (component) { return component.registerComponent(); }); for (var i = 0; i < this.transform.childCount; i++) { this.transform.getChild(i).entity.attachToScene(newScene); } @@ -342,10 +349,10 @@ var Scene = (function (_super) { __extends(Scene, _super); function Scene(displayObject) { var _this = _super.call(this) || this; - _this.entities = []; displayObject.stage.addChild(_this); _this._projectionMatrix = new Matrix2D(0, 0, 0, 0, 0, 0); _this.entityProcessors = []; + _this.entities = new EntityList(_this); _this.addEventListener(egret.Event.ACTIVATE, _this.onActive, _this); _this.addEventListener(egret.Event.DEACTIVATE, _this.onDeactive, _this); _this.addEventListener(egret.Event.ENTER_FRAME, _this.update, _this); @@ -357,15 +364,19 @@ var Scene = (function (_super) { return this.addEntity(entity); }; Scene.prototype.addEntity = function (entity) { - this.entities.push(entity); + this.entities.add(entity); entity.scene = this; + for (var i = 0; i < entity.transform.childCount; i++) + this.addEntity(entity.transform.getChild(i).entity); return entity; }; - Scene.prototype.destoryAllEntities = function () { - this.entities.forEach(function (entity) { return entity.destory(); }); + Scene.prototype.destroyAllEntities = function () { + for (var i = 0; i < this.entities.count; i++) { + this.entities.buffer[i].destory(); + } }; Scene.prototype.findEntity = function (name) { - return this.entities.firstOrDefault(function (entity) { return entity.name == name; }); + return this.entities.findEntity(name); }; Scene.prototype.addEntityProcessor = function (processor) { processor.scene = this; @@ -391,8 +402,9 @@ var Scene = (function (_super) { Scene.prototype.onDeactive = function () { }; Scene.prototype.update = function () { + this.entities.updateLists(); this.entityProcessors.forEach(function (processor) { return processor.update(); }); - this.entities.forEach(function (entity) { return entity.update(); }); + this.entities.update(); this.entityProcessors.forEach(function (processor) { return processor.lateUpdate(); }); }; Scene.prototype.prepRenderState = function () { @@ -406,8 +418,7 @@ var Scene = (function (_super) { this.removeEventListener(egret.Event.ACTIVATE, this.onActive, this); this.camera.destory(); this.camera = null; - this.entities.forEach(function (entity) { return entity.destory(); }); - this.entities.length = 0; + this.entities.removeAllEntities(); }; return Scene; }(egret.DisplayObjectContainer)); @@ -599,7 +610,7 @@ var Camera = (function (_super) { }; Camera.prototype.update = function () { var _this = this; - SceneManager.getActiveScene().entities.forEach(function (entity) { return entity.components.forEach(function (component) { + SceneManager.getActiveScene().entities.buffer.forEach(function (entity) { return entity.components.forEach(function (component) { if (component.displayRender) { var has = _this.entity.scene.$children.indexOf(component.displayRender); if (has == -1) { @@ -644,6 +655,26 @@ var EntitySystem = (function () { }); EntitySystem.prototype.initialize = function () { }; + EntitySystem.prototype.onChanged = function (entity) { + var contains = this._entities.contains(entity); + var interest = this._matcher.IsIntersted(entity); + if (interest && !contains) + this.add(entity); + else if (!interest && contains) + this.remove(entity); + }; + EntitySystem.prototype.add = function (entity) { + this._entities.push(entity); + this.onAdded(entity); + }; + EntitySystem.prototype.onAdded = function (entity) { + }; + EntitySystem.prototype.remove = function (entity) { + this._entities.remove(entity); + this.onRemoved(entity); + }; + EntitySystem.prototype.onRemoved = function (entity) { + }; EntitySystem.prototype.update = function () { this.begin(); this.process(this._entities); @@ -679,12 +710,231 @@ var EntityProcessingSystem = (function (_super) { }; return EntityProcessingSystem; }(EntitySystem)); +var BitSet = (function () { + function BitSet(nbits) { + if (nbits === void 0) { nbits = 64; } + var length = nbits >> 6; + if ((nbits & BitSet.LONG_MASK) != 0) + length++; + this._bits = new Array(length); + } + BitSet.prototype.and = function (bs) { + var max = Math.min(this._bits.length, bs._bits.length); + var i; + for (var i_1 = 0; i_1 < max; ++i_1) + this._bits[i_1] &= bs._bits[i_1]; + while (i < this._bits.length) + this._bits[i++] = 0; + }; + BitSet.prototype.andNot = function (bs) { + var i = Math.min(this._bits.length, bs._bits.length); + while (--i >= 0) + this._bits[i] &= ~bs._bits[i]; + }; + BitSet.prototype.cardinality = function () { + var card = 0; + for (var i = this._bits.length - 1; i >= 0; i--) { + var a = this._bits[i]; + if (a == 0) + continue; + if (a == -1) { + card += 64; + continue; + } + a = ((a >> 1) & 0x5555555555555555) + (a & 0x5555555555555555); + a = ((a >> 2) & 0x3333333333333333) + (a & 0x3333333333333333); + var b = ((a >> 32) + a); + b = ((b >> 4) & 0x0f0f0f0f) + (b & 0x0f0f0f0f); + b = ((b >> 8) & 0x00ff00ff) + (b & 0x00ff00ff); + card += ((b >> 16) & 0x0000ffff) + (b & 0x0000ffff); + } + return card; + }; + BitSet.prototype.clear = function (pos) { + if (pos != undefined) { + var offset = pos >> 6; + this.ensure(offset); + this._bits[offset] &= ~(1 << pos); + } + else { + for (var i = 0; i < this._bits.length; i++) + this._bits[i] = 0; + } + }; + BitSet.prototype.ensure = function (lastElt) { + if (lastElt >= this._bits.length) { + var nd = new Number[lastElt + 1]; + nd = this._bits.copyWithin(0, 0, this._bits.length); + this._bits = nd; + } + }; + BitSet.prototype.get = function (pos) { + var offset = pos >> 6; + if (offset >= this._bits.length) + return false; + return (this._bits[offset] & (1 << pos)) != 0; + }; + BitSet.prototype.intersects = function (set) { + var i = Math.min(this._bits.length, set._bits.length); + while (--i >= 0) { + if ((this._bits[i] & set._bits[i]) != 0) + return true; + } + return false; + }; + BitSet.prototype.isEmpty = function () { + for (var i = this._bits.length - 1; i >= 0; i--) { + if (this._bits[i] != 0) + return false; + } + return true; + }; + BitSet.prototype.nextSetBit = function (from) { + var offset = from >> 6; + var mask = 1 << from; + while (offset < this._bits.length) { + var h = this._bits[offset]; + do { + if ((h & mask) != 0) + return from; + mask <<= 1; + from++; + } while (mask != 0); + mask = 1; + offset++; + } + return -1; + }; + BitSet.prototype.set = function (pos) { + var offset = pos >> 6; + this.ensure(offset); + this._bits[offset] |= 1 << pos; + }; + BitSet.LONG_MASK = 0x3f; + return BitSet; +}()); +var ComponentTypeManager = (function () { + function ComponentTypeManager() { + } + ComponentTypeManager.add = function (type) { + if (!this._componentTypesMask.has(type)) + this._componentTypesMask[type] = this._componentTypesMask.size; + }; + ComponentTypeManager.getIndexFor = function (type) { + var v = -1; + if (!this._componentTypesMask.has(type)) { + this.add(type); + v = this._componentTypesMask.get(type); + } + return v; + }; + ComponentTypeManager._componentTypesMask = new Map(); + return ComponentTypeManager; +}()); +var EntityList = (function () { + function EntityList(scene) { + this._entitiesToRemove = []; + this._entitiesToAdded = []; + this._tempEntityList = []; + this._entities = []; + this.scene = scene; + } + Object.defineProperty(EntityList.prototype, "count", { + get: function () { + return this._entities.length; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(EntityList.prototype, "buffer", { + get: function () { + return this._entities; + }, + enumerable: true, + configurable: true + }); + EntityList.prototype.add = function (entity) { + this._entitiesToAdded.push(entity); + }; + EntityList.prototype.remove = function (entity) { + if (this._entitiesToAdded.contains(entity)) { + this._entitiesToAdded.remove(entity); + return; + } + if (!this._entitiesToRemove.contains(entity)) + this._entitiesToRemove.push(entity); + }; + EntityList.prototype.findEntity = function (name) { + for (var i = 0; i < this._entities.length; i++) { + if (this._entities[i].name == name) + return this._entities[i]; + } + return this._entitiesToAdded.firstOrDefault(function (entity) { return entity.name == name; }); + }; + EntityList.prototype.update = function () { + for (var i = 0; i < this._entities.length; i++) { + var entity = this._entities[i]; + if (entity.enabled) + entity.update(); + } + }; + EntityList.prototype.removeAllEntities = function () { + this._entitiesToAdded.length = 0; + this.updateLists(); + for (var i = 0; i < this._entities.length; i++) { + this._entities[i].scene = null; + } + this._entities.length = 0; + }; + EntityList.prototype.updateLists = function () { + var _this = this; + if (this._entitiesToRemove.length > 0) { + var temp = this._entitiesToRemove; + this._entitiesToRemove = this._tempEntityList; + this._tempEntityList = temp; + this._tempEntityList.forEach(function (entity) { + _this._entities.remove(entity); + entity.scene = null; + _this.scene.entityProcessors.forEach(function (processor) { return processor.remove(entity); }); + }); + this._tempEntityList.length = 0; + } + if (this._entitiesToAdded.length > 0) { + var temp = this._entitiesToAdded; + this._entitiesToAdded = this._tempEntityList; + this._tempEntityList = temp; + this._tempEntityList.forEach(function (entity) { + _this._entities.push(entity); + entity.scene = _this.scene; + _this.scene.entityProcessors.forEach(function (processor) { return processor.onChanged(entity); }); + }); + this._tempEntityList.length = 0; + } + }; + return EntityList; +}()); var Matcher = (function () { function Matcher() { + this.allSet = new BitSet(); + this.exclusionSet = new BitSet(); + this.oneSet = new BitSet(); } Matcher.empty = function () { return new Matcher(); }; + Matcher.prototype.IsIntersted = function (e) { + if (!this.allSet.isEmpty()) { + for (var i = this.allSet.nextSetBit(0); i >= 0; i = this.allSet.nextSetBit(i + 1)) { + if (!e.componentBits.get(i)) + return false; + } + } + if (!this.exclusionSet.isEmpty() && this.exclusionSet.intersects(e.componentBits)) + return false; + if (!this.oneSet.isEmpty() && !this.oneSet.intersects(e.componentBits)) + return false; + return true; + }; return Matcher; }()); var MathHelper = (function () { diff --git a/source/bin/framework.min.js b/source/bin/framework.min.js index 43fd6dcb..b9104ab8 100644 --- a/source/bin/framework.min.js +++ b/source/bin/framework.min.js @@ -1 +1 @@ -window.framework={},window.__extends=this&&this.__extends||function(){var t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n])};return function(e,n){function i(){this.constructor=e}t(e,n),e.prototype=null===n?Object.create(n):(i.prototype=n.prototype,new i)}}(),Array.prototype.findIndex=function(t){return function(t,e){for(var n=0,i=t.length;n-1}(this,t)},Array.prototype.firstOrDefault=function(t){return function(t,e){var n=t.findIndex(e);return-1==n?null:t[n]}(this,t)},Array.prototype.find=function(t){return function(t,e){return t.firstOrDefault(e)}(this,t)},Array.prototype.where=function(t){return function(t,e){if("function"==typeof t.reduce)return t.reduce(function(n,i,r){return e.call(arguments[2],i,r,t)&&n.push(i),n},[]);for(var n=[],i=0,r=t.length;i=0&&t.splice(n,1)}while(n>=0)}(this,t)},Array.prototype.remove=function(t){return function(t,e){var n=t.findIndex(function(t){return t===e});return n>=0&&(t.splice(n,1),!0)}(this,t)},Array.prototype.removeAt=function(t){return function(t,e){t.splice(e,1)}(this,t)},Array.prototype.removeRange=function(t,e){return function(t,e,n){t.splice(e,n)}(this,t,e)},Array.prototype.select=function(t){return function(t,e){if("function"==typeof t.reduce)return t.reduce(function(n,i,r){return n.push(e.call(arguments[2],i,r,t)),n},[]);for(var n=[],i=0,r=t.length;io?1:-1}),t}(this,t,e)},Array.prototype.orderByDescending=function(t,e){return function(t,e,n){return t.sort(function(t,i){var r=e(t),o=e(i);return n?-n(r,o):r=0;t--){this.transform.getChild(t).entity.destory()}},t}(),Scene=function(t){function e(e){var n=t.call(this)||this;return n.entities=[],e.stage.addChild(n),n._projectionMatrix=new Matrix2D(0,0,0,0,0,0),n.entityProcessors=[],n.addEventListener(egret.Event.ACTIVATE,n.onActive,n),n.addEventListener(egret.Event.DEACTIVATE,n.onDeactive,n),n.addEventListener(egret.Event.ENTER_FRAME,n.update,n),n}return __extends(e,t),e.prototype.createEntity=function(t){var e=new Entity(t);return e.transform.position=new Vector2(0,0),this.addEntity(e)},e.prototype.addEntity=function(t){return this.entities.push(t),t.scene=this,t},e.prototype.destoryAllEntities=function(){this.entities.forEach(function(t){return t.destory()})},e.prototype.findEntity=function(t){return this.entities.firstOrDefault(function(e){return e.name==t})},e.prototype.addEntityProcessor=function(t){return t.scene=this,this.entityProcessors.push(t),t},e.prototype.removeEntityProcessor=function(t){this.entityProcessors.remove(t)},e.prototype.getEntityProcessor=function(){return this.entityProcessors.firstOrDefault(function(t){return t instanceof EntitySystem})},e.prototype.setActive=function(){return SceneManager.setActiveScene(this),this},e.prototype.initialize=function(){this.camera=this.createEntity("camera").addComponent(new Camera),this.entityProcessors.forEach(function(t){return t.initialize()})},e.prototype.onActive=function(){},e.prototype.onDeactive=function(){},e.prototype.update=function(){this.entityProcessors.forEach(function(t){return t.update()}),this.entities.forEach(function(t){return t.update()}),this.entityProcessors.forEach(function(t){return t.lateUpdate()})},e.prototype.prepRenderState=function(){this._projectionMatrix.m11=2/this.stage.width,this._projectionMatrix.m22=-2/this.stage.height,this._transformMatrix=this.camera.transformMatrix,this._matrixTransformMatrix=Matrix2D.multiply(this._transformMatrix,this._projectionMatrix)},e.prototype.destory=function(){this.removeEventListener(egret.Event.DEACTIVATE,this.onDeactive,this),this.removeEventListener(egret.Event.ACTIVATE,this.onActive,this),this.camera.destory(),this.camera=null,this.entities.forEach(function(t){return t.destory()}),this.entities.length=0},e}(egret.DisplayObjectContainer),SceneManager=function(){function t(){}return t.createScene=function(t,e){return e.name=t,this._loadedScenes.set(t,e),e},t.setActiveScene=function(t){if(this._activeScene){if(this._activeScene==t)return;this._lastScene=this._activeScene,this._activeScene.destory()}return this._activeScene=t,this._activeScene.initialize(),t},t.getActiveScene=function(){return this._activeScene},t._loadedScenes=new Map,t}();!function(t){t[t.clean=0]="clean",t[t.positionDirty=1]="positionDirty",t[t.scaleDirty=2]="scaleDirty",t[t.rotationDirty=3]="rotationDirty"}(DirtyType||(DirtyType={}));var Transform=function(){function t(t){this._localRotation=0,this._worldTransform=Matrix2D.identity,this._worldToLocalTransform=Matrix2D.identity,this._worldInverseTransform=Matrix2D.identity,this._rotation=0,this.entity=t,this._scale=this._localScale=Vector2.One,this._children=[]}return Object.defineProperty(t.prototype,"childCount",{get:function(){return this._children.length},enumerable:!0,configurable:!0}),t.prototype.getChild=function(t){return this._children[t]},Object.defineProperty(t.prototype,"parent",{get:function(){return this._parent},set:function(t){this.setParent(t)},enumerable:!0,configurable:!0}),t.prototype.setParent=function(t){return this._parent==t?this:(this._parent&&this._parent._children.remove(this),t&&t._children.push(this),this._parent=t,this)},Object.defineProperty(t.prototype,"position",{get:function(){return this.updateTransform(),this.parent?(this.parent.updateTransform(),this._position=Vector2.transform(this._localPosition,this.parent._worldTransform)):this._position=this._localPosition,this._position},set:function(t){this.setPosition(t)},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"localPosition",{get:function(){return this.updateTransform(),this._localPosition},set:function(t){this.setLocalPosition(t)},enumerable:!0,configurable:!0}),t.prototype.setLocalPosition=function(t){return t==this._localPosition?this:(this._localPosition=t,this._localDirty=this._positionDirty=this._localPositionDirty=this._localRotationDirty=this._localScaleDirty=!0,this)},t.prototype.setPosition=function(t){if(t==this._position)return this;this._position=t,this.parent?this.localPosition=Vector2.transform(this._position,this._worldToLocalTransform):this.localPosition=t;for(var e=0;e-1}(this,t)},Array.prototype.firstOrDefault=function(t){return function(t,e){var n=t.findIndex(e);return-1==n?null:t[n]}(this,t)},Array.prototype.find=function(t){return function(t,e){return t.firstOrDefault(e)}(this,t)},Array.prototype.where=function(t){return function(t,e){if("function"==typeof t.reduce)return t.reduce(function(n,i,r){return e.call(arguments[2],i,r,t)&&n.push(i),n},[]);for(var n=[],i=0,r=t.length;i=0&&t.splice(n,1)}while(n>=0)}(this,t)},Array.prototype.remove=function(t){return function(t,e){var n=t.findIndex(function(t){return t===e});return n>=0&&(t.splice(n,1),!0)}(this,t)},Array.prototype.removeAt=function(t){return function(t,e){t.splice(e,1)}(this,t)},Array.prototype.removeRange=function(t,e){return function(t,e,n){t.splice(e,n)}(this,t,e)},Array.prototype.select=function(t){return function(t,e){if("function"==typeof t.reduce)return t.reduce(function(n,i,r){return n.push(e.call(arguments[2],i,r,t)),n},[]);for(var n=[],i=0,r=t.length;io?1:-1}),t}(this,t,e)},Array.prototype.orderByDescending=function(t,e){return function(t,e,n){return t.sort(function(t,i){var r=e(t),o=e(i);return n?-n(r,o):r=0;t--){this.transform.getChild(t).entity.destory()}},t}(),Scene=function(t){function e(e){var n=t.call(this)||this;return e.stage.addChild(n),n._projectionMatrix=new Matrix2D(0,0,0,0,0,0),n.entityProcessors=[],n.entities=new EntityList(n),n.addEventListener(egret.Event.ACTIVATE,n.onActive,n),n.addEventListener(egret.Event.DEACTIVATE,n.onDeactive,n),n.addEventListener(egret.Event.ENTER_FRAME,n.update,n),n}return __extends(e,t),e.prototype.createEntity=function(t){var e=new Entity(t);return e.transform.position=new Vector2(0,0),this.addEntity(e)},e.prototype.addEntity=function(t){this.entities.add(t),t.scene=this;for(var e=0;e>6;0!=(e&t.LONG_MASK)&&n++,this._bits=new Array(n)}return t.prototype.and=function(t){for(var e,n=Math.min(this._bits.length,t._bits.length),i=0;i=0;)this._bits[e]&=~t._bits[e]},t.prototype.cardinality=function(){for(var t=0,e=this._bits.length-1;e>=0;e--){var n=this._bits[e];if(0!=n)if(-1!=n){var i=((n=((n=(n>>1&0x5555555555555400)+(0x5555555555555400&n))>>2&0x3333333333333400)+(0x3333333333333400&n))>>32)+n;t+=((i=((i=(i>>4&252645135)+(252645135&i))>>8&16711935)+(16711935&i))>>16&65535)+(65535&i)}else t+=64}return t},t.prototype.clear=function(t){if(null!=t){var e=t>>6;this.ensure(e),this._bits[e]&=~(1<=this._bits.length){var e=new Number[t+1];e=this._bits.copyWithin(0,0,this._bits.length),this._bits=e}},t.prototype.get=function(t){var e=t>>6;return!(e>=this._bits.length)&&0!=(this._bits[e]&1<=0;)if(0!=(this._bits[e]&t._bits[e]))return!0;return!1},t.prototype.isEmpty=function(){for(var t=this._bits.length-1;t>=0;t--)if(0!=this._bits[t])return!1;return!0},t.prototype.nextSetBit=function(t){for(var e=t>>6,n=1<>6;this.ensure(e),this._bits[e]|=1<0){var e=this._entitiesToRemove;this._entitiesToRemove=this._tempEntityList,this._tempEntityList=e,this._tempEntityList.forEach(function(e){t._entities.remove(e),e.scene=null,t.scene.entityProcessors.forEach(function(t){return t.remove(e)})}),this._tempEntityList.length=0}if(this._entitiesToAdded.length>0){e=this._entitiesToAdded;this._entitiesToAdded=this._tempEntityList,this._tempEntityList=e,this._tempEntityList.forEach(function(e){t._entities.push(e),e.scene=t.scene,t.scene.entityProcessors.forEach(function(t){return t.onChanged(e)})}),this._tempEntityList.length=0}},t}(),Matcher=function(){function t(){this.allSet=new BitSet,this.exclusionSet=new BitSet,this.oneSet=new BitSet}return t.empty=function(){return new t},t.prototype.IsIntersted=function(t){if(!this.allSet.isEmpty())for(var e=this.allSet.nextSetBit(0);e>=0;e=this.allSet.nextSetBit(e+1))if(!t.componentBits.get(e))return!1;return!(!this.exclusionSet.isEmpty()&&this.exclusionSet.intersects(t.componentBits))&&!(!this.oneSet.isEmpty()&&!this.oneSet.intersects(t.componentBits))},t}(),MathHelper=function(){function t(){}return t.toDegrees=function(t){return 57.29577951308232*t},t.toRadians=function(t){return.017453292519943295*t},t}(),Matrix2D=function(){function t(t,e,n,i,r,o){this.m11=0,this.m12=0,this.m21=0,this.m22=0,this.m31=0,this.m32=0,this.m11=t,this.m12=e,this.m21=n,this.m22=i,this.m31=r,this.m32=o}return Object.defineProperty(t,"identity",{get:function(){return t._identity},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"translation",{get:function(){return new Vector2(this.m31,this.m32)},set:function(t){this.m31=t.x,this.m32=t.y},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"rotation",{get:function(){return Math.atan2(this.m21,this.m11)},set:function(t){var e=Math.cos(t),n=Math.sin(t);this.m11=e,this.m12=n,this.m21=-n,this.m22=e},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"rotationDegrees",{get:function(){return MathHelper.toDegrees(this.rotation)},set:function(t){this.rotation=MathHelper.toRadians(t)},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"scale",{get:function(){return new Vector2(this.m11,this.m22)},set:function(t){this.m11=t.x,this.m12=t.y},enumerable:!0,configurable:!0}),t.add=function(t,e){return t.m11+=e.m11,t.m12+=e.m12,t.m21+=e.m21,t.m22+=e.m22,t.m31+=e.m31,t.m32+=e.m32,t},t.divide=function(t,e){return t.m11/=e.m11,t.m12/=e.m12,t.m21/=e.m21,t.m22/=e.m22,t.m31/=e.m31,t.m32/=e.m32,t},t.multiply=function(t,e){var n=t.m11*e.m11+t.m12*e.m21,i=t.m11*e.m12+t.m12*e.m22,r=t.m21*e.m11+t.m22*e.m21,o=t.m21*e.m12+t.m22*e.m22,s=t.m31*e.m11+t.m32*e.m21+e.m31,a=t.m31*e.m12+t.m32*e.m22+e.m32;return t.m11=n,t.m12=i,t.m21=r,t.m22=o,t.m31=s,t.m32=a,t},t.multiplyTranslation=function(e,n,i){var r=t.createTranslation(n,i);return t.multiply(e,r)},t.prototype.determinant=function(){return this.m11*this.m22-this.m12*this.m21},t.invert=function(t,e){var n=1/t.determinant();return e.m11=t.m22*n,e.m12=-t.m12*n,e.m21=-t.m21*n,e.m22=t.m11*n,e.m31=(t.m32*t.m21-t.m31*t.m22)*n,e.m32=-(t.m32*t.m11-t.m31*t.m12)*n,e},t.createTranslation=function(e,n,i){return void 0===i&&(i=t.identity),i.m11=1,i.m12=0,i.m21=0,i.m22=1,i.m31=e,i.m32=n,i},t.createRotation=function(e,n){n=t.identity;var i=Math.cos(e),r=Math.sin(e);return n.m11=i,n.m12=r,n.m21=-r,n.m22=i,n},t.createScale=function(e,n,i){return void 0===i&&(i=t.identity),i.m11=e,i.m12=0,i.m21=0,i.m22=n,i.m31=0,i.m32=0,i},t._identity=new t(1,0,0,1,0,0),t}(),Vector2=function(){function t(t,e){this.x=0,this.y=0,this.x=t,this.y=e}return Object.defineProperty(t,"One",{get:function(){return this.unitVector2},enumerable:!0,configurable:!0}),t.add=function(t,e){return t.x+=e.x,t.y+=e.y,t},t.divide=function(t,e){return t.x/=e.x,t.y/=e.y,t},t.multiply=function(t,e){return t.x*=e.x,t.y*=e.y,t},t.subtract=function(t,e){return t.x-=e.x,t.y-=e.y,t},t.prototype.normalize=function(){var t=1/Math.sqrt(this.x*this.x+this.y*this.y);this.x*=t,this.y*=t},t.transform=function(e,n){return new t(e.x*n.m11+e.y*n.m21,e.x*n.m12+e.y*n.m22)},t.unitVector2=new t(1,1),t}(); \ No newline at end of file diff --git a/source/src/ECS/Component.ts b/source/src/ECS/Component.ts index 7d74cc55..138f6d0f 100644 --- a/source/src/ECS/Component.ts +++ b/source/src/ECS/Component.ts @@ -30,4 +30,10 @@ abstract class Component { return this; } + + /** 内部使用 运行时不应该调用 */ + public registerComponent(){ + this.entity.componentBits.set(ComponentTypeManager.getIndexFor(this)); + this.entity.scene.entityProcessors.forEach(processor => processor.onChanged(this.entity)); + } } \ No newline at end of file diff --git a/source/src/ECS/Components/Camera.ts b/source/src/ECS/Components/Camera.ts index 81012375..30f90a13 100644 --- a/source/src/ECS/Components/Camera.ts +++ b/source/src/ECS/Components/Camera.ts @@ -20,7 +20,7 @@ class Camera extends Component { } public update(){ - SceneManager.getActiveScene().entities.forEach(entity => entity.components.forEach(component => { + SceneManager.getActiveScene().entities.buffer.forEach(entity => entity.components.forEach(component => { if (component.displayRender){ let has = this.entity.scene.$children.indexOf(component.displayRender) if (has == -1){ diff --git a/source/src/ECS/Entity.ts b/source/src/ECS/Entity.ts index a4c00ea7..1eeeaf08 100644 --- a/source/src/ECS/Entity.ts +++ b/source/src/ECS/Entity.ts @@ -9,6 +9,8 @@ class Entity { private _updateOrder: number = 0; private _enabled: boolean = true; + public componentBits: BitSet; + public get enabled(){ return this._enabled; } @@ -29,6 +31,7 @@ class Entity { this.name = name; this.transform = new Transform(this); this.components = []; + this.componentBits = new BitSet(); } public get updateOrder(){ @@ -52,7 +55,8 @@ class Entity { public attachToScene(newScene: Scene){ this.scene = newScene; - newScene.entities.push(this); + newScene.entities.add(this); + this.components.forEach(component => component.registerComponent()); for (let i = 0; i < this.transform.childCount; i ++){ this.transform.getChild(i).entity.attachToScene(newScene); diff --git a/source/src/ECS/Scene.ts b/source/src/ECS/Scene.ts index 01c668ba..661b582e 100644 --- a/source/src/ECS/Scene.ts +++ b/source/src/ECS/Scene.ts @@ -1,7 +1,7 @@ /** 场景 */ class Scene extends egret.DisplayObjectContainer { public camera: Camera; - public entities: Entity[] = []; + public readonly entities: EntityList; private _projectionMatrix: Matrix2D; private _transformMatrix: Matrix2D; @@ -14,6 +14,7 @@ class Scene extends egret.DisplayObjectContainer { displayObject.stage.addChild(this); this._projectionMatrix = new Matrix2D(0, 0, 0, 0, 0, 0); this.entityProcessors = []; + this.entities = new EntityList(this); this.addEventListener(egret.Event.ACTIVATE, this.onActive, this); this.addEventListener(egret.Event.DEACTIVATE, this.onDeactive, this); @@ -27,18 +28,23 @@ class Scene extends egret.DisplayObjectContainer { } public addEntity(entity: Entity){ - this.entities.push(entity); + this.entities.add(entity); entity.scene = this; + for (let i = 0; i < entity.transform.childCount; i ++) + this.addEntity(entity.transform.getChild(i).entity); + return entity; } - public destoryAllEntities(){ - this.entities.forEach(entity => entity.destory()); + public destroyAllEntities(){ + for (let i = 0; i < this.entities.count; i ++){ + this.entities.buffer[i].destory(); + } } public findEntity(name: string): Entity{ - return this.entities.firstOrDefault(entity => entity.name == name); + return this.entities.findEntity(name); } /** @@ -74,7 +80,7 @@ class Scene extends egret.DisplayObjectContainer { /** 场景激活 */ public onActive(){ - + } /** 场景失去焦点 */ @@ -83,8 +89,10 @@ class Scene extends egret.DisplayObjectContainer { } public update(){ + this.entities.updateLists(); + this.entityProcessors.forEach(processor => processor.update()); - this.entities.forEach(entity => entity.update()); + this.entities.update(); this.entityProcessors.forEach(processor => processor.lateUpdate()); } @@ -103,7 +111,6 @@ class Scene extends egret.DisplayObjectContainer { this.camera.destory(); this.camera = null; - this.entities.forEach(entity => entity.destory()); - this.entities.length = 0; + this.entities.removeAllEntities(); } } \ No newline at end of file diff --git a/source/src/ECS/Systems/EntitySystem.ts b/source/src/ECS/Systems/EntitySystem.ts index 5d4d5108..8ec9cdc6 100644 --- a/source/src/ECS/Systems/EntitySystem.ts +++ b/source/src/ECS/Systems/EntitySystem.ts @@ -21,7 +21,34 @@ class EntitySystem { } public initialize(){ - + + } + + public onChanged(entity: Entity){ + let contains = this._entities.contains(entity); + let interest = this._matcher.IsIntersted(entity); + + if (interest && !contains) + this.add(entity); + else if(!interest && contains) + this.remove(entity); + } + + public add(entity: Entity){ + this._entities.push(entity); + this.onAdded(entity); + } + + public onAdded(entity: Entity){ + } + + public remove(entity: Entity){ + this._entities.remove(entity); + this.onRemoved(entity); + } + + public onRemoved(entity: Entity){ + } public update(){ diff --git a/source/src/ECS/Utils/BitSet.ts b/source/src/ECS/Utils/BitSet.ts new file mode 100644 index 00000000..3bbd10e8 --- /dev/null +++ b/source/src/ECS/Utils/BitSet.ts @@ -0,0 +1,129 @@ +/** + * 这个类可以从两方面来考虑。你可以把它看成一个位向量或者一组非负整数。这个名字有点误导人。 + * + * 它是由一个位向量实现的,但同样可以把它看成是一个非负整数的集合;集合中的每个整数由对应索引处的集合位表示。该结构的大小由集合中的最大整数决定。 + */ +class BitSet{ + private static LONG_MASK: number = 0x3f; + private _bits: number[]; + + constructor(nbits: number = 64){ + let length = nbits >> 6; + if ((nbits & BitSet.LONG_MASK) != 0) + length ++; + + this._bits = new Array(length); + } + + public and(bs: BitSet){ + let max = Math.min(this._bits.length, bs._bits.length); + let i; + for (let i = 0; i < max; ++i) + this._bits[i] &= bs._bits[i]; + + while (i < this._bits.length) + this._bits[i ++] = 0; + } + + public andNot(bs: BitSet){ + let i = Math.min(this._bits.length, bs._bits.length); + while(--i >= 0) + this._bits[i] &= ~bs._bits[i]; + } + + public cardinality(): number{ + let card = 0; + for (let i = this._bits.length - 1; i >= 0; i --){ + let a = this._bits[i]; + + if (a == 0) + continue; + + if (a == -1){ + card += 64; + continue; + } + + a = ((a >> 1) & 0x5555555555555555) + (a & 0x5555555555555555); + a = ((a >> 2) & 0x3333333333333333) + (a & 0x3333333333333333); + let b = ((a >> 32) + a); + b = ((b >> 4) & 0x0f0f0f0f) + (b & 0x0f0f0f0f); + b = ((b >> 8) & 0x00ff00ff) + (b & 0x00ff00ff); + card += ((b >> 16) & 0x0000ffff) + (b & 0x0000ffff); + } + + return card; + } + + public clear(pos?: number){ + if (pos != undefined){ + let offset = pos >> 6; + this.ensure(offset); + this._bits[offset] &= ~(1 << pos); + }else{ + for (let i = 0; i < this._bits.length; i ++) + this._bits[i] = 0; + } + } + + private ensure(lastElt: number){ + if (lastElt >= this._bits.length){ + let nd = new Number[lastElt + 1]; + nd = this._bits.copyWithin(0, 0, this._bits.length); + this._bits = nd; + } + } + + public get(pos: number): boolean{ + let offset = pos >> 6; + if (offset >= this._bits.length) + return false; + + return (this._bits[offset] & (1 << pos)) != 0; + } + + public intersects(set: BitSet){ + let i = Math.min(this._bits.length, set._bits.length); + while (--i >= 0){ + if ((this._bits[i] & set._bits[i]) != 0) + return true; + } + + return false; + } + + public isEmpty(): boolean{ + for (let i = this._bits.length - 1; i >= 0; i --){ + if (this._bits[i] != 0) + return false; + } + + return true; + } + + public nextSetBit(from: number){ + let offset = from >> 6; + let mask = 1 << from; + while (offset < this._bits.length){ + let h = this._bits[offset]; + do { + if ((h & mask) != 0) + return from; + + mask <<= 1; + from ++; + } while (mask != 0); + + mask = 1; + offset ++; + } + + return -1; + } + + public set(pos: number){ + let offset = pos >> 6; + this.ensure(offset); + this._bits[offset] |= 1 << pos; + } +} \ No newline at end of file diff --git a/source/src/ECS/Utils/ComponentTypeManager.ts b/source/src/ECS/Utils/ComponentTypeManager.ts new file mode 100644 index 00000000..f8ca330c --- /dev/null +++ b/source/src/ECS/Utils/ComponentTypeManager.ts @@ -0,0 +1,18 @@ +class ComponentTypeManager{ + private static _componentTypesMask: Map = new Map(); + + public static add(type){ + if (!this._componentTypesMask.has(type)) + this._componentTypesMask[type] = this._componentTypesMask.size; + } + + public static getIndexFor(type){ + let v = -1; + if (!this._componentTypesMask.has(type)){ + this.add(type); + v = this._componentTypesMask.get(type); + } + + return v; + } +} \ No newline at end of file diff --git a/source/src/ECS/Utils/EntityList.ts b/source/src/ECS/Utils/EntityList.ts new file mode 100644 index 00000000..47415e07 --- /dev/null +++ b/source/src/ECS/Utils/EntityList.ts @@ -0,0 +1,92 @@ +class EntityList{ + public scene: Scene; + private _entitiesToRemove: Entity[] = []; + private _entitiesToAdded: Entity[] = []; + private _tempEntityList: Entity[] = []; + private _entities: Entity[] = []; + + constructor(scene: Scene){ + this.scene = scene; + } + + public get count(){ + return this._entities.length; + } + + public get buffer(){ + return this._entities; + } + + public add(entity: Entity){ + this._entitiesToAdded.push(entity); + } + + public remove(entity: Entity){ + if (this._entitiesToAdded.contains(entity)){ + this._entitiesToAdded.remove(entity); + return; + } + + if (!this._entitiesToRemove.contains(entity)) + this._entitiesToRemove.push(entity); + } + + public findEntity(name: string){ + for (let i = 0; i < this._entities.length; i ++){ + if (this._entities[i].name == name) + return this._entities[i]; + } + + return this._entitiesToAdded.firstOrDefault(entity => entity.name == name); + } + + public update(){ + for (let i = 0; i < this._entities.length; i++){ + let entity = this._entities[i]; + if (entity.enabled) + entity.update(); + } + } + + public removeAllEntities(){ + this._entitiesToAdded.length = 0; + + this.updateLists(); + + for (let i = 0; i < this._entities.length; i ++){ + this._entities[i].scene = null; + } + + this._entities.length = 0; + } + + public updateLists(){ + if (this._entitiesToRemove.length > 0){ + let temp = this._entitiesToRemove; + this._entitiesToRemove = this._tempEntityList; + this._tempEntityList = temp; + this._tempEntityList.forEach(entity => { + this._entities.remove(entity); + entity.scene = null; + + this.scene.entityProcessors.forEach(processor => processor.remove(entity)); + }); + + this._tempEntityList.length = 0; + } + + if (this._entitiesToAdded.length > 0){ + let temp = this._entitiesToAdded; + this._entitiesToAdded = this._tempEntityList; + this._tempEntityList = temp; + this._tempEntityList.forEach(entity => { + this._entities.push(entity); + entity.scene = this.scene; + + this.scene.entityProcessors.forEach(processor => processor.onChanged(entity)); + }); + + this._tempEntityList.length = 0; + } + } +} \ No newline at end of file diff --git a/source/src/ECS/Utils/Matcher.ts b/source/src/ECS/Utils/Matcher.ts index 04c7cc4d..f702f4f4 100644 --- a/source/src/ECS/Utils/Matcher.ts +++ b/source/src/ECS/Utils/Matcher.ts @@ -1,5 +1,26 @@ class Matcher{ + protected allSet = new BitSet(); + protected exclusionSet = new BitSet(); + protected oneSet = new BitSet(); + public static empty(){ return new Matcher(); } + + public IsIntersted(e: Entity){ + if (!this.allSet.isEmpty()){ + for (let i = this.allSet.nextSetBit(0); i >= 0; i = this.allSet.nextSetBit(i + 1)){ + if (!e.componentBits.get(i)) + return false; + } + } + + if (!this.exclusionSet.isEmpty() && this.exclusionSet.intersects(e.componentBits)) + return false; + + if (!this.oneSet.isEmpty() && !this.oneSet.intersects(e.componentBits)) + return false; + + return true; + } } \ No newline at end of file