UNPKG

wolf-ecs

Version:

An entity component system framework for JavaScript and TypeScript

2 lines (1 loc) 5.56 kB
const t={custom:function(t){return t?[t]:[]},any:Array,int8:Int8Array,i8:Int8Array,char:Int8Array,uint8:Uint8Array,u8:Uint8Array,uchar:Uint8Array,int16:Int16Array,i16:Int16Array,short:Int16Array,uint16:Uint16Array,u16:Uint16Array,ushort:Uint16Array,int32:Int32Array,i32:Int32Array,int:Int32Array,uint32:Uint32Array,u32:Uint32Array,uint:Uint32Array,float32:Float32Array,f32:Float32Array,float:Float32Array,float64:Float64Array,f64:Float64Array,double:Float64Array,int64:BigInt64Array,bigint64:BigInt64Array,i64:BigInt64Array,long:BigInt64Array,uint64:BigUint64Array,biguint64:BigUint64Array,u64:BigUint64Array,ulong:BigUint64Array},e=Symbol("componentData");function r(t,e){if("function"==typeof t)return new t(e);if(t instanceof Array)return t.length?[...new Array(e)].map(t[0]):new Array(e);const n={};for(let s in t)n[s]=r(t[s],e);return n}function n(...t){if(!t.length)throw new Error("no arguments passed");return{op:n,dt:t}}function s(t){return{op:s,dt:"function"==typeof t.op?t:n(t)}}function i(...t){if(!t.length)throw new Error("no arguments passed");return{op:i,dt:t}}class a{mask;archetypes=[];a=this.archetypes;ecs;constructor(t,r){const i=r=>{if(r.op===s)return{op:r.op,dt:i(r.dt)};const n=[],a=[{op:r.op,dt:new Uint32Array}];for(let s of r.dt)if(e in s){if(s[e].ecs!==t)throw new Error("component does not belong to this ECS");n.push(s[e].id)}else a.push(i(s));a[0].dt=new Uint32Array(Math.ceil((Math.max(-1,...n)+1)/32));for(let t of n)a[0].dt[Math.floor(t/32)]|=1<<t%32;return{op:r.op,dt:a}};this.mask=r?i(r):{op:n,dt:new Uint32Array},this.ecs=t}forEach(t){for(let e=0,r=this.a.length;e<r;e++){const r=this.a[e].e;for(let e=r.length;e>0;e--)t(r[e-1],this.ecs)}this.ecs.destroyPending(),this.ecs.updatePending()}_forEach(t){this.forEach(t)}static match(t,e){if("BYTES_PER_ELEMENT"in e.dt)return a.partial(t,e);if(e.op===s)return!a.match(t,e.dt);if(e.op===n){for(let r of e.dt)if(!a.match(t,r))return!1;return!0}for(let r of e.dt)if(a.match(t,r))return!0;return!1}static partial(t,e){if(e.op===n){for(let r=0;r<e.dt.length;r++)if((t[r]&e.dt[r])<e.dt[r])return!1;return!0}for(let r=0;r<e.dt.length;r++)if((t[r]&e.dt[r])>0)return!0;return!1}}class h{packed=[];sparse=[];has(t){return this.sparse[t]<this.packed.length&&this.packed[this.sparse[t]]===t}add(t){this.has(t)||(this.sparse[t]=this.packed.length,this.packed.push(t))}remove(t){if(this.has(t)){const e=this.packed.pop();t!==e&&(this.sparse[e]=this.sparse[t],this.packed[this.sparse[t]]=e)}}}class o{sset=new h;entities=this.sset.packed;e=this.entities;mask;change=[];constructor(t){this.mask=t}has(t){return this.sset.has(t)}}class c{_arch=new Map;_queries=[];_ent=[];_updateTo=[];_toUpdate=new h;_toDestroy=new h;_rm=new h;_empty=new o(new Uint32Array);cmpID=0;entID=0;MAX_ENTITIES;DEFAULT_DEFER;constructor(t=1e4,e=!1){this.MAX_ENTITIES=t,this.DEFAULT_DEFER=e}bind(){const t=c.prototype,e={};for(let r of Object.getOwnPropertyNames(t))"function"==typeof t[r]&&"bind"!==r&&(e[r]=t[r].bind(this));return e}defineComponent(t={}){if(this.entID)throw new Error("cannot define component after entity creation");return this.registerComponent(r(t,this.MAX_ENTITIES))}registerComponent(t){return Object.assign(t,{[e]:{ecs:this,id:this.cmpID++}})}createQuery(...t){const e=new a(this,n(...t));return this._arch.forEach((t=>{a.match(t.mask,e.mask)&&e.a.push(t)})),this._queries.push(e),e}_validID(t){return"number"==typeof t&&!(this._rm.has(t)||this.entID<=t)}_getArch(t){if(!this._arch.has(t.toString())){const e=new o(t.slice());this._arch.set(t.toString(),e);for(let r of this._queries)a.match(t,r.mask)&&r.a.push(e)}return this._arch.get(t.toString())}_hasComponent(t,e){return t[~~(e/32)]&1<<e%32}_archChange(t,e){return t.change[e]||(t.mask[~~(e/32)]^=1<<e%32,t.change[e]=this._getArch(t.mask),t.mask[~~(e/32)]^=1<<e%32),t.change[e]}_crEnt(t){this._ent[t]=this._updateTo[t]=this._empty,this._empty.sset.add(t)}createEntity(){if(this._rm.packed.length){const t=this._rm.packed.pop();return this._crEnt(t),t}if(this.entID||(this._empty.mask=new Uint32Array(Math.ceil(this.cmpID/32)),this._arch.set(this._empty.mask.toString(),this._empty)),this.entID===this.MAX_ENTITIES)throw new Error("maximum entity limit reached");return this._crEnt(this.entID),this.entID++}destroyEntity(t,e=this.DEFAULT_DEFER){e?this._toDestroy.add(t):(this._ent[t].sset.remove(t),this._toDestroy.remove(t),this._rm.add(t))}destroyPending(){for(;this._toDestroy.packed.length>0;)this.destroyEntity(this._toDestroy.packed[0]);this._toDestroy.packed.length=0}addComponent(t,r,n=this.DEFAULT_DEFER){if(!this._validID(t))throw new Error("invalid entity id");const s=r[e].id;return n?this._toUpdate.add(t):this._hasComponent(this._ent[t].mask,s)||(this._ent[t].sset.remove(t),this._ent[t]=this._archChange(this._ent[t],s),this._ent[t].sset.add(t)),this._hasComponent(this._updateTo[t].mask,s)||(this._updateTo[t]=this._archChange(this._updateTo[t],s)),this}removeComponent(t,r,n=this.DEFAULT_DEFER){if(!this._validID(t))throw new Error("invalid entity id");const s=r[e].id;return n?this._toUpdate.add(t):this._hasComponent(this._ent[t].mask,s)&&(this._ent[t].sset.remove(t),this._ent[t]=this._archChange(this._ent[t],s),this._ent[t].sset.add(t)),this._hasComponent(this._updateTo[t].mask,s)&&(this._updateTo[t]=this._archChange(this._updateTo[t],s)),this}updatePending(){const t=this._toUpdate.packed;for(let e=0;e<t.length;e++){const r=t[e];this._validID(r)&&(this._ent[r].sset.remove(r),this._ent[r]=this._updateTo[r],this._ent[r].sset.add(r))}this._toUpdate.packed=[]}}export{c as ECS,n as all,i as any,s as not,t as types};