littlejsengine 1.16.2 → 1.17.5
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/dist/littlejs.d.ts +291 -246
- package/dist/littlejs.esm.js +1520 -1271
- package/dist/littlejs.esm.min.js +1 -1
- package/dist/littlejs.js +1178 -920
- package/dist/littlejs.min.js +1 -1
- package/dist/littlejs.release.js +1170 -912
- package/examples/box2d/gameObjects.js +4 -4
- package/examples/breakout/game.js +5 -6
- package/examples/electron/index.html +2 -2
- package/examples/electron/tiles.png +0 -0
- package/examples/htmlMenu/tiles.png +0 -0
- package/examples/index.html +9 -8
- package/examples/logo.png +0 -0
- package/examples/module/build.bat +7 -0
- package/examples/module/build.js +121 -0
- package/examples/module/tiles.png +0 -0
- package/examples/platformer/game.js +1 -1
- package/examples/platformer/gameEffects.js +25 -29
- package/examples/platformer/gameLevel.js +27 -28
- package/examples/puzzle/game.js +2 -2
- package/examples/shorts/base.html +4 -1
- package/examples/shorts/box2dPool.js +2 -2
- package/examples/shorts/box2dTileLayer.js +48 -0
- package/examples/shorts/clock.js +3 -3
- package/examples/shorts/fontImage.js +4 -3
- package/examples/shorts/music.js +2 -2
- package/examples/shorts/musicPlayer.js +2 -2
- package/examples/shorts/parallax.js +1 -1
- package/examples/shorts/sequencer.js +1 -1
- package/examples/shorts/shapes.js +1 -1
- package/examples/shorts/texture.js +10 -6
- package/examples/shorts/tiles.png +0 -0
- package/examples/shorts/tiltedView.js +2 -0
- package/examples/starter/index.html +2 -2
- package/examples/starter/tiles.png +0 -0
- package/examples/typescript/tiles.png +0 -0
- package/examples/uiSystem/game.js +1 -1
- package/examples/uiSystem/tiles.png +0 -0
- package/package.json +5 -5
- package/plugins/box2d.js +167 -63
- package/plugins/postProcess.js +47 -28
- package/plugins/uiSystem.js +18 -18
- package/plugins/zzfxm.js +2 -2
- package/reference.md +4 -3
- package/src/engine.js +182 -181
- package/src/engineAudio.js +47 -63
- package/src/engineDebug.js +8 -8
- package/src/engineDraw.js +184 -124
- package/src/engineExport.js +325 -334
- package/src/engineFont.png +0 -0
- package/src/engineInput.js +18 -24
- package/src/engineMath.js +24 -113
- package/src/engineObject.js +27 -26
- package/src/engineParticles.js +2 -2
- package/src/engineTileLayer.js +229 -192
- package/src/engineUtilities.js +100 -4
- package/src/engineWebGL.js +124 -73
|
@@ -76,7 +76,7 @@ export function spawnRandomEdges()
|
|
|
76
76
|
edgePoints.push(vec2(40,0));
|
|
77
77
|
for (let i=40, y=0; i--;)
|
|
78
78
|
edgePoints.push(vec2(i, y=LJS.clamp(y+LJS.rand(-2,2),0,5)));
|
|
79
|
-
edgePoints.push(vec2(
|
|
79
|
+
edgePoints.push(vec2());
|
|
80
80
|
const o = new LJS.Box2dStaticObject;
|
|
81
81
|
o.addEdgeList(edgePoints);
|
|
82
82
|
}
|
|
@@ -126,7 +126,7 @@ export class CarObject extends LJS.Box2dObject
|
|
|
126
126
|
const maxTorque = 35;
|
|
127
127
|
const sprite = Game.spriteAtlas.wheel;
|
|
128
128
|
this.wheels = [];
|
|
129
|
-
const makeWheel = (pos, isMotor)
|
|
129
|
+
const makeWheel = (pos, isMotor)=>
|
|
130
130
|
{
|
|
131
131
|
const wheel = new LJS.Box2dObject(pos, vec2(diameter), sprite);
|
|
132
132
|
const joint = new LJS.Box2dWheelJoint(this, wheel);
|
|
@@ -284,7 +284,7 @@ export class SoftBodyObject extends LJS.Box2dObject
|
|
|
284
284
|
for (let x=sizeCount.x; x--;)
|
|
285
285
|
{
|
|
286
286
|
const o = this.getNode(x, y);
|
|
287
|
-
const tryAddJoint = (xo, yo)
|
|
287
|
+
const tryAddJoint = (xo, yo)=>
|
|
288
288
|
{
|
|
289
289
|
const o2 = this.getNode(x+xo, y+yo);
|
|
290
290
|
const joint = o2 ? new LJS.Box2dWeldJoint(o, o2) : 0;
|
|
@@ -360,7 +360,7 @@ export class ClothObject extends LJS.Box2dStaticObject
|
|
|
360
360
|
const d = y%2 ? 1 : -1;
|
|
361
361
|
const x2 = d>1 ? x : sizeCount.x-1-x;
|
|
362
362
|
const o = this.getNode(x2, y);
|
|
363
|
-
const tryAddJoint = (xo, yo)
|
|
363
|
+
const tryAddJoint = (xo, yo)=>
|
|
364
364
|
{
|
|
365
365
|
const o2 = this.getNode(x2+xo, y+yo);
|
|
366
366
|
const joint = o2 ? new LJS.Box2dRopeJoint(o, o2) : 0;
|
|
@@ -103,13 +103,12 @@ function gameRender()
|
|
|
103
103
|
function gameRenderPost()
|
|
104
104
|
{
|
|
105
105
|
// use built in image font for text
|
|
106
|
-
const font =
|
|
107
|
-
|
|
108
|
-
font.drawText('Score: ' + score, LJS.cameraPos.add(vec2(0,9.7)), .15, true);
|
|
106
|
+
const font = LJS.engineFontImage;
|
|
107
|
+
font.drawText('Score: ' + score, LJS.cameraPos.add(vec2(0,9.2)), 1);
|
|
109
108
|
if (!brickCount)
|
|
110
|
-
font.drawText('You Win!', LJS.cameraPos.add(vec2(0,-5)),
|
|
109
|
+
font.drawText('You Win!', LJS.cameraPos.add(vec2(0,-5)), 2);
|
|
111
110
|
else if (!ball)
|
|
112
|
-
font.drawText('Click to Play', LJS.cameraPos.add(vec2(0,-5)),
|
|
111
|
+
font.drawText('Click to Play', LJS.cameraPos.add(vec2(0,-5)), 2);
|
|
113
112
|
}
|
|
114
113
|
|
|
115
114
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -138,7 +137,7 @@ function setupPostProcess()
|
|
|
138
137
|
|
|
139
138
|
// scan lines
|
|
140
139
|
const float scanlineScale = 2.;
|
|
141
|
-
const float scanlineAlpha = .
|
|
140
|
+
const float scanlineAlpha = .6;
|
|
142
141
|
c *= 1. - scanlineAlpha*cos(p.y*2.*iResolution.y/scanlineScale);
|
|
143
142
|
|
|
144
143
|
{
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
</head><body>
|
|
8
8
|
|
|
9
9
|
<!-- LittleJS Engine -->
|
|
10
|
-
<script src=../../dist/littlejs.js?1.
|
|
10
|
+
<script src=../../dist/littlejs.js?1.17.5></script>
|
|
11
11
|
|
|
12
12
|
<!-- Add your game scripts here -->
|
|
13
|
-
<script src=game.js?1.
|
|
13
|
+
<script src=game.js?1.17.5></script>
|
|
Binary file
|
|
Binary file
|
package/examples/index.html
CHANGED
|
@@ -113,7 +113,6 @@ const exampleList =
|
|
|
113
113
|
new ExampleInfo('Piano', 'piano.js', 'Interactive piano keyboard', false, 'music, sound, audio, notes, ui, instrument'),
|
|
114
114
|
new ExampleInfo('Step Sequencer', 'sequencer.js', 'Simple music loop creation tool', false, 'music, sound, audio, notes, ui, instrument'),
|
|
115
115
|
new ExampleInfo('Music Player', 'musicPlayer.js', 'Music player with audio seeking and drag and drop', false, 'sound, loading, audio, ui'),
|
|
116
|
-
new ExampleInfo('WebGL Shader', 'shader.js', 'Full canvas webgl shader', false, 'webgl, visual, effect'),
|
|
117
116
|
new ExampleInfo('--- MINI GAMES ---'),
|
|
118
117
|
new ExampleInfo('Pong Game', 'pongGame.js', 'Classic paddle ball bouncing', false, 'objects, collision'),
|
|
119
118
|
new ExampleInfo('Platformer Game', 'platformer.js', 'Jump and run side view', false, 'objects, gravity, level, tiles, camera'),
|
|
@@ -128,7 +127,9 @@ const exampleList =
|
|
|
128
127
|
new ExampleInfo('--- PLUGINS ---'),
|
|
129
128
|
new ExampleInfo('Box2d Demo', 'box2d.js', 'Box2D physics plugin', false, 'objects, mouse'),
|
|
130
129
|
new ExampleInfo('Box2d Car', 'box2dCar.js', 'Drivable car with Box2D physics', false, 'objects, vehicle, suspension, wheels'),
|
|
131
|
-
new ExampleInfo('Box2d Pool', 'box2dPool.js', 'Billiard table pool game with physics', false, 'objects, game'),
|
|
130
|
+
new ExampleInfo('Box2d Pool', 'box2dPool.js', 'Billiard table pool game with Box2d physics', false, 'objects, game'),
|
|
131
|
+
new ExampleInfo('Box2d Tile Layer', 'box2dTileLayer.js', 'Tile layer with Box2d physics', false, 'objects, level, map, grid'),
|
|
132
|
+
new ExampleInfo('WebGL Shader', 'shader.js', 'Full canvas webgl shader', false, 'webgl, visual, effect'),
|
|
132
133
|
new ExampleInfo('Post Processing', 'postProcess.js', 'Shader effects and filters', false, 'webgl, visual, effect'),
|
|
133
134
|
new ExampleInfo('UI System', 'uiSystem.js', 'Buttons, sliders, and checkboxes', false, 'objects, widgets, interactive'),
|
|
134
135
|
new ExampleInfo('Nine Slice', 'nineSlice.js', 'Scalable UI panels', false, 'three slice, stretch, corners, text, tiles'),
|
|
@@ -159,7 +160,7 @@ function initExampleBrowser()
|
|
|
159
160
|
// set the selected example from the URL parameters
|
|
160
161
|
const urlParams = new URLSearchParams(window.location.search);
|
|
161
162
|
const selectedExample = urlParams.get('example') || '';
|
|
162
|
-
let exampleIndex = exampleList.findIndex((example)
|
|
163
|
+
let exampleIndex = exampleList.findIndex((example)=> example.name === selectedExample);
|
|
163
164
|
if (exampleIndex < 0)
|
|
164
165
|
exampleIndex = 1;
|
|
165
166
|
selectExample.selectedIndex = exampleIndex;
|
|
@@ -398,7 +399,7 @@ function codeInput()
|
|
|
398
399
|
// debounce input - get content from code mirror if available, otherwise from textarea
|
|
399
400
|
clearTimeout(inputTimeout);
|
|
400
401
|
const code = codeMirror ? codeMirror.getValue() : textareaCode.value;
|
|
401
|
-
inputTimeout = setTimeout(()
|
|
402
|
+
inputTimeout = setTimeout(()=> setCode(code), 500);
|
|
402
403
|
}
|
|
403
404
|
|
|
404
405
|
function restartCode()
|
|
@@ -453,7 +454,7 @@ function setCode(code, filename)
|
|
|
453
454
|
const anonymousMatch = stack?.match(/(<anonymous>|injectedScript):(\d+)/);
|
|
454
455
|
return anonymousMatch ? parseInt(anonymousMatch[2]) : -1;
|
|
455
456
|
}
|
|
456
|
-
iframeContent.onerror = (message, source, lineno, colno, error)
|
|
457
|
+
iframeContent.onerror = (message, source, lineno, colno, error)=>
|
|
457
458
|
{
|
|
458
459
|
let text = message;
|
|
459
460
|
if (lineno)
|
|
@@ -463,7 +464,7 @@ function setCode(code, filename)
|
|
|
463
464
|
setErrorMessage(text);
|
|
464
465
|
setErrorLine(lineno);
|
|
465
466
|
}
|
|
466
|
-
iframeContent.onunhandledrejection = (event)
|
|
467
|
+
iframeContent.onunhandledrejection = (event)=>
|
|
467
468
|
{
|
|
468
469
|
let text = event.reason;
|
|
469
470
|
setErrorMessage(text);
|
|
@@ -510,14 +511,14 @@ function setCode(code, filename)
|
|
|
510
511
|
|
|
511
512
|
{
|
|
512
513
|
// hook up buttons
|
|
513
|
-
buttonScreenshot.onclick = ()
|
|
514
|
+
buttonScreenshot.onclick = ()=>
|
|
514
515
|
{
|
|
515
516
|
if (iframeContent.debugScreenshot)
|
|
516
517
|
iframeContent.debugScreenshot();
|
|
517
518
|
}
|
|
518
519
|
|
|
519
520
|
// pause/resume functionality
|
|
520
|
-
buttonPause.onclick = ()
|
|
521
|
+
buttonPause.onclick = ()=>
|
|
521
522
|
{
|
|
522
523
|
if (!iframeContent.getPaused || !iframeContent.setPaused)
|
|
523
524
|
return;
|
package/examples/logo.png
CHANGED
|
Binary file
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* LittleJS Build System
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const PROGRAM_TITLE = 'Little JS Starter Project';
|
|
10
|
+
const PROGRAM_NAME = 'game';
|
|
11
|
+
const BUILD_FOLDER = 'build';
|
|
12
|
+
const sourceFiles =
|
|
13
|
+
[
|
|
14
|
+
'game.js',
|
|
15
|
+
// add your game's source files here
|
|
16
|
+
];
|
|
17
|
+
const engineFile = '../../dist/littlejs.esm.min.js'; // Use the minified ES module
|
|
18
|
+
const dataFiles =
|
|
19
|
+
[
|
|
20
|
+
'tiles.png',
|
|
21
|
+
// add your game's data files here
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
console.log(`Building ${PROGRAM_NAME}...`);
|
|
25
|
+
const startTime = Date.now();
|
|
26
|
+
const fs = require('node:fs');
|
|
27
|
+
const child_process = require('node:child_process');
|
|
28
|
+
|
|
29
|
+
// rebuild engine
|
|
30
|
+
//child_process.execSync(`npm run build`, { stdio: 'inherit' });
|
|
31
|
+
|
|
32
|
+
// remove old files and setup build folder
|
|
33
|
+
fs.rmSync(BUILD_FOLDER, { recursive: true, force: true });
|
|
34
|
+
fs.rmSync(`${PROGRAM_NAME}.zip`, { force: true });
|
|
35
|
+
fs.mkdirSync(BUILD_FOLDER);
|
|
36
|
+
|
|
37
|
+
// copy data files
|
|
38
|
+
for (const file of dataFiles)
|
|
39
|
+
fs.copyFileSync(file, `${BUILD_FOLDER}/${file}`);
|
|
40
|
+
|
|
41
|
+
// copy engine module
|
|
42
|
+
fs.copyFileSync(engineFile, `${BUILD_FOLDER}/littlejs.esm.min.js`);
|
|
43
|
+
|
|
44
|
+
Build
|
|
45
|
+
(
|
|
46
|
+
sourceFiles,
|
|
47
|
+
[htmlBuildStep, zipBuildStep]
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
console.log('');
|
|
51
|
+
console.log(`Build Completed in ${((Date.now() - startTime)/1e3).toFixed(2)} seconds!`);
|
|
52
|
+
|
|
53
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
54
|
+
|
|
55
|
+
// A single build with its own source files, build steps, and output file
|
|
56
|
+
// - each build step is a callback that accepts a single filename
|
|
57
|
+
function Build(files=[], buildSteps=[])
|
|
58
|
+
{
|
|
59
|
+
// process each source file separately (don't concatenate modules!)
|
|
60
|
+
for (const file of files)
|
|
61
|
+
{
|
|
62
|
+
const outputFile = `${BUILD_FOLDER}/${file}`;
|
|
63
|
+
fs.copyFileSync(file, outputFile);
|
|
64
|
+
moduleFixStep(outputFile);
|
|
65
|
+
uglifyBuildStep(outputFile);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// execute build steps in order
|
|
69
|
+
for (const buildStep of buildSteps)
|
|
70
|
+
buildStep();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function moduleFixStep(filename)
|
|
74
|
+
{
|
|
75
|
+
console.log(`Fixing module imports in ${filename}...`);
|
|
76
|
+
|
|
77
|
+
let code = fs.readFileSync(filename, 'utf8');
|
|
78
|
+
|
|
79
|
+
// update the import path to use the local minified version
|
|
80
|
+
code = code.replace(/import \* as LJS from ['"].*littlejs\.esm(?:\.min)?\.js['"];?/g,
|
|
81
|
+
"import * as LJS from './littlejs.esm.min.js';");
|
|
82
|
+
|
|
83
|
+
// also fix relative imports to other game modules
|
|
84
|
+
code = code.replace(/from ['"]\.\.\//g, "from './");
|
|
85
|
+
|
|
86
|
+
fs.writeFileSync(filename, code);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function uglifyBuildStep(filename)
|
|
90
|
+
{
|
|
91
|
+
console.log('Running uglify...');
|
|
92
|
+
child_process.execSync(`npx uglifyjs ${filename} -c -m -o ${filename}`, {stdio: 'inherit'});
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
function htmlBuildStep()
|
|
96
|
+
{
|
|
97
|
+
console.log('Building html...');
|
|
98
|
+
|
|
99
|
+
// create html file with module script tag pointing to main game file
|
|
100
|
+
let buffer = ''
|
|
101
|
+
buffer += '<!DOCTYPE html>';
|
|
102
|
+
buffer += '<head>';
|
|
103
|
+
buffer += `<title>${PROGRAM_TITLE}</title>`;
|
|
104
|
+
buffer += '<meta charset=utf-8>';
|
|
105
|
+
buffer += '</head>';
|
|
106
|
+
buffer += '<body>';
|
|
107
|
+
buffer += '<script src="game.js" type="module"></script>';
|
|
108
|
+
buffer += '</body>';
|
|
109
|
+
|
|
110
|
+
// output html file
|
|
111
|
+
fs.writeFileSync(`${BUILD_FOLDER}/index.html`, buffer, {flag: 'w+'});
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
function zipBuildStep()
|
|
115
|
+
{
|
|
116
|
+
console.log('Zipping...');
|
|
117
|
+
const sources = ['index.html', 'littlejs.esm.min.js', ...sourceFiles, ...dataFiles];
|
|
118
|
+
const sourceList = sources.join(' ');
|
|
119
|
+
child_process.execSync(`npx bestzip ../${PROGRAM_NAME}.zip ${sourceList}`, {cwd:BUILD_FOLDER, stdio: 'inherit'});
|
|
120
|
+
console.log(`Size of ${PROGRAM_NAME}.zip: ${fs.statSync(`${PROGRAM_NAME}.zip`).size} bytes`);
|
|
121
|
+
};
|
|
Binary file
|
|
@@ -127,7 +127,7 @@ function gameRender()
|
|
|
127
127
|
function gameRenderPost()
|
|
128
128
|
{
|
|
129
129
|
// draw to main canvas for hud rendering
|
|
130
|
-
const drawText = (text, x, y, size=40)
|
|
130
|
+
const drawText = (text, x, y, size=40)=>
|
|
131
131
|
{
|
|
132
132
|
const context = LJS.mainContext;
|
|
133
133
|
context.textAlign = 'center';
|
|
@@ -15,9 +15,6 @@ import * as LJS from '../../dist/littlejs.esm.js';
|
|
|
15
15
|
import * as GameLevel from './gameLevel.js';
|
|
16
16
|
const {vec2, hsl, rgb} = LJS;
|
|
17
17
|
|
|
18
|
-
// use low graphics mode on mobile devices
|
|
19
|
-
const lowGraphicsMode = LJS.isTouchDevice;
|
|
20
|
-
|
|
21
18
|
///////////////////////////////////////////////////////////////////////////////
|
|
22
19
|
// sound effects
|
|
23
20
|
|
|
@@ -36,15 +33,11 @@ export const sound_score = new LJS.Sound([,,783,,.03,.02,1,2,,,940,.03,,,
|
|
|
36
33
|
|
|
37
34
|
export const persistentParticleDestroyCallback = (particle)=>
|
|
38
35
|
{
|
|
39
|
-
if (lowGraphicsMode) return;
|
|
40
|
-
|
|
41
36
|
// draw particle to tile layer on death
|
|
42
37
|
LJS.ASSERT(!particle.tileInfo, 'quick draw to tile layer uses canvas 2d so must be untextured');
|
|
43
38
|
if (particle.groundObject)
|
|
44
39
|
{
|
|
45
40
|
GameLevel.foregroundTileLayer.drawTile(particle.pos, particle.size, particle.tileInfo, particle.color, particle.angle, particle.mirror);
|
|
46
|
-
// update WebGL texture
|
|
47
|
-
GameLevel.foregroundTileLayer.useWebGL();
|
|
48
41
|
}
|
|
49
42
|
}
|
|
50
43
|
|
|
@@ -74,26 +67,27 @@ export function explosion(pos, radius=3)
|
|
|
74
67
|
|
|
75
68
|
sound_explosion.play(pos);
|
|
76
69
|
|
|
77
|
-
// destroy level
|
|
78
|
-
for (let x = -radius; x < radius; ++x)
|
|
79
|
-
{
|
|
80
|
-
const h = (radius*radius - x*x)**.5;
|
|
81
|
-
for (let y = -h; y <= h; ++y)
|
|
82
|
-
destroyTile(pos.add(vec2(x,y)), 0, 0);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// cleanup neighbors
|
|
86
|
-
const cleanupRadius = radius + 2;
|
|
87
|
-
for (let x = -cleanupRadius; x < cleanupRadius; ++x)
|
|
88
70
|
{
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
71
|
+
// destroy level
|
|
72
|
+
const layer = GameLevel.tileLayers[1];
|
|
73
|
+
layer.redrawStart();
|
|
74
|
+
for (let x = -radius; x < radius; ++x)
|
|
75
|
+
{
|
|
76
|
+
const h = (radius*radius - x*x)**.5;
|
|
77
|
+
for (let y = -h; y <= h; ++y)
|
|
78
|
+
destroyTile(pos.add(vec2(x,y)), 0, 0);
|
|
79
|
+
}
|
|
80
|
+
// cleanup neighbors
|
|
81
|
+
const cleanupRadius = radius + 2;
|
|
82
|
+
for (let x = -cleanupRadius; x < cleanupRadius; ++x)
|
|
83
|
+
{
|
|
84
|
+
const h = (cleanupRadius**2 - x**2)**.5;
|
|
85
|
+
for (let y = -h; y < h; ++y)
|
|
86
|
+
GameLevel.decorateTile(pos.add(vec2(x,y)).floor(), layer);
|
|
87
|
+
}
|
|
88
|
+
layer.redrawEnd();
|
|
92
89
|
}
|
|
93
90
|
|
|
94
|
-
// update WebGL texture
|
|
95
|
-
GameLevel.foregroundTileLayer.useWebGL();
|
|
96
|
-
|
|
97
91
|
// kill/push objects
|
|
98
92
|
LJS.engineObjectsCallback(pos, radius*3, (o)=>
|
|
99
93
|
{
|
|
@@ -159,16 +153,18 @@ export function destroyTile(pos, makeSound = 1, cleanup = 1)
|
|
|
159
153
|
makeSound && sound_destroyObject.play(centerPos);
|
|
160
154
|
|
|
161
155
|
// set and clear tile
|
|
162
|
-
layer.
|
|
156
|
+
layer.clearData(pos, true);
|
|
163
157
|
layer.setCollisionData(pos, GameLevel.tileType_empty);
|
|
164
158
|
|
|
165
159
|
// cleanup neighbors and rebuild WebGL
|
|
166
160
|
if (cleanup)
|
|
167
161
|
{
|
|
162
|
+
const layer = GameLevel.tileLayers[1];
|
|
163
|
+
layer.redrawStart();
|
|
168
164
|
for (let i=-1;i<=1;++i)
|
|
169
165
|
for (let j=-1;j<=1;++j)
|
|
170
|
-
GameLevel.decorateTile(pos.add(vec2(i,j)));
|
|
171
|
-
layer.
|
|
166
|
+
GameLevel.decorateTile(pos.add(vec2(i,j)), layer);
|
|
167
|
+
layer.redrawEnd();
|
|
172
168
|
}
|
|
173
169
|
|
|
174
170
|
return true;
|
|
@@ -198,7 +194,7 @@ export class Sky extends LJS.EngineObject
|
|
|
198
194
|
// draw stars
|
|
199
195
|
LJS.setBlendMode(true);
|
|
200
196
|
const random = new LJS.RandomGenerator(this.seed);
|
|
201
|
-
for (let i=
|
|
197
|
+
for (let i = 1e3; i--;)
|
|
202
198
|
{
|
|
203
199
|
const size = random.float(.5,2)**2;
|
|
204
200
|
const speed = random.float() < .9 ? random.float(5) : random.float(9,99);
|
|
@@ -260,7 +256,7 @@ export class ParallaxLayer extends LJS.CanvasLayer
|
|
|
260
256
|
this.context.clearRect(0,0,1,h);
|
|
261
257
|
|
|
262
258
|
// make WebGL texture
|
|
263
|
-
this.
|
|
259
|
+
this.updateWebGL();
|
|
264
260
|
}
|
|
265
261
|
|
|
266
262
|
render()
|
|
@@ -96,8 +96,8 @@ function loadLevelData()
|
|
|
96
96
|
new GameObjects.Coin(objectPos);
|
|
97
97
|
|
|
98
98
|
// replace with empty tile and empty collision
|
|
99
|
-
tileLayer.
|
|
100
|
-
tileLayer.
|
|
99
|
+
tileLayer.clearData(pos);
|
|
100
|
+
tileLayer.clearCollisionData(pos);
|
|
101
101
|
continue;
|
|
102
102
|
}
|
|
103
103
|
|
|
@@ -126,35 +126,32 @@ function loadLevelData()
|
|
|
126
126
|
}
|
|
127
127
|
}
|
|
128
128
|
}
|
|
129
|
+
|
|
130
|
+
tileLayer.onRedraw = ()=>
|
|
131
|
+
{
|
|
132
|
+
// apply decoration to level tiles
|
|
133
|
+
for (let x=levelSize.x; x--;)
|
|
134
|
+
for (let y=levelSize.y; y--;)
|
|
135
|
+
decorateTile(vec2(x,y), tileLayer);
|
|
136
|
+
}
|
|
129
137
|
tileLayer.redraw();
|
|
130
138
|
}
|
|
131
|
-
|
|
132
|
-
// apply decoration to all level tiles
|
|
133
|
-
const pos = vec2();
|
|
134
|
-
const layerCount = tileMapData.layers.length;
|
|
135
|
-
for (let layer=layerCount; layer--;)
|
|
136
|
-
{
|
|
137
|
-
for (pos.x=levelSize.x; pos.x--;)
|
|
138
|
-
for (pos.y=levelSize.y; pos.y--;)
|
|
139
|
-
decorateTile(pos, layer);
|
|
140
|
-
tileLayers[layer].useWebGL();
|
|
141
|
-
}
|
|
142
139
|
}
|
|
143
140
|
|
|
144
|
-
export function decorateTile(pos,
|
|
141
|
+
export function decorateTile(pos, tileLayer)
|
|
145
142
|
{
|
|
146
143
|
LJS.ASSERT((pos.x|0) == pos.x && (pos.y|0)== pos.y);
|
|
147
|
-
const tileLayer = tileLayers[layer];
|
|
148
144
|
if (!tileLayer)
|
|
149
145
|
return;
|
|
150
146
|
|
|
147
|
+
const w = tileLayer.tileInfo.size.x;
|
|
151
148
|
if (tileLayer == foregroundTileLayer)
|
|
152
149
|
{
|
|
153
150
|
const tileType = tileLayer.getCollisionData(pos);
|
|
154
151
|
if (tileType <= 0)
|
|
155
152
|
{
|
|
156
153
|
// force it to clear if it is empty
|
|
157
|
-
tileType || tileLayer.
|
|
154
|
+
tileType || tileLayer.clearData(pos, true);
|
|
158
155
|
return;
|
|
159
156
|
}
|
|
160
157
|
if (tileType == tileType_breakable)
|
|
@@ -167,12 +164,11 @@ export function decorateTile(pos, layer=1)
|
|
|
167
164
|
|
|
168
165
|
// make pixel perfect outlines
|
|
169
166
|
const size = i&1 ? vec2(2, 16) : vec2(16, 2);
|
|
170
|
-
tileLayer.context.fillStyle = levelOutlineColor.mutate(.1);
|
|
171
167
|
const drawPos = pos.scale(16)
|
|
172
168
|
.add(vec2(i==1?14:0,(i==0?14:0)))
|
|
173
169
|
.subtract((i&1? vec2(0,8-size.y/2) : vec2(8-size.x/2,0)));
|
|
174
|
-
|
|
175
|
-
|
|
170
|
+
const color = levelOutlineColor.mutate(.1);
|
|
171
|
+
tileLayer.drawLayerRect(drawPos, size, color);
|
|
176
172
|
}
|
|
177
173
|
}
|
|
178
174
|
else
|
|
@@ -185,15 +181,18 @@ export function decorateTile(pos, layer=1)
|
|
|
185
181
|
const neighborTileDataB = tileLayer.getData(pos.add(vec2().setDirection((i+1)%4))).tile;
|
|
186
182
|
if (neighborTileDataA > 0 || neighborTileDataB > 0)
|
|
187
183
|
continue;
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
184
|
+
|
|
185
|
+
// get direction of corner
|
|
186
|
+
const directionVector = vec2();
|
|
187
|
+
if (i==0) directionVector.set(w-1,w-1);
|
|
188
|
+
if (i==1) directionVector.set(w-1,1);
|
|
189
|
+
if (i==2) directionVector.set(1,1);
|
|
190
|
+
if (i==3) directionVector.set(1,w-1);
|
|
191
|
+
|
|
192
|
+
// clear rect from corner
|
|
193
|
+
const s = vec2(2);
|
|
194
|
+
const drawPos = pos.scale(w).add(directionVector).subtract(s.scale(.5));
|
|
195
|
+
tileLayer.clearLayerRect(drawPos, s);
|
|
197
196
|
}
|
|
198
197
|
}
|
|
199
198
|
}
|
package/examples/puzzle/game.js
CHANGED
|
@@ -44,8 +44,8 @@ const tileColors =
|
|
|
44
44
|
];
|
|
45
45
|
const tileTypeCount = tileColors.length;
|
|
46
46
|
|
|
47
|
-
const getTile = (pos)
|
|
48
|
-
const setTile = (pos, data)
|
|
47
|
+
const getTile = (pos)=> level[pos.x + pos.y * levelSize.x];
|
|
48
|
+
const setTile = (pos, data)=> level[pos.x + pos.y * levelSize.x] = data;
|
|
49
49
|
|
|
50
50
|
///////////////////////////////////////////////////////////////////////////////
|
|
51
51
|
function gameReset()
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<!DOCTYPE html><meta charset=utf-8><body>
|
|
2
|
-
<script src=../../dist/littlejs.js?1.
|
|
2
|
+
<script src=../../dist/littlejs.js?1.17.5></script>
|
|
3
3
|
<script src=../../dist/box2d.wasm.js></script>
|
|
4
4
|
|
|
5
5
|
<!-- LittleJS Engine Source
|
|
@@ -38,6 +38,9 @@ canvasPixelated = false;
|
|
|
38
38
|
// disable watermark
|
|
39
39
|
debugWatermark = false;
|
|
40
40
|
|
|
41
|
+
// disable showing the engine version in console
|
|
42
|
+
showEngineVersion = false;
|
|
43
|
+
|
|
41
44
|
// these functions can be overridden for each example
|
|
42
45
|
function gameInit() {}
|
|
43
46
|
function gameUpdate() {}
|
|
@@ -3,10 +3,10 @@ const maxHitDistance = 6;
|
|
|
3
3
|
|
|
4
4
|
class Ball extends Box2dObject
|
|
5
5
|
{
|
|
6
|
-
constructor(
|
|
6
|
+
constructor(pos, number=0)
|
|
7
7
|
{
|
|
8
8
|
const color = hsl(number/9, 1, number? .5 : 1);
|
|
9
|
-
super(
|
|
9
|
+
super(pos, vec2(), 0, 0, color);
|
|
10
10
|
this.number = number;
|
|
11
11
|
|
|
12
12
|
// setup pool ball physics
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
let box2DTileLayer;
|
|
2
|
+
|
|
3
|
+
async function gameInit()
|
|
4
|
+
{
|
|
5
|
+
// setup box2d
|
|
6
|
+
await box2dInit();
|
|
7
|
+
cameraPos = vec2(16); // setup camera
|
|
8
|
+
gravity.y = -30; // enable gravity
|
|
9
|
+
canvasClearColor = hsl(0,0,.2); // background color
|
|
10
|
+
|
|
11
|
+
// create tile layer
|
|
12
|
+
const pos = vec2();
|
|
13
|
+
const tileLayer = new TileCollisionLayer(pos, vec2(32));
|
|
14
|
+
for (pos.x = tileLayer.size.x; pos.x--;)
|
|
15
|
+
for (pos.y = tileLayer.size.y; pos.y--;)
|
|
16
|
+
{
|
|
17
|
+
// check if tile should be solid
|
|
18
|
+
if (randBool(.7))
|
|
19
|
+
continue;
|
|
20
|
+
|
|
21
|
+
// set tile data
|
|
22
|
+
const tileIndex = 11;
|
|
23
|
+
const direction = randInt(4)
|
|
24
|
+
const mirror = randBool();
|
|
25
|
+
const color = randColor(WHITE, hsl(0,0,.2));
|
|
26
|
+
const data = new TileLayerData(tileIndex, direction, mirror, color);
|
|
27
|
+
tileLayer.setData(pos, data);
|
|
28
|
+
tileLayer.setCollisionData(pos);
|
|
29
|
+
}
|
|
30
|
+
tileLayer.redraw(); // redraw tile layer with new data
|
|
31
|
+
box2DTileLayer = new Box2dTileLayer(tileLayer);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function gameUpdate()
|
|
35
|
+
{
|
|
36
|
+
if (mouseWasPressed(0))
|
|
37
|
+
{
|
|
38
|
+
// clear tile that was clicked
|
|
39
|
+
box2DTileLayer.tileLayer.clearData(mousePos, true);
|
|
40
|
+
box2DTileLayer.tileLayer.clearCollisionData(mousePos);
|
|
41
|
+
box2DTileLayer.buildCollision();
|
|
42
|
+
|
|
43
|
+
// spawn box2d object at mouse position
|
|
44
|
+
const o = new Box2dObject(mousePos, vec2(), 0, 0, randColor());
|
|
45
|
+
const friction = .2, restitution = .5;
|
|
46
|
+
o.addCircle(rand(.5,1), vec2(), 1, friction, restitution);
|
|
47
|
+
}
|
|
48
|
+
}
|
package/examples/shorts/clock.js
CHANGED
|
@@ -15,7 +15,7 @@ function gameRender()
|
|
|
15
15
|
const h = (d.slice(0,2)|0) + m/60;
|
|
16
16
|
|
|
17
17
|
// draw clock hands
|
|
18
|
-
drawLine(vec2(
|
|
19
|
-
drawLine(vec2(
|
|
20
|
-
drawLine(vec2(
|
|
18
|
+
drawLine(vec2(), vec2(0,4).rotate(h/12*2*PI), 1);
|
|
19
|
+
drawLine(vec2(), vec2(0,6).rotate(m/60*2*PI), .4);
|
|
20
|
+
drawLine(vec2(), vec2(0,8).rotate(s/60*2*PI), .1);
|
|
21
21
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
function gameRender()
|
|
2
2
|
{
|
|
3
3
|
// draw text with built in engine font image
|
|
4
|
-
const font =
|
|
5
|
-
font.drawText('Engine Font
|
|
4
|
+
const font = engineFontImage
|
|
5
|
+
font.drawText('Engine Font', vec2(0,3), 2);
|
|
6
6
|
|
|
7
7
|
// show every character in the font
|
|
8
8
|
let s = '';
|
|
@@ -12,5 +12,6 @@ function gameRender()
|
|
|
12
12
|
s += '\n';
|
|
13
13
|
s += String.fromCharCode(i);
|
|
14
14
|
}
|
|
15
|
-
|
|
15
|
+
|
|
16
|
+
font.drawText(s, vec2());
|
|
16
17
|
}
|
package/examples/shorts/music.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const musicSound = new
|
|
1
|
+
const musicSound = new Sound('song.mp3');
|
|
2
2
|
let musicVolume = 1, musicInstance;
|
|
3
3
|
|
|
4
4
|
function gameInit()
|
|
@@ -23,7 +23,7 @@ function gameInit()
|
|
|
23
23
|
const volumeSlider = new UIScrollbar(vec2(0, -20), vec2(400, 30),
|
|
24
24
|
musicVolume, 'Music Volume');
|
|
25
25
|
musicPlayer.addChild(volumeSlider);
|
|
26
|
-
volumeSlider.onChange = ()
|
|
26
|
+
volumeSlider.onChange = ()=>
|
|
27
27
|
{
|
|
28
28
|
musicVolume = volumeSlider.value;
|
|
29
29
|
musicInstance?.setVolume(musicVolume);
|
|
@@ -29,7 +29,7 @@ function gameInit()
|
|
|
29
29
|
const volumeSlider = new UIScrollbar(vec2(0, -20), vec2(400, 30),
|
|
30
30
|
musicVolume, 'Music Volume');
|
|
31
31
|
musicPlayer.addChild(volumeSlider);
|
|
32
|
-
volumeSlider.onChange = ()
|
|
32
|
+
volumeSlider.onChange = ()=>
|
|
33
33
|
{
|
|
34
34
|
musicVolume = volumeSlider.value;
|
|
35
35
|
musicInstance?.setVolume(musicVolume);
|
|
@@ -89,7 +89,7 @@ function gameInit()
|
|
|
89
89
|
|
|
90
90
|
// create new sound from dropped file
|
|
91
91
|
const fileURL = URL.createObjectURL(file);
|
|
92
|
-
musicSound = new
|
|
92
|
+
musicSound = new Sound(fileURL, musicVolume);
|
|
93
93
|
dropZoneText.text = file.name;
|
|
94
94
|
|
|
95
95
|
// reset UI
|