littlejsengine 1.25.0 → 1.26.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 (51) hide show
  1. package/CHANGELOG.md +99 -0
  2. package/FAQ.md +49 -11
  3. package/README.md +6 -2
  4. package/REFERENCE.md +129 -21
  5. package/dist/littlejs.d.ts +487 -102
  6. package/dist/littlejs.esm.js +2898 -719
  7. package/dist/littlejs.esm.min.js +3 -3
  8. package/dist/littlejs.js +2888 -716
  9. package/dist/littlejs.min.js +2 -2
  10. package/dist/littlejs.release.js +2702 -527
  11. package/package.json +4 -4
  12. package/plugins/box2d.js +110 -34
  13. package/plugins/gltf.js +74 -22
  14. package/plugins/lightSystem.js +410 -28
  15. package/plugins/math3d.js +4 -2
  16. package/plugins/medalSystem.js +19 -7
  17. package/plugins/newgrounds.js +4 -2
  18. package/plugins/parallax.js +1 -0
  19. package/plugins/particleEffects.js +44 -10
  20. package/plugins/pathFinder.js +9 -11
  21. package/plugins/pluginExport.js +9 -0
  22. package/plugins/postProcess.js +9 -3
  23. package/plugins/render3d.js +499 -2389
  24. package/plugins/render3dDebug.js +5 -8
  25. package/plugins/render3dEditor.js +81 -6
  26. package/plugins/render3dExtras.js +103 -31
  27. package/plugins/render3dLevel.js +53 -5
  28. package/plugins/render3dMesh.js +1158 -0
  29. package/plugins/render3dObject.js +1451 -0
  30. package/plugins/render3dVoxels.js +15 -3
  31. package/plugins/textureSheet.js +9 -8
  32. package/plugins/threejs.js +1 -1
  33. package/plugins/tweenSystem.js +76 -51
  34. package/plugins/uiSystem.js +37 -8
  35. package/plugins/wavedash.js +229 -0
  36. package/src/engine.js +199 -40
  37. package/src/engineAudio.js +37 -21
  38. package/src/engineBuild.mjs +6 -1
  39. package/src/engineDebug.js +22 -162
  40. package/src/engineDraw.js +142 -34
  41. package/src/engineEditor.js +79 -18
  42. package/src/engineExport.js +1 -3
  43. package/src/engineInput.js +34 -24
  44. package/src/engineMath.js +35 -14
  45. package/src/engineObject.js +60 -41
  46. package/src/engineParticles.js +58 -11
  47. package/src/engineRelease.js +1 -5
  48. package/src/engineSettings.js +48 -9
  49. package/src/engineTileLayer.js +11 -4
  50. package/src/engineUtilities.js +41 -9
  51. package/src/engineWebGL.js +37 -13
package/REFERENCE.md CHANGED
@@ -25,8 +25,9 @@ engineName // Name of the engine: 'LittleJS'
25
25
  engineVersion // Version of the engine
26
26
  frameRate // Fixed frame rate for updates (60)
27
27
  frame // Current update frame
28
- time // Game time since start in seconds (stops when paused)
28
+ time // Game time since start in seconds (stops when paused, falls behind the clock below 20 FPS)
29
29
  timeReal // Real time since start in seconds (keeps running when paused; the debug speed keys scale it)
30
+ averageFPS // Frames drawn per second, smoothed
30
31
  timeDelta // Seconds the update covers: 1/60, or the display frame's time with engineVariableStep
31
32
  timeScale = 1 // Game speed, more or fewer fixed updates per second; with engineVariableStep it scales timeDelta
32
33
  paused // Is the game paused? (set with setPaused)
@@ -94,7 +95,11 @@ let remaining = 36000;
94
95
  gravity, at the fixed 60 updates a second
95
96
  - **Seconds:** `Timer`, `Tween`, `time`, a particle emitter's `emitTime` and `particleTime` and its `emitRate` (per
96
97
  second) and `lifeTime`; a particle's `fadeRate` is a part of its life, not seconds
98
+ - **Materials:** `roughness`, `reflectivity` and `specular` run 0 to 1, `shininess` 1 up, about 10,000 a mirror (a roughness of 0 sets it far higher), and
99
+ `emissive` from 0 up
97
100
  - **Milliseconds:** `gamepadVibrate`'s duration, `vibrate` patterns and `saveDataURL`'s `revokeTime`
101
+ - **Limits:** the GPU draws with 32-bit floats: keep the world within about ±10,000 units of the origin, and a
102
+ shader's `iTime` gets coarse in a game left running for days; the FAQ says more
98
103
 
99
104
  ## What functions give back
100
105
  - **Raycasts:** where the ray hits, a Vector2: `tileCollisionRaycast`, `lineTest`; how far along the ray, a number:
@@ -321,8 +326,13 @@ toggleFullscreen()
321
326
  // Shader Object - a custom fragment shader for objects and draws, 2D or 3D
322
327
  new Shader(fragmentCode) // fragmentCode defines void mainImage(out vec4 c, vec2 uv) in the post processing
323
328
  // style; it gives the surface color, then the object's color and additive color
324
- // apply in 2D, and the lighting, shadows and fog in 3D
329
+ // apply in 2D, and the lighting, shadows and fog in 3D; in 3D it may also define
330
+ // void mainNormal(inout vec3 n), the world normal after the normal map, to bend
331
+ // it per pixel (waves, ripples) for the lighting, specular and reflection
325
332
  obj.shader = shader // any EngineObject or EngineObject3D; draws that share a Shader share a batch
333
+ shader.dispose() // frees its programs and takes it off the engine's list, which keeps every Shader
334
+ // for a lost context; for one made each time a scene starts; drawn again it
335
+ // compiles again, so let go of only one nothing draws with
326
336
  // names in the snippet: iChannel0 the texture, iTime, iResolution, localUV 0 to 1 across the sprite or the mesh uv,
327
337
  // premultipliedTexture true for a render target or a smooth image (tilesPixelated false), whose rgb and alpha
328
338
  // change together;
@@ -447,7 +457,9 @@ glDraw(x, y, sizeX, sizeY, angle=0, uv0X, uv0Y, uv1X, uv1Y, rgba=-1, rgbaAdditiv
447
457
  ```javascript
448
458
  // Sound Object
449
459
  Sound(zzfxSound, randomness, range, taper, onloadCallback) // Create a zzfx sound
450
- Sound(filename, randomness, range, taper, onloadCallback) // Load a wave, mp3, or ogg
460
+ zzfx(...zzfxSound) // Generate and play a zzfx sound at once, no Sound kept
461
+ Sound(filename, randomness, range, taper, onloadCallback) // Load a wave, mp3, or ogg; onloadCallback(sound) is
462
+ // called once it loads or fails to, isLoaded says which
451
463
  Sound.play(pos, volume=1, pitch=1, randomnessScale=1, loop=false, paused=false) // Play a sound, returns SoundInstance
452
464
  Sound.playLoop(pos, volume=1, pitch=1, randomnessScale=1, paused=false) // Play on a loop, like play with loop on
453
465
  Sound.playMusic(volume=1, loop=true, paused=false) // Play as music with looping
@@ -480,7 +492,8 @@ speakStop() // Stop all queued speech
480
492
  getNoteFrequency(semitoneOffset, rootFrequency=220) // Get frequency for musical notes
481
493
 
482
494
  // Audio settings
483
- soundEnable = true // Should sound be enabled?
495
+ soundEnable = true // Should sound be enabled? set it before sounds are made, setSoundEnable(false)
496
+ // before they are, setSoundVolume(0) to mute while the game runs
484
497
  soundIgnoreSilentSwitch = false // play with an iPhone's silent switch on, as media, pausing the player's music;
485
498
  // off, the switch mutes the game; setSoundIgnoreSilentSwitch(true)
486
499
  soundVolume = .3 // Volume scale to apply to all sound
@@ -678,7 +691,7 @@ EngineObject.clampSpeed // Hold each axis of velocity to objectMaxSpeed while
678
691
  EngineObject.parent / children // Set by addChild, a child is placed by its parent and sits out solid collision
679
692
 
680
693
  // Engine Object settings
681
- enablePhysicsSolver = true // Enable collisions, between objects and with tiles?
694
+ enablePhysicsSolver = true // Enable collisions, between objects and with tiles, and in 3D with solids and the level?
682
695
  objectDefaultMass = 1 // Default object mass for collisions
683
696
  objectDefaultDamping = 1 // Fraction of velocity kept each frame (1 keeps all)
684
697
  objectDefaultAngleDamping = 1 // Fraction of angular velocity kept each frame (1 keeps all)
@@ -849,7 +862,8 @@ particleEffectApply(emitter, effect) // set a live 2D emitter to an effect
849
862
  particleEffectApply3D(emitter, effect) // the same for a ParticleEmitter3D, its particles, place and scale kept
850
863
  particleEffectFromEmitter(emitter, name='Effect') // a 2D emitter's settings as an effect, to save or build in 3D
851
864
  particleEffectsAddBehavior(name, update, update3D, min=-2, max=2, value=1, description='') // add a behavior effects
852
- // can name, with 2D and 3D pushes; min, max and value for the designer
865
+ // can name, with 2D and 3D pushes; min and max for the designer and to clamp
866
+ // a strength to, value to start at
853
867
  particleEffectsAdd(effects) // add effects to play by name, one of the same name replaces it
854
868
  particleEffectsGet(name) // an effect's data, any case, to change or build by hand
855
869
  await particleEffectsLoad(url) // load the file the particle designer's Save Library wrote, and add its effects
@@ -1124,6 +1138,20 @@ light.glow = 0 // size across of a soft hazy glow over the light, like
1124
1138
  // over the lit scene after the lightmap, so it shows in the dark, in front of everything
1125
1139
  light.glowFalloff = 1 // how fast the glow fades from its middle, .5 a wide haze, 2 a tight bright core
1126
1140
 
1141
+ // One directional light, a sun: lights the whole scene from one direction, added with the point lights
1142
+ new DirectionalLight(sunDirection=vec2(-1,1), color=WHITE) // toward the sun, as render3D.sunDirection; its color
1143
+ sun.sunDirection // toward the sun, it shines the other way
1144
+ sun.castShadow = true // foreground casters (castShadow) throw long shadows; no lightSystem.shadows needed
1145
+ sun.shadowLength = 20 // world units a long shadow reaches before it has faded out; a caster casts from inside
1146
+ // the shadow map only, (shadowMapScale - 1) / 2 of a view past the screen
1147
+ sun.backgroundDepth = 3 // world units the light gets into a background area from its edges facing it
1148
+ lightSystem.directionalLight // the one there is, or undefined; a second one asserts, destroy the first
1149
+ // (a release build destroys the first itself)
1150
+ lightSystem.directionalTextureSize = 512 // pixels across its work textures, which cover the shadow map's area; a
1151
+ // power of two, as shadowMapSize is, or the shadows shimmer as the view pans
1152
+ obj.castBackgroundShadow = false // a background: dark to the directional light inside, lit at its edges;
1153
+ // a background layer sets castShadow = false and this true
1154
+
1127
1155
  // Per-object lightmap contribution hook (on every EngineObject)
1128
1156
  class LavaTile extends EngineObject {
1129
1157
  renderLight() {
@@ -1154,6 +1182,7 @@ postProcessBloom(threshold=.6, strength=1, size=6, includeMainCanvas=false) // s
1154
1182
  // and past a size of 32 it would take hundreds, so that is as wide as it goes
1155
1183
  postProcessBloomShader(threshold, strength, size) // its shader code, to pass to PostProcessPlugin or build on
1156
1184
  postProcess.setShaderCode(shaderCode) // shade with new code from the next frame, to switch effects while running
1185
+ postProcess.enabled = true // false skips the pass, the frame showing as drawn, to turn an effect off and back on
1157
1186
 
1158
1187
  // Built in effects: each is a piece of shader code with its settings written in, working on c, the pixel's color,
1159
1188
  // and uv, where it is on the screen from 0 to 1; postProcessEffects joins pieces in order into one shader, and your
@@ -1308,6 +1337,7 @@ render3D.camera.follow(target, offset, percent=1) // chase camera: ease toward t
1308
1337
  // is how far it moves each call, so call it every frame, from
1309
1338
  // gameUpdatePost once the target has moved
1310
1339
  render3D.camera.align2D = true // lock to the 2D camera so the z=0 plane matches world space, false by default
1340
+ render3D.camera.align2DZ = 0 // the z of the plane align2D lines up, the camera its distance in front
1311
1341
  render3D.camera.getForward() .getRight() .getUp() // the camera's axes as it is right now; render3D.cameraRight
1312
1342
  // .cameraUp .cameraForward are this frame's, read only
1313
1343
  render3D.viewMatrix .projectionMatrix .viewProjection .shadowMatrix // this frame's, rebuilt by updateMatrices()
@@ -1383,7 +1413,8 @@ render3D.shadowLight = flashlight // a spotlight, a Light3D with a coneAngle
1383
1413
  render3D.shadowRange = 40 // world size the map covers around shadowCenter, smaller is sharper
1384
1414
  // it is a square facing the light, so ~1.5x an area's width covers it
1385
1415
  render3D.shadowCenter = undefined // Vector3 center of the shadowed area, read each frame; undefined follows the camera
1386
- render3D.shadowBias = .003 // raise if lit surfaces get speckled with their own shadow, lower if shadows float away
1416
+ render3D.shadowBias = .003 // raise if lit surfaces get speckled with their own shadow, lower if shadows float away;
1417
+ // a share of the map's depth (2 * shadowRange for the sun), so the gap grows with the range
1387
1418
  // from their casters
1388
1419
  render3D.shadowSoftness = 1 // how far to blur the shadow edge, in shadow map pixels
1389
1420
 
@@ -1394,6 +1425,19 @@ render3D.setSky(topColor, horizonColor, bottomColor, ambient=.5) // dome colors
1394
1425
  // color from below, both times ambient; 0 leaves ambient dark
1395
1426
  render3D.setFog(fogStart, fogEnd, fogColor) // the fog distances and color at once, no color keeps the current one
1396
1427
  render3D.sky = buildSky(topColor, horizonColor, bottomColor, sides, rings) // or set a dome yourself
1428
+ render3D.skyBox = cubeMap // a CubeMap drawn behind everything in place of the dome, undefined by default;
1429
+ // fog fades to fogColor, so set it to the sky box's horizon color
1430
+ render3D.environment = cubeMap // what reflective surfaces reflect, undefined reflects the sky's colors; one
1431
+ // made again and again disposes the one it replaces, its GPU memory
1432
+ makeCubeMap(size, colorOf) // a CubeMap of six size by size faces, colorOf(direction) the Color each way,
1433
+ // a unit Vector3; paint a sky in code, 64 or so is plenty for a blurry one
1434
+ await loadCubeMap([px, nx, py, ny, pz, nz]) // a CubeMap from six square image urls, +x, -x, +y, -y, +z and -z,
1435
+ // a sky box set shown as three.js shows it, each face as its image is drawn
1436
+ cubeMap.size cubeMap.faces cubeMap.dispose() // its face size, its six faces, and freeing its GPU texture
1437
+ const mirror = new CubeMap(128) // no faces: a cube map to draw the scene into
1438
+ mirror.capture(pos3D) // draw the scene around pos3D into it in the next frame's pass, six views of
1439
+ // the whole scene: once for a still scene, every few frames for a moving one
1440
+ mirror.capturePos // where it was last captured from, captured again from there after a lost context
1397
1441
 
1398
1442
  // Draw state, read at each draw; the pass sets it from each object's flags before render3D() and resets it before each
1399
1443
  // callback, so set it inside those, or use the object flags below
@@ -1405,6 +1449,7 @@ render3D.specular = 0 // Phong highlight strength, the shiny spo
1405
1449
  // out; shininess sets the size of the spot
1406
1450
  render3D.shininess = 16 // the highlight's exponent, 4 broad like rubber, 100 sharp like polished metal
1407
1451
  render3D.normalMap render3D.normalScale render3D.reflectivity render3D.emissiveMap render3D.emissiveMapColor
1452
+ render3D.environmentMap // the cube map reflected in place of render3D.environment, from obj.environment
1408
1453
  // the material, set from each object's fields of the same names
1409
1454
  render3D.receiveShadow = true // false keeps the next draws out of the shadow map's darkening
1410
1455
  render3D.shader = undefined // a Shader for the next draws, set from each object's shader; with emissive 1
@@ -1525,13 +1570,20 @@ obj.emissive = 1 // 0 by default; how much it lights itse
1525
1570
  // things, between partly self lit, above 1 brighter for bloom; still casts
1526
1571
  obj.specular = .5 // highlight strength, 0 is none and 1 is full, as render3D.specular; 0 by default
1527
1572
  obj.shininess = 100 // 16 by default; the highlight's exponent, higher is smaller and sharper
1573
+ obj.roughness = .5 // shininess set the way glTF measures it, 0 a mirror to 1 matte: it sets
1574
+ // shininess = 2 / roughness^4 - 2, at least 1, and reads it back, as
1575
+ // .9 from about .9 up, where shininess is 1
1528
1576
  obj.normalMap = textureInfo // bumps and grooves that catch the light, read at the color texture's uvs;
1529
1577
  // green points up the image (OpenGL and glTF), flip the green of a DirectX one;
1530
1578
  // no tangents needed, any mesh with uvs works; normalMapFromHeight makes one
1531
1579
  obj.normalScale = 1 // how strongly it bends the surface, 0 turns it off
1532
- obj.reflectivity = .5 // 0 by default, 1 a mirror of the sky; more at a glancing angle (Fresnel);
1533
- // shows the colors of render3D.sky from setSky or buildSky, or the ambient
1534
- // ones with no sky; not the scene
1580
+ obj.reflectivity = .5 // 0 by default, 1 a mirror; more at a glancing angle (Fresnel); shows
1581
+ // render3D.environment, sharper the higher shininess, as the highlight
1582
+ // is: 10000 a mirror, 1000 polished, 10 a wide blur;
1583
+ // with none, the colors of render3D.sky from setSky or buildSky, or the
1584
+ // ambient ones with no sky
1585
+ obj.environment = mirror // a CubeMap it reflects in place of render3D.environment, as one captured
1586
+ // from its own middle; undefined by default
1535
1587
  obj.emissiveMap = textureInfo // where it glows, added on top of the lit surface so it shows in the dark
1536
1588
  obj.emissiveMapColor = WHITE // multiplies the emissive map
1537
1589
  obj.castShadow = false // true by default, false keeps it out of the shadow map; sprites and cut out
@@ -1752,7 +1804,7 @@ model.parts // one GLTFPart per primitive of every node
1752
1804
  // are the set the texture's texCoord names, moved by KHR_texture_transform;
1753
1805
  // normalMap and normalScale from normalTexture, emissiveMap and
1754
1806
  // emissiveMapColor from emissiveTexture and emissiveFactor, read at the base
1755
- // color texture's uvs
1807
+ // color texture's uvs, and roughness from roughnessFactor, 1 when it has none
1756
1808
  model.mesh, model.textureInfo // everything as one Mesh tinted by its materials, and its texture when every
1757
1809
  // part uses the same one; a model mixing plain and textured parts, or using
1758
1810
  // several textures or unlit parts, draws right through createObject
@@ -1795,6 +1847,7 @@ emitter3D.gravityScale = 0 // a share of render3D.gravity added on top of its ow
1795
1847
  // an emitter with an emitTime destroys itself once its last particle is gone, so a burst is fire and forget
1796
1848
  // untextured particles are soft round dots, textured ones are billboards of the tile
1797
1849
  emitter.collideLevel = false // particles hit the height maps and voxel maps, bounce by restitution, slide by friction
1850
+ // only: not solid objects, as 2D particles hit only tile layers
1798
1851
  emitter.particleCreateCallback / particleUpdateCallback / particleCollideCallback / particleDestroyCallback
1799
1852
  // as in 2D, each given a Particle3D {pos, velocity, age, lifeTime, emitter, destroy()}, one object
1800
1853
  // the emitter reuses, so copy what you keep; the collide callback gets (particle, level, pos) and
@@ -1875,7 +1928,9 @@ material.side = THREE.DoubleSide // mesh.doubleSided = true
1875
1928
  material.emissiveIntensity // obj.emissive
1876
1929
  material.normalMap, normalScale // obj.normalMap, obj.normalScale (a number, three.js takes a Vector2)
1877
1930
  material.shininess (MeshPhongMaterial) // obj.shininess, with obj.specular the strength
1878
- material.envMap, reflectivity // obj.reflectivity, which reflects the sky's colors, not an environment map
1931
+ material.reflectivity // obj.reflectivity, with render3D.environment or obj.environment the map
1932
+ material.metalness // none; a dark color with reflectivity reads as metal
1933
+ material.roughness (MeshStandardMaterial) // obj.roughness, which sets obj.shininess: 0 a mirror, 1 matte
1879
1934
  material.emissiveMap, emissive // obj.emissiveMap, obj.emissiveMapColor
1880
1935
  material.transparent, blending // obj.transparent, obj.additive
1881
1936
  new THREE.ShaderMaterial({fragmentShader}) // obj.shader = new Shader(code), a mainImage snippet the engine wraps;
@@ -1890,7 +1945,12 @@ new THREE.SpotLight(color, i, d, angle, penumbra) // a Light3D with coneAngle =
1890
1945
  // for its castShadow
1891
1946
  light.castShadow, light.shadow.camera // render3D.shadows, shadowRange and shadowCenter
1892
1947
  scene.fog = new THREE.Fog(c, near, far) // render3D.setFog(near, far, c)
1893
- scene.background // render3D.setSky(topColor, horizonColor, bottomColor)
1948
+ scene.background // render3D.setSky(topColor, horizonColor, bottomColor), or
1949
+ // render3D.skyBox for a cube map
1950
+ scene.environment // render3D.environment
1951
+ material.envMap on one mesh // obj.environment
1952
+ new THREE.CubeCamera(near, far, target) // new CubeMap(size), then cubeMap.capture(pos3D) in place of update
1953
+ new THREE.CubeTextureLoader().load(urls) // await loadCubeMap(urls), the same order
1894
1954
  OrbitControls // new CameraControl3D(target, distance)
1895
1955
  PointerLockControls // new FirstPersonCamera3D
1896
1956
  new THREE.Raycaster() // render3D.screenToRay, pick and engineObjectsRaycast3D
@@ -1936,7 +1996,11 @@ level3DVoxelSetup(tileInfo, (map)=> {}) // The sheet a level's block m
1936
1996
  "fog": [25, 70], "fogColor": "#d6ecff", // start and end, an end of 0 for none; the horizon color
1937
1997
  // when the level has a sky and no fogColor
1938
1998
  "shadows": true,
1939
- "lensFlare": true}, // the sun's lens flare, a LensFlare3D made for it
1999
+ "lensFlare": true, // the sun's lens flare, a LensFlare3D made for it
2000
+ "skyBox": ["px.png", "nx.png", "py.png", "ny.png", "pz.png", "nz.png"], // render3D.skyBox, six images
2001
+ "environment": ["px.png", "nx.png", "py.png", "ny.png", "pz.png", "nz.png"]}, // as loadCubeMap takes
2002
+ // them, loaded in the background, the same urls once;
2003
+ // urls are from the page, not from the level's file
1940
2004
  "objects": []}
1941
2005
 
1942
2006
  // A level can hold a map of blocks, made a VoxelMap when it loads: its corner, its size in cells, and its blocks
@@ -1952,7 +2016,8 @@ level3DVoxelSetup(tileInfo, (map)=> {}) // The sheet a level's block m
1952
2016
  // Prefabs - a prefab is a small level, objects about its own origin, placed many times under one name. The level
1953
2017
  // editor saves one as it saves a level, so it is the prefab editor too, and there every instance follows its prefab
1954
2018
  level3DAddPrefab(name, prefab) // Add {objects: [...]} as a type; its objects may be of other prefabs
1955
- await level3DLoadPrefab(name, url) // Fetch a prefab file and add it
2019
+ await level3DLoadPrefab(name, url) // Fetch a prefab file and add it; a prefab used inside it is
2020
+ // named by its type, the game adds that one too
1956
2021
  level3DSpawn(type, pos3D, rotation3D, scale3D, properties) // Make one object of any type from code, a prefab's
1957
2022
  // instance or a plain type; rotation in radians
1958
2023
  prefab = new Prefab3D // what a prefab's type makes, a handle that draws nothing:
@@ -2051,6 +2116,12 @@ obj.syncMesh() // copy the 2D transform to the mesh
2051
2116
  read only, move it with `setPosition`, `setAngle` or `setTransform` and push it with velocities and forces
2052
2117
  - Joints, raycasting, polygon/circle/edge fixtures
2053
2118
  - Angular values are clockwise like `angle`: angular velocity, torque, joint angles and limits, motor speeds
2119
+ - The EngineObject's own physics fields do nothing on a Box2dObject, Box2D moves it: `velocity`, `angleVelocity`,
2120
+ `damping`, `angleDamping`, `mass`, `friction`, `restitution` and `gravityScale`. Use `getLinearVelocity`,
2121
+ `setLinearVelocity`, `setAngularVelocity`, `setLinearDamping`, `setAngularDamping`, `setMass`, the friction and
2122
+ restitution given to its shapes, and `setGravityScale`; a debug build warns once when one is set
2123
+ - A world made again after `engineObjectsDestroy` does not step exactly as a fresh one, as Box2D reuses its ids, so
2124
+ a replay or a lockstep game that needs the same steps every time reloads the page
2054
2125
  - See `examples/box2d/` for a full demo
2055
2126
 
2056
2127
  ```javascript
@@ -2093,8 +2164,8 @@ obj.setAwake(awake=true)
2093
2164
  obj.setFixedRotation(isFixed=true) // Stop the body from rotating
2094
2165
  obj.setBullet(isBullet=true) // Continuous collision for fast bodies, so they don't pass through thin ones
2095
2166
  obj.setSensor(isSensor=true) // The fixtures it has now detect contacts without colliding
2096
- obj.setLinearDamping(damping) // Box2D's damping, 0 is none, larger slows it faster
2097
- obj.setAngularDamping(damping)
2167
+ obj.setLinearDamping(damping) // Box2D's damping, a rate: 0 is none, larger slows it faster, no upper limit
2168
+ obj.setAngularDamping(damping) // the same for its turning, where an EngineObject's damping is 0 to 1
2098
2169
  obj.setGravityScale(scale=1)
2099
2170
  obj.setMassData(localCenter, mass, momentOfInertia) // undefined leaves that one as it is, inertia is about the
2100
2171
  // center of mass
@@ -2205,6 +2276,46 @@ newgrounds.resendUnlocks() // advanced: send the ones whose request di
2205
2276
  // the minute's session check does; one the server refused is not resent
2206
2277
  ```
2207
2278
 
2279
+ ## LittleJS Wavedash
2280
+ - Optional plugin for Wavedash achievements and leaderboards, the same shape as the Newgrounds one
2281
+ - Wavedash serves the game's page and puts its SDK in `window.Wavedash`, so nothing is bundled; off Wavedash every call
2282
+ does nothing and medals unlock as any do
2283
+ - Make the plugin at the end of `gameInit`: it calls `Wavedash.init()`, until which Wavedash keeps its loading screen
2284
+ - Achievements are made with the Wavedash CLI, `--description` required; numbering the identifiers (`ACH_01_...`)
2285
+ orders them, since Wavedash lists them by identifier
2286
+ - A game built with its own minifier keeps the SDK's names (`Wavedash`, `init`, `success`, `data`, `id` and the
2287
+ methods) and real booleans: the SDK throws on a renamed call or a 1 for true (Terser's booleans_as_integers); the
2288
+ engine's own builds keep both
2289
+
2290
+ ```javascript
2291
+ // the medals, each with the identifier of its achievement on Wavedash
2292
+ const medal_finish = new WavedashMedal(0, 'ACH_01_FINISH', 'Finish', 'Finish a level');
2293
+ function gameInit()
2294
+ {
2295
+ medalsInit('My Game');
2296
+ new WavedashPlugin({LEVEL_1: {lowerWins: true, display: 'milliseconds'}, HIGH_SCORE: {}}); // last, it calls init
2297
+ }
2298
+ // later, in game code
2299
+ medal_finish.unlock(); // Wavedash's toast on Wavedash, the engine's popup anywhere else
2300
+ wavedash.postScore('LEVEL_1', timeMs);
2301
+
2302
+ new WavedashMedal(id, achievement, name, description, icon, src) // a Medal that is also a Wavedash achievement; its
2303
+ // unlock is sent until Wavedash takes it, which it refuses until it has loaded
2304
+ // the player's, a moment after launch; saved unlocks are sent too; one refused
2305
+ // for a minute warns in the console with its identifier, a typo or not made
2306
+ medal.achievement // the identifier of its Wavedash achievement
2307
+ new WavedashPlugin(leaderboards) // sets the wavedash global, calls Wavedash.init and makes the leaderboards: by
2308
+ // name, lowerWins for times and golf (higher wins when left out) and display
2309
+ // 'number' (the default), 'seconds', 'milliseconds' or 'ticks' (60 a second)
2310
+ wavedash.isActive() // whether the game is on Wavedash, its SDK on the page
2311
+ await wavedash.postScore(name, score) // a whole number, milliseconds for a time, Wavedash keeping the best; true once
2312
+ // posted, false off Wavedash or when it failed or took over 15 seconds; a board
2313
+ // not in the table is made then, higher wins
2314
+ await wavedash.getScores(name, offset=0, limit=10, friendsOnly=false) // the entries as Wavedash gives them,
2315
+ // undefined off Wavedash or when they could not be read
2316
+ wavedash.leaderboards // each board's id promise by its name
2317
+ ```
2318
+
2208
2319
  ## LittleJS Drawing Utilities
2209
2320
  - Optional plugin: nine-slice and three-slice helpers for scalable UI panels, plus a crescent shape
2210
2321
  - World-space (WebGL or 2D) and screen-space (2D by default, useWebGL for WebGL) variants
@@ -2339,7 +2450,7 @@ setLevelEditor(new MyEditor) // Make it the one in use, before the editor open
2339
2450
  - Press Escape key to toggle debug overlay
2340
2451
  - Number keys toggle debug functions while the overlay is open: 1 physics, 2 tiles (each tile layer's bounds, the
2341
2452
  collision values on screen, and the tiles under the mouse; pressing 2 again steps through the layers one at a time,
2342
- then off), 3 particles, 4 raycasts, 5 gamepads, 6 sound, 7 screenshot, 8 video capture, 9 tweakables panel,
2453
+ then off), 3 particles, 4 raycasts, 5 gamepads, 6 sound, 7 screenshot, 9 tweakables panel,
2343
2454
  0 level editor
2344
2455
  - C, while the overlay is open, is a free camera for a 3D game: the mouse looks once captured or with the right
2345
2456
  button held, WASD and QE fly, Shift is faster and the wheel sets the speed; the game runs on and reads no keys or
@@ -2364,9 +2475,6 @@ debugOverlap(posA, sizeA, posB, sizeB, color) // Draw a debug overlap between tw
2364
2475
  debugClear() // Clear all debug primitives
2365
2476
  debugScreenshot() // Save a screenshot at the end of this frame
2366
2477
  debugShowErrors() // Show full page error message when an error occurs
2367
- debugVideoCaptureStart() // Start capturing a video of the canvas
2368
- debugVideoCaptureStop() // Stop capturing and save the video to disk
2369
- debugVideoCaptureIsActive() // Is video currently being captured?
2370
2478
  createCanvasContext(width, height=width, willReadFrequently=false) // Offscreen canvas to draw into, returns its
2371
2479
  // 2D context; the canvas is context.canvas
2372
2480
  saveCanvas(canvas, filename='screenshot', type='image/png') // Save canvas to a file