ecspresso
Version:
A minimal Entity-Component-System library for typescript and javascript.
5 lines (3 loc) • 12.2 kB
JavaScript
var kJ=((J)=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(J,{get:(Q,N)=>(typeof require<"u"?require:Q)[N]}):J)(function(J){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+J+'" is not supported')});import{definePlugin as OJ}from"ecspresso";function X(J){let Q=new Map,N=new WeakMap,j=1;function K(O){let $=Q.get(O);if($!==void 0)return $;if(j===0)throw Error(`[ecspresso] ${J} layer bitmask overflow: more than 32 distinct layers registered`);let F=j;return Q.set(O,F),j<<=1,F}function G(O){let $=N.get(O);if($!==void 0)return $;let F=0;for(let z=0;z<O.length;z++)F|=K(O[z]);return N.set(O,F),F}return{getLayerBit:K,getCollidesWithMask:G}}var E={normalX:0,normalY:0,depth:0},H=0,Y=1,b=X("Collision"),e=b.getLayerBit,o=b.getCollidesWithMask;function S(J,Q,N,j,K,G,O,$){if(J.entityId=Q,J.layer=K,J.collidesWith=G,J.layerBit=e(K),J.collidesWithMask=o(G),O)return J.x=N+(O.offsetX??0),J.y=j+(O.offsetY??0),J.shape=H,J.halfWidth=O.width/2,J.halfHeight=O.height/2,J.radius=0,!0;if($)return J.x=N+($.offsetX??0),J.y=j+($.offsetY??0),J.shape=Y,J.halfWidth=0,J.halfHeight=0,J.radius=$.radius,!0;return!1}function JJ(J,Q,N,j,K,G,O,$,F){let z=K-J,V=G-Q,Z=N+O-Math.abs(z),U=j+$-Math.abs(V);if(Z<=0||U<=0)return!1;if(Z<U)return F.normalX=z>=0?1:-1,F.normalY=0,F.depth=Z,!0;return F.normalX=0,F.normalY=V>=0?1:-1,F.depth=U,!0}function QJ(J,Q,N,j,K,G,O){let $=j-J,F=K-Q,z=$*$+F*F,V=N+G;if(z>=V*V)return!1;let Z=Math.sqrt(z);if(Z===0)return O.normalX=1,O.normalY=0,O.depth=V,!0;return O.normalX=$/Z,O.normalY=F/Z,O.depth=V-Z,!0}function f(J,Q,N,j,K,G,O,$){let F=Math.max(J-N,Math.min(K,J+N)),z=Math.max(Q-j,Math.min(G,Q+j)),V=K-F,Z=G-z,U=V*V+Z*Z;if(U>=O*O)return!1;if(U===0){let M=K-(J-N),q=J+N-K,W=G-(Q-j),R=Q+j-G,k=Math.min(M,q,W,R);if(k===q)return $.normalX=1,$.normalY=0,$.depth=q+O,!0;if(k===M)return $.normalX=-1,$.normalY=0,$.depth=M+O,!0;if(k===R)return $.normalX=0,$.normalY=1,$.depth=R+O,!0;return $.normalX=0,$.normalY=-1,$.depth=W+O,!0}let T=Math.sqrt(U);return $.normalX=V/T,$.normalY=Z/T,$.depth=O-T,!0}function d(J,Q,N){if(J.shape===H&&Q.shape===H)return JJ(J.x,J.y,J.halfWidth,J.halfHeight,Q.x,Q.y,Q.halfWidth,Q.halfHeight,N);if(J.shape===Y&&Q.shape===Y)return QJ(J.x,J.y,J.radius,Q.x,Q.y,Q.radius,N);if(J.shape===H&&Q.shape===Y)return f(J.x,J.y,J.halfWidth,J.halfHeight,Q.x,Q.y,Q.radius,N);if(!f(Q.x,Q.y,Q.halfWidth,Q.halfHeight,J.x,J.y,J.radius,N))return!1;return N.normalX=-N.normalX,N.normalY=-N.normalY,!0}var x=[];function u(){return{arr:[],gen:[],current:0}}var h=!1,$J=50;function c(J,Q,N,j,K,G){if(j)VJ(J,Q,N,j,K,G);else NJ(J,Q,K,G)}function NJ(J,Q,N,j){if(!h&&Q>=$J)h=!0,console.warn(`[ecspresso] Collision detection is using O(n²) brute force with ${Q} colliders. For better performance, install createSpatialIndexPlugin() alongside your collision or physics2D plugin.`);for(let K=0;K<Q;K++){let G=J[K];if(!G)continue;for(let O=K+1;O<Q;O++){let $=J[O];if(!$)continue;if((G.collidesWithMask&$.layerBit|$.collidesWithMask&G.layerBit)===0)continue;if(!d(G,$,E))continue;N(G,$,E,j)}}}function VJ(J,Q,N,j,K,G){let{arr:O,gen:$}=N,F=++N.current;for(let z=0;z<Q;z++){let V=J[z];if(!V)continue;let Z=V.entityId;O[Z]=V,$[Z]=F}for(let z=0;z<Q;z++){let V=J[z];if(!V)continue;let Z=V.shape===H?V.halfWidth:V.radius,U=V.shape===H?V.halfHeight:V.radius;x.length=0,j.queryRectInto(V.x-Z,V.y-U,V.x+Z,V.y+U,x,V.entityId);for(let T of x){if($[T]!==F)continue;let M=O[T];if(!M)continue;if((V.collidesWithMask&M.layerBit|M.collidesWithMask&V.layerBit)===0)continue;if(!d(V,M,E))continue;K(V,M,E,G)}}}function YJ(J,Q,N,j){let K={width:J,height:Q};if(N!==void 0)K.offsetX=N;if(j!==void 0)K.offsetY=j;return{aabbCollider:K}}function EJ(J,Q,N){let j={radius:J};if(Q!==void 0)j.offsetX=Q;if(N!==void 0)j.offsetY=N;return{circleCollider:j}}function r(J,Q){return{collisionLayer:{layer:J,collidesWith:Q}}}function jJ(J){let Q={};for(let N of Object.keys(J)){let j=J[N];Q[N]=()=>r(N,j)}return Q}function n(J){let Q=J.indexOf(":");if(Q===-1)throw Error(`Invalid collision pair key "${J}": must contain a colon separator (e.g. "player:enemy")`);let N=J.slice(0,Q),j=J.slice(Q+1);if(N===""||j==="")throw Error(`Invalid collision pair key "${J}": layer names must not be empty`);return[N,j]}function KJ(J){let Q=new Map,N=new Set;for(let j of Object.keys(J))n(j),N.add(j);for(let j of Object.keys(J)){let[K,G]=n(j),O=J[j];if(!O)continue;Q.set(j,{callback:O,swapped:!1});let $=`${G}:${K}`;if($!==j&&!N.has($))Q.set($,{callback:O,swapped:!0})}return function({data:K,ecs:G}){let O=Q.get(K.layerA+":"+K.layerB);if(!O)return;if(O.swapped)O.callback(K.entityB,K.entityA,G);else O.callback(K.entityA,K.entityB,G)}}var P={entityA:0,entityB:0,layerA:"",layerB:"",normalX:0,normalY:0,depth:0};function GJ(J,Q,N,j){P.entityA=J.entityId,P.entityB=Q.entityId,P.layerA=J.layer,P.layerB=Q.layer,P.normalX=N.normalX,P.normalY=N.normalY,P.depth=N.depth,j.publish("collision",P)}function SJ(J){let{systemGroup:Q="physics",priority:N=0,phase:j="postUpdate"}=J;return OJ("collision").withComponentTypes().withEventTypes().withLabels().withGroups().requires().install((K)=>{let G=[],O=u(),$,F=!1,z=(V,Z,U,T)=>{let M=G[V];if(!M)M={entityId:Z,x:0,y:0,layer:U,collidesWith:T,layerBit:0,collidesWithMask:0,shape:H,halfWidth:0,halfHeight:0,radius:0},G[V]=M;return M};K.addSystem("collision-detection").setPriority(N).inPhase(j).inGroup(Q).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:V,ecs:Z})=>{let U=0;for(let T of V.aabbOnly){let{worldTransform:M,collisionLayer:q,aabbCollider:W}=T.components,R=z(U,T.id,q.layer,q.collidesWith);if(!S(R,T.id,M.x,M.y,q.layer,q.collidesWith,W,void 0))continue;U++}for(let T of V.circleOnly){let{worldTransform:M,collisionLayer:q,circleCollider:W}=T.components,R=z(U,T.id,q.layer,q.collidesWith);if(!S(R,T.id,M.x,M.y,q.layer,q.collidesWith,void 0,W))continue;U++}for(let T of V.both){let{worldTransform:M,collisionLayer:q,aabbCollider:W,circleCollider:R}=T.components,k=z(U,T.id,q.layer,q.collidesWith);if(!S(k,T.id,M.x,M.y,q.layer,q.collidesWith,W,R))continue;U++}if(!F)$=Z.tryGetResource("spatialIndex"),F=!0;c(G,U,O,$,GJ,Z.eventBus)})})}import{definePlugin as RJ}from"ecspresso";var m={normalX:0,normalY:0,normalZ:0,depth:0},g=0,D=1,i=X("3D collision"),UJ=i.getLayerBit,FJ=i.getCollidesWithMask;function _(J,Q,N,j,K,G,O,$,F){if(J.entityId=Q,J.layer=G,J.collidesWith=O,J.layerBit=UJ(G),J.collidesWithMask=FJ(O),$)return J.x=N+($.offsetX??0),J.y=j+($.offsetY??0),J.z=K+($.offsetZ??0),J.shape=g,J.halfWidth=$.width/2,J.halfHeight=$.height/2,J.halfDepth=$.depth/2,J.radius=0,!0;if(F)return J.x=N+(F.offsetX??0),J.y=j+(F.offsetY??0),J.z=K+(F.offsetZ??0),J.shape=D,J.halfWidth=0,J.halfHeight=0,J.halfDepth=0,J.radius=F.radius,!0;return!1}function MJ(J,Q,N,j,K,G,O,$,F,z,V,Z,U){let T=O-J,M=$-Q,q=F-N,W=j+z-Math.abs(T),R=K+V-Math.abs(M),k=G+Z-Math.abs(q);if(W<=0||R<=0||k<=0)return!1;if(W<=R&&W<=k)return U.normalX=T>=0?1:-1,U.normalY=0,U.normalZ=0,U.depth=W,!0;if(R<=k)return U.normalX=0,U.normalY=M>=0?1:-1,U.normalZ=0,U.depth=R,!0;return U.normalX=0,U.normalY=0,U.normalZ=q>=0?1:-1,U.depth=k,!0}function zJ(J,Q,N,j,K,G,O,$,F){let z=K-J,V=G-Q,Z=O-N,U=z*z+V*V+Z*Z,T=j+$;if(U>=T*T)return!1;let M=Math.sqrt(U);if(M===0)return F.normalX=1,F.normalY=0,F.normalZ=0,F.depth=T,!0;return F.normalX=z/M,F.normalY=V/M,F.normalZ=Z/M,F.depth=T-M,!0}function s(J,Q,N,j,K,G,O,$,F,z,V){let Z=Math.max(J-j,Math.min(O,J+j)),U=Math.max(Q-K,Math.min($,Q+K)),T=Math.max(N-G,Math.min(F,N+G)),M=O-Z,q=$-U,W=F-T,R=M*M+q*q+W*W;if(R>=z*z)return!1;if(R===0){let w=O-(J-j),v=J+j-O,A=$-(Q-K),C=Q+K-$,p=F-(N-G),B=N+G-F,I=Math.min(w,v,A,C,p,B);if(I===v)return V.normalX=1,V.normalY=0,V.normalZ=0,V.depth=v+z,!0;if(I===w)return V.normalX=-1,V.normalY=0,V.normalZ=0,V.depth=w+z,!0;if(I===C)return V.normalX=0,V.normalY=1,V.normalZ=0,V.depth=C+z,!0;if(I===A)return V.normalX=0,V.normalY=-1,V.normalZ=0,V.depth=A+z,!0;if(I===B)return V.normalX=0,V.normalY=0,V.normalZ=1,V.depth=B+z,!0;return V.normalX=0,V.normalY=0,V.normalZ=-1,V.depth=p+z,!0}let k=Math.sqrt(R);return V.normalX=M/k,V.normalY=q/k,V.normalZ=W/k,V.depth=z-k,!0}function t(J,Q,N){if(J.shape===g&&Q.shape===g)return MJ(J.x,J.y,J.z,J.halfWidth,J.halfHeight,J.halfDepth,Q.x,Q.y,Q.z,Q.halfWidth,Q.halfHeight,Q.halfDepth,N);if(J.shape===D&&Q.shape===D)return zJ(J.x,J.y,J.z,J.radius,Q.x,Q.y,Q.z,Q.radius,N);if(J.shape===g&&Q.shape===D)return s(J.x,J.y,J.z,J.halfWidth,J.halfHeight,J.halfDepth,Q.x,Q.y,Q.z,Q.radius,N);if(!s(Q.x,Q.y,Q.z,Q.halfWidth,Q.halfHeight,Q.halfDepth,J.x,J.y,J.z,J.radius,N))return!1;return N.normalX=-N.normalX,N.normalY=-N.normalY,N.normalZ=-N.normalZ,!0}var y=[],l=!1,TJ=50;function a(J,Q,N,j,K,G){if(j)ZJ(J,Q,N,j,K,G);else qJ(J,Q,K,G)}function qJ(J,Q,N,j){if(!l&&Q>=TJ)l=!0,console.warn(`[ecspresso] 3D collision detection is using O(n²) brute force with ${Q} colliders. For better performance, install createSpatialIndex3DPlugin() alongside your collision or physics3D plugin.`);for(let K=0;K<Q;K++){let G=J[K];if(!G)continue;for(let O=K+1;O<Q;O++){let $=J[O];if(!$)continue;if((G.collidesWithMask&$.layerBit|$.collidesWithMask&G.layerBit)===0)continue;if(!t(G,$,m))continue;N(G,$,m,j)}}}function ZJ(J,Q,N,j,K,G){N.clear();for(let O=0;O<Q;O++){let $=J[O];if(!$)continue;N.set($.entityId,$)}for(let O=0;O<Q;O++){let $=J[O];if(!$)continue;let F=$.shape===g?$.halfWidth:$.radius,z=$.shape===g?$.halfHeight:$.radius,V=$.shape===g?$.halfDepth:$.radius;y.length=0,j.queryBoxInto($.x-F,$.y-z,$.z-V,$.x+F,$.y+z,$.z+V,y,$.entityId);for(let Z of y){let U=N.get(Z);if(!U)continue;if(($.collidesWithMask&U.layerBit|U.collidesWithMask&$.layerBit)===0)continue;if(!t($,U,m))continue;K($,U,m,G)}}}function CJ(J,Q,N,j,K,G){let O={width:J,height:Q,depth:N};if(j!==void 0)O.offsetX=j;if(K!==void 0)O.offsetY=K;if(G!==void 0)O.offsetZ=G;return{aabb3DCollider:O}}function BJ(J,Q,N,j){let K={radius:J};if(Q!==void 0)K.offsetX=Q;if(N!==void 0)K.offsetY=N;if(j!==void 0)K.offsetZ=j;return{sphereCollider:K}}var L={entityA:0,entityB:0,layerA:"",layerB:"",normalX:0,normalY:0,normalZ:0,depth:0};function WJ(J,Q,N,j){L.entityA=J.entityId,L.entityB=Q.entityId,L.layerA=J.layer,L.layerB=Q.layer,L.normalX=N.normalX,L.normalY=N.normalY,L.normalZ=N.normalZ,L.depth=N.depth,j.publish("collision3D",L)}function xJ(J){let{systemGroup:Q="physics",priority:N=0,phase:j="postUpdate"}=J;return RJ("collision3D").withComponentTypes().withEventTypes().withLabels().withGroups().requires().install((K)=>{let G=[],O=new Map,$,F=!1,z=(V,Z,U,T)=>{let M=G[V];if(!M)M={entityId:Z,x:0,y:0,z:0,layer:U,collidesWith:T,layerBit:0,collidesWithMask:0,shape:g,halfWidth:0,halfHeight:0,halfDepth:0,radius:0},G[V]=M;return M};K.addSystem("collision3D-detection").setPriority(N).inPhase(j).inGroup(Q).addQuery("aabbOnly",{with:["worldTransform3D","collisionLayer","aabb3DCollider"],without:["sphereCollider"]}).addQuery("sphereOnly",{with:["worldTransform3D","collisionLayer","sphereCollider"],without:["aabb3DCollider"]}).addQuery("both",{with:["worldTransform3D","collisionLayer","aabb3DCollider","sphereCollider"]}).setProcess(({queries:V,ecs:Z})=>{let U=0;for(let T of V.aabbOnly){let{worldTransform3D:M,collisionLayer:q,aabb3DCollider:W}=T.components,R=z(U,T.id,q.layer,q.collidesWith);if(!_(R,T.id,M.x,M.y,M.z,q.layer,q.collidesWith,W,void 0))continue;U++}for(let T of V.sphereOnly){let{worldTransform3D:M,collisionLayer:q,sphereCollider:W}=T.components,R=z(U,T.id,q.layer,q.collidesWith);if(!_(R,T.id,M.x,M.y,M.z,q.layer,q.collidesWith,void 0,W))continue;U++}for(let T of V.both){let{worldTransform3D:M,collisionLayer:q,aabb3DCollider:W,sphereCollider:R}=T.components,k=z(U,T.id,q.layer,q.collidesWith);if(!_(k,T.id,M.x,M.y,M.z,q.layer,q.collidesWith,W,R))continue;U++}if(!F)$=Z.tryGetResource("spatialIndex3D"),F=!0;a(G,U,O,$,WJ,Z.eventBus)})})}export{jJ as defineCollisionLayers,BJ as createSphereCollider,KJ as createCollisionPairHandler,r as createCollisionLayer,xJ as createCollision3DPlugin,CJ as createAABB3DCollider};
//# debugId=D3C63757EDEB721A64756E2164756E21
//# sourceMappingURL=collision3D.js.map