littlejsengine 1.12.6 → 1.13.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -12
- package/dist/littlejs.d.ts +466 -236
- package/dist/littlejs.esm.js +6126 -5370
- package/dist/littlejs.esm.min.js +4 -1
- package/dist/littlejs.js +6093 -5359
- package/dist/littlejs.min.js +4 -1
- package/dist/littlejs.release.js +5614 -4907
- package/examples/box2d/game.js +2 -2
- package/examples/box2d/gameObjects.js +8 -7
- package/examples/box2d/index.html +1 -1
- package/examples/box2d/scenes.js +8 -8
- package/examples/breakout/game.js +1 -1
- package/examples/breakout/gameObjects.js +2 -2
- package/examples/breakout/index.html +1 -1
- package/examples/breakoutTutorial/README.md +46 -43
- package/examples/breakoutTutorial/game.js +3 -3
- package/examples/breakoutTutorial/index.html +1 -1
- package/examples/electron/build.bat +7 -0
- package/examples/electron/build.js +124 -0
- package/examples/electron/electron.js +35 -0
- package/examples/electron/game.js +140 -0
- package/examples/electron/index.html +13 -0
- package/examples/electron/package.json +12 -0
- package/examples/electron/tiles.png +0 -0
- package/examples/empty/game.js +1 -0
- package/examples/empty/index.html +1 -1
- package/examples/htmlMenu/game.js +1 -1
- package/examples/htmlMenu/index.html +1 -1
- package/examples/index.html +486 -182
- package/examples/module/game.js +6 -3
- package/examples/module/index.html +1 -1
- package/examples/particles/index.html +12 -3
- package/examples/platformer/game.js +4 -4
- package/examples/platformer/gameCharacter.js +6 -6
- package/examples/platformer/gameEffects.js +74 -57
- package/examples/platformer/gameLevel.js +61 -70
- package/examples/platformer/gameObjects.js +4 -4
- package/examples/platformer/index.html +1 -1
- package/examples/puzzle/index.html +1 -1
- package/examples/shorts/base.html +23 -3
- package/examples/shorts/blending.js +9 -5
- package/examples/shorts/box2d.js +1 -1
- package/examples/shorts/box2dCar.js +9 -13
- package/examples/shorts/clock.js +1 -1
- package/examples/shorts/colors.js +2 -2
- package/examples/shorts/flappyGame.js +52 -0
- package/examples/shorts/helloWorld.js +6 -3
- package/examples/shorts/hillGlideGame.js +56 -0
- package/examples/shorts/landerGame.js +32 -0
- package/examples/shorts/maze.js +47 -0
- package/examples/shorts/medals.js +42 -0
- package/examples/shorts/nineSlice.js +21 -0
- package/examples/shorts/piano.js +52 -0
- package/examples/shorts/platformer.js +24 -20
- package/examples/shorts/{pong.js → pongGame.js} +1 -1
- package/examples/shorts/raycasting.js +71 -0
- package/examples/shorts/shapes.js +9 -9
- package/examples/shorts/slidingPuzzle.js +42 -0
- package/examples/shorts/spaceGame.js +56 -0
- package/examples/shorts/starfield.js +15 -0
- package/examples/shorts/systemFont.js +1 -1
- package/examples/shorts/tileLayer.js +3 -5
- package/examples/shorts/tiles.png +0 -0
- package/examples/shorts/tiltedView.js +8 -7
- package/examples/shorts/topDown.js +18 -26
- package/examples/shorts/uiSystem.js +5 -7
- package/examples/starter/build.bat +6 -1
- package/examples/starter/build.js +9 -8
- package/examples/starter/game.js +7 -4
- package/examples/starter/index.html +23 -13
- package/examples/stress/index.html +7 -8
- package/examples/style.css +139 -0
- package/examples/typescript/build.js +2 -3
- package/examples/typescript/game.js +4 -4
- package/examples/typescript/game.ts +6 -3
- package/examples/typescript/index.html +1 -1
- package/examples/uiSystem/game.js +10 -25
- package/examples/uiSystem/index.html +1 -1
- package/package.json +2 -2
- package/plugins/box2d.js +22 -97
- package/plugins/drawUtilities.js +132 -0
- package/plugins/newgrounds.js +1 -1
- package/plugins/pluginExport.js +7 -1
- package/plugins/postProcess.js +9 -2
- package/plugins/uiSystem.js +155 -67
- package/plugins/zzfxm.js +1 -1
- package/reference.md +10 -10
- package/src/engine.js +56 -30
- package/src/engineAudio.js +15 -6
- package/src/engineBuild.bat +6 -1
- package/src/engineBuild.js +20 -5
- package/src/engineDebug.js +55 -28
- package/src/engineDraw.js +321 -177
- package/src/engineExport.js +27 -9
- package/src/engineInput.js +110 -38
- package/src/engineMedals.js +4 -4
- package/src/engineObject.js +71 -70
- package/src/engineParticles.js +33 -9
- package/src/engineSettings.js +69 -23
- package/src/engineTileLayer.js +312 -153
- package/src/engineUtilities.js +187 -135
- package/src/engineWebGL.js +70 -39
package/examples/box2d/game.js
CHANGED
|
@@ -118,7 +118,7 @@ function gameUpdate()
|
|
|
118
118
|
GameObjects.explosion(LJS.mousePos);
|
|
119
119
|
|
|
120
120
|
if (LJS.keyWasPressed('KeyR'))
|
|
121
|
-
setScene(scene); // reset scene
|
|
121
|
+
setScene(Scenes.scene); // reset scene
|
|
122
122
|
if (LJS.keyWasPressed('ArrowUp') || LJS.keyWasPressed('ArrowDown'))
|
|
123
123
|
{
|
|
124
124
|
// change scene
|
|
@@ -137,7 +137,7 @@ function gameUpdatePost()
|
|
|
137
137
|
function gameRender()
|
|
138
138
|
{
|
|
139
139
|
// draw a grey square in the background without using webgl
|
|
140
|
-
LJS.drawRect(vec2(20,8), vec2(100), hsl(0,0,.8), 0,
|
|
140
|
+
LJS.drawRect(vec2(20,8), vec2(100), hsl(0,0,.8), 0, false);
|
|
141
141
|
|
|
142
142
|
if (Scenes.scene == 5)
|
|
143
143
|
{
|
|
@@ -63,7 +63,7 @@ export function spawnPyramid(pos, count)
|
|
|
63
63
|
spawnBox(pos.add(vec2(j-i/2+.5,count-i+.5)), 1, LJS.randColor());
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
export function spawnDominoes(pos, count, size=vec2(.5,
|
|
66
|
+
export function spawnDominoes(pos, count, size=vec2(.5,3))
|
|
67
67
|
{
|
|
68
68
|
for (let i=count; i--;)
|
|
69
69
|
spawnBox(pos.add(vec2(i*size.y*.9,size.y/2)), size, LJS.randColor());
|
|
@@ -105,7 +105,7 @@ export class CarObject extends LJS.Box2dObject
|
|
|
105
105
|
{
|
|
106
106
|
constructor(pos)
|
|
107
107
|
{
|
|
108
|
-
super(pos, vec2(), 0, 0, LJS.
|
|
108
|
+
super(pos, vec2(), 0, 0, LJS.randColor());
|
|
109
109
|
const carPoints = [
|
|
110
110
|
vec2(-1.5,-.5),
|
|
111
111
|
vec2(1.5, -.5),
|
|
@@ -147,14 +147,14 @@ export class CarObject extends LJS.Box2dObject
|
|
|
147
147
|
}
|
|
148
148
|
update()
|
|
149
149
|
{
|
|
150
|
+
super.update();
|
|
151
|
+
|
|
150
152
|
// car controls
|
|
151
153
|
const maxSpeed = 40;
|
|
152
|
-
const brakeAmount = .8;
|
|
153
154
|
const input = LJS.keyDirection().x;
|
|
154
155
|
let s = this.wheelMotorJoint.getMotorSpeed();
|
|
155
|
-
s = input ? LJS.clamp(s - input, -maxSpeed, maxSpeed) :
|
|
156
|
+
s = input ? LJS.clamp(s - input, -maxSpeed, maxSpeed) : 0;
|
|
156
157
|
this.wheelMotorJoint.setMotorSpeed(s);
|
|
157
|
-
super.update();
|
|
158
158
|
}
|
|
159
159
|
destroy()
|
|
160
160
|
{
|
|
@@ -308,7 +308,7 @@ export class SoftBodyObject extends LJS.Box2dObject
|
|
|
308
308
|
for (let y = 0; y < sy.y; ++y) poly.push(this.getNode(sx-1, y).pos);
|
|
309
309
|
for (let x = sx; x--;) poly.push(this.getNode(x, sy-1).pos);
|
|
310
310
|
for (let y = sy; y--;) poly.push(this.getNode(0, y).pos);
|
|
311
|
-
LJS.
|
|
311
|
+
LJS.drawPoly(poly, LJS.BLACK)
|
|
312
312
|
}
|
|
313
313
|
getNode(x, y)
|
|
314
314
|
{
|
|
@@ -393,6 +393,8 @@ export class ClothObject extends LJS.Box2dObject
|
|
|
393
393
|
}
|
|
394
394
|
update()
|
|
395
395
|
{
|
|
396
|
+
super.update();
|
|
397
|
+
|
|
396
398
|
for (let y=this.sizeCount.y; y--;)
|
|
397
399
|
for (let x=this.sizeCount.x; x--;)
|
|
398
400
|
{
|
|
@@ -425,7 +427,6 @@ export class ClothObject extends LJS.Box2dObject
|
|
|
425
427
|
this.nodes[y*this.sizeCount.x+x] = 0;
|
|
426
428
|
}
|
|
427
429
|
}
|
|
428
|
-
super.update();
|
|
429
430
|
}
|
|
430
431
|
render()
|
|
431
432
|
{
|
package/examples/box2d/scenes.js
CHANGED
|
@@ -35,7 +35,7 @@ export function loadScene(_scene)
|
|
|
35
35
|
GameObjects.spawnRandomEdges();
|
|
36
36
|
GameObjects.spawnBox(vec2(11,8), 4, LJS.randColor(), LJS.box2d.bodyTypeStatic, false);
|
|
37
37
|
GameObjects.spawnCircle(vec2(20,8), 4, LJS.randColor(), LJS.box2d.bodyTypeStatic, false);
|
|
38
|
-
GameObjects.spawnRandomPoly(vec2(29,8), 4, LJS.randColor(), LJS.box2d.bodyTypeStatic
|
|
38
|
+
GameObjects.spawnRandomPoly(vec2(29,8), 4, LJS.randColor(), LJS.box2d.bodyTypeStatic);
|
|
39
39
|
for (let i=500;i--;)
|
|
40
40
|
GameObjects.spawnRandomObject(vec2(LJS.rand(1,39), LJS.rand(10,50)));
|
|
41
41
|
}
|
|
@@ -49,12 +49,12 @@ export function loadScene(_scene)
|
|
|
49
49
|
if (scene == 2)
|
|
50
50
|
{
|
|
51
51
|
sceneName = 'Dominoes';
|
|
52
|
-
GameObjects.spawnDominoes(vec2(11,
|
|
53
|
-
GameObjects.spawnDominoes(vec2(
|
|
54
|
-
GameObjects.spawnCircle(vec2(10,20),
|
|
55
|
-
GameObjects.spawnCircle(vec2(31.
|
|
56
|
-
GameObjects.spawnBox(vec2(16,
|
|
57
|
-
GameObjects.spawnBox(vec2(24,
|
|
52
|
+
GameObjects.spawnDominoes(vec2(11,13), 8);
|
|
53
|
+
GameObjects.spawnDominoes(vec2(3,0), 10, vec2(1,4));
|
|
54
|
+
GameObjects.spawnCircle(vec2(10.2,20), 1.5, LJS.randColor());
|
|
55
|
+
GameObjects.spawnCircle(vec2(31.9,15), 2.5, LJS.randColor());
|
|
56
|
+
GameObjects.spawnBox(vec2(16,13), vec2(32,1), LJS.randColor(), LJS.box2d.bodyTypeStatic, false);
|
|
57
|
+
GameObjects.spawnBox(vec2(24,8), vec2(34,1), LJS.randColor(), LJS.box2d.bodyTypeStatic, false, -.15);
|
|
58
58
|
}
|
|
59
59
|
if (scene == 3)
|
|
60
60
|
{
|
|
@@ -181,7 +181,7 @@ export function loadScene(_scene)
|
|
|
181
181
|
if (scene == 10)
|
|
182
182
|
{
|
|
183
183
|
sceneName = 'Softbodies';
|
|
184
|
-
for(let i=3;i--;)
|
|
184
|
+
for (let i=3;i--;)
|
|
185
185
|
new GameObjects.SoftBodyObject(vec2(20, 3+i*7), vec2(6-i), vec2(9-i), LJS.randColor());
|
|
186
186
|
}
|
|
187
187
|
}
|
|
@@ -104,7 +104,7 @@ function gameRenderPost()
|
|
|
104
104
|
{
|
|
105
105
|
// use built in image font for text
|
|
106
106
|
const font = new LJS.FontImage;
|
|
107
|
-
font.drawText('Score: ' + score, LJS.cameraPos.add(vec2(0,
|
|
107
|
+
font.drawText('Score: ' + score, LJS.cameraPos.add(vec2(0,9.7)), .15, true);
|
|
108
108
|
if (!brickCount)
|
|
109
109
|
font.drawText('You Win!', LJS.cameraPos.add(vec2(0,-5)), .2, true);
|
|
110
110
|
else if (!ball)
|
|
@@ -31,7 +31,7 @@ export class Wall extends PhysicsObject
|
|
|
31
31
|
{
|
|
32
32
|
constructor(pos, size)
|
|
33
33
|
{
|
|
34
|
-
super(pos, size, 0, 0, hsl(0,0,0));
|
|
34
|
+
super(pos, size, 0, 0, hsl(0,0,0,0));
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
37
|
|
|
@@ -103,7 +103,7 @@ export class Ball extends PhysicsObject
|
|
|
103
103
|
|
|
104
104
|
// make a bouncy ball
|
|
105
105
|
this.velocity = vec2(0, -.1);
|
|
106
|
-
this.
|
|
106
|
+
this.restitution = 1;
|
|
107
107
|
this.mass = 1;
|
|
108
108
|
|
|
109
109
|
// attach a trail effect
|
|
@@ -21,11 +21,11 @@ The first step is to make a 2D grid of brick objects in gameInit. As a placehold
|
|
|
21
21
|
|
|
22
22
|
```javascript
|
|
23
23
|
// create bricks
|
|
24
|
-
for(let x=0; x<=20; x++)
|
|
25
|
-
for(let y=0; y<=20; y++)
|
|
24
|
+
for (let x=0; x<=20; x++)
|
|
25
|
+
for (let y=0; y<=20; y++)
|
|
26
26
|
{
|
|
27
|
-
const brick = new EngineObject(vec2(x,y)); // create a brick
|
|
28
|
-
brick.color = randColor(); // give brick a random color
|
|
27
|
+
const brick = new LJS.EngineObject(vec2(x,y)); // create a brick
|
|
28
|
+
brick.color = LJS.randColor(); // give brick a random color
|
|
29
29
|
}
|
|
30
30
|
```
|
|
31
31
|
|
|
@@ -42,11 +42,11 @@ Let’s make the bricks more rectangular by passing vec2(2,1) to the size parame
|
|
|
42
42
|
```javascript
|
|
43
43
|
// create bricks
|
|
44
44
|
const levelSize = vec2(20, 20);
|
|
45
|
-
for(let x=0; x<=levelSize.x; x+=2)
|
|
46
|
-
for(let y=0; y<=levelSize.y; y+=1)
|
|
45
|
+
for (let x=0; x<=levelSize.x; x+=2)
|
|
46
|
+
for (let y=0; y<=levelSize.y; y+=1)
|
|
47
47
|
{
|
|
48
|
-
const brick = new EngineObject(vec2(x,y), vec2(2,1)); // create a brick
|
|
49
|
-
brick.color = randColor(); // give brick a random color
|
|
48
|
+
const brick = new LJS.EngineObject(vec2(x,y), vec2(2,1)); // create a brick
|
|
49
|
+
brick.color = LJS.randColor(); // give brick a random color
|
|
50
50
|
}
|
|
51
51
|
```
|
|
52
52
|
|
|
@@ -57,13 +57,13 @@ You can also change the camera zoom by using camaraScale which controls the numb
|
|
|
57
57
|
For this example the camera will just remain stationary, but in other types of games it is very useful!
|
|
58
58
|
|
|
59
59
|
```javascript
|
|
60
|
-
setCameraPos(levelSize.scale(.5)); // center camera in level
|
|
60
|
+
LJS.setCameraPos(levelSize.scale(.5)); // center camera in level
|
|
61
61
|
```
|
|
62
62
|
|
|
63
63
|
We’ll also start with a blank slate by commenting out the default “Hello World!” text in gameRenderPost.
|
|
64
64
|
|
|
65
65
|
```javascript
|
|
66
|
-
// drawTextScreen('Hello World!', mainCanvasSize.scale(.5), 80);
|
|
66
|
+
// LJS.drawTextScreen('Hello World!', LJS.mainCanvasSize.scale(.5), 80);
|
|
67
67
|
```
|
|
68
68
|
|
|
69
69
|

|
|
@@ -77,14 +77,14 @@ For this example we will use a fixed size canvas. This will let the drawing canv
|
|
|
77
77
|
To enable it we will set canvasFixedSize in gameInit to use 720p resolution.
|
|
78
78
|
|
|
79
79
|
```javascript
|
|
80
|
-
setCanvasFixedSize(vec2(1280, 720)); // use a 720p fixed size canvas
|
|
80
|
+
LJS.setCanvasFixedSize(vec2(1280, 720)); // use a 720p fixed size canvas
|
|
81
81
|
```
|
|
82
82
|
|
|
83
83
|
Before we tweak the level size, let’s add this bit of code to gameRender to show the size of the level. This will cause some rects to be drawn each frame before the engine objects. To create color objects we pass in RGB values between 0 and 1.
|
|
84
84
|
|
|
85
85
|
```javascript
|
|
86
|
-
drawRect(cameraPos, vec2(100),
|
|
87
|
-
drawRect(cameraPos, levelSize,
|
|
86
|
+
LJS.drawRect(LJS.cameraPos, vec2(100), rgb(.5,.5,.5)); // draw background
|
|
87
|
+
LJS.drawRect(LJS.cameraPos, levelSize, rgb(.1,.1,.1)); // draw level boundary
|
|
88
88
|
```
|
|
89
89
|
|
|
90
90
|
We also need to make levelSize a global by moving it to the top so it can be accessed from other functions.
|
|
@@ -98,11 +98,11 @@ Now we can see the boundaries of our level and tweak the size to fill the canvas
|
|
|
98
98
|
We can also adjust the for loop where the bricks are created to make them only fill the top middle part of the level.
|
|
99
99
|
|
|
100
100
|
```javascript
|
|
101
|
-
for(let x=2; x<=levelSize.x-2; x+=2)
|
|
102
|
-
for(let y=12; y<=levelSize.y-2; y+=1)
|
|
101
|
+
for (let x=2; x<=levelSize.x-2; x+=2)
|
|
102
|
+
for (let y=12; y<=levelSize.y-2; y+=1)
|
|
103
103
|
{
|
|
104
|
-
const brick = new EngineObject(vec2(x,y), vec2(2,1)); // create a brick
|
|
105
|
-
brick.color = randColor(); // give brick a random color
|
|
104
|
+
const brick = new LJS.EngineObject(vec2(x,y), vec2(2,1)); // create a brick
|
|
105
|
+
brick.color = LJS.randColor(); // give brick a random color
|
|
106
106
|
}
|
|
107
107
|
```
|
|
108
108
|
|
|
@@ -115,11 +115,11 @@ Now that looks almost like a breakout game!
|
|
|
115
115
|
For the player’s paddle, let’s create a new class of object that extends the built in EngineObject. This will allow us to control movement with the mouse. In this Paddle class we will just add some new code to the update function which is automatically called each frame by the engine.
|
|
116
116
|
|
|
117
117
|
```javascript
|
|
118
|
-
class Paddle extends EngineObject
|
|
118
|
+
class Paddle extends LJS.EngineObject
|
|
119
119
|
{
|
|
120
120
|
update()
|
|
121
121
|
{
|
|
122
|
-
this.pos.x = mousePos.x; // move paddle to mouse
|
|
122
|
+
this.pos.x = LJS.mousePos.x; // move paddle to mouse
|
|
123
123
|
}
|
|
124
124
|
}
|
|
125
125
|
```
|
|
@@ -149,7 +149,7 @@ You can also add this code to the paddle update that will keep the paddle from f
|
|
|
149
149
|
|
|
150
150
|
```javascript
|
|
151
151
|
// clamp paddle to level size
|
|
152
|
-
this.pos.x = clamp(this.pos.x, this.size.x/2, levelSize.x - this.size.x/2);
|
|
152
|
+
this.pos.x = LJS.clamp(this.pos.x, this.size.x/2, levelSize.x - this.size.x/2);
|
|
153
153
|
```
|
|
154
154
|
|
|
155
155
|
## Create the Ball
|
|
@@ -158,7 +158,7 @@ The final missing piece is of course that ball that moves around and bounces off
|
|
|
158
158
|
We can create a Ball class the same way we created the paddle. In this case we will also apply some velocity to ball so it starts moving right away.
|
|
159
159
|
|
|
160
160
|
```javascript
|
|
161
|
-
class Ball extends EngineObject
|
|
161
|
+
class Ball extends LJS.EngineObject
|
|
162
162
|
{
|
|
163
163
|
constructor(pos)
|
|
164
164
|
{
|
|
@@ -172,7 +172,10 @@ class Ball extends EngineObject
|
|
|
172
172
|
Then we just need to create the ball in gameInit, passing in the camera position for it’s location.
|
|
173
173
|
|
|
174
174
|
```javascript
|
|
175
|
-
|
|
175
|
+
{
|
|
176
|
+
// create a ball
|
|
177
|
+
ball = new Ball(LJS.cameraPos);
|
|
178
|
+
}
|
|
176
179
|
```
|
|
177
180
|
|
|
178
181
|
## Make the Ball Collide
|
|
@@ -189,10 +192,10 @@ Now the ball collides but unfortunately also pushes the paddle away, not quite w
|
|
|
189
192
|
this.mass = 0; // make object have static physics
|
|
190
193
|
```
|
|
191
194
|
|
|
192
|
-
Getting closer, the paddle is not pushed away but the ball doesn’t bounce either. To make it bounce, we need to set the ball’s
|
|
195
|
+
Getting closer, the paddle is not pushed away but the ball doesn’t bounce either. To make it bounce, we need to set the ball’s restitution which controls how much it will bounce.
|
|
193
196
|
|
|
194
197
|
```javascript
|
|
195
|
-
this.
|
|
198
|
+
this.restitution = 1; // make object bounce
|
|
196
199
|
```
|
|
197
200
|
|
|
198
201
|

|
|
@@ -210,7 +213,7 @@ super(pos, vec2(.5)); // set object position and size
|
|
|
210
213
|
Next, let’s make the ball bounce when it hits the top or sides of the screen. To do this, we will make a Wall object class that works similar to the paddle but without an update function.
|
|
211
214
|
|
|
212
215
|
```javascript
|
|
213
|
-
class Wall extends EngineObject
|
|
216
|
+
class Wall extends LJS.EngineObject
|
|
214
217
|
{
|
|
215
218
|
constructor(pos, size)
|
|
216
219
|
{
|
|
@@ -236,7 +239,7 @@ new Wall(vec2(levelSize.x/2,levelSize.y+.5), vec2(100,1)) // top
|
|
|
236
239
|
Now we have white walls around the outside that block the ball. These walls should be invisible so we will set their color to be transparent by adding another line to the Wall constructor.
|
|
237
240
|
|
|
238
241
|
```javascript
|
|
239
|
-
this.color =
|
|
242
|
+
this.color = rgb(0,0,0,0); // make object invisible
|
|
240
243
|
```
|
|
241
244
|
|
|
242
245
|
## Debug Display
|
|
@@ -268,7 +271,7 @@ if (!ball || ball.pos.y < -1)
|
|
|
268
271
|
ball.destroy();
|
|
269
272
|
|
|
270
273
|
// create a ball
|
|
271
|
-
ball = new Ball(cameraPos);
|
|
274
|
+
ball = new Ball(LJS.cameraPos);
|
|
272
275
|
}
|
|
273
276
|
```
|
|
274
277
|
|
|
@@ -279,7 +282,7 @@ Don’t forget to destroy the old ball before creating the new one! Though it do
|
|
|
279
282
|
Still, the ball doesn’t collide with the bricks much less breaks them and it wouldn’t be breakout without that. So we need to make the brick collide with the ball, just like the walls, so let’s start with Brick class that is the same as Wall.
|
|
280
283
|
|
|
281
284
|
```javascript
|
|
282
|
-
class Brick extends EngineObject
|
|
285
|
+
class Brick extends LJS.EngineObject
|
|
283
286
|
{
|
|
284
287
|
constructor(pos, size)
|
|
285
288
|
{
|
|
@@ -333,7 +336,7 @@ Then increment it in the Brick.collideWithObject function.
|
|
|
333
336
|
And change that drawTextScreen code we had commented out in gameRenderPost to display the score. We can adjust the position and size of the text to fit at the top of the screen.
|
|
334
337
|
|
|
335
338
|
```javascript
|
|
336
|
-
drawTextScreen("Score " + score, vec2(mainCanvasSize.x/2, 70), 50); // show score
|
|
339
|
+
LJS.drawTextScreen("Score " + score, vec2(LJS.mainCanvasSize.x/2, 70), 50); // show score
|
|
337
340
|
```
|
|
338
341
|
|
|
339
342
|

|
|
@@ -347,7 +350,7 @@ We will start with the ball bounce sound. [You can use the official ZzFX sound d
|
|
|
347
350
|
To make a sound effect with this system we will create a global object for each sound.
|
|
348
351
|
|
|
349
352
|
```javascript
|
|
350
|
-
const sound_bounce = new Sound([,,1e3,,.03,.02,1,2,,,940,.03,,,,,.2,.6,,.06], 0);
|
|
353
|
+
const sound_bounce = new LJS.Sound([,,1e3,,.03,.02,1,2,,,940,.03,,,,,.2,.6,,.06], 0);
|
|
351
354
|
```
|
|
352
355
|
|
|
353
356
|
You can use the sound I chose or copy the code for your own sound from ZzFX. There is a checkbox for LittleJS style sounds to make exporting a little easier.
|
|
@@ -366,7 +369,7 @@ Let’s also make a brick break sound. You can use the ZzFX sound designer again
|
|
|
366
369
|
Create a global object for this sound the same way we did for the bounce sound. You can replace it with your own generated sound or use mine.
|
|
367
370
|
|
|
368
371
|
```javascript
|
|
369
|
-
const sound_break = new Sound([,,90,,.01,.03,4,,,,,,,9,50,.2,,.2,.01], 0);
|
|
372
|
+
const sound_break = new LJS.Sound([,,90,,.01,.03,4,,,,,,,9,50,.2,,.2,.01], 0);
|
|
370
373
|
```
|
|
371
374
|
|
|
372
375
|
Then in the Brick’s collideWithObject function just add some code to play that sound.
|
|
@@ -384,7 +387,7 @@ Let’s improve the gameplay by letting the player click to start, instead of cr
|
|
|
384
387
|
We can also add a special sound that plays on startup. Try making an interesting start from the ZzFX sound designer, maybe using the Powerup preset.
|
|
385
388
|
|
|
386
389
|
```javascript
|
|
387
|
-
const sound_start = new Sound([,0,500,,.04,.3,1,2,,,570,.02,.02,,,,.04]);
|
|
390
|
+
const sound_start = new LJS.Sound([,0,500,,.04,.3,1,2,,,570,.02,.02,,,,.04]);
|
|
388
391
|
```
|
|
389
392
|
|
|
390
393
|
We can modify the gameUpdate to wait for player input from the mouse and play the sound when the ball is created.
|
|
@@ -396,9 +399,9 @@ if (ball && ball.pos.y < -1) // if ball is below level
|
|
|
396
399
|
ball.destroy();
|
|
397
400
|
ball = 0;
|
|
398
401
|
}
|
|
399
|
-
if (!ball && mouseWasPressed(0)) // if there is no ball and left mouse is pressed
|
|
402
|
+
if (!ball && LJS.mouseWasPressed(0)) // if there is no ball and left mouse is pressed
|
|
400
403
|
{
|
|
401
|
-
ball = new Ball(cameraPos); // create the ball
|
|
404
|
+
ball = new Ball(LJS.cameraPos); // create the ball
|
|
402
405
|
sound_start.play(); // play start sound
|
|
403
406
|
}
|
|
404
407
|
```
|
|
@@ -413,7 +416,7 @@ Make sure you replace the first parameter, vec2(), with this.pos so the effect a
|
|
|
413
416
|
|
|
414
417
|
```javascript
|
|
415
418
|
// create explosion effect
|
|
416
|
-
new ParticleEmitter(this.pos, 0, 0, 0.1, 100, 3.14, 0,
|
|
419
|
+
new LJS.ParticleEmitter(this.pos, 0, 0, 0.1, 100, 3.14, 0, rgb(1, 1, 1, 1), rgb(1, 1, 1, 1), rgb(1, 1, 1, 0), rgb(1, 1, 1, 0), 0.5, 0.1, 1, 0.1, 0.05, 1, 1, 0, 3.14, 0.1, 0.2, 0, 0, 1);
|
|
417
420
|
```
|
|
418
421
|
|
|
419
422
|
Now you should see this simple particle effect play wherever a brick breaks. You can continue tweaking the parameters to make your own effect or use the one I made which also uses this.color to change the particle’s color so it matches the brick.
|
|
@@ -421,15 +424,15 @@ Now you should see this simple particle effect play wherever a brick breaks. You
|
|
|
421
424
|
```javascript
|
|
422
425
|
// create explosion effect
|
|
423
426
|
const color = this.color;
|
|
424
|
-
new ParticleEmitter(
|
|
425
|
-
this.pos, 0,
|
|
426
|
-
this.size, .1, 200,
|
|
427
|
-
undefined,
|
|
428
|
-
color, color,
|
|
427
|
+
new LJS.ParticleEmitter(
|
|
428
|
+
this.pos, 0, // pos, angle
|
|
429
|
+
this.size, .1, 200, 3.14,// emitSize, emitTime, emitRate, emitCone
|
|
430
|
+
undefined, // tileInfo
|
|
431
|
+
color, color, // colorStartA, colorStartB
|
|
429
432
|
color.scale(1,0), color.scale(1,0), // colorEndA, colorEndB
|
|
430
433
|
.2, .5, 1, .1, .1, // time, sizeStart, sizeEnd, speed, angleSpeed
|
|
431
|
-
.99, .95, .4,
|
|
432
|
-
.1, .5, false, true //
|
|
434
|
+
.99, .95, .4, 3.14, // damp, angleDamp, gravity, cone
|
|
435
|
+
.1, .5, false, true // fade, randomness, collide, additive
|
|
433
436
|
);
|
|
434
437
|
```
|
|
435
438
|
|
|
@@ -459,7 +462,7 @@ collideWithObject(o)
|
|
|
459
462
|
this.velocity = this.velocity.rotate(.3 * deltaX);
|
|
460
463
|
|
|
461
464
|
// make sure ball is moving upwards with a minimum speed
|
|
462
|
-
this.velocity.y = max(-this.velocity.y, .2);
|
|
465
|
+
this.velocity.y = LJS.max(-this.velocity.y, .2);
|
|
463
466
|
|
|
464
467
|
// prevent default collision code
|
|
465
468
|
return false;
|
|
@@ -52,7 +52,7 @@ class Ball extends LJS.EngineObject
|
|
|
52
52
|
|
|
53
53
|
this.velocity = vec2(-.1, -.1); // give ball some movement
|
|
54
54
|
this.setCollision(); // make object collide
|
|
55
|
-
this.
|
|
55
|
+
this.restitution = 1; // make object bounce
|
|
56
56
|
}
|
|
57
57
|
collideWithObject(o)
|
|
58
58
|
{
|
|
@@ -138,8 +138,8 @@ function gameInit()
|
|
|
138
138
|
LJS.setCanvasFixedSize(vec2(1280, 720)); // use a 720p fixed size canvas
|
|
139
139
|
|
|
140
140
|
// create bricks
|
|
141
|
-
for(let x=2; x<=levelSize.x-2; x+=2)
|
|
142
|
-
for(let y=12; y<=levelSize.y-2; y+=1)
|
|
141
|
+
for (let x=2; x<=levelSize.x-2; x+=2)
|
|
142
|
+
for (let y=12; y<=levelSize.y-2; y+=1)
|
|
143
143
|
new Brick(vec2(x,y), vec2(2,1)); // create a brick
|
|
144
144
|
|
|
145
145
|
// create player paddle
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* LittleJS Build System
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const PROGRAM_TITLE = 'Little JS Electron Project';
|
|
10
|
+
const PROGRAM_NAME = 'game';
|
|
11
|
+
const BUILD_FOLDER = 'build';
|
|
12
|
+
const USE_ROADROLLER = false; // enable for extra compression
|
|
13
|
+
const sourceFiles =
|
|
14
|
+
[
|
|
15
|
+
'../../dist/littlejs.release.js',
|
|
16
|
+
'game.js',
|
|
17
|
+
// add your game's files here
|
|
18
|
+
];
|
|
19
|
+
const dataFiles =
|
|
20
|
+
[
|
|
21
|
+
'tiles.png',
|
|
22
|
+
// add your game's data files here
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
console.log(`Building ${PROGRAM_NAME}...`);
|
|
26
|
+
const startTime = Date.now();
|
|
27
|
+
const fs = require('node:fs');
|
|
28
|
+
const child_process = require('node:child_process');
|
|
29
|
+
|
|
30
|
+
// rebuild engine
|
|
31
|
+
//child_process.execSync(`npm run build`, { stdio: 'inherit' });
|
|
32
|
+
|
|
33
|
+
// remove old files and setup build folder
|
|
34
|
+
fs.rmSync(BUILD_FOLDER, { recursive: true, force: true });
|
|
35
|
+
fs.rmSync(`${PROGRAM_NAME}.zip`, { force: true });
|
|
36
|
+
fs.mkdirSync(BUILD_FOLDER);
|
|
37
|
+
|
|
38
|
+
// copy data files
|
|
39
|
+
for (const file of dataFiles)
|
|
40
|
+
fs.copyFileSync(file, `${BUILD_FOLDER}/${file}`);
|
|
41
|
+
|
|
42
|
+
Build
|
|
43
|
+
(
|
|
44
|
+
`${BUILD_FOLDER}/index.js`,
|
|
45
|
+
sourceFiles,
|
|
46
|
+
USE_ROADROLLER ?
|
|
47
|
+
[closureCompilerStep, uglifyBuildStep, roadrollerBuildStep, htmlBuildStep, electronBuildStep] :
|
|
48
|
+
[closureCompilerStep, uglifyBuildStep, htmlBuildStep, electronBuildStep]
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
console.log('');
|
|
52
|
+
console.log(`Build Completed in ${((Date.now() - startTime)/1e3).toFixed(2)} seconds!`);
|
|
53
|
+
|
|
54
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
55
|
+
|
|
56
|
+
// A single build with its own source files, build steps, and output file
|
|
57
|
+
// - each build step is a callback that accepts a single filename
|
|
58
|
+
function Build(outputFile, files=[], buildSteps=[])
|
|
59
|
+
{
|
|
60
|
+
// copy files into a buffer
|
|
61
|
+
let buffer = '';
|
|
62
|
+
for (const file of files)
|
|
63
|
+
buffer += fs.readFileSync(file) + '\n';
|
|
64
|
+
|
|
65
|
+
// output file
|
|
66
|
+
fs.writeFileSync(outputFile, buffer, {flag: 'w+'});
|
|
67
|
+
|
|
68
|
+
// execute build steps in order
|
|
69
|
+
for (const buildStep of buildSteps)
|
|
70
|
+
buildStep(outputFile);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function closureCompilerStep(filename)
|
|
74
|
+
{
|
|
75
|
+
console.log('Running closure compiler...');
|
|
76
|
+
|
|
77
|
+
// use closer compiler to minify the code
|
|
78
|
+
const filenameTemp = filename + '.tmp';
|
|
79
|
+
fs.copyFileSync(filename, filenameTemp);
|
|
80
|
+
child_process.execSync(`npx google-closure-compiler --js=${filenameTemp} --js_output_file=${filename} --compilation_level=ADVANCED --warning_level=VERBOSE --jscomp_off=* --assume_function_wrapper`, {stdio: 'inherit'});
|
|
81
|
+
fs.rmSync(filenameTemp);
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
function uglifyBuildStep(filename)
|
|
85
|
+
{
|
|
86
|
+
console.log('Running uglify...');
|
|
87
|
+
child_process.execSync(`npx uglifyjs ${filename} -c -m -o ${filename}`, {stdio: 'inherit'});
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
function roadrollerBuildStep(filename)
|
|
91
|
+
{
|
|
92
|
+
console.log('Running roadroller...');
|
|
93
|
+
child_process.execSync(`npx roadroller ${filename} -o ${filename}`, {stdio: 'inherit'});
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
function htmlBuildStep(filename)
|
|
97
|
+
{
|
|
98
|
+
console.log('Building html...');
|
|
99
|
+
|
|
100
|
+
// create html file
|
|
101
|
+
let buffer = '';
|
|
102
|
+
buffer += '<!DOCTYPE html>';
|
|
103
|
+
buffer += '<head>';
|
|
104
|
+
buffer += `<title>${PROGRAM_TITLE}</title>`;
|
|
105
|
+
buffer += '<meta charset=utf-8>';
|
|
106
|
+
buffer += '</head>';
|
|
107
|
+
buffer += '<body>';
|
|
108
|
+
buffer += '<script src="index.js"></script>';
|
|
109
|
+
|
|
110
|
+
// output html file
|
|
111
|
+
fs.writeFileSync(`${BUILD_FOLDER}/index.html`, buffer, {flag: 'w+'});
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
function electronBuildStep(filename)
|
|
115
|
+
{
|
|
116
|
+
console.log('Building executable with electron...');
|
|
117
|
+
|
|
118
|
+
// copy elecron files to build folder
|
|
119
|
+
fs.copyFileSync('electron.js', `${BUILD_FOLDER}/electron.js`);
|
|
120
|
+
fs.copyFileSync('package.json', `${BUILD_FOLDER}/package.json`);
|
|
121
|
+
|
|
122
|
+
// run electron packager
|
|
123
|
+
child_process.execSync(`npx electron-packager ./${BUILD_FOLDER} --overwrite`, {stdio: 'inherit'});
|
|
124
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
const { app, BrowserWindow } = require('electron');
|
|
2
|
+
|
|
3
|
+
function createWindow()
|
|
4
|
+
{
|
|
5
|
+
// create the browser window
|
|
6
|
+
const mainWindow = new BrowserWindow({width: 800, height: 600});
|
|
7
|
+
|
|
8
|
+
// hide the toolbar menu
|
|
9
|
+
mainWindow.setMenu(null);
|
|
10
|
+
|
|
11
|
+
// load the index.html of the app
|
|
12
|
+
mainWindow.loadFile('index.html');
|
|
13
|
+
|
|
14
|
+
// go fullscreen
|
|
15
|
+
//mainWindow.setFullScreen(true);
|
|
16
|
+
|
|
17
|
+
// open the dev tools debugging
|
|
18
|
+
//mainWindow.webContents.openDevTools()
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// wait until the app is ready and create the window
|
|
22
|
+
app.whenReady().then(createWindow);
|
|
23
|
+
|
|
24
|
+
// quit when all windows are closed (except macOS convention)
|
|
25
|
+
app.on('window-all-closed', ()=>
|
|
26
|
+
{
|
|
27
|
+
if (process.platform != 'darwin')
|
|
28
|
+
app.quit();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
app.on('activate', ()=>
|
|
32
|
+
{
|
|
33
|
+
if (BrowserWindow.getAllWindows().length == 0)
|
|
34
|
+
createWindow();
|
|
35
|
+
});
|