littlejsengine 1.12.4 → 1.13.1

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.
Files changed (88) hide show
  1. package/dist/box2d.wasm.js +630 -0
  2. package/dist/box2d.wasm.wasm +0 -0
  3. package/dist/littlejs.d.ts +437 -219
  4. package/dist/littlejs.esm.js +5950 -5307
  5. package/dist/littlejs.esm.min.js +4 -1
  6. package/dist/littlejs.js +5921 -5295
  7. package/dist/littlejs.min.js +4 -1
  8. package/dist/littlejs.release.js +5485 -4886
  9. package/examples/box2d/game.js +37 -42
  10. package/examples/box2d/gameObjects.js +41 -37
  11. package/examples/box2d/index.html +2 -2
  12. package/examples/box2d/scenes.js +24 -20
  13. package/examples/box2d/tiles.png +0 -0
  14. package/examples/breakout/game.js +1 -1
  15. package/examples/breakout/gameObjects.js +2 -2
  16. package/examples/breakout/index.html +1 -1
  17. package/examples/breakoutTutorial/README.md +2 -2
  18. package/examples/breakoutTutorial/game.js +1 -1
  19. package/examples/breakoutTutorial/index.html +1 -1
  20. package/examples/empty/index.html +1 -1
  21. package/examples/htmlMenu/index.html +1 -1
  22. package/examples/index.html +410 -159
  23. package/examples/module/game.js +1 -1
  24. package/examples/module/index.html +1 -1
  25. package/examples/platformer/game.js +6 -15
  26. package/examples/platformer/gameCharacter.js +6 -6
  27. package/examples/platformer/gameEffects.js +74 -57
  28. package/examples/platformer/gameLevel.js +61 -70
  29. package/examples/platformer/gameObjects.js +4 -4
  30. package/examples/platformer/index.html +1 -1
  31. package/examples/puzzle/index.html +1 -1
  32. package/examples/shorts/base.html +24 -3
  33. package/examples/shorts/box2d.js +46 -0
  34. package/examples/shorts/box2dCar.js +45 -0
  35. package/examples/shorts/flappyGame.js +52 -0
  36. package/examples/shorts/helloWorld.js +6 -3
  37. package/examples/shorts/hillGlideGame.js +56 -0
  38. package/examples/shorts/landerGame.js +32 -0
  39. package/examples/shorts/maze.js +47 -0
  40. package/examples/shorts/medals.js +42 -0
  41. package/examples/shorts/nineSlice.js +21 -0
  42. package/examples/shorts/piano.js +52 -0
  43. package/examples/shorts/platformer.js +24 -20
  44. package/examples/shorts/{pong.js → pongGame.js} +1 -1
  45. package/examples/shorts/postProcess.js +45 -0
  46. package/examples/shorts/raycasting.js +71 -0
  47. package/examples/shorts/slidingPuzzle.js +42 -0
  48. package/examples/shorts/spaceGame.js +56 -0
  49. package/examples/shorts/starfield.js +17 -0
  50. package/examples/shorts/tileLayer.js +3 -5
  51. package/examples/shorts/tiles.png +0 -0
  52. package/examples/shorts/tiltedView.js +8 -7
  53. package/examples/shorts/topDown.js +18 -26
  54. package/examples/shorts/uiSystem.js +36 -0
  55. package/examples/starter/build.bat +6 -1
  56. package/examples/starter/game.js +4 -2
  57. package/examples/starter/index.html +23 -13
  58. package/examples/stress/index.html +2 -3
  59. package/examples/style.css +136 -0
  60. package/examples/typescript/build.js +1 -2
  61. package/examples/typescript/game.js +2 -2
  62. package/examples/typescript/game.ts +1 -1
  63. package/examples/typescript/index.html +1 -1
  64. package/examples/uiSystem/game.js +9 -25
  65. package/examples/uiSystem/index.html +1 -1
  66. package/package.json +1 -1
  67. package/plugins/box2d.js +29 -35
  68. package/plugins/drawUtilities.js +126 -0
  69. package/plugins/newgrounds.js +1 -1
  70. package/plugins/pluginExport.js +8 -2
  71. package/plugins/postProcess.js +2 -2
  72. package/plugins/uiSystem.js +162 -68
  73. package/plugins/zzfxm.js +1 -1
  74. package/reference.md +10 -10
  75. package/src/engine.js +59 -39
  76. package/src/engineAudio.js +38 -20
  77. package/src/engineBuild.bat +6 -1
  78. package/src/engineBuild.js +32 -7
  79. package/src/engineDebug.js +53 -26
  80. package/src/engineDraw.js +108 -57
  81. package/src/engineExport.js +22 -9
  82. package/src/engineInput.js +112 -40
  83. package/src/engineObject.js +68 -67
  84. package/src/engineParticles.js +26 -9
  85. package/src/engineSettings.js +15 -16
  86. package/src/engineTileLayer.js +312 -153
  87. package/src/engineUtilities.js +198 -130
  88. package/src/engineWebGL.js +58 -35
@@ -0,0 +1,45 @@
1
+ async function gameInit()
2
+ {
3
+ // setup box2d and create the objects
4
+ await box2dInit();
5
+ gravity.y = -20;
6
+ groundObject = new Box2dObject(vec2(-8), vec2(), 0, 0, GRAY, box2d.bodyTypeStatic);
7
+ groundObject.addBox(vec2(100,6));
8
+ new CarObject(vec2(0,-2));
9
+ }
10
+
11
+ class CarObject extends Box2dObject
12
+ {
13
+ constructor(pos)
14
+ {
15
+ super(pos, vec2(), 0, 0, RED);
16
+
17
+ // create car with wheels
18
+ this.addBox(vec2(7,2));
19
+ const frequency = 4, maxTorque = 250;
20
+ this.wheels = [];
21
+ for(let i=2; i--;)
22
+ {
23
+ const wheelPos = pos.add(vec2(i?2:-2, -1));
24
+ const wheel = new Box2dObject(wheelPos, vec2(2), tile(7));
25
+ const joint = new Box2dWheelJoint(this, wheel);
26
+ joint.setSpringFrequencyHz(frequency);
27
+ joint.setMaxMotorTorque(maxTorque);
28
+ joint.enableMotor(!i);
29
+ wheel.addCircle(2, vec2(), 1, 1);
30
+ wheel.motorJoint = joint;
31
+ this.wheels[i] = wheel;
32
+ }
33
+ }
34
+ update()
35
+ {
36
+ super.update();
37
+
38
+ // car controls - use mouse, arrow keys, or A/D to drive
39
+ const maxSpeed = 40;
40
+ const input = keyDirection().x || mouseIsDown(0);
41
+ let s = this.wheels[0].motorJoint.getMotorSpeed();
42
+ s = input ? clamp(s - input, -maxSpeed, maxSpeed) : 0;
43
+ this.wheels[0].motorJoint.setMotorSpeed(s);
44
+ }
45
+ }
@@ -0,0 +1,52 @@
1
+ class Wall extends EngineObject
2
+ {
3
+ constructor(pos, size)
4
+ {
5
+ super(pos, size, 0, 0, hsl(.4,.5,.4));
6
+ this.setCollision(); // make object collide
7
+ }
8
+ update()
9
+ {
10
+ this.pos.x -= .1; // move walls to the left
11
+ }
12
+ }
13
+
14
+ class Player extends EngineObject
15
+ {
16
+ constructor(pos)
17
+ {
18
+ super(pos, vec2(1), tile(9), 0, YELLOW);
19
+ this.drawSize = vec2(2);
20
+ this.setCollision(); // make object collide
21
+ }
22
+
23
+ update()
24
+ {
25
+ super.update();
26
+
27
+ // flappy movement controls
28
+ if (mouseWasPressed(0) || keyWasPressed('Space'))
29
+ this.velocity = vec2(0,.2);
30
+ this.angle = -this.velocity.y*2;
31
+ }
32
+
33
+ collideWithObject(object)
34
+ {
35
+ // reset game
36
+ engineObjectsDestroy();
37
+ gameInit();
38
+ }
39
+ }
40
+
41
+ function gameInit()
42
+ {
43
+ // setup level
44
+ gravity.y = -.01;
45
+ for (let i=100; i--;)
46
+ {
47
+ const h=50, y=-h/2-6+rand(9), spacing=15, gap=5;
48
+ new Wall(vec2(14 + i*spacing,y+h + gap), vec2(3,h));
49
+ new Wall(vec2(14 + i*spacing,y), vec2(3,h));
50
+ }
51
+ new Player(vec2(-7,6));
52
+ }
@@ -1,8 +1,11 @@
1
1
  function gameRender()
2
2
  {
3
- // draw text
4
- drawText('Hello World', vec2(0,2), 3);
3
+ // draw background rect
4
+ drawRect(cameraPos, vec2(30), hsl(0,0,.2));
5
+
6
+ // draw text to the overlay canvas
7
+ drawTextOverlay('Hello World', vec2(0,3), 3);
5
8
 
6
9
  // draw a tile
7
- drawTile(vec2(0,-2), vec2(7), tile(3,128));
10
+ drawTile(vec2(Math.sin(time)*3,-2), vec2(7), tile(3,128));
8
11
  }
@@ -0,0 +1,56 @@
1
+ class Player extends EngineObject
2
+ {
3
+ constructor(pos)
4
+ {
5
+ super(pos, vec2(2), tile(9), 0, CYAN);
6
+ this.damping = 1; // disable damping
7
+ this.clampSpeed = false; // disable speed clamping
8
+ }
9
+
10
+ update()
11
+ {
12
+ super.update();
13
+
14
+ const h = getGroundHeight(this.pos.x);
15
+ if (this.pos.y < h + this.size.y/2)
16
+ {
17
+ // clamp to ground and reflect velocity
18
+ this.pos.y = h + this.size.y/2;
19
+ const n = vec2((h-getGroundHeight(this.pos.x+.1))/.1, 1).normalize();
20
+ this.velocity = this.velocity.reflect(n,0);
21
+ }
22
+
23
+ // apply movement controls
24
+ if (mouseIsDown(0) || keyIsDown('Space'))
25
+ this.applyAcceleration(vec2(0,-.05));
26
+ this.velocity.x = max(this.velocity.x,.4);
27
+ this.angle = this.velocity.angle() - PI/2;
28
+
29
+ // move camera with player
30
+ cameraPos = vec2(this.pos.x+9,5);
31
+ }
32
+ }
33
+
34
+ function getGroundHeight(x)
35
+ {
36
+ return Math.sin(x/4)*2 + Math.sin(x/17);
37
+ }
38
+
39
+ function gameInit()
40
+ {
41
+ gravity.y = -.01;
42
+ new Player(vec2(0,5));
43
+ }
44
+
45
+ function gameRender()
46
+ {
47
+ const h = 100, w = 20;
48
+ const pos = vec2(), size = vec2(.2,h), color = WHITE;
49
+ for (let x=cameraPos.x-w; x<cameraPos.x+w; x+=.1)
50
+ {
51
+ pos.x = x;
52
+ pos.y = getGroundHeight(x)-h/2;
53
+ color.setHSLA(.2+.2*wave(.2,1,x), .5, .5);
54
+ drawRect(pos, size, color);
55
+ }
56
+ }
@@ -0,0 +1,32 @@
1
+ class Player extends EngineObject
2
+ {
3
+ constructor(pos)
4
+ {
5
+ super(pos, vec2(1,2), tile(8), 0, RED);
6
+ this.setCollision(); // make object collide
7
+ }
8
+
9
+ update()
10
+ {
11
+ super.update();
12
+
13
+ // apply movement controls
14
+ this.applyAcceleration(keyDirection().scale(.002));
15
+ }
16
+ }
17
+
18
+ function gameInit()
19
+ {
20
+ // setup level
21
+ gravity.y = -.001;
22
+ new Player(vec2(0,4));
23
+
24
+ // create ground
25
+ for (let i=0, y=0; i<20; ++i)
26
+ {
27
+ y = (y + rand(1,-1)*4) * .5;
28
+ const o = new EngineObject(vec2(i*2-20, y-54), vec2(2, 99), 0, 0, hsl(0,0,.5+i%.29));
29
+ o.setCollision(); // make object collide
30
+ o.mass = 0; // make object have static physics
31
+ }
32
+ }
@@ -0,0 +1,47 @@
1
+ function generateMaze(size)
2
+ {
3
+ const maze = [], w = size.x, h = size.y;
4
+ maze[1 + w] = 1; // start point
5
+ for(let k=w*h*9|0; k--;)
6
+ {
7
+ // get a random position on odd coordinates
8
+ const jx = randInt(w/2-(w%2?0:2)|0)*2 + 1;
9
+ const jy = randInt(h/2-(h%2?1:2)|0)*2 + 1;
10
+ const j = jx + jy*w;
11
+
12
+ // get a random direction
13
+ const d = randSign() * (randBool() ? 1 : w);
14
+
15
+ // check if we are not the same line or column
16
+ if (j%w != (j+d*2)%w && (j/w|0) != ((j+d*2)/w|0))
17
+ continue;
18
+
19
+ // check if pos is open and the next 2 cells are closed
20
+ if (maze[j] && !maze[j+d] && !maze[j+d*2])
21
+ maze[j+d] = maze[j+d*2] = 1;
22
+ }
23
+ return maze;
24
+ }
25
+
26
+ function gameInit()
27
+ {
28
+ // generate maze data
29
+ const mazeSize = vec2(27,15);
30
+ const maze = generateMaze(mazeSize);
31
+
32
+ // create tile layer
33
+ const pos = vec2();
34
+ const tileLayer = new TileCollisionLayer(pos, mazeSize);
35
+ for (pos.x = tileLayer.size.x; pos.x--;)
36
+ for (pos.y = tileLayer.size.y; pos.y--;)
37
+ {
38
+ // check if tile should be solid
39
+ if (maze[pos.x + pos.y*mazeSize.x])
40
+ continue;
41
+
42
+ // set tile data
43
+ tileLayer.setData(pos, new TileLayerData(1));
44
+ }
45
+ tileLayer.redraw(); // redraw tile layer with new data
46
+ cameraPos = mazeSize.scale(.5); // center camera
47
+ }
@@ -0,0 +1,42 @@
1
+ // create example medals
2
+ const medal_openedExample = new Medal(0, 'Opened Example', 'You opened this example!');
3
+ const medal_leftClick = new Medal(1, 'Left Clicked', 'You left clicked!', '🐁');
4
+ const medal_rightClick = new Medal(2, 'Right Clicked', 'You right clicked!', '🐭');
5
+ const medal_spacePressed = new Medal(3, 'Pressed Spacebar', 'You pressed spacebar!', '🚀');
6
+
7
+ // setup medals
8
+ const saveName = 'Medals Example';
9
+ medalsInit(saveName);
10
+
11
+ // clear medals for testing
12
+ medalsForEach(medal=> medal.unlocked = false);
13
+ medal_openedExample.unlock();
14
+
15
+ function gameUpdate()
16
+ {
17
+ if (mouseWasPressed(0))
18
+ medal_leftClick.unlock();
19
+ if (mouseWasPressed(2))
20
+ medal_rightClick.unlock();
21
+ if (keyWasPressed('Space'))
22
+ medal_spacePressed.unlock();
23
+ }
24
+
25
+ function gameRender()
26
+ {
27
+ const size = 80;
28
+ let pos = mainCanvasSize.scale(.5).subtract(vec2(0,40));
29
+ drawTextScreen('Unlocked Medals', pos, size);
30
+
31
+ // show unlocked medals
32
+ let medalsCount = 0;
33
+ medalsForEach(medal=> medal.unlocked && medalsCount++);
34
+ pos = pos.add(vec2((1-medalsCount)*size/2, 100));
35
+ medalsForEach(medal=>
36
+ {
37
+ if (!medal.unlocked)
38
+ return;
39
+ medal.renderIcon(pos, size);
40
+ pos.x += size + 8;
41
+ });
42
+ }
@@ -0,0 +1,21 @@
1
+ function gameRender()
2
+ {
3
+ const nineSliceTile = tile(16);
4
+ const threeSliceTile = tile(19);
5
+
6
+ // draw nine slice with thin border and color
7
+ drawNineSlice(vec2(-7,4), vec2(13, 6), nineSliceTile, hsl(.1,.5,.9), .5);
8
+ drawTextOverlay('Nine Slice\nThin Border', vec2(-7,4), 1, BLACK);
9
+
10
+ // draw nine slice in screen space with thick border
11
+ drawNineSliceScreen(vec2(700,150), vec2(250), nineSliceTile);
12
+ drawTextScreen('Nine Slice\nScreen Space', vec2(700,150), 30, BLACK);
13
+
14
+ // draw three slice with thick border and additive color
15
+ drawThreeSlice(vec2(-7,-4), vec2(9, 7), threeSliceTile, WHITE, 2, hsl(.6,1,.5));
16
+ drawTextOverlay('Three Slice\nThick Border', vec2(-7,-4), 1, BLACK);
17
+
18
+ // draw three slice in screen space with thick border
19
+ drawThreeSliceScreen(vec2(700,400), vec2(400,150), threeSliceTile);
20
+ drawTextScreen('Three Slice\nScreen Space', vec2(700,400), 30, BLACK);
21
+ }
@@ -0,0 +1,52 @@
1
+ const pianoSound = new Sound([,0,220,,9]), keys = [];
2
+
3
+ class PianoKey extends EngineObject
4
+ {
5
+ constructor(pos, size, semitone, isWhite)
6
+ {
7
+ const keySize = 2;
8
+ size = size.scale(keySize);
9
+ pos = pos.scale(keySize).add(vec2(0,4-size.y/2));
10
+ super(pos, size, 0, 0, hsl(0,0,isWhite ? 1 : .3));
11
+ this.drawSize = size.subtract(vec2(.1,0));
12
+ this.semitone = semitone;
13
+ }
14
+ press()
15
+ {
16
+ if (!this.isDown)
17
+ pianoSound.playNote(this.semitone);
18
+ this.isDown = true;
19
+ }
20
+ release()
21
+ {
22
+ if (this.isDown)
23
+ pianoSound.stop(.2);
24
+ this.isDown = false;
25
+ }
26
+ render()
27
+ {
28
+ const color = this.isDown ? RED : this.color;
29
+ drawRect(this.pos, this.drawSize, color);
30
+ }
31
+ }
32
+
33
+ function gameInit()
34
+ {
35
+ // create piano keyboard
36
+ for(let i=15; i--;)
37
+ keys.unshift(new PianoKey(vec2(i-7,0), vec2(1,4), [0,2,4,5,7,9,11][i%7]+(i/7|0)*12, 1));
38
+ for(let i=10; i--;)
39
+ keys.unshift(new PianoKey(vec2([1,2,4,5,6][i%5]+(i/5|0)*7-7.5,0), vec2(1,2), [1,3,6,8,10][i%5]+(i/5|0)*12));
40
+ }
41
+
42
+ function gameUpdate()
43
+ {
44
+ // update state of piano keys
45
+ const pressedKey = keys.find(k=>k.isDown);
46
+ const newPressedKey = mouseIsDown(0) && keys.find(k=>isOverlapping(k.pos, k.size, mousePos));
47
+ if (newPressedKey != pressedKey)
48
+ {
49
+ pressedKey && pressedKey.release();
50
+ newPressedKey && newPressedKey.press();
51
+ }
52
+ }
@@ -1,31 +1,23 @@
1
- class PhysicsObject extends EngineObject
2
- {
3
- constructor(pos, size, color)
4
- {
5
- super(pos, size, 0, 0, color);
6
- this.setCollision(); // make object collide
7
- this.mass = 0; // make object have static physics
8
- }
9
- }
10
-
11
- class Player extends PhysicsObject
1
+ class Player extends EngineObject
12
2
  {
13
3
  constructor(pos)
14
4
  {
15
- super(pos, vec2(2,4), RED);
16
- this.mass = 1; // make object have dynamic physics
5
+ super(pos, vec2(2,4), 0, 0, RED);
6
+ this.setCollision(); // make object collide
17
7
  }
18
8
 
19
9
  update()
20
10
  {
11
+ super.update();
12
+
21
13
  // apply movement controls
22
14
  const moveInput = keyDirection();
23
15
  this.velocity.x += moveInput.x * (this.groundObject ? .1: .01);
24
16
  if (this.groundObject && moveInput.y > 0)
25
17
  this.velocity.y = .9; // jump
26
18
 
27
- super.update(); // call parent update function
28
- cameraPos = vec2(this.pos.x, 9); // move camera with player
19
+ // move camera with player
20
+ cameraPos = vec2(this.pos.x, 9);
29
21
  }
30
22
  }
31
23
 
@@ -33,10 +25,22 @@ function gameInit()
33
25
  {
34
26
  // setup level
35
27
  gravity.y = -.05;
36
- new Player(vec2(0,4));
28
+ new Player(vec2(5,6));
37
29
 
38
- // create random objects
39
- new PhysicsObject(vec2(), vec2(1e3,4), GRAY); // ground
40
- for (let i = 1; i < 500; ++i)
41
- new PhysicsObject(vec2(i*10+randInt(4), 0), vec2(2+randInt(20),4+randInt(8)), GREEN);
30
+ // create tile layer
31
+ const pos = vec2();
32
+ const tileLayer = new TileCollisionLayer(pos, vec2(256));
33
+ for (pos.x = tileLayer.size.x; pos.x--;)
34
+ for (pos.y = tileLayer.size.y; pos.y--;)
35
+ {
36
+ // check if tile should be solid
37
+ const levelHeight = pos.x<9 ? 2 : (pos.x/4|0)**3.1%7;
38
+ if (pos.y > levelHeight)
39
+ continue;
40
+
41
+ // set tile data
42
+ tileLayer.setData(pos, new TileLayerData(1));
43
+ tileLayer.setCollisionData(pos);
44
+ }
45
+ tileLayer.redraw(); // redraw tile layer with new data
42
46
  }
@@ -32,7 +32,7 @@ function gameUpdate()
32
32
  ball && ball.destroy(); // destroy old ball
33
33
  ball = new PhysicsObject(cameraPos); // create a ball
34
34
  ball.velocity = vec2(.2); // give ball some movement
35
- ball.elasticity = 1; // make ball bounce
35
+ ball.restitution = 1; // make ball bounce
36
36
  ball.mass = 1; // make ball have dynamic physics
37
37
  }
38
38
 
@@ -0,0 +1,45 @@
1
+ function gameInit()
2
+ {
3
+ new PostProcessPlugin(tvShader);
4
+ }
5
+
6
+ function gameRender()
7
+ {
8
+ drawRect(vec2(), vec2(99), GRAY);
9
+ drawTile(vec2(), vec2(12), tile(3,128));
10
+ }
11
+
12
+ const tvShader = `
13
+ // Simple TV Shader Code
14
+ float hash(vec2 p)
15
+ {
16
+ p=fract(p*.3197);
17
+ return fract(1.+sin(51.*p.x+73.*p.y)*13753.3);
18
+ }
19
+ float noise(vec2 p)
20
+ {
21
+ vec2 i=floor(p),f=fract(p),u=f*f*(3.-2.*f);
22
+ return mix(mix(hash(i),hash(i+vec2(1,0)),u.x),mix(hash(i+vec2(0,1)),hash(i+1.),u.x),u.y);
23
+ }
24
+ void mainImage(out vec4 c, vec2 p)
25
+ {
26
+ // setup the shader
27
+ p /= iResolution.xy;
28
+ c = texture(iChannel0, p);
29
+
30
+ // static noise
31
+ const float staticAlpha = .1;
32
+ const float staticScale = .005;
33
+ c += staticAlpha * hash(floor(p/staticScale) + mod(iTime*500., 1e3));
34
+
35
+ // scan lines
36
+ const float scanlineScale = 2.;
37
+ const float scanlineAlpha = .3;
38
+ c *= 1. - scanlineAlpha*cos(p.y*2.*iResolution.y/scanlineScale);
39
+
40
+ // black vignette around edges
41
+ const float vignette = 2.;
42
+ const float vignettePow = 6.;
43
+ float dx = 2.*p.x-1., dy = 2.*p.y-1.;
44
+ c *= 1.-pow((dx*dx + dy*dy)/vignette, vignettePow);
45
+ }`;
@@ -0,0 +1,71 @@
1
+ let playerPos = vec2(), playerAngle = 0;
2
+
3
+ // a simple function to generate a level
4
+ const levelTest = (X,Y)=> ((X|0)**3&(Y|0)**2)%30>5;
5
+
6
+ // cast a ray and return the distance to the first wall hit (0-1)
7
+ function raycast(pos, angle)
8
+ {
9
+ const maxDistance = 40, delta = .05;
10
+ const rayX = delta*Math.sin(angle);
11
+ const rayY = delta*Math.cos(angle);
12
+ let posX = pos.x, posY = pos.y;
13
+ for (let distance = 0; distance < maxDistance; distance += delta)
14
+ {
15
+ if (levelTest(posX, posY))
16
+ return distance/maxDistance;
17
+ posX += rayX;
18
+ posY += rayY;
19
+ }
20
+ return 1;
21
+ }
22
+
23
+ function gameUpdate()
24
+ {
25
+ // update camera angle with mouse pointer lock
26
+ if (mouseWasPressed(0))
27
+ pointerLockRequest();
28
+ if (keyWasPressed('Escape'))
29
+ pointerLockExit()
30
+ if (pointerLockIsActive() || isTouchDevice)
31
+ playerAngle += mouseDelta.x * .003;
32
+
33
+ // update player movement, prevent walking through walls
34
+ const velocity = keyDirection().scale(.1).rotate(playerAngle);
35
+ const newPos = playerPos.add(velocity);
36
+ if (!levelTest(newPos.x, newPos.y))
37
+ playerPos = newPos;
38
+ }
39
+
40
+ function gameRender()
41
+ {
42
+ {
43
+ // draw horizontal slizes to create floor and ceiling
44
+ const h = 9;
45
+ let pos = vec2(), size = vec2(39, .11), color = WHITE;
46
+ for (let y=-h; y<h; y+=.1)
47
+ {
48
+ const p = 1.01-abs(y/h)
49
+ pos.y = y;
50
+ color.setHSLA(.1, y>0?0:.5, .7-p*.7);
51
+ drawRect(pos, size, color);
52
+ }
53
+ }
54
+ {
55
+ // draw vertical slices to create the walls
56
+ const w = 20;
57
+ const pos = vec2(), size = vec2(.11), color = WHITE;
58
+ for (let x=-w; x<w; x+=.1)
59
+ {
60
+ const p = raycast(playerPos, playerAngle + x/w);
61
+ pos.x = x;
62
+ size.y = .4/p;
63
+ color.setHSLA(.6, .5, .7-p*.7);
64
+ drawRect(pos, size, color);
65
+ }
66
+ }
67
+ // draw instructions and pointer lock status
68
+ const instructions = pointerLockIsActive() ? 'Mouse Control Active - ESC To Exit' : 'Click To Enable Mouse Control';
69
+ if (!isTouchDevice)
70
+ drawTextScreen(instructions, vec2(mainCanvasSize.x/2, 50), 24, pointerLockIsActive() ? GREEN : WHITE);
71
+ }
@@ -0,0 +1,42 @@
1
+ const gridSize = vec2(4,3);
2
+ const pieceSize = vec2(5,4);
3
+ let emptyGridPos = vec2();
4
+
5
+ class PuzzlePiece extends EngineObject
6
+ {
7
+ constructor(gridPos, size)
8
+ {
9
+ const color = rgb(gridPos.x/(gridSize.x-1), gridPos.y/(gridSize.y-1), 1);
10
+ super(gridPos.multiply(size), size, 0, 0, color);
11
+ this.gridPos = gridPos;
12
+ this.text = gridPos.x + gridPos.y*gridSize.x;
13
+ }
14
+
15
+ update()
16
+ {
17
+ if (mouseWasPressed(0) && isOverlapping(this.pos, this.size, mousePos))
18
+ if (abs(this.gridPos.x-emptyGridPos.x) + abs(this.gridPos.y-emptyGridPos.y) == 1)
19
+ {
20
+ // swap with empty space when clicked on
21
+ this.pos = emptyGridPos.multiply(this.size);
22
+ [emptyGridPos, this.gridPos] = [this.gridPos, emptyGridPos];
23
+ }
24
+ }
25
+
26
+ render()
27
+ {
28
+ super.render();
29
+ drawTextOverlay(this.text, this.pos, 2.5, BLACK);
30
+ }
31
+ }
32
+
33
+ function gameInit()
34
+ {
35
+ // create puzzle pieces
36
+ for(let x=gridSize.x; x--;)
37
+ for(let y=gridSize.y; y--;)
38
+ (x||y) && new PuzzlePiece(vec2(x,y), pieceSize);
39
+
40
+ // center camera on grid
41
+ cameraPos = gridSize.subtract(vec2(1)).multiply(pieceSize).scale(.5);
42
+ }
@@ -0,0 +1,56 @@
1
+ class Player extends EngineObject
2
+ {
3
+ constructor(pos)
4
+ {
5
+ super(pos, vec2(1,2), tile(8), 0, CYAN);
6
+ this.damping = .97;
7
+ this.angleDamping = .95;
8
+ this.shootTimer = new Timer;
9
+ }
10
+
11
+ update()
12
+ {
13
+ super.update();
14
+
15
+ // space ship controls
16
+ const moveInput = keyDirection();
17
+ this.applyAngularAcceleration(moveInput.x * .005);
18
+ this.applyAcceleration(vec2().setAngle(this.angle, moveInput.y*.02));
19
+ if (!this.shootTimer.active() && (keyIsDown('Space') || mouseIsDown(0)))
20
+ {
21
+ // shoot bullet
22
+ const pos = this.pos.add(vec2(0,1).rotate(cameraAngle));
23
+ const bullet = new EngineObject(pos, vec2(.2,.5), 0, this.angle, YELLOW);
24
+ bullet.velocity = this.velocity.add(vec2(0,.5).rotate(this.angle));
25
+ this.shootTimer.set(.1);
26
+ }
27
+
28
+ // move camera with player
29
+ cameraPos = this.pos;
30
+ cameraAngle = this.angle;
31
+ }
32
+ }
33
+
34
+ function gameInit()
35
+ {
36
+ new Player(vec2(0,4));
37
+ }
38
+
39
+ function gameRender()
40
+ {
41
+ // draw wrapped starfield with parallax
42
+ const range = 32, halfRange = range/2;
43
+
44
+ // precreate variables to avoid overhead
45
+ const pos = vec2(), size = vec2(), color = WHITE;
46
+ for (let i=1e3; i--;)
47
+ {
48
+ // use math to generate random star positions
49
+ const parallax = i%.13-1;
50
+ pos.x = mod(i**2.1 + cameraPos.x*parallax, range) + cameraPos.x - halfRange;
51
+ pos.y = mod(i**3.1 + cameraPos.y*parallax, range) + cameraPos.y - halfRange;
52
+ size.x = size.y = i%.07+.03;
53
+ color.a = .1+i%.9;
54
+ drawRect(pos, size, color);
55
+ }
56
+ }
@@ -0,0 +1,17 @@
1
+ function gameRender()
2
+ {
3
+ setBlendMode(1);
4
+ // precreate variables to avoid overhead
5
+ const pos = vec2(), size = vec2(), color = WHITE;
6
+ for(let i=2e3; i--;)
7
+ {
8
+ // use math to generate random star positions
9
+ const offset = time*(9+i**2.1%15) + i**2.3;
10
+ pos.x = offset%70-35;
11
+ pos.y = i/120-8;
12
+ size.x = size.y = i%.11+.07;
13
+ color.setHSLA(0, 0, Math.sin(i)**4);
14
+ drawRect(pos, size, color);
15
+ }
16
+ setBlendMode(0);
17
+ }