UNPKG

ecspresso

Version:

A minimal Entity-Component-System library for typescript and javascript.

5 lines (3 loc) 6.6 kB
var d=((z)=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(z,{get:(J,Q)=>(typeof require<"u"?require:J)[Q]}):z)(function(z){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+z+'" is not supported')});import{definePlugin as h}from"ecspresso";function X(z){let J=new Map,Q=new WeakMap,N=1;function V(Z){let $=J.get(Z);if($!==void 0)return $;if(N===0)throw Error(`[ecspresso] ${z} layer bitmask overflow: more than 32 distinct layers registered`);let j=N;return J.set(Z,j),N<<=1,j}function O(Z){let $=Q.get(Z);if($!==void 0)return $;let j=0;for(let F=0;F<Z.length;F++)j|=V(Z[F]);return Q.set(Z,j),j}return{getLayerBit:V,getCollidesWithMask:O}}var L={normalX:0,normalY:0,depth:0},H=0,W=1,k=X("Collision"),v=k.getLayerBit,B=k.getCollidesWithMask;function E(z,J,Q,N,V,O,Z,$){if(z.entityId=J,z.layer=V,z.collidesWith=O,z.layerBit=v(V),z.collidesWithMask=B(O),Z)return z.x=Q+(Z.offsetX??0),z.y=N+(Z.offsetY??0),z.shape=H,z.halfWidth=Z.width/2,z.halfHeight=Z.height/2,z.radius=0,!0;if($)return z.x=Q+($.offsetX??0),z.y=N+($.offsetY??0),z.shape=W,z.halfWidth=0,z.halfHeight=0,z.radius=$.radius,!0;return!1}function C(z,J,Q,N,V,O,Z,$,j){let F=V-z,M=O-J,G=Q+Z-Math.abs(F),D=N+$-Math.abs(M);if(G<=0||D<=0)return!1;if(G<D)return j.normalX=F>=0?1:-1,j.normalY=0,j.depth=G,!0;return j.normalX=0,j.normalY=M>=0?1:-1,j.depth=D,!0}function x(z,J,Q,N,V,O,Z){let $=N-z,j=V-J,F=$*$+j*j,M=Q+O;if(F>=M*M)return!1;let G=Math.sqrt(F);if(G===0)return Z.normalX=1,Z.normalY=0,Z.depth=M,!0;return Z.normalX=$/G,Z.normalY=j/G,Z.depth=M-G,!0}function Y(z,J,Q,N,V,O,Z,$){let j=Math.max(z-Q,Math.min(V,z+Q)),F=Math.max(J-N,Math.min(O,J+N)),M=V-j,G=O-F,D=M*M+G*G;if(D>=Z*Z)return!1;if(D===0){let U=V-(z-Q),T=z+Q-V,R=O-(J-N),q=J+N-O,g=Math.min(U,T,R,q);if(g===T)return $.normalX=1,$.normalY=0,$.depth=T+Z,!0;if(g===U)return $.normalX=-1,$.normalY=0,$.depth=U+Z,!0;if(g===q)return $.normalX=0,$.normalY=1,$.depth=q+Z,!0;return $.normalX=0,$.normalY=-1,$.depth=R+Z,!0}let K=Math.sqrt(D);return $.normalX=M/K,$.normalY=G/K,$.depth=Z-K,!0}function w(z,J,Q){if(z.shape===H&&J.shape===H)return C(z.x,z.y,z.halfWidth,z.halfHeight,J.x,J.y,J.halfWidth,J.halfHeight,Q);if(z.shape===W&&J.shape===W)return x(z.x,z.y,z.radius,J.x,J.y,J.radius,Q);if(z.shape===H&&J.shape===W)return Y(z.x,z.y,z.halfWidth,z.halfHeight,J.x,J.y,J.radius,Q);if(!Y(J.x,J.y,J.halfWidth,J.halfHeight,z.x,z.y,z.radius,Q))return!1;return Q.normalX=-Q.normalX,Q.normalY=-Q.normalY,!0}var I=[];function m(){return{arr:[],gen:[],current:0}}var S=!1,p=50;function _(z,J,Q,N,V,O){if(N)y(z,J,Q,N,V,O);else f(z,J,V,O)}function f(z,J,Q,N){if(!S&&J>=p)S=!0,console.warn(`[ecspresso] Collision detection is using O(n²) brute force with ${J} colliders. For better performance, install createSpatialIndexPlugin() alongside your collision or physics2D plugin.`);for(let V=0;V<J;V++){let O=z[V];if(!O)continue;for(let Z=V+1;Z<J;Z++){let $=z[Z];if(!$)continue;if((O.collidesWithMask&$.layerBit|$.collidesWithMask&O.layerBit)===0)continue;if(!w(O,$,L))continue;Q(O,$,L,N)}}}function y(z,J,Q,N,V,O){let{arr:Z,gen:$}=Q,j=++Q.current;for(let F=0;F<J;F++){let M=z[F];if(!M)continue;let G=M.entityId;Z[G]=M,$[G]=j}for(let F=0;F<J;F++){let M=z[F];if(!M)continue;let G=M.shape===H?M.halfWidth:M.radius,D=M.shape===H?M.halfHeight:M.radius;I.length=0,N.queryRectInto(M.x-G,M.y-D,M.x+G,M.y+D,I,M.entityId);for(let K of I){if($[K]!==j)continue;let U=Z[K];if(!U)continue;if((M.collidesWithMask&U.layerBit|U.collidesWithMask&M.layerBit)===0)continue;if(!w(M,U,L))continue;V(M,U,L,O)}}}function a(z,J,Q,N){let V={width:z,height:J};if(Q!==void 0)V.offsetX=Q;if(N!==void 0)V.offsetY=N;return{aabbCollider:V}}function t(z,J,Q){let N={radius:z};if(J!==void 0)N.offsetX=J;if(Q!==void 0)N.offsetY=Q;return{circleCollider:N}}function b(z,J){return{collisionLayer:{layer:z,collidesWith:J}}}function o(z){let J={};for(let Q of Object.keys(z)){let N=z[Q];J[Q]=()=>b(Q,N)}return J}function A(z){let J=z.indexOf(":");if(J===-1)throw Error(`Invalid collision pair key "${z}": must contain a colon separator (e.g. "player:enemy")`);let Q=z.slice(0,J),N=z.slice(J+1);if(Q===""||N==="")throw Error(`Invalid collision pair key "${z}": layer names must not be empty`);return[Q,N]}function e(z){let J=new Map,Q=new Set;for(let N of Object.keys(z))A(N),Q.add(N);for(let N of Object.keys(z)){let[V,O]=A(N),Z=z[N];if(!Z)continue;J.set(N,{callback:Z,swapped:!1});let $=`${O}:${V}`;if($!==N&&!Q.has($))J.set($,{callback:Z,swapped:!0})}return function({data:V,ecs:O}){let Z=J.get(V.layerA+":"+V.layerB);if(!Z)return;if(Z.swapped)Z.callback(V.entityB,V.entityA,O);else Z.callback(V.entityA,V.entityB,O)}}var P={entityA:0,entityB:0,layerA:"",layerB:"",normalX:0,normalY:0,depth:0};function u(z,J,Q,N){P.entityA=z.entityId,P.entityB=J.entityId,P.layerA=z.layer,P.layerB=J.layer,P.normalX=Q.normalX,P.normalY=Q.normalY,P.depth=Q.depth,N.publish("collision",P)}function zz(z){let{systemGroup:J="physics",priority:Q=0,phase:N="postUpdate"}=z;return h("collision").withComponentTypes().withEventTypes().withLabels().withGroups().requires().install((V)=>{let O=[],Z=m(),$,j=!1,F=(M,G,D,K)=>{let U=O[M];if(!U)U={entityId:G,x:0,y:0,layer:D,collidesWith:K,layerBit:0,collidesWithMask:0,shape:H,halfWidth:0,halfHeight:0,radius:0},O[M]=U;return U};V.addSystem("collision-detection").setPriority(Q).inPhase(N).inGroup(J).addQuery("aabbOnly",{with:["worldTransform","collisionLayer","aabbCollider"],without:["circleCollider"]}).addQuery("circleOnly",{with:["worldTransform","collisionLayer","circleCollider"],without:["aabbCollider"]}).addQuery("both",{with:["worldTransform","collisionLayer","aabbCollider","circleCollider"]}).setProcess(({queries:M,ecs:G})=>{let D=0;for(let K of M.aabbOnly){let{worldTransform:U,collisionLayer:T,aabbCollider:R}=K.components,q=F(D,K.id,T.layer,T.collidesWith);if(!E(q,K.id,U.x,U.y,T.layer,T.collidesWith,R,void 0))continue;D++}for(let K of M.circleOnly){let{worldTransform:U,collisionLayer:T,circleCollider:R}=K.components,q=F(D,K.id,T.layer,T.collidesWith);if(!E(q,K.id,U.x,U.y,T.layer,T.collidesWith,void 0,R))continue;D++}for(let K of M.both){let{worldTransform:U,collisionLayer:T,aabbCollider:R,circleCollider:q}=K.components,g=F(D,K.id,T.layer,T.collidesWith);if(!E(g,K.id,U.x,U.y,T.layer,T.collidesWith,R,q))continue;D++}if(!j)$=G.tryGetResource("spatialIndex"),j=!0;_(O,D,Z,$,u,G.eventBus)})})}export{o as defineCollisionLayers,zz as createCollisionPlugin,e as createCollisionPairHandler,b as createCollisionLayer,t as createCircleCollider,a as createAABBCollider}; //# debugId=54872B8338B09DBE64756E2164756E21 //# sourceMappingURL=collision.js.map