littlejsengine 1.18.4 → 1.18.8
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/COPYRIGHT.txt +38 -0
- package/FAQ.md +633 -0
- package/README.md +19 -1
- package/dist/littlejs.d.ts +354 -55
- package/dist/littlejs.esm.js +1531 -489
- package/dist/littlejs.esm.min.js +1 -1
- package/dist/littlejs.js +1499 -476
- package/dist/littlejs.min.js +1 -1
- package/dist/littlejs.release.js +1493 -470
- package/package.json +8 -1
- package/plugins/box2d.js +32 -7
- package/{src/engineMedals.js → plugins/medalSystem.js} +248 -196
- package/plugins/pathFinder.js +758 -0
- package/plugins/pluginExport.js +19 -0
- package/plugins/{tween.js → tweenSystem.js} +7 -7
- package/plugins/uiSystem.js +186 -25
- package/src/engine.js +2 -141
- package/src/engineAudio.js +3 -3
- package/src/engineBuild.mjs +4 -2
- package/src/engineDebug.js +7 -7
- package/src/engineDraw.js +26 -8
- package/src/engineExport.js +13 -13
- package/src/engineInput.js +8 -8
- package/src/engineLogo.js +146 -0
- package/src/engineMath.js +15 -6
- package/src/engineObject.js +11 -5
- package/src/engineParticles.js +6 -3
- package/src/engineRelease.js +1 -1
- package/src/engineSettings.js +0 -47
- package/src/engineUtilities.js +13 -8
- package/src/engineWebGL.js +34 -7
- package/src/jsconfig.json +3 -1
- package/.github/workflows/test.yml +0 -17
- package/AI.md +0 -173
- package/CLAUDE.md +0 -1
- package/examples/box2d/game.js +0 -192
- package/examples/box2d/gameObjects.js +0 -564
- package/examples/box2d/index.html +0 -9
- package/examples/box2d/scenes.js +0 -194
- package/examples/box2d/tiles.png +0 -0
- package/examples/breakout/game.js +0 -175
- package/examples/breakout/gameObjects.js +0 -147
- package/examples/breakout/index.html +0 -8
- package/examples/breakout/tiles.png +0 -0
- package/examples/breakoutTutorial/README.md +0 -521
- package/examples/breakoutTutorial/game.js +0 -191
- package/examples/breakoutTutorial/images/1.png +0 -0
- package/examples/breakoutTutorial/images/10.png +0 -0
- package/examples/breakoutTutorial/images/11.png +0 -0
- package/examples/breakoutTutorial/images/2.png +0 -0
- package/examples/breakoutTutorial/images/3.png +0 -0
- package/examples/breakoutTutorial/images/4.png +0 -0
- package/examples/breakoutTutorial/images/5.png +0 -0
- package/examples/breakoutTutorial/images/6.png +0 -0
- package/examples/breakoutTutorial/images/7.png +0 -0
- package/examples/breakoutTutorial/images/8.png +0 -0
- package/examples/breakoutTutorial/images/9.png +0 -0
- package/examples/breakoutTutorial/index.html +0 -8
- package/examples/electron/build.mjs +0 -127
- package/examples/electron/electron.js +0 -35
- package/examples/electron/game.js +0 -54
- package/examples/electron/index.html +0 -13
- package/examples/electron/package.json +0 -5
- package/examples/electron/tiles.png +0 -0
- package/examples/empty/game.js +0 -51
- package/examples/empty/index.html +0 -5
- package/examples/empty/tiles.png +0 -0
- package/examples/favicon.png +0 -0
- package/examples/games.jpg +0 -0
- package/examples/htmlMenu/game.js +0 -82
- package/examples/htmlMenu/index.html +0 -45
- package/examples/htmlMenu/tiles.png +0 -0
- package/examples/index.html +0 -62
- package/examples/logo.png +0 -0
- package/examples/logo2.png +0 -0
- package/examples/module/build.mjs +0 -124
- package/examples/module/game.js +0 -132
- package/examples/module/index.html +0 -10
- package/examples/module/tiles.png +0 -0
- package/examples/particles/index.html +0 -426
- package/examples/particles/tiles.png +0 -0
- package/examples/platformer/data/gameLevelData.tmx +0 -143
- package/examples/platformer/data/gameLevelData.tsx +0 -4
- package/examples/platformer/game.js +0 -147
- package/examples/platformer/gameCharacter.js +0 -309
- package/examples/platformer/gameEffects.js +0 -278
- package/examples/platformer/gameLevel.js +0 -205
- package/examples/platformer/gameLevelData.json +0 -171
- package/examples/platformer/gameObjects.js +0 -378
- package/examples/platformer/gamePlayer.js +0 -35
- package/examples/platformer/index.html +0 -9
- package/examples/platformer/tiles.png +0 -0
- package/examples/platformer/tilesLevel.png +0 -0
- package/examples/puzzle/game.js +0 -331
- package/examples/puzzle/index.html +0 -8
- package/examples/puzzle/tiles.png +0 -0
- package/examples/screenshot.jpg +0 -0
- package/examples/shorts/animation.js +0 -18
- package/examples/shorts/base.html +0 -54
- package/examples/shorts/blending.js +0 -14
- package/examples/shorts/box2d.js +0 -51
- package/examples/shorts/box2dCar.js +0 -59
- package/examples/shorts/box2dPool.js +0 -107
- package/examples/shorts/box2dTileLayer.js +0 -48
- package/examples/shorts/cameraDrag.js +0 -22
- package/examples/shorts/clock.js +0 -21
- package/examples/shorts/colors.js +0 -25
- package/examples/shorts/debugDraw.js +0 -37
- package/examples/shorts/empty.js +0 -30
- package/examples/shorts/flappyGame.js +0 -55
- package/examples/shorts/fontImage.js +0 -17
- package/examples/shorts/fps.js +0 -90
- package/examples/shorts/helloWorld.js +0 -11
- package/examples/shorts/hillGlideGame.js +0 -63
- package/examples/shorts/input.js +0 -64
- package/examples/shorts/landerGame.js +0 -57
- package/examples/shorts/maze.js +0 -48
- package/examples/shorts/medals.js +0 -51
- package/examples/shorts/music.js +0 -78
- package/examples/shorts/musicPlayer.js +0 -137
- package/examples/shorts/nineSlice.js +0 -40
- package/examples/shorts/parallax.js +0 -72
- package/examples/shorts/particles.js +0 -28
- package/examples/shorts/piano.js +0 -44
- package/examples/shorts/platformer.js +0 -45
- package/examples/shorts/pongGame.js +0 -41
- package/examples/shorts/postProcess.js +0 -62
- package/examples/shorts/sequencer.js +0 -124
- package/examples/shorts/shader.js +0 -29
- package/examples/shorts/shapes.js +0 -21
- package/examples/shorts/slidingPuzzle.js +0 -52
- package/examples/shorts/song.mp3 +0 -0
- package/examples/shorts/sound.js +0 -36
- package/examples/shorts/spaceGame.js +0 -58
- package/examples/shorts/spriteAtlas.js +0 -31
- package/examples/shorts/starfield.js +0 -15
- package/examples/shorts/texture.js +0 -16
- package/examples/shorts/textureWrapped.js +0 -12
- package/examples/shorts/tileLayer.js +0 -48
- package/examples/shorts/tileRaycast.js +0 -39
- package/examples/shorts/tiles.png +0 -0
- package/examples/shorts/tiltedView.js +0 -63
- package/examples/shorts/timers.js +0 -53
- package/examples/shorts/topDown.js +0 -38
- package/examples/shorts/tween.js +0 -24
- package/examples/shorts/uiSystem.js +0 -61
- package/examples/shorts/video.webm +0 -0
- package/examples/shorts/videoPlayer.js +0 -34
- package/examples/shorts.js +0 -743
- package/examples/starter/build.bat +0 -7
- package/examples/starter/build.mjs +0 -126
- package/examples/starter/game.js +0 -137
- package/examples/starter/index.html +0 -35
- package/examples/starter/tiles.png +0 -0
- package/examples/stress/index.html +0 -173
- package/examples/style.css +0 -150
- package/examples/tweenSystem/game.js +0 -171
- package/examples/tweenSystem/index.html +0 -10
- package/examples/tweenSystem/tiles.png +0 -0
- package/examples/typescript/build.mjs +0 -60
- package/examples/typescript/game.js +0 -100
- package/examples/typescript/game.ts +0 -132
- package/examples/typescript/index.html +0 -10
- package/examples/typescript/tiles.png +0 -0
- package/examples/typescript/tsconfig.json +0 -17
- package/examples/uiSystem/game.js +0 -139
- package/examples/uiSystem/index.html +0 -10
- package/examples/uiSystem/tiles.png +0 -0
- package/jsconfig.json +0 -12
- package/plugins/desktop.ini +0 -2
- package/reference.md +0 -448
- package/test/math.test.mjs +0 -774
- package/test/setup.mjs +0 -22
- package/test/smoke.test.mjs +0 -274
- package/test/tween.test.mjs +0 -576
- package/test/util.test.mjs +0 -80
package/src/engineWebGL.js
CHANGED
|
@@ -99,7 +99,7 @@ function glInit(rootElement)
|
|
|
99
99
|
// reinit WebGL and restore textures
|
|
100
100
|
initWebGL();
|
|
101
101
|
for (const info of glTextureInfos)
|
|
102
|
-
info.glTexture = glCreateTexture(info.image);
|
|
102
|
+
info.glTexture = glCreateTexture(info.image, info.wrap);
|
|
103
103
|
pluginList.forEach(plugin=>plugin.glContextRestored?.());
|
|
104
104
|
});
|
|
105
105
|
|
|
@@ -315,6 +315,30 @@ function glSetTexture(texture)
|
|
|
315
315
|
glContext.bindTexture(glContext.TEXTURE_2D, glActiveTexture);
|
|
316
316
|
}
|
|
317
317
|
|
|
318
|
+
/** Set the wrap mode (REPEAT or CLAMP_TO_EDGE) on an existing WebGL texture
|
|
319
|
+
* Flushes the current batch only if the texture is the active one
|
|
320
|
+
* @param {WebGLTexture} texture
|
|
321
|
+
* @param {boolean} [wrap] - true for REPEAT, false for CLAMP_TO_EDGE
|
|
322
|
+
* @memberof WebGL */
|
|
323
|
+
function glSetTextureWrap(texture, wrap=true)
|
|
324
|
+
{
|
|
325
|
+
if (!glContext || !texture) return;
|
|
326
|
+
|
|
327
|
+
// flush only if changing wrap on the currently bound texture
|
|
328
|
+
const isCurrent = texture === glActiveTexture;
|
|
329
|
+
if (isCurrent)
|
|
330
|
+
glFlush();
|
|
331
|
+
else
|
|
332
|
+
glContext.bindTexture(glContext.TEXTURE_2D, texture);
|
|
333
|
+
|
|
334
|
+
const wrapMode = wrap ? glContext.REPEAT : glContext.CLAMP_TO_EDGE;
|
|
335
|
+
glContext.texParameteri(glContext.TEXTURE_2D, glContext.TEXTURE_WRAP_S, wrapMode);
|
|
336
|
+
glContext.texParameteri(glContext.TEXTURE_2D, glContext.TEXTURE_WRAP_T, wrapMode);
|
|
337
|
+
|
|
338
|
+
if (!isCurrent && glActiveTexture)
|
|
339
|
+
glContext.bindTexture(glContext.TEXTURE_2D, glActiveTexture);
|
|
340
|
+
}
|
|
341
|
+
|
|
318
342
|
/** Compile WebGL shader of the given type, will throw errors if in debug mode
|
|
319
343
|
* @param {string} source
|
|
320
344
|
* @param {number} type
|
|
@@ -359,9 +383,10 @@ function glCreateProgram(vsSource, fsSource)
|
|
|
359
383
|
/** Create WebGL texture from an image and init the texture settings
|
|
360
384
|
* Restores the active texture when done
|
|
361
385
|
* @param {HTMLImageElement|HTMLCanvasElement|OffscreenCanvas} [image]
|
|
386
|
+
* @param {boolean} [wrap] - true for REPEAT, false for CLAMP_TO_EDGE
|
|
362
387
|
* @return {WebGLTexture}
|
|
363
388
|
* @memberof WebGL */
|
|
364
|
-
function glCreateTexture(image)
|
|
389
|
+
function glCreateTexture(image, wrap=false)
|
|
365
390
|
{
|
|
366
391
|
if (!glContext) return;
|
|
367
392
|
|
|
@@ -387,8 +412,9 @@ function glCreateTexture(image)
|
|
|
387
412
|
const minFilter = mipMap ? glContext.LINEAR_MIPMAP_LINEAR : magFilter;
|
|
388
413
|
glContext.texParameteri(glContext.TEXTURE_2D, glContext.TEXTURE_MAG_FILTER, magFilter);
|
|
389
414
|
glContext.texParameteri(glContext.TEXTURE_2D, glContext.TEXTURE_MIN_FILTER, minFilter);
|
|
390
|
-
|
|
391
|
-
glContext.texParameteri(glContext.TEXTURE_2D, glContext.
|
|
415
|
+
const wrapMode = wrap ? glContext.REPEAT : glContext.CLAMP_TO_EDGE;
|
|
416
|
+
glContext.texParameteri(glContext.TEXTURE_2D, glContext.TEXTURE_WRAP_S, wrapMode);
|
|
417
|
+
glContext.texParameteri(glContext.TEXTURE_2D, glContext.TEXTURE_WRAP_T, wrapMode);
|
|
392
418
|
if (mipMap)
|
|
393
419
|
glContext.generateMipmap(glContext.TEXTURE_2D);
|
|
394
420
|
|
|
@@ -440,7 +466,7 @@ function glRegisterTextureInfo(textureInfo)
|
|
|
440
466
|
if (textureInfo.glTexture)
|
|
441
467
|
glSetTextureData(textureInfo.glTexture, textureInfo.image);
|
|
442
468
|
else
|
|
443
|
-
textureInfo.glTexture = glCreateTexture(textureInfo.image);
|
|
469
|
+
textureInfo.glTexture = glCreateTexture(textureInfo.image, textureInfo.wrap);
|
|
444
470
|
}
|
|
445
471
|
|
|
446
472
|
/** Tells WebGL to destroy the glTexture and stop tracking it
|
|
@@ -659,6 +685,7 @@ function glSetRenderTarget(texture, clear=false)
|
|
|
659
685
|
glFlush();
|
|
660
686
|
glRenderTarget = undefined;
|
|
661
687
|
glContext.bindFramebuffer(glContext.FRAMEBUFFER, null);
|
|
688
|
+
glContext.viewport(0, 0, mainCanvasSize.x, mainCanvasSize.y);
|
|
662
689
|
}
|
|
663
690
|
}
|
|
664
691
|
|
|
@@ -781,9 +808,9 @@ function glPolyStrip(points)
|
|
|
781
808
|
return area;
|
|
782
809
|
}
|
|
783
810
|
|
|
784
|
-
// ensure counter-clockwise winding
|
|
811
|
+
// ensure counter-clockwise winding (slice first so we don't mutate caller's array)
|
|
785
812
|
if (signedArea(points) < 0)
|
|
786
|
-
points = points.reverse();
|
|
813
|
+
points = points.slice().reverse();
|
|
787
814
|
|
|
788
815
|
// check if point is inside triangle
|
|
789
816
|
const e = 1e-9;
|
package/src/jsconfig.json
CHANGED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
name: Test
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
pull_request:
|
|
6
|
-
|
|
7
|
-
jobs:
|
|
8
|
-
test:
|
|
9
|
-
runs-on: ubuntu-latest
|
|
10
|
-
steps:
|
|
11
|
-
- uses: actions/checkout@v5
|
|
12
|
-
- uses: actions/setup-node@v5
|
|
13
|
-
with:
|
|
14
|
-
node-version: '22'
|
|
15
|
-
- run: npm install
|
|
16
|
-
- run: npm run build
|
|
17
|
-
- run: npm test
|
package/AI.md
DELETED
|
@@ -1,173 +0,0 @@
|
|
|
1
|
-
# LittleJS Project - AI Agent Instructions
|
|
2
|
-
|
|
3
|
-
These instructions are for making changes in the LittleJS repo safely. Optimize for small diffs, clarity, and ease of use.
|
|
4
|
-
|
|
5
|
-
## Non-negotiable rules
|
|
6
|
-
|
|
7
|
-
- **Prefer minimal, local changes.** Do not refactor for style unless asked.
|
|
8
|
-
- **No new runtime dependencies.** Keep LittleJS dependency-free at runtime.
|
|
9
|
-
- **Do not hand-edit generated build artifacts.**
|
|
10
|
-
- Treat `dist/` as generated output.
|
|
11
|
-
- Make changes in `src/` (and `plugins/` when appropriate), then run the build.
|
|
12
|
-
- **Match surrounding style.** Follow the conventions in the files you touch.
|
|
13
|
-
- **Avoid breaking public APIs.** If a change could break users, call it out clearly and offer a compatible alternative.
|
|
14
|
-
|
|
15
|
-
If anything in this doc conflicts with the actual repo behavior, follow the repo behavior and update this doc.
|
|
16
|
-
|
|
17
|
-
## Key resources
|
|
18
|
-
|
|
19
|
-
- `README.md` - Overview and getting started
|
|
20
|
-
- `reference.md` - API quick reference
|
|
21
|
-
- `examples/` - Working examples demonstrating engine features
|
|
22
|
-
|
|
23
|
-
## Architecture overview
|
|
24
|
-
|
|
25
|
-
LittleJS is a modular HTML5 game engine with:
|
|
26
|
-
|
|
27
|
-
- **Core engine**: `src/engine*.js` (main loop, objects, rendering, physics, input, etc.)
|
|
28
|
-
- **Plugins**: `plugins/*.js` (optional features like Box2D, post-processing, UI, audio helpers, etc.)
|
|
29
|
-
- **Build system**: `src/engineBuild.mjs` (concatenates modules into distributable bundles)
|
|
30
|
-
|
|
31
|
-
## Repo structure and file types
|
|
32
|
-
|
|
33
|
-
### Engine source (`src/*.js`)
|
|
34
|
-
- Modular architecture (one subsystem per file)
|
|
35
|
-
- Concatenated at build time (internal source does not use ES modules)
|
|
36
|
-
- Code is vanilla JavaScript with type info expressed via JSDoc comments
|
|
37
|
-
|
|
38
|
-
### Build output (`dist/`)
|
|
39
|
-
Common outputs include:
|
|
40
|
-
- `littlejs.js` - Full bundle (debug features included)
|
|
41
|
-
- `littlejs.release.js` - Production bundle (debug stripped)
|
|
42
|
-
- `littlejs.esm.js` - ES module build (import/export)
|
|
43
|
-
- `littlejs.esm.min.js` - Minified ES module
|
|
44
|
-
- `littlejs.d.ts` - TypeScript definitions
|
|
45
|
-
|
|
46
|
-
Use via script tag or ES module import:
|
|
47
|
-
- `<script src="dist/littlejs.js"></script>`
|
|
48
|
-
- `import * as LJS from './dist/littlejs.esm.js'`
|
|
49
|
-
|
|
50
|
-
Prefer adding new optional features as plugins when it keeps the core simpler.
|
|
51
|
-
|
|
52
|
-
### Examples
|
|
53
|
-
- `examples/starter/` - Plain JavaScript global usage via `<script>` (recommended starting point)
|
|
54
|
-
- `examples/module/` - ES module import pattern
|
|
55
|
-
- `examples/typescript/` - TypeScript example usage
|
|
56
|
-
- `examples/shorts/*.js` - Single-file demos loaded by the shorts harness
|
|
57
|
-
|
|
58
|
-
### Short examples (`examples/shorts/*.js`)
|
|
59
|
-
Short examples are special:
|
|
60
|
-
- Pure JS code file, no HTML
|
|
61
|
-
- No imports, do not use LJS namespace - engine APIs are available globally
|
|
62
|
-
- Override hooks: `gameInit()`, `gameUpdate()`, `gameUpdatePost()`, `gameRender()`, `gameRenderPost()`
|
|
63
|
-
|
|
64
|
-
## Coding conventions
|
|
65
|
-
|
|
66
|
-
### Factory functions vs constructors
|
|
67
|
-
Prefer factory functions for core types:
|
|
68
|
-
- `vec2(x, y)` not `new Vector2(x, y)`
|
|
69
|
-
- `rgb(r, g, b, a)` or `hsl(h, s, l, a)` not `new Color(...)`
|
|
70
|
-
- `tile(index, size)` for tile info
|
|
71
|
-
|
|
72
|
-
Use constructors for game objects and complex types:
|
|
73
|
-
- `new EngineObject(pos, size)`
|
|
74
|
-
- `new ParticleEmitter(...)`
|
|
75
|
-
- `new Sound(zzfxParams)`
|
|
76
|
-
- `new Timer(duration)`
|
|
77
|
-
|
|
78
|
-
### Naming
|
|
79
|
-
- `camelCase` for variables and functions
|
|
80
|
-
- `PascalCase` for classes
|
|
81
|
-
- `UPPER_CASE` for constants that are truly constant (like `PI`)
|
|
82
|
-
|
|
83
|
-
### Code style
|
|
84
|
-
- Use JSDoc with `@memberof` grouping (namespaces: Engine, Math, Draw, Input, Audio, Debug, Settings, etc.)
|
|
85
|
-
- Prefer single-line comments: `// comment`
|
|
86
|
-
- Use `ASSERT(condition, 'error message')` for validation (stripped in release)
|
|
87
|
-
- Use `LOG(...)` for debug output (stripped in release)
|
|
88
|
-
|
|
89
|
-
### Type checking
|
|
90
|
-
Use built-in type helpers for validation:
|
|
91
|
-
```javascript
|
|
92
|
-
isNumber(n) // true if number and not NaN
|
|
93
|
-
isString(s) // true if not null/undefined (has toString)
|
|
94
|
-
isArray(a) // true if array
|
|
95
|
-
isVector2(v) // true if valid Vector2
|
|
96
|
-
isColor(c) // true if valid Color
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
### Global variables
|
|
100
|
-
- Engine time: `time`, `timeReal`, `frame`, `timeDelta`
|
|
101
|
-
- Camera: `cameraPos`, `cameraScale`, `cameraAngle`
|
|
102
|
-
- Input: `mousePos`, `mousePosScreen`, `mouseWheel`
|
|
103
|
-
- State: `paused`, `debug`, `debugOverlay`
|
|
104
|
-
- Settings are in `engineSettings.js` with corresponding setter functions
|
|
105
|
-
|
|
106
|
-
## Common patterns
|
|
107
|
-
|
|
108
|
-
### Game structure
|
|
109
|
-
```javascript
|
|
110
|
-
function gameInit() { } // Called once after engine starts
|
|
111
|
-
function gameUpdate() { } // Called every frame for game logic
|
|
112
|
-
function gameUpdatePost() { } // Called after physics, even when paused
|
|
113
|
-
function gameRender() { } // Called before objects render
|
|
114
|
-
function gameRenderPost() { } // Called after objects render
|
|
115
|
-
|
|
116
|
-
engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, ['tiles.png']);
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
### Creating objects
|
|
120
|
-
```javascript
|
|
121
|
-
class Player extends EngineObject {
|
|
122
|
-
constructor(pos) {
|
|
123
|
-
super(pos, vec2(1), tile(0, 16));
|
|
124
|
-
this.setCollision();
|
|
125
|
-
}
|
|
126
|
-
update() {
|
|
127
|
-
super.update();
|
|
128
|
-
// custom logic
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
### Common drawing functions
|
|
134
|
-
```javascript
|
|
135
|
-
drawRect(pos, size, color) // solid rectangle
|
|
136
|
-
drawTile(pos, size, tileInfo, color) // sprite from tile sheet
|
|
137
|
-
drawText(text, pos, size, color) // text rendering
|
|
138
|
-
drawLine(posA, posB, thickness, color) // line between points
|
|
139
|
-
drawEllipse(pos, size, color) // filled ellipse
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
## Common pitfalls
|
|
143
|
-
|
|
144
|
-
- **New public APIs must be added to `src/engineExport.js`** - Variables and functions added to engine source files are accessible in script-tag builds automatically, but the ESM build (`littlejs.esm.js`) and TypeScript definitions (`littlejs.d.ts`) only include what's listed in `engineExport.js`. Plugin exports go in `plugins/pluginExport.js`. Forgetting this means ESM/TS users can't access the new API.
|
|
145
|
-
- **ASSERT and LOG are stripped in release builds** - Don't rely on side effects
|
|
146
|
-
- **Don't modify constant colors** - `WHITE`, `BLACK`, `RED`, etc. are frozen; use `.copy()` first
|
|
147
|
-
- **Time variables are global** - `time`, `frame` update automatically each frame
|
|
148
|
-
- **Fixed 60 FPS timestep** - Physics runs at 60 FPS regardless of display refresh rate
|
|
149
|
-
- **WebGL is enabled by default** - Set `glEnable = false` before `engineInit()` for Canvas2D only
|
|
150
|
-
- **Tile coordinates are bottom-left origin** - Y increases upward in world space
|
|
151
|
-
|
|
152
|
-
## Developer workflows
|
|
153
|
-
|
|
154
|
-
### Build
|
|
155
|
-
```bash
|
|
156
|
-
npm run build
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
### Testing
|
|
160
|
-
```bash
|
|
161
|
-
npm test
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
- Tests target `dist/littlejs.esm.js` — rebuild with `npm run build` after changing source.
|
|
165
|
-
- [test/setup.mjs](test/setup.mjs) stubs minimal DOM and enables headless mode. Tests shouldn't call `engineInit`, `render()`, or assume `time` advances.
|
|
166
|
-
- Zero test dependencies — uses Node's built-in `node --test`. Match the style in [test/](test/) when adding new ones.
|
|
167
|
-
- CI runs build + test on every push/PR ([.github/workflows/test.yml](.github/workflows/test.yml)).
|
|
168
|
-
|
|
169
|
-
### Debug features
|
|
170
|
-
- Press `Esc` to toggle debug overlay
|
|
171
|
-
- Number keys toggle visualizations
|
|
172
|
-
- `+`/`-` keys control time scale
|
|
173
|
-
- Debug functions: `debugRect()`, `debugCircle()`, `debugLine()`, `debugText()`
|
package/CLAUDE.md
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
See @AI.md for instructions.
|
package/examples/box2d/game.js
DELETED
|
@@ -1,192 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
Little JS Box2d Demo
|
|
3
|
-
- Demonstrates how to use Box2D with LittleJS
|
|
4
|
-
- Several scenes to demonstrate Box2D features
|
|
5
|
-
- Every type of shape and joint
|
|
6
|
-
- Contact begin and end callbacks
|
|
7
|
-
- Raycasting and querying
|
|
8
|
-
- Collision filtering
|
|
9
|
-
- User Interaction
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
'use strict';
|
|
13
|
-
|
|
14
|
-
// import LittleJS module
|
|
15
|
-
import * as LJS from '../../dist/littlejs.esm.js';
|
|
16
|
-
import * as GameObjects from './gameObjects.js';
|
|
17
|
-
import * as Scenes from './scenes.js';
|
|
18
|
-
const {vec2, hsl} = LJS;
|
|
19
|
-
|
|
20
|
-
// use HD textures
|
|
21
|
-
LJS.setCanvasPixelated(false);
|
|
22
|
-
LJS.setTilesPixelated(false);
|
|
23
|
-
|
|
24
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
25
|
-
// game variables
|
|
26
|
-
|
|
27
|
-
const sceneCount = 12;
|
|
28
|
-
const startScene = 0;
|
|
29
|
-
export let spriteAtlas, groundObject, mouseJoint, repeatSpawnTimer = new LJS.Timer;
|
|
30
|
-
const sound_click = new LJS.Sound([.2,.1,,,,.01,,,,,,,,,,,,,,,-500]);
|
|
31
|
-
|
|
32
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
33
|
-
function setScene(scene)
|
|
34
|
-
{
|
|
35
|
-
// setup
|
|
36
|
-
LJS.setCameraPos(vec2(20,10));
|
|
37
|
-
LJS.setGravity(vec2(0,-20));
|
|
38
|
-
|
|
39
|
-
// destroy old scene
|
|
40
|
-
LJS.engineObjectsDestroy();
|
|
41
|
-
mouseJoint = 0;
|
|
42
|
-
|
|
43
|
-
// create walls
|
|
44
|
-
groundObject = GameObjects.spawnBox(vec2(0,-4), vec2(1e3,8), hsl(0,0,.2), LJS.box2d.bodyTypeStatic);
|
|
45
|
-
GameObjects.spawnBox(vec2(-4, 0), vec2(8,1e3), LJS.BLACK, LJS.box2d.bodyTypeStatic);
|
|
46
|
-
GameObjects.spawnBox(vec2(44, 0), vec2(8,1e3), LJS.BLACK, LJS.box2d.bodyTypeStatic);
|
|
47
|
-
GameObjects.spawnBox(vec2(0,100), vec2(1e3,8), LJS.BLACK, LJS.box2d.bodyTypeStatic);
|
|
48
|
-
|
|
49
|
-
// load the scene
|
|
50
|
-
Scenes.loadScene(scene);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
54
|
-
async function gameInit()
|
|
55
|
-
{
|
|
56
|
-
// start up LittleJS Box2D plugin
|
|
57
|
-
await LJS.box2dInit();
|
|
58
|
-
//LJS.box2dSetDebug(true); // enable box2d debug draw
|
|
59
|
-
|
|
60
|
-
// create a table of all sprites
|
|
61
|
-
const gameTile = (i)=> LJS.tile(i, 124, 0, 2);
|
|
62
|
-
spriteAtlas =
|
|
63
|
-
{
|
|
64
|
-
circle: gameTile(0),
|
|
65
|
-
dot: gameTile(1),
|
|
66
|
-
circleOutline: gameTile(2),
|
|
67
|
-
squareOutline: gameTile(3),
|
|
68
|
-
wheel: gameTile(4),
|
|
69
|
-
gear: gameTile(5),
|
|
70
|
-
squareOutline2: gameTile(6),
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
// startup the demo
|
|
74
|
-
LJS.setCanvasClearColor(hsl(0,0,.8));
|
|
75
|
-
setScene(startScene);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
79
|
-
function gameUpdate()
|
|
80
|
-
{
|
|
81
|
-
// scale canvas to fit based on 1080p
|
|
82
|
-
LJS.setCameraScale(LJS.mainCanvasSize.y * 48 / 1080);
|
|
83
|
-
|
|
84
|
-
// mouse controls
|
|
85
|
-
if (mouseJoint)
|
|
86
|
-
{
|
|
87
|
-
// update mouse joint
|
|
88
|
-
mouseJoint.setTarget(LJS.mousePos);
|
|
89
|
-
if (LJS.mouseWasReleased(0))
|
|
90
|
-
{
|
|
91
|
-
// release object
|
|
92
|
-
sound_click.play(LJS.mousePos, 1, .5);
|
|
93
|
-
mouseJoint.destroy();
|
|
94
|
-
mouseJoint = 0;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
else if (LJS.mouseWasPressed(0))
|
|
98
|
-
{
|
|
99
|
-
// grab object
|
|
100
|
-
sound_click.play(LJS.mousePos);
|
|
101
|
-
const object = LJS.box2d.pointCast(LJS.mousePos);
|
|
102
|
-
if (object)
|
|
103
|
-
mouseJoint = new LJS.Box2dTargetJoint(object, groundObject, LJS.mousePos);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// controls
|
|
107
|
-
if (LJS.mouseIsDown(1) || LJS.keyIsDown('KeyZ'))
|
|
108
|
-
{
|
|
109
|
-
const isSet = repeatSpawnTimer.isSet();
|
|
110
|
-
if (!isSet || repeatSpawnTimer.elapsed())
|
|
111
|
-
{
|
|
112
|
-
// spawn continuously after a delay
|
|
113
|
-
isSet || repeatSpawnTimer.set(.5);
|
|
114
|
-
GameObjects.spawnRandomObject(LJS.mousePos);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
else
|
|
118
|
-
repeatSpawnTimer.unset();
|
|
119
|
-
if (LJS.mouseWasPressed(2) || LJS.keyWasPressed('KeyX'))
|
|
120
|
-
GameObjects.explosion(LJS.mousePos);
|
|
121
|
-
|
|
122
|
-
if (LJS.keyWasPressed('KeyR'))
|
|
123
|
-
setScene(Scenes.scene); // reset scene
|
|
124
|
-
if (LJS.keyWasPressed('ArrowUp') || LJS.keyWasPressed('ArrowDown'))
|
|
125
|
-
{
|
|
126
|
-
// change scene
|
|
127
|
-
const upPressed = LJS.keyWasPressed('ArrowUp');
|
|
128
|
-
setScene(LJS.mod(Scenes.scene + (upPressed?1:-1), sceneCount));
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
133
|
-
function gameUpdatePost()
|
|
134
|
-
{
|
|
135
|
-
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
139
|
-
function gameRender()
|
|
140
|
-
{
|
|
141
|
-
if (Scenes.scene == 5)
|
|
142
|
-
{
|
|
143
|
-
// raycast test
|
|
144
|
-
const count = 100;
|
|
145
|
-
const distance = 10;
|
|
146
|
-
for (let i=count;i--;)
|
|
147
|
-
{
|
|
148
|
-
const start = LJS.mousePos;
|
|
149
|
-
const end = start.add(vec2(distance,0).rotate(i/count*LJS.PI*2));
|
|
150
|
-
const result = LJS.box2d.raycast(start, end);
|
|
151
|
-
const color = result ? hsl(0,1,.5,.5) : hsl(.5,1,.5,.5);
|
|
152
|
-
LJS.drawLine(start, result ? result.point : end, .1, color);
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
158
|
-
function gameRenderPost()
|
|
159
|
-
{
|
|
160
|
-
if (mouseJoint)
|
|
161
|
-
{
|
|
162
|
-
// draw mouse joint
|
|
163
|
-
const ab = mouseJoint.getAnchorB();
|
|
164
|
-
LJS.drawTile(ab, vec2(.3), spriteAtlas.circle, LJS.BLACK);
|
|
165
|
-
LJS.drawLine(LJS.mousePos, ab, .1, LJS.BLACK);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// draw demo info
|
|
169
|
-
const pos = vec2(LJS.mainCanvasSize.x/2, 50);
|
|
170
|
-
drawText('LittleJS Box2D Demo', 65, 70);
|
|
171
|
-
drawText(Scenes.sceneName, 50, 100);
|
|
172
|
-
if (Scenes.scene == 0)
|
|
173
|
-
{
|
|
174
|
-
drawText('Mouse Left = Grab');
|
|
175
|
-
drawText('Mouse Middle or Z = Spawn');
|
|
176
|
-
drawText('Mouse Right or X = Explode');
|
|
177
|
-
drawText('Arrows Up/Down = Change Scene');
|
|
178
|
-
}
|
|
179
|
-
if (Scenes.scene == 3)
|
|
180
|
-
{
|
|
181
|
-
drawText('Right = Accelerate');
|
|
182
|
-
drawText('Left = Reverse');
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
function drawText(text, size=40, gap=50)
|
|
186
|
-
{ LJS.drawTextScreen(text, pos, size, LJS.WHITE, size*.1); pos.y += gap; }
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
190
|
-
// Startup LittleJS Engine with Box2D
|
|
191
|
-
|
|
192
|
-
LJS.engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, ['tiles.png']);
|