littlejsengine 1.18.0 → 1.18.2
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/.claude/settings.local.json +7 -0
- package/.github/workflows/test.yml +17 -0
- package/AI.md +10 -0
- package/CLAUDE.md +1 -0
- package/README.md +15 -6
- package/dist/littlejs.d.ts +47 -7
- package/dist/littlejs.esm.js +97 -30
- package/dist/littlejs.esm.min.js +1 -1
- package/dist/littlejs.js +91 -28
- package/dist/littlejs.min.js +1 -1
- package/dist/littlejs.release.js +89 -28
- package/examples/box2d/game.js +2 -2
- package/examples/box2d/gameObjects.js +74 -0
- package/examples/box2d/scenes.js +7 -0
- package/examples/shorts/base.html +1 -1
- package/examples/starter/index.html +2 -2
- package/examples/stress/index.html +1 -1
- package/package.json +3 -2
- package/src/engine.js +23 -6
- package/src/engineBuild.mjs +1 -1
- package/src/engineDebug.js +2 -0
- package/src/engineDraw.js +1 -0
- package/src/engineExport.js +6 -2
- package/src/engineInput.js +34 -10
- package/src/engineMath.js +1 -1
- package/src/engineObject.js +4 -3
- package/src/engineSettings.js +26 -8
- package/test/math.test.mjs +774 -0
- package/test/setup.mjs +22 -0
- package/test/smoke.test.mjs +258 -0
- package/test/util.test.mjs +80 -0
|
@@ -0,0 +1,17 @@
|
|
|
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
CHANGED
|
@@ -155,6 +155,16 @@ drawEllipse(pos, size, color) // filled ellipse
|
|
|
155
155
|
npm run build
|
|
156
156
|
```
|
|
157
157
|
|
|
158
|
+
### Testing
|
|
159
|
+
```bash
|
|
160
|
+
npm test
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
- Tests target `dist/littlejs.esm.js` — rebuild with `npm run build` after changing source.
|
|
164
|
+
- [test/setup.mjs](test/setup.mjs) stubs minimal DOM and enables headless mode. Tests shouldn't call `engineInit`, `render()`, or assume `time` advances.
|
|
165
|
+
- Zero test dependencies — uses Node's built-in `node --test`. Match the style in [test/](test/) when adding new ones.
|
|
166
|
+
- CI runs build + test on every push/PR ([.github/workflows/test.yml](.github/workflows/test.yml)).
|
|
167
|
+
|
|
158
168
|
### Debug features
|
|
159
169
|
- Press `Esc` to toggle debug overlay
|
|
160
170
|
- Number keys toggle visualizations
|
package/CLAUDE.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
See @AI.md for instructions.
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# LittleJS - The Tiny Fast JavaScript Game Engine
|
|
2
2
|
|
|
3
|
-
<div align='center'>
|
|
3
|
+
<div align='center' markdown='1'>
|
|
4
4
|
|
|
5
5
|

|
|
6
6
|
|
|
@@ -18,7 +18,11 @@ LittleJS is a fast, lightweight, and fully open source HTML5 game engine designe
|
|
|
18
18
|
Its small footprint is packed with a comprehensive feature set including rendering, physics, particles, sound, and input handling.
|
|
19
19
|
The code is very clean and well documented with many examples to get you started quickly.
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
### 🚀 Join the LittleJS Game Jam
|
|
22
|
+
|
|
23
|
+
*The Third Annual LittleJS Game Jam will take place From Oct 2 to Nov 2! Unleash your creativity and develop amazing games using the LittleJS game engine. 🕹️🎮 [Sign up today and get more info about the jam on itch.io!](https://itch.io/jam/littlejs-jam)*
|
|
24
|
+
|
|
25
|
+
<div align='center' markdown='1'>
|
|
22
26
|
|
|
23
27
|
## [Demos](https://killedbyapixel.github.io/LittleJS/examples) | [Docs](https://killedbyapixel.github.io/LittleJS/docs) | [Trailer](https://youtu.be/chuBzGjv7Ms) | [Discord](https://discord.gg/zb7hcGkyZe) | [FAQ](https://github.com/KilledByAPixel/LittleJS/blob/main/FAQ.md)
|
|
24
28
|
|
|
@@ -30,14 +34,15 @@ The code is very clean and well documented with many examples to get you started
|
|
|
30
34
|
|
|
31
35
|
### ✨ Graphics
|
|
32
36
|
|
|
33
|
-
-
|
|
37
|
+
- Blazing fast WebGL2 + Canvas2D hybrid rendering system
|
|
34
38
|
- Apply [Shadertoy](https://www.shadertoy.com) style shaders for post-processing effects
|
|
35
39
|
- Robust particle effect system and [effect design tool](https://killedbyapixel.github.io/LittleJS/examples/particles/)
|
|
36
40
|
|
|
37
41
|
### 🔊 Audio
|
|
38
42
|
|
|
39
43
|
- Sound and music with mp3, ogg, or wave files
|
|
40
|
-
- Use [ZzFX](https://killedbyapixel.github.io/ZzFX/) sound
|
|
44
|
+
- Use [ZzFX](https://killedbyapixel.github.io/ZzFX/) sound generator to play sounds without asset files
|
|
45
|
+
- Spatial audio stereo panning
|
|
41
46
|
|
|
42
47
|
### 🎮 Input
|
|
43
48
|
|
|
@@ -53,13 +58,14 @@ The code is very clean and well documented with many examples to get you started
|
|
|
53
58
|
### 🚀 Flexibility
|
|
54
59
|
|
|
55
60
|
- Compatible with all modern web browsers and mobile devices
|
|
56
|
-
- TypeScript and Module support with example projects for both
|
|
61
|
+
- Full TypeScript and Module support with example projects for both
|
|
57
62
|
- Great for size coding competitions like [Js13kGames](https://js13kgames.com/)
|
|
58
63
|
- Open Source and [MIT licensed](https://github.com/KilledByAPixel/LittleJS/blob/main/LICENSE)
|
|
59
64
|
|
|
60
65
|
### 🛠️ Developer Tools
|
|
61
66
|
|
|
62
67
|
- Live example browser with code editor
|
|
68
|
+
- Import level editor data from [Tiled](https://github.com/mapeditor/tiled) or other JSON
|
|
63
69
|
- Debug overlay and primitive rendering system
|
|
64
70
|
- Medal tracking system with [Newgrounds](https://www.newgrounds.com/) support
|
|
65
71
|
- Node.js build system
|
|
@@ -74,6 +80,8 @@ To get started download the latest LittleJS package from GitHub or install via n
|
|
|
74
80
|
- [LittleJS Quick Reference Sheet](https://github.com/KilledByAPixel/LittleJS/blob/main/reference.md) - A reference sheet to help you get started.
|
|
75
81
|
- [Little JS FAQ](https://github.com/KilledByAPixel/LittleJS/blob/main/FAQ.md) - Answers to common questions about LittleJS.
|
|
76
82
|
- [JS13k Branch](https://github.com/KilledByAPixel/LittleJS/tree/js13k) - For size coding events like JS13k there is a special branch that builds to a 7KB zip.
|
|
83
|
+
- [LittleJS GPT AI](https://chatgpt.com/g/g-67c7c080b5bc81919736bc8815836be6-make-games-with-littlejs) - Use AI to make games without writing any code!
|
|
84
|
+
- [LittleJS AI Info](https://github.com/KilledByAPixel/LittleJS-AI) - Experiments, games, and templates to improve LittleJS + AI workflows.
|
|
77
85
|
|
|
78
86
|
## Examples
|
|
79
87
|
|
|
@@ -82,7 +90,7 @@ LittleJS comes with a several demos both for learning and using as starter proje
|
|
|
82
90
|
- [Example Browser](https://killedbyapixel.github.io/LittleJS/examples/) - Live example browser with all examples
|
|
83
91
|
- [Breakout](https://killedbyapixel.github.io/LittleJS/examples/breakout/) - Block breaking game with post-processing effects
|
|
84
92
|
- [Puzzle Game](https://killedbyapixel.github.io/LittleJS/examples/puzzle/) - Match 3 puzzle game with HD rendering and high score tracking
|
|
85
|
-
- [Platformer](https://killedbyapixel.github.io/LittleJS/examples/platformer/) - Platformer/shooter that loads level data
|
|
93
|
+
- [Platformer](https://killedbyapixel.github.io/LittleJS/examples/platformer/) - Platformer/shooter demo that loads level data
|
|
86
94
|
- [Box2D Demo](https://killedbyapixel.github.io/LittleJS/examples/box2d/) - Box2D plugin demonstration and testbed
|
|
87
95
|
- [Stress Test](https://killedbyapixel.github.io/LittleJS/examples/stress/) - Sprite rendering benchmark and music system demo
|
|
88
96
|
- [Particle System Designer](https://killedbyapixel.github.io/LittleJS/examples/particles/) - Particle system editor and visualizer
|
|
@@ -117,6 +125,7 @@ Here are a few of the many amazing games created with LittleJS...
|
|
|
117
125
|
- [Bug&Bee](https://itch.io/jam/littlejs-game-jam-2025/results) - Low fi shoot em up with co-op gameplay. LittleJS Jam 1st place! by [eoinmcg](https://eoinmcg.itch.io/)
|
|
118
126
|
- [Little Platformer](https://psemo.itch.io/little-platformer) - Platformer with many mechanics. LittleJS Jam 2st place! by [PSEMO](https://psemo.itch.io/), [Solita666](https://itch.io/profile/solita666), [GabrielRG](https://gabrielrg.itch.io/), [Nate](https://natesassoon.itch.io/)
|
|
119
127
|
- [Rogue Pong](https://itch.io/jam/littlejs-game-jam-2025/rate/4004165) - Roguelike crossed with classic pong gameplay. LittleJS Jam 3rd place! by [webdevbrian](https://webdevbrian.itch.io/)
|
|
128
|
+
- [Orbitswarm](https://focaccai.itch.io/orbitswarm) - Fast paced space strategy game. by [Focaccai](https://www.focaccai.com/)
|
|
120
129
|
- [LittleJS Jam 2024 Results](https://itch.io/jam/littlejs-jam-2024/results) - All the games from the first LittleJS Game Jam.
|
|
121
130
|
- [LittleJS Jam 2025 Results](https://itch.io/jam/littlejs-game-jam-2025/results) - All the games from the second LittleJS Game Jam.
|
|
122
131
|
|
package/dist/littlejs.d.ts
CHANGED
|
@@ -377,6 +377,12 @@ declare module "littlejsengine" {
|
|
|
377
377
|
* @default
|
|
378
378
|
* @memberof Settings */
|
|
379
379
|
export let tilesPixelated: boolean;
|
|
380
|
+
/** Scale factor applied to the canvas backing store for native-resolution rendering.
|
|
381
|
+
* Pass 1 for no scaling, a number for an explicit ratio, or undefined to track devicePixelRatio each frame.
|
|
382
|
+
* @type {number|undefined}
|
|
383
|
+
* @default
|
|
384
|
+
* @memberof Settings */
|
|
385
|
+
export let canvasPixelRatio: number | undefined;
|
|
380
386
|
/** Default font used for text rendering
|
|
381
387
|
* @type {string}
|
|
382
388
|
* @default
|
|
@@ -474,17 +480,21 @@ declare module "littlejsengine" {
|
|
|
474
480
|
export let inputWASDEmulateDirection: boolean;
|
|
475
481
|
/** True if touch gamepad should appear on mobile devices
|
|
476
482
|
* - Supports left analog stick, 4 face buttons and start button (button 9)
|
|
483
|
+
* - setTouchGamepadButtonCount(1) to use face buttons as right analog stick
|
|
484
|
+
* - Analog stick buttons 10 and 11 are also activated when virtual sticks are touched
|
|
485
|
+
|
|
477
486
|
* @type {boolean}
|
|
478
487
|
* @default
|
|
479
488
|
* @memberof Settings */
|
|
480
489
|
export let touchGamepadEnable: boolean;
|
|
481
490
|
/** True if touch gamepad should have start button in the center
|
|
491
|
+
* - Prevents activating if overlappng with virtual stick or buttons if they are enabled
|
|
482
492
|
* - When the game is paused, any touch will press the button
|
|
483
|
-
* -
|
|
484
|
-
* @type {
|
|
493
|
+
* - Set size to enable the center button
|
|
494
|
+
* @type {number}
|
|
485
495
|
* @default
|
|
486
496
|
* @memberof Settings */
|
|
487
|
-
export let
|
|
497
|
+
export let touchGamepadCenterButtonSize: number;
|
|
488
498
|
/** True if touch gamepad should be analog stick or false to use if 8 way dpad
|
|
489
499
|
* @type {boolean}
|
|
490
500
|
* @default
|
|
@@ -592,6 +602,11 @@ declare module "littlejsengine" {
|
|
|
592
602
|
* @param {boolean} pixelated
|
|
593
603
|
* @memberof Settings */
|
|
594
604
|
export function setTilesPixelated(pixelated: boolean): void;
|
|
605
|
+
/** Set the canvas pixel ratio.
|
|
606
|
+
* Pass a number for an explicit ratio, or call with no argument to track devicePixelRatio each frame.
|
|
607
|
+
* @param {number} [pixelRatio]
|
|
608
|
+
* @memberof Settings */
|
|
609
|
+
export function setCanvasPixelRatio(pixelRatio?: number): void;
|
|
595
610
|
/** Set default font used for text rendering
|
|
596
611
|
* @param {string} font
|
|
597
612
|
* @memberof Settings */
|
|
@@ -676,11 +691,12 @@ declare module "littlejsengine" {
|
|
|
676
691
|
* @param {boolean} enable
|
|
677
692
|
* @memberof Settings */
|
|
678
693
|
export function setTouchGamepadEnable(enable: boolean): void;
|
|
679
|
-
/**
|
|
680
|
-
* -
|
|
681
|
-
*
|
|
694
|
+
/** Set if touch gamepad should have start button in the center
|
|
695
|
+
* - Set size to enable the center button
|
|
696
|
+
* - When the game is paused, any touch will press the button
|
|
697
|
+
* @param {number} size
|
|
682
698
|
* @memberof Settings */
|
|
683
|
-
export function
|
|
699
|
+
export function setTouchGamepadCenterButtonSize(size: number): void;
|
|
684
700
|
/** Set number of buttons on touch gamepad (0-4), if 1 also acts as right analog stick
|
|
685
701
|
* @param {number} count
|
|
686
702
|
* @memberof Settings */
|
|
@@ -882,6 +898,16 @@ declare module "littlejsengine" {
|
|
|
882
898
|
* @return {number}
|
|
883
899
|
* @memberof Math */
|
|
884
900
|
export function lerp(valueA: number, valueB: number, percent: number): number;
|
|
901
|
+
/** Gets percent between percentA and percentB and linearly interpolates between lerpA and lerpB
|
|
902
|
+
* A shortcut for lerp(lerpA, lerpB, percent(value, percentA, percentB))
|
|
903
|
+
* @param {number} value
|
|
904
|
+
* @param {number} percentA
|
|
905
|
+
* @param {number} percentB
|
|
906
|
+
* @param {number} lerpA
|
|
907
|
+
* @param {number} lerpB
|
|
908
|
+
* @return {number}
|
|
909
|
+
* @memberof Math */
|
|
910
|
+
export function percentLerp(value: number, percentA: number, percentB: number, lerpA: number, lerpB: number): number;
|
|
885
911
|
/** Applies smoothstep function to the percentage value
|
|
886
912
|
* @param {number} percent
|
|
887
913
|
* @return {number}
|
|
@@ -909,6 +935,20 @@ declare module "littlejsengine" {
|
|
|
909
935
|
* @return {boolean} - True if intersecting
|
|
910
936
|
* @memberof Math */
|
|
911
937
|
export function isIntersecting(start: Vector2, end: Vector2, pos: Vector2, size: Vector2): boolean;
|
|
938
|
+
/**
|
|
939
|
+
* @callback LineTestFunction - Checks if a position is colliding
|
|
940
|
+
* @param {Vector2} pos
|
|
941
|
+
* @memberof Draw
|
|
942
|
+
*/
|
|
943
|
+
/**
|
|
944
|
+
* Casts a ray and returns position of the first collision found, or undefined if none are found
|
|
945
|
+
* @param {Vector2} posStart
|
|
946
|
+
* @param {Vector2} posEnd
|
|
947
|
+
* @param {LineTestFunction} testFunction - Check if colliding
|
|
948
|
+
* @param {Vector2} [normal] - Optional vector to store the normal
|
|
949
|
+
* @return {Vector2|undefined} - Position of the collision or undefined if none found
|
|
950
|
+
* @memberof Math */
|
|
951
|
+
export function lineTest(posStart: Vector2, posEnd: Vector2, testFunction: LineTestFunction, normal?: Vector2): Vector2 | undefined;
|
|
912
952
|
/** Returns an oscillating wave between 0 and amplitude with frequency of 1 Hz by default
|
|
913
953
|
* @param {number} [frequency] - Frequency of the wave in Hz
|
|
914
954
|
* @param {number} [amplitude] - Amplitude (max height) of the wave
|
package/dist/littlejs.esm.js
CHANGED
|
@@ -35,7 +35,7 @@ const engineName = 'LittleJS';
|
|
|
35
35
|
* @type {string}
|
|
36
36
|
* @default
|
|
37
37
|
* @memberof Engine */
|
|
38
|
-
const engineVersion = '1.18.
|
|
38
|
+
const engineVersion = '1.18.2';
|
|
39
39
|
|
|
40
40
|
/** Frames per second to update
|
|
41
41
|
* @type {number}
|
|
@@ -318,12 +318,17 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
318
318
|
}
|
|
319
319
|
else
|
|
320
320
|
{
|
|
321
|
+
// apply device pixel ratio for crisp rendering
|
|
322
|
+
const dpr = canvasPixelRatio ?? (devicePixelRatio || 1);
|
|
323
|
+
const viewWidth = innerWidth * dpr | 0;
|
|
324
|
+
const viewHeight = innerHeight * dpr | 0;
|
|
325
|
+
|
|
321
326
|
// get main canvas size based on window size
|
|
322
|
-
mainCanvasSize.x = min(
|
|
323
|
-
mainCanvasSize.y = min(
|
|
327
|
+
mainCanvasSize.x = min(viewWidth, canvasMaxSize.x);
|
|
328
|
+
mainCanvasSize.y = min(viewHeight, canvasMaxSize.y);
|
|
324
329
|
|
|
325
330
|
// responsive aspect ratio with native resolution
|
|
326
|
-
const innerAspect =
|
|
331
|
+
const innerAspect = viewWidth / viewHeight;
|
|
327
332
|
ASSERT(canvasMinAspect <= canvasMaxAspect);
|
|
328
333
|
if (canvasMaxAspect && innerAspect > canvasMaxAspect)
|
|
329
334
|
{
|
|
@@ -337,6 +342,17 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
337
342
|
const h = mainCanvasSize.x / canvasMinAspect | 0;
|
|
338
343
|
mainCanvasSize.y = min(h, canvasMaxSize.y);
|
|
339
344
|
}
|
|
345
|
+
|
|
346
|
+
// set CSS display size so backing store renders at viewport size
|
|
347
|
+
const cssW = (mainCanvasSize.x / dpr | 0) + 'px';
|
|
348
|
+
const cssH = (mainCanvasSize.y / dpr | 0) + 'px';
|
|
349
|
+
mainCanvas.style.width = cssW;
|
|
350
|
+
mainCanvas.style.height = cssH;
|
|
351
|
+
if (glCanvas)
|
|
352
|
+
{
|
|
353
|
+
glCanvas.style.width = cssW;
|
|
354
|
+
glCanvas.style.height = cssH;
|
|
355
|
+
}
|
|
340
356
|
}
|
|
341
357
|
|
|
342
358
|
// clear main canvas and set size
|
|
@@ -556,8 +572,9 @@ function drawEngineLogo(t)
|
|
|
556
572
|
|
|
557
573
|
// LittleJS Logo and Splash Screen
|
|
558
574
|
const x = mainContext;
|
|
559
|
-
const
|
|
560
|
-
const
|
|
575
|
+
const dpr = canvasPixelRatio ?? (devicePixelRatio || 1);
|
|
576
|
+
const w = mainCanvas.width = innerWidth * dpr;
|
|
577
|
+
const h = mainCanvas.height = innerHeight * dpr;
|
|
561
578
|
{
|
|
562
579
|
// background
|
|
563
580
|
const p3 = percent(t, 1, .8);
|
|
@@ -951,6 +968,8 @@ function debugShowErrors()
|
|
|
951
968
|
|
|
952
969
|
function debugInit()
|
|
953
970
|
{
|
|
971
|
+
if (showEngineVersion)
|
|
972
|
+
console.warn("LittleJS DEBUG build loaded. Use the release build for production.");
|
|
954
973
|
}
|
|
955
974
|
|
|
956
975
|
function debugUpdate()
|
|
@@ -1732,7 +1751,7 @@ function lineTest(posStart, posEnd, testFunction, normal)
|
|
|
1732
1751
|
// get ray direction and length
|
|
1733
1752
|
const dx = posEnd.x - posStart.x;
|
|
1734
1753
|
const dy = posEnd.y - posStart.y;
|
|
1735
|
-
const totalLength =
|
|
1754
|
+
const totalLength = (dx*dx + dy*dy)**.5;
|
|
1736
1755
|
if (!totalLength) return;
|
|
1737
1756
|
|
|
1738
1757
|
// current integer cell we are in
|
|
@@ -2836,6 +2855,13 @@ let canvasPixelated = false;
|
|
|
2836
2855
|
* @memberof Settings */
|
|
2837
2856
|
let tilesPixelated = true;
|
|
2838
2857
|
|
|
2858
|
+
/** Scale factor applied to the canvas backing store for native-resolution rendering.
|
|
2859
|
+
* Pass 1 for no scaling, a number for an explicit ratio, or undefined to track devicePixelRatio each frame.
|
|
2860
|
+
* @type {number|undefined}
|
|
2861
|
+
* @default
|
|
2862
|
+
* @memberof Settings */
|
|
2863
|
+
let canvasPixelRatio = 1;
|
|
2864
|
+
|
|
2839
2865
|
/** Default font used for text rendering
|
|
2840
2866
|
* @type {string}
|
|
2841
2867
|
* @default
|
|
@@ -2977,18 +3003,22 @@ let touchInputEnable = true;
|
|
|
2977
3003
|
|
|
2978
3004
|
/** True if touch gamepad should appear on mobile devices
|
|
2979
3005
|
* - Supports left analog stick, 4 face buttons and start button (button 9)
|
|
3006
|
+
* - setTouchGamepadButtonCount(1) to use face buttons as right analog stick
|
|
3007
|
+
* - Analog stick buttons 10 and 11 are also activated when virtual sticks are touched
|
|
3008
|
+
|
|
2980
3009
|
* @type {boolean}
|
|
2981
3010
|
* @default
|
|
2982
3011
|
* @memberof Settings */
|
|
2983
3012
|
let touchGamepadEnable = false;
|
|
2984
3013
|
|
|
2985
3014
|
/** True if touch gamepad should have start button in the center
|
|
3015
|
+
* - Prevents activating if overlappng with virtual stick or buttons if they are enabled
|
|
2986
3016
|
* - When the game is paused, any touch will press the button
|
|
2987
|
-
* -
|
|
2988
|
-
* @type {
|
|
3017
|
+
* - Set size to enable the center button
|
|
3018
|
+
* @type {number}
|
|
2989
3019
|
* @default
|
|
2990
3020
|
* @memberof Settings */
|
|
2991
|
-
let
|
|
3021
|
+
let touchGamepadCenterButtonSize = 300;
|
|
2992
3022
|
|
|
2993
3023
|
/** Number of buttons on touch gamepad (0-4), if 1 also acts as right analog stick
|
|
2994
3024
|
* @type {number}
|
|
@@ -3006,7 +3036,7 @@ let touchGamepadAnalog = true;
|
|
|
3006
3036
|
* @type {number}
|
|
3007
3037
|
* @default
|
|
3008
3038
|
* @memberof Settings */
|
|
3009
|
-
let touchGamepadSize =
|
|
3039
|
+
let touchGamepadSize = 100;
|
|
3010
3040
|
|
|
3011
3041
|
/** Transparency of touch gamepad overlay
|
|
3012
3042
|
* @type {number}
|
|
@@ -3148,6 +3178,12 @@ function setCanvasPixelated(pixelated)
|
|
|
3148
3178
|
* @memberof Settings */
|
|
3149
3179
|
function setTilesPixelated(pixelated) { tilesPixelated = pixelated; }
|
|
3150
3180
|
|
|
3181
|
+
/** Set the canvas pixel ratio.
|
|
3182
|
+
* Pass a number for an explicit ratio, or call with no argument to track devicePixelRatio each frame.
|
|
3183
|
+
* @param {number} [pixelRatio]
|
|
3184
|
+
* @memberof Settings */
|
|
3185
|
+
function setCanvasPixelRatio(pixelRatio) { canvasPixelRatio = pixelRatio; }
|
|
3186
|
+
|
|
3151
3187
|
/** Set default font used for text rendering
|
|
3152
3188
|
* @param {string} font
|
|
3153
3189
|
* @memberof Settings */
|
|
@@ -3268,11 +3304,12 @@ function setTouchInputEnable(enable) { touchInputEnable = enable; }
|
|
|
3268
3304
|
* @memberof Settings */
|
|
3269
3305
|
function setTouchGamepadEnable(enable) { touchGamepadEnable = enable; }
|
|
3270
3306
|
|
|
3271
|
-
/**
|
|
3272
|
-
* -
|
|
3273
|
-
*
|
|
3307
|
+
/** Set if touch gamepad should have start button in the center
|
|
3308
|
+
* - Set size to enable the center button
|
|
3309
|
+
* - When the game is paused, any touch will press the button
|
|
3310
|
+
* @param {number} size
|
|
3274
3311
|
* @memberof Settings */
|
|
3275
|
-
function
|
|
3312
|
+
function setTouchGamepadCenterButtonSize(size) { touchGamepadCenterButtonSize = size; }
|
|
3276
3313
|
|
|
3277
3314
|
/** Set number of buttons on touch gamepad (0-4), if 1 also acts as right analog stick
|
|
3278
3315
|
* @param {number} count
|
|
@@ -3570,10 +3607,10 @@ class EngineObject
|
|
|
3570
3607
|
// if already was touching, try to push away
|
|
3571
3608
|
const deltaPos = oldPos.subtract(o.pos);
|
|
3572
3609
|
const length = deltaPos.length();
|
|
3573
|
-
const pushAwayAccel = .001;
|
|
3574
|
-
const velocity = length < .
|
|
3610
|
+
const pushAwayAccel = .001;
|
|
3611
|
+
const velocity = length < .001 ? vec2(0,1) : deltaPos.scale(pushAwayAccel/length);
|
|
3575
3612
|
this.velocity = this.velocity.add(velocity);
|
|
3576
|
-
if (o.mass) // push away if not fixed
|
|
3613
|
+
if (o.mass) // push away other object if not fixed
|
|
3577
3614
|
o.velocity = o.velocity.subtract(velocity);
|
|
3578
3615
|
|
|
3579
3616
|
debugPhysics && debugOverlap(this.pos, this.size, o.pos, o.size, '#f00');
|
|
@@ -3810,6 +3847,7 @@ class EngineObject
|
|
|
3810
3847
|
child.parent = this;
|
|
3811
3848
|
child.localPos = localPos.copy();
|
|
3812
3849
|
child.localAngle = localAngle;
|
|
3850
|
+
child.updateTransforms();
|
|
3813
3851
|
return child;
|
|
3814
3852
|
}
|
|
3815
3853
|
|
|
@@ -4002,6 +4040,7 @@ function tile(index=new Vector2, size=tileDefaultSize, texture=0, padding=tileDe
|
|
|
4002
4040
|
const textureInfo = typeof texture === 'number' ?
|
|
4003
4041
|
textureInfos[texture] : texture;
|
|
4004
4042
|
ASSERT(textureInfo instanceof TextureInfo, 'tile texture is not loaded');
|
|
4043
|
+
ASSERT(textureInfo.size.x > 0, 'tile texture is not loaded');
|
|
4005
4044
|
|
|
4006
4045
|
// get the position of the tile
|
|
4007
4046
|
const sizePaddedX = size.x + padding*2;
|
|
@@ -5364,7 +5403,7 @@ function inputInit()
|
|
|
5364
5403
|
document.addEventListener('mouseup', onMouseUp);
|
|
5365
5404
|
document.addEventListener('mousemove', onMouseMove);
|
|
5366
5405
|
document.addEventListener('mouseleave', onMouseLeave);
|
|
5367
|
-
document.addEventListener('wheel', onMouseWheel);
|
|
5406
|
+
document.addEventListener('wheel', onMouseWheel, { passive: false });
|
|
5368
5407
|
document.addEventListener('contextmenu', onContextMenu);
|
|
5369
5408
|
document.addEventListener('blur', onBlur);
|
|
5370
5409
|
|
|
@@ -5383,7 +5422,7 @@ function inputInit()
|
|
|
5383
5422
|
}
|
|
5384
5423
|
|
|
5385
5424
|
// try to prevent default browser handling of input
|
|
5386
|
-
if (!inputPreventDefault || !
|
|
5425
|
+
if (!inputPreventDefault || !e.cancelable || !document.hasFocus()) return;
|
|
5387
5426
|
|
|
5388
5427
|
// don't break browser shortcuts
|
|
5389
5428
|
if (e.ctrlKey || e.metaKey || e.altKey) return;
|
|
@@ -5442,7 +5481,7 @@ function inputInit()
|
|
|
5442
5481
|
mousePosScreen = mouseEventToScreen(vec2(e.x,e.y));
|
|
5443
5482
|
mouseDeltaScreen = mouseDeltaScreen.add(mousePosScreen.subtract(mousePosScreenLast));
|
|
5444
5483
|
|
|
5445
|
-
if (inputPreventDefault &&
|
|
5484
|
+
if (inputPreventDefault && e.cancelable && document.hasFocus())
|
|
5446
5485
|
e.preventDefault();
|
|
5447
5486
|
}
|
|
5448
5487
|
function onMouseUp(e)
|
|
@@ -5464,7 +5503,12 @@ function inputInit()
|
|
|
5464
5503
|
mouseDeltaScreen = mouseDeltaScreen.add(movement);
|
|
5465
5504
|
}
|
|
5466
5505
|
function onMouseLeave() { mouseInWindow = false; } // mouse moved off window
|
|
5467
|
-
function onMouseWheel(e)
|
|
5506
|
+
function onMouseWheel(e)
|
|
5507
|
+
{
|
|
5508
|
+
mouseWheel = e.ctrlKey ? 0 : sign(e.deltaY);
|
|
5509
|
+
if (inputPreventDefault && e.cancelable && document.hasFocus())
|
|
5510
|
+
e.preventDefault(); // prevent page scrolling
|
|
5511
|
+
}
|
|
5468
5512
|
function onContextMenu(e) { e.preventDefault(); } // prevent right click menu
|
|
5469
5513
|
function onBlur() { inputClear(); } // reset input when focus is lost
|
|
5470
5514
|
|
|
@@ -5514,7 +5558,7 @@ function inputInit()
|
|
|
5514
5558
|
wasTouching = touching;
|
|
5515
5559
|
|
|
5516
5560
|
// prevent default handling like copy, magnifier lens, and scrolling
|
|
5517
|
-
if (inputPreventDefault &&
|
|
5561
|
+
if (inputPreventDefault && e.cancelable && document.hasFocus())
|
|
5518
5562
|
e.preventDefault();
|
|
5519
5563
|
|
|
5520
5564
|
// must return true so the document will get focus
|
|
@@ -5535,7 +5579,7 @@ function inputInit()
|
|
|
5535
5579
|
if (touching)
|
|
5536
5580
|
{
|
|
5537
5581
|
touchGamepadTimer.set();
|
|
5538
|
-
if (
|
|
5582
|
+
if (touchGamepadCenterButtonSize && !wasTouching && paused)
|
|
5539
5583
|
{
|
|
5540
5584
|
// touch anywhere to press start when paused
|
|
5541
5585
|
touchGamepadButtons[9] = 1;
|
|
@@ -5560,6 +5604,7 @@ function inputInit()
|
|
|
5560
5604
|
// virtual analog stick
|
|
5561
5605
|
const delta = touchPos.subtract(stickCenter);
|
|
5562
5606
|
touchGamepadSticks[0] = delta.scale(2/touchGamepadSize).clampLength();
|
|
5607
|
+
touchGamepadButtons[10] = 1; // also press a button when touching stick
|
|
5563
5608
|
}
|
|
5564
5609
|
else if (buttonCenter.distance(touchPos) < touchGamepadSize)
|
|
5565
5610
|
{
|
|
@@ -5568,6 +5613,7 @@ function inputInit()
|
|
|
5568
5613
|
// virtual right analog stick
|
|
5569
5614
|
const delta = touchPos.subtract(buttonCenter);
|
|
5570
5615
|
touchGamepadSticks[1] = delta.scale(2/touchGamepadSize).clampLength();
|
|
5616
|
+
touchGamepadButtons[11] = 1; // also press a button when touching right stick
|
|
5571
5617
|
}
|
|
5572
5618
|
// virtual face buttons
|
|
5573
5619
|
let button = buttonCenter.subtract(touchPos).direction();
|
|
@@ -5584,8 +5630,7 @@ function inputInit()
|
|
|
5584
5630
|
if (button < touchGamepadButtonCount)
|
|
5585
5631
|
touchGamepadButtons[button] = 1;
|
|
5586
5632
|
}
|
|
5587
|
-
else if (
|
|
5588
|
-
startCenter.distance(touchPos) < touchGamepadSize)
|
|
5633
|
+
else if (startCenter.distance(touchPos) < touchGamepadCenterButtonSize)
|
|
5589
5634
|
{
|
|
5590
5635
|
// virtual start button in center
|
|
5591
5636
|
touchGamepadButtons[9] = 1;
|
|
@@ -5634,6 +5679,18 @@ function inputUpdate()
|
|
|
5634
5679
|
// update touch gamepad if enabled
|
|
5635
5680
|
if (touchGamepadEnable && isTouchDevice)
|
|
5636
5681
|
{
|
|
5682
|
+
if (debugGamepads)
|
|
5683
|
+
{
|
|
5684
|
+
const stickCenter = vec2(touchGamepadSize, mainCanvasSize.y-touchGamepadSize);
|
|
5685
|
+
const buttonCenter = touchGamepadButtonCenter();
|
|
5686
|
+
const startCenter = mainCanvasSize.scale(.5);
|
|
5687
|
+
|
|
5688
|
+
debugCircle(stickCenter, 2*touchGamepadSize, 'cyan', 0, false, true);
|
|
5689
|
+
debugCircle(buttonCenter, 2*touchGamepadSize, 'cyan', 0, false, true);
|
|
5690
|
+
if (touchGamepadCenterButtonSize)
|
|
5691
|
+
debugCircle(startCenter, 2*touchGamepadCenterButtonSize, 'cyan', 0, false, true);
|
|
5692
|
+
}
|
|
5693
|
+
|
|
5637
5694
|
if (!touchGamepadTimer.isSet()) return;
|
|
5638
5695
|
|
|
5639
5696
|
// read virtual analog stick
|
|
@@ -5661,7 +5718,7 @@ function inputUpdate()
|
|
|
5661
5718
|
|
|
5662
5719
|
// read virtual gamepad buttons
|
|
5663
5720
|
const data = inputData[1] ?? (inputData[1] = []);
|
|
5664
|
-
for (let i=
|
|
5721
|
+
for (let i=12; i--;)
|
|
5665
5722
|
{
|
|
5666
5723
|
const wasDown = gamepadIsDown(i,0);
|
|
5667
5724
|
data[i] = touchGamepadButtons[i] ? wasDown ? 1 : 3 : wasDown ? 4 : 0;
|
|
@@ -5672,7 +5729,13 @@ function inputUpdate()
|
|
|
5672
5729
|
}
|
|
5673
5730
|
|
|
5674
5731
|
// return if gamepads are disabled or not supported
|
|
5675
|
-
|
|
5732
|
+
try {
|
|
5733
|
+
// protect against getGamepads disallowed security error
|
|
5734
|
+
if (!gamepadsEnable || !navigator?.getGamepads)
|
|
5735
|
+
return;
|
|
5736
|
+
} catch(e) {
|
|
5737
|
+
return;
|
|
5738
|
+
}
|
|
5676
5739
|
|
|
5677
5740
|
// only poll gamepads when focused or in debug mode
|
|
5678
5741
|
if (!debug && !document.hasFocus()) return;
|
|
@@ -13060,6 +13123,7 @@ export
|
|
|
13060
13123
|
canvasFixedSize,
|
|
13061
13124
|
canvasPixelated,
|
|
13062
13125
|
tilesPixelated,
|
|
13126
|
+
canvasPixelRatio,
|
|
13063
13127
|
fontDefault,
|
|
13064
13128
|
showSplashScreen,
|
|
13065
13129
|
headlessMode,
|
|
@@ -13080,7 +13144,7 @@ export
|
|
|
13080
13144
|
gamepadDirectionEmulateStick,
|
|
13081
13145
|
inputWASDEmulateDirection,
|
|
13082
13146
|
touchGamepadEnable,
|
|
13083
|
-
|
|
13147
|
+
touchGamepadCenterButtonSize,
|
|
13084
13148
|
touchGamepadAnalog,
|
|
13085
13149
|
touchGamepadSize,
|
|
13086
13150
|
touchGamepadAlpha,
|
|
@@ -13106,6 +13170,7 @@ export
|
|
|
13106
13170
|
setCanvasFixedSize,
|
|
13107
13171
|
setCanvasPixelated,
|
|
13108
13172
|
setTilesPixelated,
|
|
13173
|
+
setCanvasPixelRatio,
|
|
13109
13174
|
setFontDefault,
|
|
13110
13175
|
setShowSplashScreen,
|
|
13111
13176
|
setHeadlessMode,
|
|
@@ -13127,7 +13192,7 @@ export
|
|
|
13127
13192
|
setGamepadDirectionEmulateStick,
|
|
13128
13193
|
setInputWASDEmulateDirection,
|
|
13129
13194
|
setTouchGamepadEnable,
|
|
13130
|
-
|
|
13195
|
+
setTouchGamepadCenterButtonSize,
|
|
13131
13196
|
setTouchGamepadButtonCount,
|
|
13132
13197
|
setTouchGamepadAnalog,
|
|
13133
13198
|
setTouchGamepadSize,
|
|
@@ -13167,10 +13232,12 @@ export
|
|
|
13167
13232
|
distanceAngle,
|
|
13168
13233
|
lerpAngle,
|
|
13169
13234
|
lerp,
|
|
13235
|
+
percentLerp,
|
|
13170
13236
|
smoothStep,
|
|
13171
13237
|
nearestPowerOfTwo,
|
|
13172
13238
|
isOverlapping,
|
|
13173
13239
|
isIntersecting,
|
|
13240
|
+
lineTest,
|
|
13174
13241
|
oscillate,
|
|
13175
13242
|
|
|
13176
13243
|
// Utilities
|