littlejsengine 1.11.2 → 1.11.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/.vscode/launch.json +14 -0
- package/README.md +16 -18
- package/dist/littlejs.d.ts +41 -30
- package/dist/littlejs.esm.js +165 -143
- package/dist/littlejs.esm.min.js +1 -1
- package/dist/littlejs.js +161 -142
- package/dist/littlejs.min.js +1 -1
- package/dist/littlejs.release.js +157 -135
- package/examples/box2d/gameObjects.js +4 -4
- package/examples/box2d/scenes.js +2 -2
- package/examples/breakout/game.js +1 -1
- package/examples/breakout/gameObjects.js +28 -29
- package/examples/breakout/index.html +1 -1
- package/examples/breakoutTutorial/game.js +1 -1
- package/examples/index.html +1 -7
- package/examples/logo.png +0 -0
- package/examples/module/game.js +1 -1
- package/examples/platformer/game.js +2 -2
- package/examples/platformer/gameCharacter.js +5 -5
- package/examples/platformer/gameEffects.js +4 -4
- package/examples/platformer/gameObjects.js +2 -2
- package/examples/puzzle/game.js +3 -3
- package/examples/starter/game.js +11 -45
- package/examples/starter/tiles.png +0 -0
- package/jsconfig.json +1 -0
- package/package.json +1 -1
- package/plugins/box2d.js +10 -10
- package/plugins/newgrounds.js +1 -1
- package/plugins/postProcess.js +8 -8
- package/shortExamples/base.html +36 -0
- package/shortExamples/code/blending.js +12 -0
- package/shortExamples/code/helloWorld.js +7 -0
- package/shortExamples/code/particles.js +28 -0
- package/shortExamples/code/playSound.js +36 -0
- package/shortExamples/code/pong.js +40 -0
- package/shortExamples/code/shapes.js +24 -0
- package/shortExamples/code/spriteAtlas.js +27 -0
- package/shortExamples/code/systemFont.js +14 -0
- package/shortExamples/code/texture.js +12 -0
- package/shortExamples/code/tileLayer.js +50 -0
- package/shortExamples/index.html +242 -0
- package/shortExamples/tiles.png +0 -0
- package/src/engine.js +17 -3
- package/src/engineAudio.js +4 -4
- package/src/engineBuild.js +2 -1
- package/src/engineDebug.js +4 -7
- package/src/engineDraw.js +50 -25
- package/src/engineExport.js +4 -1
- package/src/engineInput.js +1 -1
- package/src/engineObject.js +8 -8
- package/src/engineParticles.js +7 -7
- package/src/engineSettings.js +5 -5
- package/src/engineTileLayer.js +6 -2
- package/src/engineUtilities.js +5 -5
- package/src/engineWebGL.js +52 -74
- package/examples/electron/build.js +0 -107
- package/examples/electron/electron.js +0 -43
- package/examples/electron/game.js +0 -131
- package/examples/electron/index.html +0 -13
- package/examples/electron/package.json +0 -22
- package/examples/electron/tiles.png +0 -0
- package/examples/js13k/build.bat +0 -2
- package/examples/js13k/build.js +0 -131
- package/examples/js13k/game.js +0 -118
- package/examples/js13k/index.html +0 -21
- package/examples/js13k/tiles.png +0 -0
- package/examples/typescript/build.bat +0 -5
- package/examples/typescript/build.js +0 -33
- package/examples/typescript/game.js +0 -102
- package/examples/typescript/game.ts +0 -134
- package/examples/typescript/index.html +0 -10
- package/examples/typescript/tiles.png +0 -0
- package/examples/typescript/tsconfig.json +0 -8
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
function gameInit()
|
|
2
|
+
{
|
|
3
|
+
gravity = -.01; // set default gravity
|
|
4
|
+
|
|
5
|
+
// fire
|
|
6
|
+
new ParticleEmitter(
|
|
7
|
+
vec2(-5,-2), 0, // pos, angle
|
|
8
|
+
2, 0, 200, PI, // emitSize, emitTime, emitRate, emitCone
|
|
9
|
+
tile(0), // tileInfo
|
|
10
|
+
rgb(1,.5,.1), rgb(1,.1,.1), // colorStartA, colorStartB
|
|
11
|
+
rgb(1,.5,.1,0), rgb(1,.1,.1,0), // colorEndA, colorEndB
|
|
12
|
+
.7, 2, 0, .2, .05, // time, sizeStart, sizeEnd, speed, angleSpeed
|
|
13
|
+
.9, 1, -1, PI, .05, // damp, angleDamp, gravity, particleCone, fade
|
|
14
|
+
.5, 0, 1, 0, 1e9 // randomness, collide, additive, colorLinear, renderOrder
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
// smoke
|
|
18
|
+
new ParticleEmitter(
|
|
19
|
+
vec2(5,-2), 0, // pos, angle
|
|
20
|
+
3, 0, 100, PI, // emitSize, emitTime, emitRate, emitCone
|
|
21
|
+
tile(0), // tileInfo
|
|
22
|
+
hsl(0,0,0,.5), hsl(0,0,1,.5), // colorStartA, colorStartB
|
|
23
|
+
hsl(0,0,0,0), hsl(0,0,1,0), // colorEndA, colorEndB
|
|
24
|
+
1, 1, 5, .2, .01, // time, sizeStart, sizeEnd, speed, angleSpeed
|
|
25
|
+
.85, 1, -1, PI, .3, // damp, angleDamp, gravity, particleCone, fade
|
|
26
|
+
.5, 0, 0, 1, 1e8 // randomness, collide, additive, colorLinear, renderOrder
|
|
27
|
+
);
|
|
28
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
class SoundButton extends EngineObject
|
|
2
|
+
{
|
|
3
|
+
constructor(pos, icon, sound)
|
|
4
|
+
{
|
|
5
|
+
super(pos, vec2(5,4));
|
|
6
|
+
this.icon = icon;
|
|
7
|
+
this.sound = sound;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
update()
|
|
11
|
+
{
|
|
12
|
+
if (mouseWasPressed(0) && isOverlapping(this.pos, this.size, mousePos))
|
|
13
|
+
this.sound.play(this.pos);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
render()
|
|
17
|
+
{
|
|
18
|
+
drawRect(this.pos, this.size, hsl(0,0,.8));
|
|
19
|
+
drawTextOverlay(this.icon, this.pos, 3);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function gameInit()
|
|
24
|
+
{
|
|
25
|
+
new SoundButton(vec2(-6, 5),'💰', new Sound([,,1675,,.06,.24,1,1.82,,,837,.06]));
|
|
26
|
+
new SoundButton(vec2( 0, 5),'🥊', new Sound([,,925,.04,.3,.6,1,.3,,6.27,-184,.09,.17]));
|
|
27
|
+
new SoundButton(vec2( 6, 5),'✨', new Sound([,,539,0,.04,.29,1,1.92,,,567,.02,.02,,,,.04]));
|
|
28
|
+
|
|
29
|
+
new SoundButton(vec2(-6, 0),'🐁', new Sound([,.2,1e3,.02,,.01,2,,18,,475,.01,.01]));
|
|
30
|
+
new SoundButton(vec2( 0, 0),'🎹', new Sound([1.5,.5,270,,.1,,1,1.5,,,,,,,,.1,.01]));
|
|
31
|
+
new SoundButton(vec2( 6, 0),'🏌️', new Sound([,,150,.05,,.05,,1.3,,,,,,3]));
|
|
32
|
+
|
|
33
|
+
new SoundButton(vec2(-6,-5),'🌊', new Sound([,.2,40,.5,,1.5,,11,,,,,,199]));
|
|
34
|
+
new SoundButton(vec2( 0,-5),'🛰️', new Sound([,.5,847,.02,.3,.9,1,1.67,,,-294,.04,.13,,,,.1]));
|
|
35
|
+
new SoundButton(vec2( 6,-5),'⚡', new Sound([,,471,,.09,.47,4,1.06,-6.7,,,,,.9,61,.1,,.82,.1]));
|
|
36
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// globals objects
|
|
2
|
+
let paddle, ball;
|
|
3
|
+
|
|
4
|
+
class PhysicsObject extends EngineObject
|
|
5
|
+
{
|
|
6
|
+
constructor(pos, size)
|
|
7
|
+
{
|
|
8
|
+
super(pos, size); // set object position and size
|
|
9
|
+
this.setCollision(); // make object collide
|
|
10
|
+
this.mass = 0; // make object have static physics
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function gameInit()
|
|
15
|
+
{
|
|
16
|
+
// setup level
|
|
17
|
+
const levelSize = vec2(38, 21); // size of play area
|
|
18
|
+
cameraPos = levelSize.scale(.5); // center camera in level
|
|
19
|
+
canvasFixedSize = vec2(1280, 720); // use a 720p fixed size canvas
|
|
20
|
+
|
|
21
|
+
// create objects
|
|
22
|
+
paddle = new PhysicsObject(vec2(0,1), vec2(6,1)); // create player's paddle
|
|
23
|
+
new PhysicsObject(vec2(-.5,levelSize.y/2), vec2(1,100)) // top wall
|
|
24
|
+
new PhysicsObject(vec2(levelSize.x+.5,levelSize.y/2), vec2(1,100)) // left wall
|
|
25
|
+
new PhysicsObject(vec2(levelSize.x/2,levelSize.y+.5), vec2(100,1)) // right wall
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function gameUpdate()
|
|
29
|
+
{
|
|
30
|
+
if (!ball || ball.pos.y < -1) // spawn ball
|
|
31
|
+
{
|
|
32
|
+
ball && ball.destroy(); // destroy old ball
|
|
33
|
+
ball = new PhysicsObject(cameraPos); // create a ball
|
|
34
|
+
ball.velocity = vec2(.2); // give ball some movement
|
|
35
|
+
ball.elasticity = 1; // make ball bounce
|
|
36
|
+
ball.mass = 1; // make ball have dynamic physics
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
paddle.pos.x = mousePos.x; // move paddle to mouse
|
|
40
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
function gameRender()
|
|
2
|
+
{
|
|
3
|
+
// polygon shapes
|
|
4
|
+
for(let j=3; j<7; ++j)
|
|
5
|
+
{
|
|
6
|
+
let points = [];
|
|
7
|
+
for(let i=0; i<j; ++i)
|
|
8
|
+
{
|
|
9
|
+
let angle = i * Math.PI * 2 / j;
|
|
10
|
+
points.push(vec2(j*4-18,0).add(vec2(Math.sin(angle), Math.cos(angle))));
|
|
11
|
+
}
|
|
12
|
+
drawPoly(points, hsl(j/4,1,.5)); // draw a star
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// circles and ellipses
|
|
16
|
+
drawCircle(vec2(0,5), 1, 0, hsl(.15,1,.5));
|
|
17
|
+
drawEllipse(vec2(-5,5), 2, 1, 0, hsl(0,1,.5));
|
|
18
|
+
drawEllipse(vec2(5,5), 1, .5, 0, hsl(.55,1,.5));
|
|
19
|
+
|
|
20
|
+
// lines and rects
|
|
21
|
+
drawRect(vec2(0,-5), vec2(13,2), hsl(.6,1,.5));
|
|
22
|
+
drawLine(vec2(-5,-7), vec2(5,-3), 1, hsl(0,0,1));
|
|
23
|
+
drawLine(vec2(-5,-3), vec2(5,-7), .2, hsl(1,1,.5));
|
|
24
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
let spriteAtlas;
|
|
2
|
+
|
|
3
|
+
function gameInit()
|
|
4
|
+
{
|
|
5
|
+
// create a table of all sprites
|
|
6
|
+
const gameTile = (i, size=16)=> tile(i, size);
|
|
7
|
+
spriteAtlas =
|
|
8
|
+
{
|
|
9
|
+
circle: gameTile(0),
|
|
10
|
+
crate: gameTile(1),
|
|
11
|
+
icon: gameTile(2),
|
|
12
|
+
largeIcon: gameTile(vec2(1,1),128),
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function gameRender()
|
|
17
|
+
{
|
|
18
|
+
drawRect(vec2(0,0), vec2(100), GRAY); // draw background
|
|
19
|
+
|
|
20
|
+
// draw a sprite from the atlas
|
|
21
|
+
let pos = vec2(0,0); // world position to draw
|
|
22
|
+
let angle = 0; // world space angle to draw the tile
|
|
23
|
+
let size = vec2(15); // world size of the tile
|
|
24
|
+
let mirror = 0; // should tile be mirrored?
|
|
25
|
+
let color = hsl(0,0,1); // color to multiply the tile by
|
|
26
|
+
drawTile(pos, size, spriteAtlas.largeIcon, color, angle, mirror);
|
|
27
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
function gameRender()
|
|
2
|
+
{
|
|
3
|
+
const font = new FontImage;
|
|
4
|
+
font.drawText('System Font Test ', cameraPos.add(vec2(0,3)), .2, true);
|
|
5
|
+
|
|
6
|
+
for(let j=1; j<4; j++)
|
|
7
|
+
{
|
|
8
|
+
let s = '';
|
|
9
|
+
for(let i=0; i<32; ++i)
|
|
10
|
+
s += String.fromCharCode(32*j + i);
|
|
11
|
+
font.drawText(s, cameraPos.add(vec2(0,1-j)), .1, true);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
function gameRender()
|
|
2
|
+
{
|
|
3
|
+
// show the full texture
|
|
4
|
+
let pos = vec2(0,0); // world position to draw
|
|
5
|
+
let size = vec2(15); // world size of the tile
|
|
6
|
+
let color = hsl(0,0,1); // color to multiply the tile by
|
|
7
|
+
let tilePos = vec2(0,0); // top left corner of tile in pixels
|
|
8
|
+
let tileSize = vec2(256); // size of tile in pixels
|
|
9
|
+
let tileInfo = new TileInfo(tilePos, tileSize); // tile info
|
|
10
|
+
drawRect(pos, size, GRAY); // draw background
|
|
11
|
+
drawTile(pos, size, tileInfo, color);
|
|
12
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
function gameInit()
|
|
2
|
+
{
|
|
3
|
+
cameraPos = vec2(16); // setup camera
|
|
4
|
+
gravity = -.01; // enable gravity
|
|
5
|
+
|
|
6
|
+
// create tile collision and visible tile layer
|
|
7
|
+
initTileCollision(vec2(32));
|
|
8
|
+
const pos = vec2();
|
|
9
|
+
const tileLayer = new TileLayer(pos, tileCollisionSize);
|
|
10
|
+
|
|
11
|
+
// init the tile layer
|
|
12
|
+
for (pos.x = tileCollisionSize.x; pos.x--;)
|
|
13
|
+
for (pos.y = tileCollisionSize.y; pos.y--;)
|
|
14
|
+
{
|
|
15
|
+
// check if tile should be solid
|
|
16
|
+
if (rand() < .7)
|
|
17
|
+
continue;
|
|
18
|
+
|
|
19
|
+
// set tile data
|
|
20
|
+
const tileIndex = 1;
|
|
21
|
+
const direction = randInt(4)
|
|
22
|
+
const mirror = !randInt(2);
|
|
23
|
+
const color = randColor(WHITE, hsl(0,0,.2));
|
|
24
|
+
const data = new TileLayerData(tileIndex, direction, mirror, color);
|
|
25
|
+
tileLayer.setData(pos, data);
|
|
26
|
+
setTileCollisionData(pos, 1);
|
|
27
|
+
}
|
|
28
|
+
tileLayer.redraw(); // redraw tile layer with new data
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function gameUpdate()
|
|
32
|
+
{
|
|
33
|
+
if (mouseWasPressed(0))
|
|
34
|
+
{
|
|
35
|
+
// create particle emitter
|
|
36
|
+
const hue = rand();
|
|
37
|
+
let particleEmitter = new ParticleEmitter(
|
|
38
|
+
mousePos, 0, // emitPos, emitAngle
|
|
39
|
+
0, 0.1, 500, PI, // emitSize, emitTime, emitRate, emitCone
|
|
40
|
+
tile(0, 16), // tileIndex, tileSize
|
|
41
|
+
hsl(hue,1,.5), hsl(hue,1,1), // colorStartA, colorStartB
|
|
42
|
+
hsl(hue,1,.5,0), hsl(hue,1,1,0), // colorEndA, colorEndB
|
|
43
|
+
2, .2, .2, .2, .05, // time, sizeStart, sizeEnd, speed, angleSpeed
|
|
44
|
+
.99, 1, 1, PI, // damping, angleDamping, gravityScale, cone
|
|
45
|
+
.05, .8, true, false // fadeRate, randomness, collide, additive
|
|
46
|
+
);
|
|
47
|
+
particleEmitter.elasticity = .5; // bounce when it collides
|
|
48
|
+
particleEmitter.trailScale = 2; // stretch in direction of motion
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
<!DOCTYPE html><head>
|
|
2
|
+
<title>LittleJS Short Examples</title>
|
|
3
|
+
<link rel=icon href=../examples/favicon.png>
|
|
4
|
+
<meta charset=utf-8>
|
|
5
|
+
<style>
|
|
6
|
+
html, body
|
|
7
|
+
{
|
|
8
|
+
height: 100%;
|
|
9
|
+
display: flex;
|
|
10
|
+
flex-direction: column;
|
|
11
|
+
}
|
|
12
|
+
#container1
|
|
13
|
+
{
|
|
14
|
+
height: 100%;
|
|
15
|
+
display: flex;
|
|
16
|
+
}
|
|
17
|
+
#container2
|
|
18
|
+
{
|
|
19
|
+
display: flex;
|
|
20
|
+
flex-direction: column;
|
|
21
|
+
}
|
|
22
|
+
#iframeContainer
|
|
23
|
+
{
|
|
24
|
+
border: 2px solid;
|
|
25
|
+
background: #000;
|
|
26
|
+
}
|
|
27
|
+
iframe
|
|
28
|
+
{
|
|
29
|
+
width: 100%;
|
|
30
|
+
height: 100%;
|
|
31
|
+
border: none;
|
|
32
|
+
}
|
|
33
|
+
#selectExample
|
|
34
|
+
{
|
|
35
|
+
flex-grow: 1;
|
|
36
|
+
}
|
|
37
|
+
#textareaCode
|
|
38
|
+
{
|
|
39
|
+
flex-grow: 1;
|
|
40
|
+
padding: 5px;
|
|
41
|
+
resize: none;
|
|
42
|
+
color: #fff;
|
|
43
|
+
background: #000;
|
|
44
|
+
}
|
|
45
|
+
#textareaError
|
|
46
|
+
{
|
|
47
|
+
flex-grow: 1;
|
|
48
|
+
padding: 5px;
|
|
49
|
+
resize: none;
|
|
50
|
+
display: none;
|
|
51
|
+
color:#f22;
|
|
52
|
+
background: #111;
|
|
53
|
+
}
|
|
54
|
+
#container3
|
|
55
|
+
{
|
|
56
|
+
width: 100%;
|
|
57
|
+
height: 100%;
|
|
58
|
+
display: flex;
|
|
59
|
+
flex-direction: column;
|
|
60
|
+
}
|
|
61
|
+
#titleInfo
|
|
62
|
+
{
|
|
63
|
+
margin: 5px;
|
|
64
|
+
text-align: center;
|
|
65
|
+
font-size: 50px;
|
|
66
|
+
}
|
|
67
|
+
#exampleName
|
|
68
|
+
{
|
|
69
|
+
margin: 0px;
|
|
70
|
+
text-align: center;
|
|
71
|
+
font-size: 30px;
|
|
72
|
+
}
|
|
73
|
+
select
|
|
74
|
+
{
|
|
75
|
+
color:#fff;
|
|
76
|
+
background-color: #111;
|
|
77
|
+
width:100%;
|
|
78
|
+
}
|
|
79
|
+
</style>
|
|
80
|
+
</head><body>
|
|
81
|
+
<div id=container1>
|
|
82
|
+
<div id=container3>
|
|
83
|
+
<p id=titleInfo>LittleJS Short Examples</p>
|
|
84
|
+
<p id=exampleName></p>
|
|
85
|
+
<textarea id=textareaCode oninput=codeInput() spellcheck=false></textarea>
|
|
86
|
+
<textarea id=textareaError readonly spellcheck=false></textarea>
|
|
87
|
+
<div><input type=checkbox id=checkboxLiveEdit checked>Live Edit</div>
|
|
88
|
+
</div>
|
|
89
|
+
<div id=container2>
|
|
90
|
+
<div id=iframeContainer></div>
|
|
91
|
+
<select id=selectExample autofocus onchange=setExample() size=2></select>
|
|
92
|
+
</div>
|
|
93
|
+
</div>
|
|
94
|
+
<script>
|
|
95
|
+
'use strict';
|
|
96
|
+
|
|
97
|
+
class ExampleInfo
|
|
98
|
+
{
|
|
99
|
+
constructor(name, filename, description)
|
|
100
|
+
{
|
|
101
|
+
this.name = name;
|
|
102
|
+
this.filename = filename;
|
|
103
|
+
this.description = description;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const exampleList =
|
|
108
|
+
[
|
|
109
|
+
new ExampleInfo('Hello World', 'helloWorld.js', 'Simplest example'),
|
|
110
|
+
new ExampleInfo('Shapes', 'shapes.js', 'How to draw geometric shapes'),
|
|
111
|
+
new ExampleInfo('Blending', 'blending.js', 'Shows different blending modes'),
|
|
112
|
+
new ExampleInfo('Texture', 'texture.js', 'How to display a full texture'),
|
|
113
|
+
new ExampleInfo('Particles', 'particles.js', 'How to use the particle system'),
|
|
114
|
+
new ExampleInfo('Play Sound', 'playSound.js', 'How to play sounds with zzfx'),
|
|
115
|
+
new ExampleInfo('System Font', 'systemFont.js', 'Demo of the included system font'),
|
|
116
|
+
new ExampleInfo('Sprite Atlas', 'spriteAtlas.js', 'How to set up a simple sprite atlas'),
|
|
117
|
+
new ExampleInfo('Tile Layer', 'tileLayer.js', 'How to use a tile layer for collision and rendering'),
|
|
118
|
+
new ExampleInfo('Pong Game', 'pong.js', 'A simple pong game using LittleJS physics'),
|
|
119
|
+
|
|
120
|
+
// add more examples here!
|
|
121
|
+
];
|
|
122
|
+
|
|
123
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
124
|
+
|
|
125
|
+
let iframeExample, inputTimeout;
|
|
126
|
+
|
|
127
|
+
// load the examples into the list
|
|
128
|
+
for (const example of exampleList)
|
|
129
|
+
{
|
|
130
|
+
const text = example.name + (example.description?' - ' + example.description : '');
|
|
131
|
+
selectExample.add(new Option(text));
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// select first option
|
|
135
|
+
selectExample.selectedIndex = 0;
|
|
136
|
+
setExample();
|
|
137
|
+
|
|
138
|
+
onresize = () =>
|
|
139
|
+
{
|
|
140
|
+
// resize iframe to fit half the window
|
|
141
|
+
const aspect = 1920 / 1080;
|
|
142
|
+
let w = innerWidth / 2 | 0;
|
|
143
|
+
let h = w / aspect | 0;
|
|
144
|
+
iframeContainer.style.width = w + 'px';
|
|
145
|
+
iframeContainer.style.height = h + 'px';
|
|
146
|
+
}
|
|
147
|
+
onresize();
|
|
148
|
+
|
|
149
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
150
|
+
|
|
151
|
+
function setExample()
|
|
152
|
+
{
|
|
153
|
+
const example = exampleList[selectExample.selectedIndex];
|
|
154
|
+
exampleName.innerText = example.name + ' - ' + example.filename +
|
|
155
|
+
(example.description ? '\n' + example.description : '');
|
|
156
|
+
loadFile(example.filename);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function codeInput()
|
|
160
|
+
{
|
|
161
|
+
if (!checkboxLiveEdit.checked)
|
|
162
|
+
return;
|
|
163
|
+
|
|
164
|
+
// debounce input
|
|
165
|
+
clearTimeout(inputTimeout);
|
|
166
|
+
inputTimeout = setTimeout(() => setCode(textareaCode.value), 500);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function loadFile(filename)
|
|
170
|
+
{
|
|
171
|
+
fetch('code/' + filename)
|
|
172
|
+
.then(response => {
|
|
173
|
+
if (!response.ok)
|
|
174
|
+
throw new Error('Could not load file: '+filename);
|
|
175
|
+
return response.text();
|
|
176
|
+
})
|
|
177
|
+
.then(text => setCode(textareaCode.value = text))
|
|
178
|
+
.catch(error => setErrorMessage(error.message));
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function setCode(code)
|
|
182
|
+
{
|
|
183
|
+
clearTimeout(inputTimeout);
|
|
184
|
+
if (iframeExample)
|
|
185
|
+
iframeContainer.removeChild(iframeExample);
|
|
186
|
+
|
|
187
|
+
setErrorMessage('');
|
|
188
|
+
iframeExample = document.createElement('iframe');
|
|
189
|
+
iframeContainer.appendChild(iframeExample);
|
|
190
|
+
iframeExample.onload = () =>
|
|
191
|
+
{
|
|
192
|
+
const iframeContent = iframeExample.contentWindow;
|
|
193
|
+
const iframeDocument = iframeContent.document;
|
|
194
|
+
|
|
195
|
+
// create error event listeners
|
|
196
|
+
iframeContent.onerror = (message, source, lineno, colno, error) =>
|
|
197
|
+
{
|
|
198
|
+
let text = message;
|
|
199
|
+
if (lineno)
|
|
200
|
+
text += ` (Line:${lineno}, Column:${colno})`
|
|
201
|
+
if (error && error.stack)
|
|
202
|
+
text += `\n\n` + error.stack;
|
|
203
|
+
setErrorMessage(text);
|
|
204
|
+
}
|
|
205
|
+
iframeContent.onunhandledrejection = (event) =>
|
|
206
|
+
{
|
|
207
|
+
let text = event.reason;
|
|
208
|
+
if (event.reason.stack)
|
|
209
|
+
text += `\n\n` + event.reason.stack;
|
|
210
|
+
setErrorMessage(text);
|
|
211
|
+
};
|
|
212
|
+
const originalAssert = iframeContent.console.assert;
|
|
213
|
+
iframeContent.console.assert = function (condition, output)
|
|
214
|
+
{
|
|
215
|
+
if (!condition)
|
|
216
|
+
setErrorMessage('Assertion failed: ' + (output || ''));
|
|
217
|
+
originalAssert.apply(this, arguments);
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
// create a script element that overrides the default functions
|
|
221
|
+
const overrideScript = iframeDocument.createElement('script');
|
|
222
|
+
iframeDocument.body.appendChild(overrideScript);
|
|
223
|
+
overrideScript.text = code;
|
|
224
|
+
|
|
225
|
+
if (textareaError.style.display == 'block')
|
|
226
|
+
return; // stop if script error
|
|
227
|
+
|
|
228
|
+
// start LittleJS engine
|
|
229
|
+
iframeContent.engineInit(iframeContent.gameInit, iframeContent.gameUpdate, iframeContent.gameUpdatePost, iframeContent.gameRender, iframeContent.gameRenderPost, ['tiles.png']);
|
|
230
|
+
}
|
|
231
|
+
iframeExample.src = 'base.html';
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function setErrorMessage(message)
|
|
235
|
+
{
|
|
236
|
+
textareaError.value = message
|
|
237
|
+
textareaError.style.display = message ? 'block' : 'none';
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
</script>
|
|
241
|
+
</body>
|
|
242
|
+
</html>
|
|
Binary file
|
package/src/engine.js
CHANGED
|
@@ -30,7 +30,7 @@ const engineName = 'LittleJS';
|
|
|
30
30
|
* @type {String}
|
|
31
31
|
* @default
|
|
32
32
|
* @memberof Engine */
|
|
33
|
-
const engineVersion = '1.11.
|
|
33
|
+
const engineVersion = '1.11.5';
|
|
34
34
|
|
|
35
35
|
/** Frames per second to update
|
|
36
36
|
* @type {Number}
|
|
@@ -113,8 +113,21 @@ function engineAddPlugin(updateFunction, renderFunction)
|
|
|
113
113
|
* @memberof Engine */
|
|
114
114
|
function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, imageSources=[], rootElement=document.body)
|
|
115
115
|
{
|
|
116
|
+
ASSERT(!mainContext, 'engine already initialized');
|
|
116
117
|
ASSERT(Array.isArray(imageSources), 'pass in images as array');
|
|
117
118
|
|
|
119
|
+
// allow passing in empty functions
|
|
120
|
+
if (!gameInit)
|
|
121
|
+
gameInit = ()=>{};
|
|
122
|
+
if (!gameUpdate)
|
|
123
|
+
gameUpdate = ()=>{};
|
|
124
|
+
if (!gameUpdatePost)
|
|
125
|
+
gameUpdatePost = ()=>{};
|
|
126
|
+
if (!gameRender)
|
|
127
|
+
gameRender = ()=>{};
|
|
128
|
+
if (!gameRenderPost)
|
|
129
|
+
gameRenderPost = ()=>{};
|
|
130
|
+
|
|
118
131
|
// Called automatically by engine to setup render system
|
|
119
132
|
function enginePreRender()
|
|
120
133
|
{
|
|
@@ -279,7 +292,7 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
279
292
|
(canvasPixelated ? 'image-rendering:pixelated;' : '') + // pixel art
|
|
280
293
|
'user-select:none;' + // prevent hold to select
|
|
281
294
|
'-webkit-user-select:none;' + // compatibility for ios
|
|
282
|
-
(!touchInputEnable ? '' : // no touch css
|
|
295
|
+
(!touchInputEnable ? '' : // no touch css settings
|
|
283
296
|
'touch-action:none;' + // prevent mobile pinch to resize
|
|
284
297
|
'-webkit-touch-callout:none');// compatibility for ios
|
|
285
298
|
rootElement.style.cssText = styleRoot;
|
|
@@ -308,6 +321,7 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
308
321
|
new Promise(resolve =>
|
|
309
322
|
{
|
|
310
323
|
const image = new Image;
|
|
324
|
+
image.crossOrigin = 'anonymous';
|
|
311
325
|
image.onerror = image.onload = ()=>
|
|
312
326
|
{
|
|
313
327
|
textureInfos[textureIndex] = new TextureInfo(image);
|
|
@@ -352,7 +366,7 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
352
366
|
* @memberof Engine */
|
|
353
367
|
function engineObjectsUpdate()
|
|
354
368
|
{
|
|
355
|
-
// get list of solid objects for physics
|
|
369
|
+
// get list of solid objects for physics optimization
|
|
356
370
|
engineObjectsCollide = engineObjects.filter(o=>o.collideSolidObjects);
|
|
357
371
|
|
|
358
372
|
// recursive object update
|
package/src/engineAudio.js
CHANGED
|
@@ -25,7 +25,7 @@ function audioInit()
|
|
|
25
25
|
{
|
|
26
26
|
if (!soundEnable || headlessMode) return;
|
|
27
27
|
|
|
28
|
-
// (createGain is more widely
|
|
28
|
+
// (createGain is more widely supported then GainNode constructor)
|
|
29
29
|
audioGainNode = audioContext.createGain();
|
|
30
30
|
audioGainNode.connect(audioContext.destination);
|
|
31
31
|
audioGainNode.gain.value = soundVolume; // set starting value
|
|
@@ -375,7 +375,7 @@ const zzfxR = 44100;
|
|
|
375
375
|
* @param {Number} [sustain] - Sustain time, how long sound holds (seconds)
|
|
376
376
|
* @param {Number} [release] - Release time, how fast sound fades out (seconds)
|
|
377
377
|
* @param {Number} [shape] - Shape of the sound wave
|
|
378
|
-
* @param {Number} [shapeCurve] -
|
|
378
|
+
* @param {Number} [shapeCurve] - Squareness of wave (0=square, 1=normal, 2=pointy)
|
|
379
379
|
* @param {Number} [slide] - How much to slide frequency (kHz/s)
|
|
380
380
|
* @param {Number} [deltaSlide] - How much to change slide (kHz/s/s)
|
|
381
381
|
* @param {Number} [pitchJump] - Frequency of pitch jump (Hz)
|
|
@@ -387,7 +387,7 @@ const zzfxR = 44100;
|
|
|
387
387
|
* @param {Number} [delay] - Overlap sound with itself for reverb and flanger effects (seconds)
|
|
388
388
|
* @param {Number} [sustainVolume] - Volume level for sustain (percent)
|
|
389
389
|
* @param {Number} [decay] - Decay time, how long to reach sustain after attack (seconds)
|
|
390
|
-
* @param {Number} [tremolo] - Trembling effect, rate controlled by repeat time (
|
|
390
|
+
* @param {Number} [tremolo] - Trembling effect, rate controlled by repeat time (percent)
|
|
391
391
|
* @param {Number} [filter] - Filter cutoff frequency, positive for HPF, negative for LPF (Hz)
|
|
392
392
|
* @return {Array} - Array of audio samples
|
|
393
393
|
* @memberof Audio
|
|
@@ -491,7 +491,7 @@ function zzfxG
|
|
|
491
491
|
// ZzFX Music Renderer v2.0.3 by Keith Clark and Frank Force
|
|
492
492
|
|
|
493
493
|
/** Generate samples for a ZzFM song with given parameters
|
|
494
|
-
* @param {Array} instruments - Array of ZzFX sound
|
|
494
|
+
* @param {Array} instruments - Array of ZzFX sound parameters
|
|
495
495
|
* @param {Array} patterns - Array of pattern data
|
|
496
496
|
* @param {Array} sequence - Array of pattern indexes
|
|
497
497
|
* @param {Number} [BPM] - Playback speed of the song in BPM
|
package/src/engineBuild.js
CHANGED
|
@@ -34,7 +34,8 @@ const asciiArt =`
|
|
|
34
34
|
.|________|_._|______|_._|__|_|_|}
|
|
35
35
|
OOO OOO OO OO OO=OO-oo\\
|
|
36
36
|
`;
|
|
37
|
-
const license = '// LittleJS - MIT License - Copyright 2021 Frank Force\n'
|
|
37
|
+
const license = '// LittleJS Engine - MIT License - Copyright 2021 Frank Force\n'+
|
|
38
|
+
'// https://github.com/KilledByAPixel/LittleJS\n';
|
|
38
39
|
|
|
39
40
|
console.log(asciiArt);
|
|
40
41
|
console.log('Choo Choo... Building LittleJS Engine!\n');
|
package/src/engineDebug.js
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* @memberof Debug */
|
|
17
17
|
const debug = true;
|
|
18
18
|
|
|
19
|
-
/** True if asserts are
|
|
19
|
+
/** True if asserts are enabled
|
|
20
20
|
* @type {Boolean}
|
|
21
21
|
* @default
|
|
22
22
|
* @memberof Debug */
|
|
@@ -200,6 +200,7 @@ function debugShowErrors()
|
|
|
200
200
|
const showError = (message)=>
|
|
201
201
|
{
|
|
202
202
|
// replace entire page with error message
|
|
203
|
+
document.body.style.display = '';
|
|
203
204
|
document.body.style.backgroundColor = '#111';
|
|
204
205
|
document.body.innerHTML = `<pre style=color:#f00;font-size:50px>` + message;
|
|
205
206
|
}
|
|
@@ -244,12 +245,8 @@ function debugRender()
|
|
|
244
245
|
|
|
245
246
|
if (debugTakeScreenshot)
|
|
246
247
|
{
|
|
247
|
-
//
|
|
248
|
-
|
|
249
|
-
mainContext.drawImage(overlayCanvas, 0, 0);
|
|
250
|
-
overlayCanvas.width |= 0;
|
|
251
|
-
|
|
252
|
-
// remove alpha and save
|
|
248
|
+
// combine canvases, remove alpha and save
|
|
249
|
+
combineCanvases();
|
|
253
250
|
const w = mainCanvas.width, h = mainCanvas.height;
|
|
254
251
|
overlayContext.fillRect(0,0,w,h);
|
|
255
252
|
overlayContext.drawImage(mainCanvas, 0, 0);
|