kippy 0.7.1 → 0.7.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +71 -44
- package/dist/bundle.min.js +1 -1
- package/dist/types/entity.d.ts +8 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
<div align="center">
|
|
2
|
+
<img src="./assets/logo.png"/>
|
|
3
|
+
</div>
|
|
2
4
|
|
|
3
5
|
Kippy is a 2D JS game engine written purely for fun and simplicity. It currently utilizes the Canvas 2D context for rendering and aims to have a small set of APIs viable for game dev, but do expect a lot of components to change in the future.
|
|
4
6
|
|
|
@@ -90,13 +92,19 @@ A scene can have multiple entities like players, mobs, obstacles, etc in it. Thi
|
|
|
90
92
|
import { Entity } from "kippy";
|
|
91
93
|
|
|
92
94
|
const entity = new Entity({
|
|
95
|
+
// These are all optional, and can also be set later
|
|
96
|
+
|
|
97
|
+
// Graphics
|
|
93
98
|
animator, // Entity's animator to be rendered, type Animator
|
|
94
99
|
sprite, // Entity's sprite to be rendered, type Sprite
|
|
100
|
+
// Position
|
|
95
101
|
position, // Entity's position (centered), type Vector2
|
|
96
102
|
rotation, // Entity's rotation in radians, type number
|
|
103
|
+
// Physics
|
|
97
104
|
body, // Entity's physical body, type EntityBody
|
|
98
105
|
collider, // Entity's collider, type Collider
|
|
99
|
-
|
|
106
|
+
// Effects
|
|
107
|
+
glow
|
|
100
108
|
});
|
|
101
109
|
|
|
102
110
|
// Add it to a scene
|
|
@@ -128,6 +136,54 @@ const sprite = new Sprite({
|
|
|
128
136
|
entity.sprite = sprite;
|
|
129
137
|
```
|
|
130
138
|
|
|
139
|
+
### Animation
|
|
140
|
+
|
|
141
|
+
First you create a sprite sheet that contains your frames:
|
|
142
|
+
```js
|
|
143
|
+
import { SpriteSheet } from "kippy";
|
|
144
|
+
|
|
145
|
+
const spriteSheet = new SpriteSheet({
|
|
146
|
+
texture, // Similar to sprite texture
|
|
147
|
+
frameWidth, // Width of each frame, type number
|
|
148
|
+
frameHeight, // Height of each frame, type number
|
|
149
|
+
width, // Width when render, default is frameWidth
|
|
150
|
+
height // Height when render, default is frameHeight
|
|
151
|
+
});
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Then you create an animation object which defines the order of frame to play, fps, and whether to loop or not:
|
|
155
|
+
```js
|
|
156
|
+
import { Animation } from "kippy";
|
|
157
|
+
|
|
158
|
+
const animation = new Animation({
|
|
159
|
+
spriteSheet, // A SpriteSheet instance
|
|
160
|
+
frames, // Order of frames, type number[]
|
|
161
|
+
fps, // Frames per second, type number
|
|
162
|
+
loop // Loop animation endlessly, type boolean
|
|
163
|
+
});
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Then you create an animator, attach it to an entity, and then play an animation:
|
|
167
|
+
```js
|
|
168
|
+
import { Animator } from "kippy";
|
|
169
|
+
|
|
170
|
+
const animator = new Animator({
|
|
171
|
+
animations, // A Record<string, Animation> map that contains all animations
|
|
172
|
+
default // The animation that will be played first, type string
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
// Attach it to an entity
|
|
176
|
+
entity.animator = animator
|
|
177
|
+
|
|
178
|
+
// Play an animation
|
|
179
|
+
entity.animator.play("animationName");
|
|
180
|
+
|
|
181
|
+
// You can also call a handler when animation ends
|
|
182
|
+
entity.animator.onEnd = function(name) {
|
|
183
|
+
// Do something
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
131
187
|
### Add controls
|
|
132
188
|
|
|
133
189
|
Game controls like mouse presses, key presses, touch, and cursor tracking (in the game canvas, not the web window) can be done by using the input handler from your `game` instance:
|
|
@@ -318,58 +374,29 @@ camera.zoom // Camera zoom level, default is 1
|
|
|
318
374
|
camera.screenToWorld(input.pointer); // Return new vector
|
|
319
375
|
```
|
|
320
376
|
|
|
321
|
-
###
|
|
377
|
+
### Audio
|
|
322
378
|
|
|
323
|
-
|
|
324
|
-
```js
|
|
325
|
-
import { SpriteSheet } from "kippy";
|
|
379
|
+
To be added, for now use web's built-in `Audio` class.
|
|
326
380
|
|
|
327
|
-
|
|
328
|
-
texture, // Similar to sprite texture
|
|
329
|
-
frameWidth, // Width of each frame, type number
|
|
330
|
-
frameHeight, // Height of each frame, type number
|
|
331
|
-
width, // Width when render, default is frameWidth
|
|
332
|
-
height // Height when render, default is frameHeight
|
|
333
|
-
});
|
|
334
|
-
```
|
|
381
|
+
### Effects
|
|
335
382
|
|
|
336
|
-
|
|
337
|
-
```js
|
|
338
|
-
import { Animation } from "kippy";
|
|
383
|
+
Kippy comes with some graphical effects to make things look nicer :)
|
|
339
384
|
|
|
340
|
-
|
|
341
|
-
spriteSheet, // A SpriteSheet instance
|
|
342
|
-
frames, // Order of frames, type number[]
|
|
343
|
-
fps, // Frames per second, type number
|
|
344
|
-
loop // Loop animation endlessly, type boolean
|
|
345
|
-
});
|
|
346
|
-
```
|
|
385
|
+
#### Entity glow
|
|
347
386
|
|
|
348
|
-
Then you create an animator, attach it to an entity, and then play an animation:
|
|
349
387
|
```js
|
|
350
|
-
|
|
388
|
+
entity.glow = {
|
|
389
|
+
color, // Color code as string
|
|
390
|
+
bloom, // Glow bloom/range of spread
|
|
391
|
+
intensity, // Glow intensity/boldness
|
|
392
|
+
}
|
|
351
393
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
394
|
+
// You can also assign it during entity initialization in case you forgot
|
|
395
|
+
const newEntity = new Entity({
|
|
396
|
+
glow: { /* Your config */ }
|
|
355
397
|
});
|
|
356
|
-
|
|
357
|
-
// Attach it to an entity
|
|
358
|
-
entity.animator = animator
|
|
359
|
-
|
|
360
|
-
// Play an animation
|
|
361
|
-
entity.animator.play("animationName");
|
|
362
|
-
|
|
363
|
-
// You can also call a handler when animation ends
|
|
364
|
-
entity.animator.onEnd = function(name) {
|
|
365
|
-
// Do something
|
|
366
|
-
}
|
|
367
398
|
```
|
|
368
399
|
|
|
369
|
-
### Audio
|
|
370
|
-
|
|
371
|
-
To be added, for now use web's built-in `Audio` class.
|
|
372
|
-
|
|
373
400
|
### Sleep system
|
|
374
401
|
|
|
375
402
|
When a body's velocity is too low for too long, the body will enter sleep state, which means its position will not be affected by the physics engine until a force is applied, a collision happens, or the velocity is above threshold again, this is to prevent jittering and optimize performance.
|
package/dist/bundle.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
class t{x;y;static ZERO=new t(0,0);static ONE=new t(1,1);static UP=new t(0,-1);static DOWN=new t(0,1);static LEFT=new t(-1,0);static RIGHT=new t(1,0);constructor(t,e){this.x=t,this.y=e}toString(){return`Vector2(${this.x}, ${this.y})`}add(e){return new t(this.x+e.x,this.y+e.y)}sub(e){return new t(this.x-e.x,this.y-e.y)}mul(e){return new t(this.x*e.x,this.y*e.y)}div(e){return new t(this.x/e.x,this.y/e.y)}neg(){return new t(-this.x,-this.y)}scale(e){return new t(this.x*e,this.y*e)}magnitude(){return Math.sqrt(this.x*this.x+this.y*this.y)}magnitudeSquared(){return this.x*this.x+this.y*this.y}normalize(){const e=this.magnitude();return e>0?new t(this.x/e,this.y/e):new t(0,0)}dot(t){return this.x*t.x+this.y*t.y}cross(t){return this.x*t.y-this.y*t.x}project(t){const e=this.dot(t)/t.magnitudeSquared();return t.scale(e)}min(e){return new t(Math.min(this.x,e.x),Math.min(this.y,e.y))}max(e){return new t(Math.max(this.x,e.x),Math.max(this.y,e.y))}floor(){return new t(Math.floor(this.x),Math.floor(this.y))}ceil(){return new t(Math.ceil(this.x),Math.ceil(this.y))}round(){return new t(Math.round(this.x),Math.round(this.y))}distance(t){return Math.sqrt((this.x-t.x)**2+(this.y-t.y)**2)}distanceSquared(t){return(this.x-t.x)**2+(this.y-t.y)**2}copy(){return new t(this.x,this.y)}lerp(t,e){return this.add(t.sub(this).scale(e))}clamp(t){const e=this.magnitude();return e>t?this.scale(t/e):this.copy()}rotate(e){const i=Math.cos(e),s=Math.sin(e);return new t(this.x*i-this.y*s,this.x*s+this.y*i)}orthogonal(){return new t(-this.y,this.x)}angle(){return Math.atan2(this.y,this.x)}angleTo(t){return Math.atan2(t.y-this.y,t.x-this.x)}reflect(t){const e=this.dot(t);return this.sub(t.scale(2*e))}equals(t){return this.x===t.x&&this.y===t.y}}class e{canvas;keys=new Set;keysPressed=new Set;keysReleased=new Set;pointer=new t(0,0);pointers=new Set;pointersPressed=new Set;pointersReleased=new Set;constructor(t){this.canvas=t.canvas,window.addEventListener("keydown",t=>{this.keys.has(t.key)||this.keysPressed.add(t.key),this.keys.add(t.key)}),window.addEventListener("keyup",t=>{this.keys.delete(t.key),this.keysReleased.add(t.key)}),this.canvas.addEventListener("mousemove",t=>{const e=this.canvas.getBoundingClientRect();this.pointer.x=t.clientX-e.left,this.pointer.y=t.clientY-e.top}),this.canvas.addEventListener("mousedown",t=>{this.pointers.has(t.button)||this.pointersPressed.add(t.button),this.pointers.add(t.button)}),this.canvas.addEventListener("mouseup",t=>{this.pointers.delete(t.button),this.pointersReleased.add(t.button)}),this.canvas.addEventListener("touchmove",t=>{t.preventDefault();const e=this.canvas.getBoundingClientRect(),i=t.touches[0];this.pointer.x=i.clientX-e.left,this.pointer.y=i.clientY-e.top}),this.canvas.addEventListener("touchstart",t=>{t.preventDefault(),this.pointers.has(2)||this.pointersPressed.add(2),this.pointers.add(2)}),this.canvas.addEventListener("touchend",t=>{t.preventDefault(),this.pointers.delete(2),this.pointersReleased.add(2)}),this.canvas.addEventListener("contextmenu",t=>{t.preventDefault()})}update(){this.keysPressed.clear(),this.keysReleased.clear(),this.pointersPressed.clear(),this.pointersReleased.clear()}isKeyDown(t){return this.keys.has(t)}isKeyPressed(t){return this.keysPressed.has(t)}isKeyReleased(t){return this.keysReleased.has(t)}isPointerDown(t=0){return this.pointers.has(t)}isPointerPressed(t=0){return this.pointersPressed.has(t)}isPointerReleased(t=0){return this.pointersReleased.has(t)}}class i{velocity;rotationVelocity;mass;inertia;force;torque;restitution;sleepThreshold;sleepTimeThreshold;isSleeping;sleepTimer;constructor(e={}){this.velocity=e.velocity??new t(0,0),this.rotationVelocity=e.rotationVelocity??0,this.mass=e.mass??1,this.inertia=e.inertia??1,this.force=e.force??new t(0,0),this.torque=e.torque??0,this.restitution=e.restitution??0,this.sleepThreshold=e.sleepThreshold??.1,this.sleepTimeThreshold=e.sleepTimeThreshold??.5,this.isSleeping=e.isSleeping??!1,this.sleepTimer=e.sleepTimer??0}wake(){this.isSleeping=!1,this.sleepTimer=0}}class s{radius;offset;isTrigger;layer;mask;constructor(e){this.radius=e.radius,this.offset=e.offset??new t(0,0),this.isTrigger=e.isTrigger??!1,this.layer=e.layer??1,this.mask=e.mask??4294967295}}class o{width;height;offset;isTrigger;layer;mask;constructor(e){this.width=e.width,this.height=e.height,this.offset=e.offset??new t(0,0),this.isTrigger=e.isTrigger??!1,this.layer=e.layer??1,this.mask=e.mask??4294967295}}class n{cellSize;grid;constructor(t={}){this.cellSize=t.cellSize||100,this.grid=t.grid||new Map}clear(){this.grid.clear()}adaptCellSize(t){const e=[];for(const i of t){const t=this.getEntityBounds(i),s=t.maxX-t.minX,o=t.maxY-t.minY,n=Math.max(s,o);e.push(n)}e.sort((t,e)=>t-e);const i=e[Math.floor(.75*e.length)];this.cellSize=2.5*i}insert(t){if(t.collider){const e=this.getEntityBounds(t),i=Math.floor(e.minX/this.cellSize),s=Math.floor(e.maxX/this.cellSize),o=Math.floor(e.minY/this.cellSize),n=Math.floor(e.maxY/this.cellSize);for(let e=i;e<=s;e++)for(let i=o;i<=n;i++){const s=`${e},${i}`;this.grid.has(s)||this.grid.set(s,new Set),this.grid.get(s).add(t)}}}getNearby(t){const e=Math.floor(t.position.x/this.cellSize),i=Math.floor(t.position.y/this.cellSize),s=new Set;for(let o=-1;o<=1;o++)for(let n=-1;n<=1;n++){const r=`${e+o},${i+n}`,a=this.grid.get(r);if(a)for(const e of a)e!==t&&s.add(e)}return Array.from(s)}getEntityBounds(t){if(t.collider instanceof s){const e=t.collider.radius;return{minX:t.position.x-e,maxX:t.position.x+e,minY:t.position.y-e,maxY:t.position.y+e}}if(t.collider instanceof o){const e=t.collider.width,i=t.collider.height;return{minX:t.position.x-e,maxX:t.position.x+e,minY:t.position.y-i,maxY:t.position.y+i}}throw new Error("Collider type not supported")}}class r{collisionPairs=new Map;spatialGrid=new n;entityCount=0;update(e,s){for(const t of e)if(t.body instanceof i){if((t.body.force.magnitudeSquared()>0||0!==t.body.torque||t.body.velocity.magnitudeSquared()>t.body.sleepThreshold**2||Math.abs(t.body.rotationVelocity)>t.body.sleepThreshold)&&t.body.wake(),t.body.isSleeping)continue;t.body.velocity.x+=t.body.force.x/t.body.mass*s,t.body.velocity.y+=t.body.force.y/t.body.mass*s,t.body.rotationVelocity+=t.body.torque/t.body.inertia*s,t.body.force.x=0,t.body.force.y=0,t.body.torque=0}const o=e.filter(t=>t.collider);this.spatialGrid.clear(),this.entityCount!==o.length&&(this.entityCount=o.length,this.spatialGrid.adaptCellSize(o));for(const t of o)this.spatialGrid.insert(t);const n=new Map,r=[];for(const e of o){const i=this.spatialGrid.getNearby(e);for(const s of i)if(s.collider){if(n.has(e)&&n.get(e)?.has(s)||n.has(s)&&n.get(s)?.has(e))continue;const i=this.checkCollision(e,s);if(i.info){e.body?.isSleeping&&s.body?.isSleeping||(e.body?.wake(),s.body?.wake()),n.has(e)||n.set(e,new Map),n.get(e)?.set(s,i.info);this.collisionPairs.get(e)?.has(s)||this.collisionPairs.get(s)?.has(e)?i.isTrigger?(e.onTriggerStay?.(s),s.onTriggerStay?.(e)):(e.onCollisionStay?.(s,i.info),s.onCollisionStay?.(e,{...i.info,normal:new t(-i.info.normal.x,-i.info.normal.y)})):i.isTrigger?(e.onTriggerEnter?.(s),s.onTriggerEnter?.(e)):(e.onCollisionEnter?.(s,i.info),s.onCollisionEnter?.(e,{...i.info,normal:new t(-i.info.normal.x,-i.info.normal.y)})),i.isTrigger||r.push({entityA:e,entityB:s,info:i.info})}}}for(let t=0;t<6;t++)for(const t of r)this.resolveCollision(t.entityA,t.entityB,t.info,s);for(const[e,i]of this.collisionPairs)for(const[s,o]of i){if(!(n.get(e)?.has(s)||n.get(s)?.has(e))){e.collider?.isTrigger||s.collider?.isTrigger?(e.onTriggerExit?.(s),s.onTriggerExit?.(e)):(e.onCollisionExit?.(s,o),s.onCollisionExit?.(e,{...o,normal:new t(-o.normal.x,-o.normal.y)}))}}this.collisionPairs=n;for(const t of e)if(t.body instanceof i){if(t.body.isSleeping)continue;t.position.x+=t.body.velocity.x*s,t.position.y+=t.body.velocity.y*s,t.rotation+=t.body.rotationVelocity*s;const e=t.body.velocity.magnitude(),i=Math.abs(t.body.rotationVelocity);e<t.body.sleepThreshold&&i<t.body.sleepThreshold?(t.body.sleepTimer+=s,t.body.sleepTimer>=t.body.sleepTimeThreshold&&(t.body.isSleeping=!0,t.body.velocity.x=0,t.body.velocity.y=0,t.body.rotationVelocity=0)):t.body.sleepTimer=0}}checkCollision(e,i){if(e.collider&&i.collider){if(0===(e.collider.mask&i.collider.layer)||0===(i.collider.mask&e.collider.layer))return{isTrigger:!1};const n=e.collider.isTrigger||i.collider.isTrigger,r=e.position.add(e.collider.offset),a=i.position.add(i.collider.offset);if(e.collider instanceof s&&i.collider instanceof s){const s=r.distance(a),o=e.collider.radius+i.collider.radius;if(s>=o)return{isTrigger:!1};const h=o-s,l=a.sub(r);let c=s>0?l.scale(1/s):new t(1,0),d=0;const u=this.collisionPairs.get(e)?.get(i)||this.collisionPairs.get(i)?.get(e);return u&&(d=u.accumulatedNormalImpulse),{isTrigger:n,info:{normal:c,penetration:h,contact:r.add(c.scale(e.collider.radius)),accumulatedNormalImpulse:d}}}if(e.collider instanceof o&&i.collider instanceof o){const s=e.collider.width/2,o=e.collider.height/2,h=i.collider.width/2,l=i.collider.height/2,c=a.sub(r),d=s+h-Math.abs(c.x),u=o+l-Math.abs(c.y);if(d<=0||u<=0)return{isTrigger:!1};let y,m;d<u?(m=d,y=new t(c.x<0?-1:1,0)):(m=u,y=new t(0,c.y<0?-1:1));const p=r.x+Math.sign(c.x)*(s-d/2),f=r.y+Math.sign(c.y)*(o-u/2);let g=0;const x=this.collisionPairs.get(e)?.get(i)||this.collisionPairs.get(i)?.get(e);return x&&(g=x.accumulatedNormalImpulse),{isTrigger:n,info:{normal:y,penetration:m,contact:new t(p,f),accumulatedNormalImpulse:g}}}if(e.collider instanceof s&&i.collider instanceof o||e.collider instanceof o&&i.collider instanceof s){const o=e.collider instanceof s,h=o?r:a,l=o?a:r,c=(o?e:i).collider,d=(o?i:e).collider,u=d.width/2,y=d.height/2,m=h.sub(l);let p,f,g;if(Math.abs(m.x)<u&&Math.abs(m.y)<y){const e=u-Math.abs(m.x),i=y-Math.abs(m.y);if(e<i){const i=m.x<0?-1:1,s=new t(i,0);p=o?s.neg():s,f=c.radius+e,g=new t(l.x+i*u,h.y)}else{const e=m.y<0?-1:1,s=new t(0,e);p=o?s.neg():s,f=c.radius+i,g=new t(h.x,l.y+e*y)}}else{const e=Math.max(-u,Math.min(u,m.x)),i=Math.max(-y,Math.min(y,m.y)),s=l.add(new t(e,i)),n=h.sub(s),r=n.magnitudeSquared();if(r>=c.radius*c.radius)return{isTrigger:!1};const a=Math.sqrt(r),d=a>0?n.scale(1/a):new t(1,0);p=o?d.neg():d,f=c.radius-a,g=s}let x=0;const w=this.collisionPairs.get(e)?.get(i)||this.collisionPairs.get(i)?.get(e);return w&&(x=w.accumulatedNormalImpulse),{isTrigger:n,info:{normal:p,penetration:f,contact:g,accumulatedNormalImpulse:x}}}return{isTrigger:!1}}return{isTrigger:!1}}resolveCollision(t,e,i,s){if(!t.body||!e.body)return;const o=t.body,n=e.body,r=isFinite(o.mass)?1/o.mass:0,a=isFinite(n.mass)?1/n.mass:0,h=r+a;if(0===h)return;const l=n.velocity.sub(o.velocity).dot(i.normal);if(l>0)return;let c=Math.max(o.restitution,n.restitution);Math.abs(l)<.5&&(c=0);const d=(-(1+c)*l+(0===c?.2*Math.max(0,i.penetration-.01)/s:0))/h,u=i.accumulatedNormalImpulse||0;i.accumulatedNormalImpulse=Math.max(0,u+d);const y=i.accumulatedNormalImpulse-u;o.velocity=o.velocity.sub(i.normal.scale(y*r)),n.velocity=n.velocity.add(i.normal.scale(y*a))}}class a{canvas;ctx;scene;lastTime=0;input;physics;paused=!1;constructor(t){this.canvas=t.canvas,this.ctx=this.canvas.getContext("2d"),this.input=t.input??new e({canvas:this.canvas}),this.physics=t.physics??new r}setCanvas(t){this.canvas=t,this.ctx=this.canvas.getContext("2d")}setScene(t){this.scene?.exit(),t.ctx=this.ctx,this.scene=t,this.scene.init()}start(){window.addEventListener("blur",()=>{this.paused=!0}),window.addEventListener("focus",()=>{this.paused=!1,this.lastTime=performance.now()}),requestAnimationFrame(this.loop.bind(this))}loop(t){if(this.paused)return void requestAnimationFrame(this.loop.bind(this));const e=(t-this.lastTime)/1e3;if(this.lastTime=t,!this.scene)throw new Error("Can not run game loop without a scene");this.scene.update(e),this.scene.animationUpdate(e),this.physics.update(this.scene.entities,e),this.ctx.clearRect(0,0,this.ctx.canvas.width,this.ctx.canvas.height),this.scene.render(),this.input.update(),requestAnimationFrame(this.loop.bind(this))}}class h{position;rotation;zoom;scene;constructor(e){this.position=e.position??new t(0,0),this.rotation=e.rotation??0,this.zoom=e.zoom??1,this.scene=e.scene}apply(){const t=this.scene.ctx;if(t){const e=t.canvas.width/2,i=t.canvas.height/2;t.translate(e,i),t.scale(this.zoom,this.zoom),t.rotate(this.rotation),t.translate(-this.position.x-e/this.zoom,-this.position.y-i/this.zoom)}}screenToWorld(e){const i=this.scene.ctx;if(i){const s=i.canvas.width/2,o=i.canvas.height/2;let n=e.x-s,r=e.y-o;const a=Math.cos(-this.rotation),h=Math.sin(-this.rotation),l=n*h+r*a,c=(n*a-r*h)/this.zoom,d=l/this.zoom;return new t(c+this.position.x+s/this.zoom,d+this.position.y+o/this.zoom)}return new t(0,0)}}class l{ctx;camera=new h({scene:this});entities=[];init(){}update(t){}exit(){}addEntity(t){this.entities.push(t)}removeEntity(t){this.entities=this.entities.filter(e=>e!==t)}animationUpdate(t){for(const e of this.entities)e.animator?.update(t)}render(){const t=this.ctx;if(t){t.save(),this.camera.apply();for(const e of this.entities)e.render(t);t.restore()}}}class c{animator;sprite;position;rotation;body;collider;constructor(e={}){this.animator=e.animator,this.sprite=e.sprite,this.position=e.position??new t(0,0),this.rotation=e.rotation??0,this.body=e.body,this.collider=e.collider}onCollisionEnter;onCollisionStay;onCollisionExit;onTriggerEnter;onTriggerStay;onTriggerExit;render(t){if(this.animator){const e=this.animator.currentAnimation.spriteSheet,i=this.animator.currentFrame,s=Math.floor(e.texture.width/e.frameWidth),o=i%s*e.frameWidth,n=Math.floor(i/s)*e.frameHeight,r=e.width??e.frameWidth,a=e.height??e.frameHeight;t.save(),t.translate(this.position.x,this.position.y),t.rotate(this.rotation),t.drawImage(e.texture,o,n,e.frameWidth,e.frameHeight,-r/2,-a/2,r,a),t.restore()}else this.sprite&&(t.save(),t.translate(this.position.x,this.position.y),t.rotate(this.rotation),t.drawImage(this.sprite.texture,-this.sprite.width/2,-this.sprite.height/2,this.sprite.width,this.sprite.height),t.restore())}}class d{texture;width;height;constructor(t){this.texture=t.texture,this.width=t.width??this.texture.width,this.height=t.height??this.texture.height}}class u{texture;frameWidth;frameHeight;width;height;constructor(t){this.texture=t.texture,this.frameWidth=t.frameWidth,this.frameHeight=t.frameHeight,this.width=t.width??t.frameWidth,this.height=t.height??t.frameHeight}}class y{spriteSheet;frames;fps;loop;constructor(t){this.spriteSheet=t.spriteSheet,this.frames=t.frames,this.fps=t.fps,this.loop=t.loop??!1}}class m{animations;currentAnimation;currentAnimationName;frameIndex=0;stopped=!1;elapsed=0;onEnd;constructor(t){this.animations=t.animations,this.currentAnimation=this.animations[t.default],this.currentAnimationName=t.default}play(t){this.currentAnimation=this.animations[t],this.currentAnimationName=t,this.frameIndex=0,this.stopped=!1,this.elapsed=0}update(t){if(this.stopped)return;this.elapsed+=t;const e=1/this.currentAnimation.fps;this.elapsed>=e&&(this.elapsed-=e,this.frameIndex++,this.frameIndex>=this.currentAnimation.frames.length&&(this.currentAnimation.loop?this.frameIndex=0:(this.frameIndex=this.currentAnimation.frames.length-1,this.stopped=!0,this.onEnd?.(this.currentAnimationName))))}get currentFrame(){return this.currentAnimation.frames[this.frameIndex]}}export{y as Animation,m as Animator,o as BoxCollider,h as Camera,s as CircleCollider,c as Entity,a as Game,e as Input,r as Physics,i as RigidBody,l as Scene,n as SpatialGrid,d as Sprite,u as SpriteSheet,t as Vector2};
|
|
1
|
+
class t{x;y;static ZERO=new t(0,0);static ONE=new t(1,1);static UP=new t(0,-1);static DOWN=new t(0,1);static LEFT=new t(-1,0);static RIGHT=new t(1,0);constructor(t,e){this.x=t,this.y=e}toString(){return`Vector2(${this.x}, ${this.y})`}add(e){return new t(this.x+e.x,this.y+e.y)}sub(e){return new t(this.x-e.x,this.y-e.y)}mul(e){return new t(this.x*e.x,this.y*e.y)}div(e){return new t(this.x/e.x,this.y/e.y)}neg(){return new t(-this.x,-this.y)}scale(e){return new t(this.x*e,this.y*e)}magnitude(){return Math.sqrt(this.x*this.x+this.y*this.y)}magnitudeSquared(){return this.x*this.x+this.y*this.y}normalize(){const e=this.magnitude();return e>0?new t(this.x/e,this.y/e):new t(0,0)}dot(t){return this.x*t.x+this.y*t.y}cross(t){return this.x*t.y-this.y*t.x}project(t){const e=this.dot(t)/t.magnitudeSquared();return t.scale(e)}min(e){return new t(Math.min(this.x,e.x),Math.min(this.y,e.y))}max(e){return new t(Math.max(this.x,e.x),Math.max(this.y,e.y))}floor(){return new t(Math.floor(this.x),Math.floor(this.y))}ceil(){return new t(Math.ceil(this.x),Math.ceil(this.y))}round(){return new t(Math.round(this.x),Math.round(this.y))}distance(t){return Math.sqrt((this.x-t.x)**2+(this.y-t.y)**2)}distanceSquared(t){return(this.x-t.x)**2+(this.y-t.y)**2}copy(){return new t(this.x,this.y)}lerp(t,e){return this.add(t.sub(this).scale(e))}clamp(t){const e=this.magnitude();return e>t?this.scale(t/e):this.copy()}rotate(e){const i=Math.cos(e),s=Math.sin(e);return new t(this.x*i-this.y*s,this.x*s+this.y*i)}orthogonal(){return new t(-this.y,this.x)}angle(){return Math.atan2(this.y,this.x)}angleTo(t){return Math.atan2(t.y-this.y,t.x-this.x)}reflect(t){const e=this.dot(t);return this.sub(t.scale(2*e))}equals(t){return this.x===t.x&&this.y===t.y}}class e{canvas;keys=new Set;keysPressed=new Set;keysReleased=new Set;pointer=new t(0,0);pointers=new Set;pointersPressed=new Set;pointersReleased=new Set;constructor(t){this.canvas=t.canvas,window.addEventListener("keydown",t=>{this.keys.has(t.key)||this.keysPressed.add(t.key),this.keys.add(t.key)}),window.addEventListener("keyup",t=>{this.keys.delete(t.key),this.keysReleased.add(t.key)}),this.canvas.addEventListener("mousemove",t=>{const e=this.canvas.getBoundingClientRect();this.pointer.x=t.clientX-e.left,this.pointer.y=t.clientY-e.top}),this.canvas.addEventListener("mousedown",t=>{this.pointers.has(t.button)||this.pointersPressed.add(t.button),this.pointers.add(t.button)}),this.canvas.addEventListener("mouseup",t=>{this.pointers.delete(t.button),this.pointersReleased.add(t.button)}),this.canvas.addEventListener("touchmove",t=>{t.preventDefault();const e=this.canvas.getBoundingClientRect(),i=t.touches[0];this.pointer.x=i.clientX-e.left,this.pointer.y=i.clientY-e.top}),this.canvas.addEventListener("touchstart",t=>{t.preventDefault(),this.pointers.has(2)||this.pointersPressed.add(2),this.pointers.add(2)}),this.canvas.addEventListener("touchend",t=>{t.preventDefault(),this.pointers.delete(2),this.pointersReleased.add(2)}),this.canvas.addEventListener("contextmenu",t=>{t.preventDefault()})}update(){this.keysPressed.clear(),this.keysReleased.clear(),this.pointersPressed.clear(),this.pointersReleased.clear()}isKeyDown(t){return this.keys.has(t)}isKeyPressed(t){return this.keysPressed.has(t)}isKeyReleased(t){return this.keysReleased.has(t)}isPointerDown(t=0){return this.pointers.has(t)}isPointerPressed(t=0){return this.pointersPressed.has(t)}isPointerReleased(t=0){return this.pointersReleased.has(t)}}class i{velocity;rotationVelocity;mass;inertia;force;torque;restitution;sleepThreshold;sleepTimeThreshold;isSleeping;sleepTimer;constructor(e={}){this.velocity=e.velocity??new t(0,0),this.rotationVelocity=e.rotationVelocity??0,this.mass=e.mass??1,this.inertia=e.inertia??1,this.force=e.force??new t(0,0),this.torque=e.torque??0,this.restitution=e.restitution??0,this.sleepThreshold=e.sleepThreshold??.1,this.sleepTimeThreshold=e.sleepTimeThreshold??.5,this.isSleeping=e.isSleeping??!1,this.sleepTimer=e.sleepTimer??0}wake(){this.isSleeping=!1,this.sleepTimer=0}}class s{radius;offset;isTrigger;layer;mask;constructor(e){this.radius=e.radius,this.offset=e.offset??new t(0,0),this.isTrigger=e.isTrigger??!1,this.layer=e.layer??1,this.mask=e.mask??4294967295}}class o{width;height;offset;isTrigger;layer;mask;constructor(e){this.width=e.width,this.height=e.height,this.offset=e.offset??new t(0,0),this.isTrigger=e.isTrigger??!1,this.layer=e.layer??1,this.mask=e.mask??4294967295}}class n{cellSize;grid;constructor(t={}){this.cellSize=t.cellSize||100,this.grid=t.grid||new Map}clear(){this.grid.clear()}adaptCellSize(t){const e=[];for(const i of t){const t=this.getEntityBounds(i),s=t.maxX-t.minX,o=t.maxY-t.minY,n=Math.max(s,o);e.push(n)}e.sort((t,e)=>t-e);const i=e[Math.floor(.75*e.length)];this.cellSize=2.5*i}insert(t){if(t.collider){const e=this.getEntityBounds(t),i=Math.floor(e.minX/this.cellSize),s=Math.floor(e.maxX/this.cellSize),o=Math.floor(e.minY/this.cellSize),n=Math.floor(e.maxY/this.cellSize);for(let e=i;e<=s;e++)for(let i=o;i<=n;i++){const s=`${e},${i}`;this.grid.has(s)||this.grid.set(s,new Set),this.grid.get(s).add(t)}}}getNearby(t){const e=this.getEntityBounds(t),i=Math.floor(e.minX/this.cellSize),s=Math.floor(e.maxX/this.cellSize),o=Math.floor(e.minY/this.cellSize),n=Math.floor(e.maxY/this.cellSize),r=new Set;for(let e=i;e<=s;e++)for(let i=o;i<=n;i++){const s=this.grid.get(`${e},${i}`);if(s)for(const e of s)e!==t&&r.add(e)}return Array.from(r)}getEntityBounds(t){if(t.collider instanceof s){const e=t.collider.radius;return{minX:t.position.x-e,maxX:t.position.x+e,minY:t.position.y-e,maxY:t.position.y+e}}if(t.collider instanceof o){const e=t.collider.width/2,i=t.collider.height/2;return{minX:t.position.x-e,maxX:t.position.x+e,minY:t.position.y-i,maxY:t.position.y+i}}throw new Error("Collider type not supported")}}class r{collisionPairs=new Map;spatialGrid=new n;entityCount=0;update(e,s){for(const t of e)if(t.body instanceof i){if((t.body.force.magnitudeSquared()>0||0!==t.body.torque||t.body.velocity.magnitudeSquared()>t.body.sleepThreshold**2||Math.abs(t.body.rotationVelocity)>t.body.sleepThreshold)&&t.body.wake(),t.body.isSleeping)continue;t.body.velocity.x+=t.body.force.x/t.body.mass*s,t.body.velocity.y+=t.body.force.y/t.body.mass*s,t.body.rotationVelocity+=t.body.torque/t.body.inertia*s,t.body.force.x=0,t.body.force.y=0,t.body.torque=0}const o=e.filter(t=>t.collider);this.spatialGrid.clear(),this.entityCount!==o.length&&(this.entityCount=o.length,this.spatialGrid.adaptCellSize(o));for(const t of o)this.spatialGrid.insert(t);const n=new Map,r=[];for(const e of o){const i=this.spatialGrid.getNearby(e);for(const s of i)if(s.collider){if(n.has(e)&&n.get(e)?.has(s)||n.has(s)&&n.get(s)?.has(e))continue;const i=this.checkCollision(e,s);if(i.info){e.body?.isSleeping&&s.body?.isSleeping||(e.body?.wake(),s.body?.wake()),n.has(e)||n.set(e,new Map),n.get(e)?.set(s,i.info);this.collisionPairs.get(e)?.has(s)||this.collisionPairs.get(s)?.has(e)?i.isTrigger?(e.onTriggerStay?.(s),s.onTriggerStay?.(e)):(e.onCollisionStay?.(s,i.info),s.onCollisionStay?.(e,{...i.info,normal:new t(-i.info.normal.x,-i.info.normal.y)})):i.isTrigger?(e.onTriggerEnter?.(s),s.onTriggerEnter?.(e)):(e.onCollisionEnter?.(s,i.info),s.onCollisionEnter?.(e,{...i.info,normal:new t(-i.info.normal.x,-i.info.normal.y)})),i.isTrigger||r.push({entityA:e,entityB:s,info:i.info})}}}for(let t=0;t<6;t++)for(const t of r)this.resolveCollision(t.entityA,t.entityB,t.info,s);for(const[e,i]of this.collisionPairs)for(const[s,o]of i){if(!(n.get(e)?.has(s)||n.get(s)?.has(e))){e.collider?.isTrigger||s.collider?.isTrigger?(e.onTriggerExit?.(s),s.onTriggerExit?.(e)):(e.onCollisionExit?.(s,o),s.onCollisionExit?.(e,{...o,normal:new t(-o.normal.x,-o.normal.y)}))}}this.collisionPairs=n;for(const t of e)if(t.body instanceof i){if(t.body.isSleeping)continue;t.position.x+=t.body.velocity.x*s,t.position.y+=t.body.velocity.y*s,t.rotation+=t.body.rotationVelocity*s;const e=t.body.velocity.magnitude(),i=Math.abs(t.body.rotationVelocity);e<t.body.sleepThreshold&&i<t.body.sleepThreshold?(t.body.sleepTimer+=s,t.body.sleepTimer>=t.body.sleepTimeThreshold&&(t.body.isSleeping=!0,t.body.velocity.x=0,t.body.velocity.y=0,t.body.rotationVelocity=0)):t.body.sleepTimer=0}}checkCollision(e,i){if(e.collider&&i.collider){if(0===(e.collider.mask&i.collider.layer)||0===(i.collider.mask&e.collider.layer))return{isTrigger:!1};const n=e.collider.isTrigger||i.collider.isTrigger,r=e.position.add(e.collider.offset),a=i.position.add(i.collider.offset);if(e.collider instanceof s&&i.collider instanceof s){const s=r.distance(a),o=e.collider.radius+i.collider.radius;if(s>=o)return{isTrigger:!1};const h=o-s,l=a.sub(r);let c=s>0?l.scale(1/s):new t(1,0),d=0;const u=this.collisionPairs.get(e)?.get(i)||this.collisionPairs.get(i)?.get(e);return u&&(d=u.accumulatedNormalImpulse),{isTrigger:n,info:{normal:c,penetration:h,contact:r.add(c.scale(e.collider.radius)),accumulatedNormalImpulse:d}}}if(e.collider instanceof o&&i.collider instanceof o){const s=e.collider.width/2,o=e.collider.height/2,h=i.collider.width/2,l=i.collider.height/2,c=a.sub(r),d=s+h-Math.abs(c.x),u=o+l-Math.abs(c.y);if(d<=0||u<=0)return{isTrigger:!1};let m,y;d<u?(y=d,m=new t(c.x<0?-1:1,0)):(y=u,m=new t(0,c.y<0?-1:1));const p=r.x+Math.sign(c.x)*(s-d/2),f=r.y+Math.sign(c.y)*(o-u/2);let g=0;const x=this.collisionPairs.get(e)?.get(i)||this.collisionPairs.get(i)?.get(e);return x&&(g=x.accumulatedNormalImpulse),{isTrigger:n,info:{normal:m,penetration:y,contact:new t(p,f),accumulatedNormalImpulse:g}}}if(e.collider instanceof s&&i.collider instanceof o||e.collider instanceof o&&i.collider instanceof s){const o=e.collider instanceof s,h=o?r:a,l=o?a:r,c=(o?e:i).collider,d=(o?i:e).collider,u=d.width/2,m=d.height/2,y=h.sub(l);let p,f,g;if(Math.abs(y.x)<u&&Math.abs(y.y)<m){const e=u-Math.abs(y.x),i=m-Math.abs(y.y);if(e<i){const i=y.x<0?-1:1,s=new t(i,0);p=o?s.neg():s,f=c.radius+e,g=new t(l.x+i*u,h.y)}else{const e=y.y<0?-1:1,s=new t(0,e);p=o?s.neg():s,f=c.radius+i,g=new t(h.x,l.y+e*m)}}else{const e=Math.max(-u,Math.min(u,y.x)),i=Math.max(-m,Math.min(m,y.y)),s=l.add(new t(e,i)),n=h.sub(s),r=n.magnitudeSquared();if(r>=c.radius*c.radius)return{isTrigger:!1};const a=Math.sqrt(r),d=a>0?n.scale(1/a):new t(1,0);p=o?d.neg():d,f=c.radius-a,g=s}let x=0;const w=this.collisionPairs.get(e)?.get(i)||this.collisionPairs.get(i)?.get(e);return w&&(x=w.accumulatedNormalImpulse),{isTrigger:n,info:{normal:p,penetration:f,contact:g,accumulatedNormalImpulse:x}}}return{isTrigger:!1}}return{isTrigger:!1}}resolveCollision(t,e,i,s){if(!t.body||!e.body)return;const o=t.body,n=e.body,r=isFinite(o.mass)?1/o.mass:0,a=isFinite(n.mass)?1/n.mass:0,h=r+a;if(0===h)return;const l=n.velocity.sub(o.velocity).dot(i.normal);if(l>0)return;let c=Math.max(o.restitution,n.restitution);Math.abs(l)<.5&&(c=0);const d=(-(1+c)*l+(0===c?.2*Math.max(0,i.penetration-.01)/s:0))/h,u=i.accumulatedNormalImpulse||0;i.accumulatedNormalImpulse=Math.max(0,u+d);const m=i.accumulatedNormalImpulse-u;o.velocity=o.velocity.sub(i.normal.scale(m*r)),n.velocity=n.velocity.add(i.normal.scale(m*a))}}class a{canvas;ctx;scene;lastTime=0;input;physics;paused=!1;constructor(t){this.canvas=t.canvas,this.ctx=this.canvas.getContext("2d"),this.input=t.input??new e({canvas:this.canvas}),this.physics=t.physics??new r}setCanvas(t){this.canvas=t,this.ctx=this.canvas.getContext("2d")}setScene(t){this.scene?.exit(),t.ctx=this.ctx,this.scene=t,this.scene.init()}start(){window.addEventListener("blur",()=>{this.paused=!0}),window.addEventListener("focus",()=>{this.paused=!1,this.lastTime=performance.now()}),requestAnimationFrame(this.loop.bind(this))}loop(t){if(this.paused)return void requestAnimationFrame(this.loop.bind(this));const e=(t-this.lastTime)/1e3;if(this.lastTime=t,!this.scene)throw new Error("Can not run game loop without a scene");this.scene.update(e),this.scene.animationUpdate(e),this.physics.update(this.scene.entities,e),this.ctx.clearRect(0,0,this.ctx.canvas.width,this.ctx.canvas.height),this.scene.render(),this.input.update(),requestAnimationFrame(this.loop.bind(this))}}class h{position;rotation;zoom;scene;constructor(e){this.position=e.position??new t(0,0),this.rotation=e.rotation??0,this.zoom=e.zoom??1,this.scene=e.scene}apply(){const t=this.scene.ctx;if(t){const e=t.canvas.width/2,i=t.canvas.height/2;t.translate(e,i),t.scale(this.zoom,this.zoom),t.rotate(this.rotation),t.translate(-this.position.x-e/this.zoom,-this.position.y-i/this.zoom)}}screenToWorld(e){const i=this.scene.ctx;if(i){const s=i.canvas.width/2,o=i.canvas.height/2;let n=e.x-s,r=e.y-o;const a=Math.cos(-this.rotation),h=Math.sin(-this.rotation),l=n*h+r*a,c=(n*a-r*h)/this.zoom,d=l/this.zoom;return new t(c+this.position.x+s/this.zoom,d+this.position.y+o/this.zoom)}return new t(0,0)}}class l{ctx;camera=new h({scene:this});entities=[];init(){}update(t){}exit(){}addEntity(t){this.entities.push(t)}removeEntity(t){this.entities=this.entities.filter(e=>e!==t)}animationUpdate(t){for(const e of this.entities)e.animator?.update(t)}render(){const t=this.ctx;if(t){t.save(),this.camera.apply();for(const e of this.entities)e.render(t);t.restore()}}}class c{animator;sprite;position;rotation;body;collider;glow;constructor(e={}){this.animator=e.animator,this.sprite=e.sprite,this.position=e.position??new t(0,0),this.rotation=e.rotation??0,this.body=e.body,this.collider=e.collider,this.glow=e.glow}onCollisionEnter;onCollisionStay;onCollisionExit;onTriggerEnter;onTriggerStay;onTriggerExit;drawSelf(t){if(this.animator){const e=this.animator.currentAnimation.spriteSheet,i=this.animator.currentFrame,s=Math.floor(e.texture.width/e.frameWidth),o=i%s*e.frameWidth,n=Math.floor(i/s)*e.frameHeight,r=e.width??e.frameWidth,a=e.height??e.frameHeight;t.drawImage(e.texture,o,n,e.frameWidth,e.frameHeight,-r/2,-a/2,r,a)}else this.sprite&&t.drawImage(this.sprite.texture,-this.sprite.width/2,-this.sprite.height/2,this.sprite.width,this.sprite.height)}render(t){if(t.save(),t.translate(this.position.x,this.position.y),t.rotate(this.rotation),this.glow){const e=this.animator?.currentAnimation.spriteSheet,i=e?e.width??e.frameWidth:this.sprite?.width??0,s=e?e.height??e.frameHeight:this.sprite?.height??0,o=2*this.glow.bloom,n=i+o,r=s+o,a=new OffscreenCanvas(n,r),h=a.getContext("2d");h.translate(n/2,r/2),this.drawSelf(h),t.shadowColor=this.glow.color,t.shadowBlur=this.glow.bloom;for(let e=0;e<this.glow.intensity;e++)t.drawImage(a,-n/2,-r/2);t.shadowColor="transparent",t.shadowBlur=0}this.drawSelf(t),t.restore()}}class d{texture;width;height;constructor(t){this.texture=t.texture,this.width=t.width??this.texture.width,this.height=t.height??this.texture.height}}class u{texture;frameWidth;frameHeight;width;height;constructor(t){this.texture=t.texture,this.frameWidth=t.frameWidth,this.frameHeight=t.frameHeight,this.width=t.width??t.frameWidth,this.height=t.height??t.frameHeight}}class m{spriteSheet;frames;fps;loop;constructor(t){this.spriteSheet=t.spriteSheet,this.frames=t.frames,this.fps=t.fps,this.loop=t.loop??!1}}class y{animations;currentAnimation;currentAnimationName;frameIndex=0;stopped=!1;elapsed=0;onEnd;constructor(t){this.animations=t.animations,this.currentAnimation=this.animations[t.default],this.currentAnimationName=t.default}play(t){this.currentAnimation=this.animations[t],this.currentAnimationName=t,this.frameIndex=0,this.stopped=!1,this.elapsed=0}update(t){if(this.stopped)return;this.elapsed+=t;const e=1/this.currentAnimation.fps;this.elapsed>=e&&(this.elapsed-=e,this.frameIndex++,this.frameIndex>=this.currentAnimation.frames.length&&(this.currentAnimation.loop?this.frameIndex=0:(this.frameIndex=this.currentAnimation.frames.length-1,this.stopped=!0,this.onEnd?.(this.currentAnimationName))))}get currentFrame(){return this.currentAnimation.frames[this.frameIndex]}}export{m as Animation,y as Animator,o as BoxCollider,h as Camera,s as CircleCollider,c as Entity,a as Game,e as Input,r as Physics,i as RigidBody,l as Scene,n as SpatialGrid,d as Sprite,u as SpriteSheet,t as Vector2};
|
package/dist/types/entity.d.ts
CHANGED
|
@@ -2,6 +2,11 @@ import { Animator } from "./animation.js";
|
|
|
2
2
|
import { Collider, CollisionInfo, EntityBody } from "./physics.js";
|
|
3
3
|
import { Sprite } from "./sprite.js";
|
|
4
4
|
import { Vector2 } from "./vector.js";
|
|
5
|
+
export interface GlowEffect {
|
|
6
|
+
color: string;
|
|
7
|
+
bloom: number;
|
|
8
|
+
intensity: number;
|
|
9
|
+
}
|
|
5
10
|
export interface EntityOptions {
|
|
6
11
|
animator?: Animator;
|
|
7
12
|
sprite?: Sprite;
|
|
@@ -9,6 +14,7 @@ export interface EntityOptions {
|
|
|
9
14
|
rotation?: number;
|
|
10
15
|
body?: EntityBody;
|
|
11
16
|
collider?: Collider;
|
|
17
|
+
glow?: GlowEffect;
|
|
12
18
|
}
|
|
13
19
|
export declare class Entity {
|
|
14
20
|
animator?: Animator;
|
|
@@ -17,6 +23,7 @@ export declare class Entity {
|
|
|
17
23
|
rotation: number;
|
|
18
24
|
body?: EntityBody;
|
|
19
25
|
collider?: Collider;
|
|
26
|
+
glow?: GlowEffect;
|
|
20
27
|
constructor(options?: EntityOptions);
|
|
21
28
|
onCollisionEnter?: (other: Entity, info: CollisionInfo) => void;
|
|
22
29
|
onCollisionStay?: (other: Entity, info: CollisionInfo) => void;
|
|
@@ -24,5 +31,6 @@ export declare class Entity {
|
|
|
24
31
|
onTriggerEnter?: (other: Entity) => void;
|
|
25
32
|
onTriggerStay?: (other: Entity) => void;
|
|
26
33
|
onTriggerExit?: (other: Entity) => void;
|
|
34
|
+
drawSelf(ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D): void;
|
|
27
35
|
render(ctx: CanvasRenderingContext2D): void;
|
|
28
36
|
}
|