@lampdotdev/lamp 0.0.0-stage → 0.35.0
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/LICENSE +21 -0
- package/README.md +357 -2
- package/Resources/Devices/roku.json +410 -0
- package/Resources/Devices/xbox-one.json +367 -0
- package/Resources/GameKit/NOTICE.md +63 -0
- package/Resources/GameKit/apple/LampPage.swift +1646 -0
- package/Resources/GameKit/apple/LampPlayer.swift +46 -0
- package/Resources/GameKit/apple/LampWebApp.swift +63 -0
- package/Resources/GameKit/apple/LampWebGame.swift +87 -0
- package/Resources/GameKit/apple/Native.swift +12 -0
- package/Resources/GameKit/build.sh +55 -0
- package/Resources/GameKit/lamp_game.h +116 -0
- package/Resources/GameKit/lamp_host.h +19 -0
- package/Resources/GameKit/lamp_kit.cpp +61 -0
- package/Resources/GameKit/lamp_sound.cpp +455 -0
- package/Resources/GameKit/lamp_xbox.cpp +374 -0
- package/Resources/GameKit/libraries/HandmadeMath.h +3940 -0
- package/Resources/GameKit/libraries/cgltf.h +7240 -0
- package/Resources/GameKit/libraries/dr_mp3.h +5430 -0
- package/Resources/GameKit/libraries/dr_wav.h +9209 -0
- package/Resources/GameKit/libraries/sokol_debugtext.h +4754 -0
- package/Resources/GameKit/libraries/sokol_gfx.h +29558 -0
- package/Resources/GameKit/libraries/sokol_gl.h +4538 -0
- package/Resources/GameKit/libraries/sokol_log.h +335 -0
- package/Resources/GameKit/libraries/sokol_shape.h +1531 -0
- package/Resources/GameKit/libraries/stb_image.h +7988 -0
- package/Resources/GameKit/libraries/stb_vorbis.c +5584 -0
- package/Resources/GameKit/scaffold/Game/game.cpp +142 -0
- package/Resources/GameKit/scaffold/Game/scene.glsl +49 -0
- package/Resources/GameKit/scaffold-web/Web/game.js +31 -0
- package/Resources/GameKit/scaffold-web/Web/index.html +13 -0
- package/Resources/GameKit/scaffold-web/Web/making.js +33 -0
- package/Resources/GameKit/scaffold-web/Web/world.js +33 -0
- package/Resources/GameKit/web/lamp-game.js +3036 -0
- package/Resources/GameKit/web/lamp-phaser.js +128 -0
- package/Resources/WebHost/App.exe +0 -0
- package/Resources/WebHost/Microsoft.Web.WebView2.Core.dll +0 -0
- package/Resources/WebHost/WebView2-LICENSE.txt +27 -0
- package/Resources/WebHost/lamp_controllers.h +1506 -0
- package/Resources/WebHost/lamp_web_host.cpp +1173 -0
- package/Resources/WebLibraries/earlier.txt +318 -0
- package/Resources/WebLibraries/phaser/LICENSE.md +20 -0
- package/Resources/WebLibraries/phaser/NOTICE.md +2 -0
- package/Resources/WebLibraries/phaser/phaser.esm.min.js +1 -0
- package/Resources/WebLibraries/qrcode/LICENSE +21 -0
- package/Resources/WebLibraries/qrcode/NOTICE.md +4 -0
- package/Resources/WebLibraries/qrcode/qrcode.js +2297 -0
- package/Resources/WebLibraries/rapier/LICENSE +201 -0
- package/Resources/WebLibraries/rapier/NOTICE.md +3 -0
- package/Resources/WebLibraries/rapier/rapier.js +2 -0
- package/Resources/WebLibraries/three/LICENSE +21 -0
- package/Resources/WebLibraries/three/NOTICE.md +4 -0
- package/Resources/WebLibraries/three/addons/animation/AnimationClipCreator.js +168 -0
- package/Resources/WebLibraries/three/addons/animation/CCDIKSolver.js +607 -0
- package/Resources/WebLibraries/three/addons/controls/ArcballControls.js +3543 -0
- package/Resources/WebLibraries/three/addons/controls/DragControls.js +452 -0
- package/Resources/WebLibraries/three/addons/controls/FirstPersonControls.js +512 -0
- package/Resources/WebLibraries/three/addons/controls/FlyControls.js +384 -0
- package/Resources/WebLibraries/three/addons/controls/MapControls.js +116 -0
- package/Resources/WebLibraries/three/addons/controls/OrbitControls.js +1985 -0
- package/Resources/WebLibraries/three/addons/controls/PointerLockControls.js +264 -0
- package/Resources/WebLibraries/three/addons/controls/TrackballControls.js +1106 -0
- package/Resources/WebLibraries/three/addons/controls/TransformControls.js +2003 -0
- package/Resources/WebLibraries/three/addons/curves/CurveExtras.js +694 -0
- package/Resources/WebLibraries/three/addons/curves/NURBSCurve.js +169 -0
- package/Resources/WebLibraries/three/addons/curves/NURBSSurface.js +98 -0
- package/Resources/WebLibraries/three/addons/curves/NURBSUtils.js +532 -0
- package/Resources/WebLibraries/three/addons/curves/NURBSVolume.js +82 -0
- package/Resources/WebLibraries/three/addons/effects/AnaglyphEffect.js +274 -0
- package/Resources/WebLibraries/three/addons/effects/AsciiEffect.js +323 -0
- package/Resources/WebLibraries/three/addons/effects/OutlineEffect.js +489 -0
- package/Resources/WebLibraries/three/addons/effects/ParallaxBarrierEffect.js +155 -0
- package/Resources/WebLibraries/three/addons/effects/StereoEffect.js +91 -0
- package/Resources/WebLibraries/three/addons/environments/ColorEnvironment.js +59 -0
- package/Resources/WebLibraries/three/addons/environments/DebugEnvironment.js +102 -0
- package/Resources/WebLibraries/three/addons/environments/RoomEnvironment.js +185 -0
- package/Resources/WebLibraries/three/addons/geometries/BoxLineGeometry.js +92 -0
- package/Resources/WebLibraries/three/addons/geometries/ConvexGeometry.js +72 -0
- package/Resources/WebLibraries/three/addons/geometries/DecalGeometry.js +420 -0
- package/Resources/WebLibraries/three/addons/geometries/LoftGeometry.js +355 -0
- package/Resources/WebLibraries/three/addons/geometries/ParametricFunctions.js +100 -0
- package/Resources/WebLibraries/three/addons/geometries/ParametricGeometry.js +172 -0
- package/Resources/WebLibraries/three/addons/geometries/RoundedBoxGeometry.js +216 -0
- package/Resources/WebLibraries/three/addons/geometries/TeapotGeometry.js +689 -0
- package/Resources/WebLibraries/three/addons/geometries/TextGeometry.js +102 -0
- package/Resources/WebLibraries/three/addons/helpers/AnimationPathHelper.js +302 -0
- package/Resources/WebLibraries/three/addons/helpers/LightProbeGridHelper.js +166 -0
- package/Resources/WebLibraries/three/addons/helpers/LightProbeGridHelperWebGL.js +221 -0
- package/Resources/WebLibraries/three/addons/helpers/LightProbeHelper.js +166 -0
- package/Resources/WebLibraries/three/addons/helpers/LightProbeHelperGPU.js +102 -0
- package/Resources/WebLibraries/three/addons/helpers/OctreeHelper.js +109 -0
- package/Resources/WebLibraries/three/addons/helpers/PositionalAudioHelper.js +169 -0
- package/Resources/WebLibraries/three/addons/helpers/RapierHelper.js +59 -0
- package/Resources/WebLibraries/three/addons/helpers/RectAreaLightHelper.js +118 -0
- package/Resources/WebLibraries/three/addons/helpers/TextureHelper.js +265 -0
- package/Resources/WebLibraries/three/addons/helpers/TextureHelperGPU.js +214 -0
- package/Resources/WebLibraries/three/addons/helpers/VertexNormalsHelper.js +155 -0
- package/Resources/WebLibraries/three/addons/helpers/VertexTangentsHelper.js +133 -0
- package/Resources/WebLibraries/three/addons/helpers/ViewHelper.js +529 -0
- package/Resources/WebLibraries/three/addons/interactive/HTMLMesh.js +617 -0
- package/Resources/WebLibraries/three/addons/interactive/InteractiveGroup.js +224 -0
- package/Resources/WebLibraries/three/addons/interactive/SelectionBox.js +324 -0
- package/Resources/WebLibraries/three/addons/interactive/SelectionHelper.js +150 -0
- package/Resources/WebLibraries/three/addons/libs/fflate.module.js +2699 -0
- package/Resources/WebLibraries/three/addons/libs/lil-gui.module.min.js +8 -0
- package/Resources/WebLibraries/three/addons/libs/meshopt_decoder.module.js +196 -0
- package/Resources/WebLibraries/three/addons/libs/potpack.module.js +125 -0
- package/Resources/WebLibraries/three/addons/libs/stats.module.js +167 -0
- package/Resources/WebLibraries/three/addons/libs/tween.module.js +876 -0
- package/Resources/WebLibraries/three/addons/lighting/ClusteredLighting.js +153 -0
- package/Resources/WebLibraries/three/addons/lighting/DynamicLighting.js +82 -0
- package/Resources/WebLibraries/three/addons/lighting/LightMapper.js +295 -0
- package/Resources/WebLibraries/three/addons/lighting/LightProbeGrid.js +923 -0
- package/Resources/WebLibraries/three/addons/lighting/LightProbeGridUtils.js +107 -0
- package/Resources/WebLibraries/three/addons/lighting/LightProbeGridWebGL.js +846 -0
- package/Resources/WebLibraries/three/addons/lighting/vxgi/VXGIConeTracer.js +243 -0
- package/Resources/WebLibraries/three/addons/lighting/vxgi/VXGIIrradiance.js +43 -0
- package/Resources/WebLibraries/three/addons/lighting/vxgi/VXGINode.js +617 -0
- package/Resources/WebLibraries/three/addons/lighting/vxgi/VXGISceneCollector.js +793 -0
- package/Resources/WebLibraries/three/addons/lighting/vxgi/VXGIVolume.js +1440 -0
- package/Resources/WebLibraries/three/addons/lights/LightProbeGenerator.js +337 -0
- package/Resources/WebLibraries/three/addons/lights/RectAreaLightTexturesLib.js +127 -0
- package/Resources/WebLibraries/three/addons/lights/RectAreaLightUniformsLib.js +40 -0
- package/Resources/WebLibraries/three/addons/lights/SunLight.js +100 -0
- package/Resources/WebLibraries/three/addons/lights/SunLightNode.js +56 -0
- package/Resources/WebLibraries/three/addons/lights/SunLightShadow.js +319 -0
- package/Resources/WebLibraries/three/addons/lights/SunShadowNode.js +312 -0
- package/Resources/WebLibraries/three/addons/lines/Line2.js +56 -0
- package/Resources/WebLibraries/three/addons/lines/LineGeometry.js +157 -0
- package/Resources/WebLibraries/three/addons/lines/LineMaterial.js +742 -0
- package/Resources/WebLibraries/three/addons/lines/LineSegments2.js +434 -0
- package/Resources/WebLibraries/three/addons/lines/LineSegmentsGeometry.js +298 -0
- package/Resources/WebLibraries/three/addons/lines/Wireframe.js +108 -0
- package/Resources/WebLibraries/three/addons/lines/WireframeGeometry2.js +49 -0
- package/Resources/WebLibraries/three/addons/lines/webgpu/Line2.js +46 -0
- package/Resources/WebLibraries/three/addons/lines/webgpu/LineSegments2.js +419 -0
- package/Resources/WebLibraries/three/addons/lines/webgpu/Wireframe.js +86 -0
- package/Resources/WebLibraries/three/addons/loaders/EXRLoader.js +3411 -0
- package/Resources/WebLibraries/three/addons/loaders/FBXLoader.js +4660 -0
- package/Resources/WebLibraries/three/addons/loaders/FontLoader.js +262 -0
- package/Resources/WebLibraries/three/addons/loaders/GLTFLoader.js +5006 -0
- package/Resources/WebLibraries/three/addons/loaders/HDRLoader.js +463 -0
- package/Resources/WebLibraries/three/addons/loaders/MTLLoader.js +601 -0
- package/Resources/WebLibraries/three/addons/loaders/OBJLoader.js +961 -0
- package/Resources/WebLibraries/three/addons/loaders/PLYLoader.js +992 -0
- package/Resources/WebLibraries/three/addons/loaders/RGBELoader.js +18 -0
- package/Resources/WebLibraries/three/addons/loaders/STLLoader.js +423 -0
- package/Resources/WebLibraries/three/addons/loaders/SVGLoader.js +3297 -0
- package/Resources/WebLibraries/three/addons/loaders/TGALoader.js +539 -0
- package/Resources/WebLibraries/three/addons/loaders/UltraHDRLoader.js +757 -0
- package/Resources/WebLibraries/three/addons/math/Capsule.js +159 -0
- package/Resources/WebLibraries/three/addons/math/ColorConverter.js +62 -0
- package/Resources/WebLibraries/three/addons/math/ColorSpaces.js +147 -0
- package/Resources/WebLibraries/three/addons/math/ConvexHull.js +1695 -0
- package/Resources/WebLibraries/three/addons/math/ImprovedNoise.js +88 -0
- package/Resources/WebLibraries/three/addons/math/Lut.js +317 -0
- package/Resources/WebLibraries/three/addons/math/MeshSurfaceSampler.js +315 -0
- package/Resources/WebLibraries/three/addons/math/OBB.js +535 -0
- package/Resources/WebLibraries/three/addons/math/Octree.js +825 -0
- package/Resources/WebLibraries/three/addons/math/SimplexNoise.js +470 -0
- package/Resources/WebLibraries/three/addons/misc/ConvexObjectBreaker.js +539 -0
- package/Resources/WebLibraries/three/addons/misc/GPUComputationRenderer.js +508 -0
- package/Resources/WebLibraries/three/addons/misc/Gyroscope.js +78 -0
- package/Resources/WebLibraries/three/addons/misc/MorphAnimMesh.js +119 -0
- package/Resources/WebLibraries/three/addons/misc/MorphBlendMesh.js +425 -0
- package/Resources/WebLibraries/three/addons/misc/ProgressiveLightMap.js +370 -0
- package/Resources/WebLibraries/three/addons/misc/ProgressiveLightMapGPU.js +322 -0
- package/Resources/WebLibraries/three/addons/misc/RollerCoaster.js +661 -0
- package/Resources/WebLibraries/three/addons/misc/Sculptor.js +1679 -0
- package/Resources/WebLibraries/three/addons/misc/SculptorMesh.js +1504 -0
- package/Resources/WebLibraries/three/addons/misc/SculptorTools.js +1180 -0
- package/Resources/WebLibraries/three/addons/misc/SculptorUtils.js +345 -0
- package/Resources/WebLibraries/three/addons/misc/TileCreasedNormalsPlugin.js +270 -0
- package/Resources/WebLibraries/three/addons/misc/TubePainter.js +611 -0
- package/Resources/WebLibraries/three/addons/misc/Volume.js +520 -0
- package/Resources/WebLibraries/three/addons/misc/VolumeSlice.js +275 -0
- package/Resources/WebLibraries/three/addons/modifiers/CurveModifier.js +374 -0
- package/Resources/WebLibraries/three/addons/modifiers/CurveModifierGPU.js +256 -0
- package/Resources/WebLibraries/three/addons/modifiers/EdgeSplitModifier.js +299 -0
- package/Resources/WebLibraries/three/addons/modifiers/TessellateModifier.js +340 -0
- package/Resources/WebLibraries/three/addons/objects/GroundedSkybox.js +69 -0
- package/Resources/WebLibraries/three/addons/objects/Lensflare.js +489 -0
- package/Resources/WebLibraries/three/addons/objects/LensflareMesh.js +376 -0
- package/Resources/WebLibraries/three/addons/objects/MarchingCubes.js +1261 -0
- package/Resources/WebLibraries/three/addons/objects/Reflector.js +390 -0
- package/Resources/WebLibraries/three/addons/objects/ReflectorForSSRPass.js +393 -0
- package/Resources/WebLibraries/three/addons/objects/Refractor.js +389 -0
- package/Resources/WebLibraries/three/addons/objects/ShadowMesh.js +130 -0
- package/Resources/WebLibraries/three/addons/objects/Sky.js +342 -0
- package/Resources/WebLibraries/three/addons/objects/SkyMesh.js +403 -0
- package/Resources/WebLibraries/three/addons/objects/Water.js +374 -0
- package/Resources/WebLibraries/three/addons/objects/Water2.js +403 -0
- package/Resources/WebLibraries/three/addons/objects/Water2Mesh.js +199 -0
- package/Resources/WebLibraries/three/addons/objects/WaterMesh.js +194 -0
- package/Resources/WebLibraries/three/addons/postprocessing/AfterimagePass.js +187 -0
- package/Resources/WebLibraries/three/addons/postprocessing/BloomPass.js +274 -0
- package/Resources/WebLibraries/three/addons/postprocessing/BokehPass.js +218 -0
- package/Resources/WebLibraries/three/addons/postprocessing/ClearPass.js +97 -0
- package/Resources/WebLibraries/three/addons/postprocessing/CubeTexturePass.js +146 -0
- package/Resources/WebLibraries/three/addons/postprocessing/DotScreenPass.js +114 -0
- package/Resources/WebLibraries/three/addons/postprocessing/EffectComposer.js +365 -0
- package/Resources/WebLibraries/three/addons/postprocessing/FXAAPass.js +40 -0
- package/Resources/WebLibraries/three/addons/postprocessing/FilmPass.js +113 -0
- package/Resources/WebLibraries/three/addons/postprocessing/GTAOPass.js +735 -0
- package/Resources/WebLibraries/three/addons/postprocessing/GlitchPass.js +177 -0
- package/Resources/WebLibraries/three/addons/postprocessing/HalftonePass.js +134 -0
- package/Resources/WebLibraries/three/addons/postprocessing/LUTPass.js +138 -0
- package/Resources/WebLibraries/three/addons/postprocessing/MaskPass.js +195 -0
- package/Resources/WebLibraries/three/addons/postprocessing/OutlinePass.js +776 -0
- package/Resources/WebLibraries/three/addons/postprocessing/OutputPass.js +148 -0
- package/Resources/WebLibraries/three/addons/postprocessing/Pass.js +191 -0
- package/Resources/WebLibraries/three/addons/postprocessing/RenderPass.js +193 -0
- package/Resources/WebLibraries/three/addons/postprocessing/RenderPixelatedPass.js +314 -0
- package/Resources/WebLibraries/three/addons/postprocessing/RenderTransitionPass.js +267 -0
- package/Resources/WebLibraries/three/addons/postprocessing/SAOPass.js +407 -0
- package/Resources/WebLibraries/three/addons/postprocessing/SMAAPass.js +230 -0
- package/Resources/WebLibraries/three/addons/postprocessing/SSAARenderPass.js +313 -0
- package/Resources/WebLibraries/three/addons/postprocessing/SSAOPass.js +527 -0
- package/Resources/WebLibraries/three/addons/postprocessing/SSRPass.js +858 -0
- package/Resources/WebLibraries/three/addons/postprocessing/SavePass.js +132 -0
- package/Resources/WebLibraries/three/addons/postprocessing/ShaderPass.js +135 -0
- package/Resources/WebLibraries/three/addons/postprocessing/TAARenderPass.js +243 -0
- package/Resources/WebLibraries/three/addons/postprocessing/TexturePass.js +131 -0
- package/Resources/WebLibraries/three/addons/postprocessing/UnrealBloomPass.js +541 -0
- package/Resources/WebLibraries/three/addons/shaders/ACESFilmicToneMappingShader.js +96 -0
- package/Resources/WebLibraries/three/addons/shaders/AfterimageShader.js +63 -0
- package/Resources/WebLibraries/three/addons/shaders/BasicShader.js +36 -0
- package/Resources/WebLibraries/three/addons/shaders/BleachBypassShader.js +68 -0
- package/Resources/WebLibraries/three/addons/shaders/BlendShader.js +56 -0
- package/Resources/WebLibraries/three/addons/shaders/BokehShader.js +151 -0
- package/Resources/WebLibraries/three/addons/shaders/BokehShader2.js +404 -0
- package/Resources/WebLibraries/three/addons/shaders/BrightnessContrastShader.js +62 -0
- package/Resources/WebLibraries/three/addons/shaders/ColorCorrectionShader.js +59 -0
- package/Resources/WebLibraries/three/addons/shaders/ColorifyShader.js +57 -0
- package/Resources/WebLibraries/three/addons/shaders/ConvolutionShader.js +74 -0
- package/Resources/WebLibraries/three/addons/shaders/CopyShader.js +52 -0
- package/Resources/WebLibraries/three/addons/shaders/DOFMipMapShader.js +63 -0
- package/Resources/WebLibraries/three/addons/shaders/DepthLimitedBlurShader.js +183 -0
- package/Resources/WebLibraries/three/addons/shaders/DigitalGlitch.js +104 -0
- package/Resources/WebLibraries/three/addons/shaders/DotScreenShader.js +75 -0
- package/Resources/WebLibraries/three/addons/shaders/ExposureShader.js +51 -0
- package/Resources/WebLibraries/three/addons/shaders/FXAAShader.js +298 -0
- package/Resources/WebLibraries/three/addons/shaders/FilmShader.js +73 -0
- package/Resources/WebLibraries/three/addons/shaders/FocusShader.js +94 -0
- package/Resources/WebLibraries/three/addons/shaders/FreiChenShader.js +103 -0
- package/Resources/WebLibraries/three/addons/shaders/GTAOShader.js +437 -0
- package/Resources/WebLibraries/three/addons/shaders/GammaCorrectionShader.js +52 -0
- package/Resources/WebLibraries/three/addons/shaders/HalftoneShader.js +332 -0
- package/Resources/WebLibraries/three/addons/shaders/HorizontalBlurShader.js +68 -0
- package/Resources/WebLibraries/three/addons/shaders/HorizontalTiltShiftShader.js +70 -0
- package/Resources/WebLibraries/three/addons/shaders/HueSaturationShader.js +74 -0
- package/Resources/WebLibraries/three/addons/shaders/KaleidoShader.js +65 -0
- package/Resources/WebLibraries/three/addons/shaders/LuminosityHighPassShader.js +68 -0
- package/Resources/WebLibraries/three/addons/shaders/LuminosityShader.js +54 -0
- package/Resources/WebLibraries/three/addons/shaders/MirrorShader.js +62 -0
- package/Resources/WebLibraries/three/addons/shaders/NormalMapShader.js +60 -0
- package/Resources/WebLibraries/three/addons/shaders/OutputShader.js +103 -0
- package/Resources/WebLibraries/three/addons/shaders/PoissonDenoiseShader.js +230 -0
- package/Resources/WebLibraries/three/addons/shaders/RGBShiftShader.js +61 -0
- package/Resources/WebLibraries/three/addons/shaders/SAOShader.js +201 -0
- package/Resources/WebLibraries/three/addons/shaders/SMAAShader.js +489 -0
- package/Resources/WebLibraries/three/addons/shaders/SSAOShader.js +331 -0
- package/Resources/WebLibraries/three/addons/shaders/SSRShader.js +396 -0
- package/Resources/WebLibraries/three/addons/shaders/SepiaShader.js +57 -0
- package/Resources/WebLibraries/three/addons/shaders/SobelOperatorShader.js +98 -0
- package/Resources/WebLibraries/three/addons/shaders/SubsurfaceScatteringShader.js +95 -0
- package/Resources/WebLibraries/three/addons/shaders/TechnicolorShader.js +51 -0
- package/Resources/WebLibraries/three/addons/shaders/ToonShader.js +349 -0
- package/Resources/WebLibraries/three/addons/shaders/TriangleBlurShader.js +79 -0
- package/Resources/WebLibraries/three/addons/shaders/UnpackDepthRGBAShader.js +60 -0
- package/Resources/WebLibraries/three/addons/shaders/VelocityShader.js +137 -0
- package/Resources/WebLibraries/three/addons/shaders/VerticalBlurShader.js +66 -0
- package/Resources/WebLibraries/three/addons/shaders/VerticalTiltShiftShader.js +70 -0
- package/Resources/WebLibraries/three/addons/shaders/VignetteShader.js +56 -0
- package/Resources/WebLibraries/three/addons/shaders/VolumeShader.js +285 -0
- package/Resources/WebLibraries/three/addons/shaders/WaterRefractionShader.js +106 -0
- package/Resources/WebLibraries/three/addons/textures/FlakesTexture.js +54 -0
- package/Resources/WebLibraries/three/addons/tsl/WebGLNodesHandler.js +607 -0
- package/Resources/WebLibraries/three/addons/tsl/display/AfterImageNode.js +256 -0
- package/Resources/WebLibraries/three/addons/tsl/display/AnaglyphPassNode.js +550 -0
- package/Resources/WebLibraries/three/addons/tsl/display/BilateralBlurNode.js +377 -0
- package/Resources/WebLibraries/three/addons/tsl/display/BleachBypass.js +33 -0
- package/Resources/WebLibraries/three/addons/tsl/display/BloomNode.js +599 -0
- package/Resources/WebLibraries/three/addons/tsl/display/CRT.js +150 -0
- package/Resources/WebLibraries/three/addons/tsl/display/ChromaticAberrationNode.js +163 -0
- package/Resources/WebLibraries/three/addons/tsl/display/DenoiseNode.js +354 -0
- package/Resources/WebLibraries/three/addons/tsl/display/DepthOfFieldNode.js +572 -0
- package/Resources/WebLibraries/three/addons/tsl/display/DotScreenNode.js +104 -0
- package/Resources/WebLibraries/three/addons/tsl/display/FSR1Node.js +481 -0
- package/Resources/WebLibraries/three/addons/tsl/display/FXAANode.js +357 -0
- package/Resources/WebLibraries/three/addons/tsl/display/FilmNode.js +101 -0
- package/Resources/WebLibraries/three/addons/tsl/display/GTAONode.js +727 -0
- package/Resources/WebLibraries/three/addons/tsl/display/GaussianBlurNode.js +412 -0
- package/Resources/WebLibraries/three/addons/tsl/display/GodraysNode.js +604 -0
- package/Resources/WebLibraries/three/addons/tsl/display/ImportanceSampledEnvironment.js +564 -0
- package/Resources/WebLibraries/three/addons/tsl/display/LensflareNode.js +282 -0
- package/Resources/WebLibraries/three/addons/tsl/display/Lut3DNode.js +109 -0
- package/Resources/WebLibraries/three/addons/tsl/display/MotionBlur.js +33 -0
- package/Resources/WebLibraries/three/addons/tsl/display/OITPassNode.js +341 -0
- package/Resources/WebLibraries/three/addons/tsl/display/OutlineNode.js +916 -0
- package/Resources/WebLibraries/three/addons/tsl/display/ParallaxBarrierPassNode.js +90 -0
- package/Resources/WebLibraries/three/addons/tsl/display/PixelationPassNode.js +227 -0
- package/Resources/WebLibraries/three/addons/tsl/display/RGBShiftNode.js +96 -0
- package/Resources/WebLibraries/three/addons/tsl/display/RecurrentDenoiseNode.js +812 -0
- package/Resources/WebLibraries/three/addons/tsl/display/RetroPassNode.js +263 -0
- package/Resources/WebLibraries/three/addons/tsl/display/SMAANode.js +729 -0
- package/Resources/WebLibraries/three/addons/tsl/display/SSAAPassNode.js +344 -0
- package/Resources/WebLibraries/three/addons/tsl/display/SSAONode.js +422 -0
- package/Resources/WebLibraries/three/addons/tsl/display/SSGINode.js +698 -0
- package/Resources/WebLibraries/three/addons/tsl/display/SSRNode.js +1430 -0
- package/Resources/WebLibraries/three/addons/tsl/display/SSSNode.js +479 -0
- package/Resources/WebLibraries/three/addons/tsl/display/Sepia.js +24 -0
- package/Resources/WebLibraries/three/addons/tsl/display/Shape.js +29 -0
- package/Resources/WebLibraries/three/addons/tsl/display/SharpenNode.js +284 -0
- package/Resources/WebLibraries/three/addons/tsl/display/SobelOperatorNode.js +168 -0
- package/Resources/WebLibraries/three/addons/tsl/display/StereoCompositePassNode.js +190 -0
- package/Resources/WebLibraries/three/addons/tsl/display/StereoPassNode.js +117 -0
- package/Resources/WebLibraries/three/addons/tsl/display/TAAUNode.js +700 -0
- package/Resources/WebLibraries/three/addons/tsl/display/TRAANode.js +609 -0
- package/Resources/WebLibraries/three/addons/tsl/display/TemporalReprojectNode.js +1024 -0
- package/Resources/WebLibraries/three/addons/tsl/display/TransitionNode.js +141 -0
- package/Resources/WebLibraries/three/addons/tsl/display/boxBlur.js +65 -0
- package/Resources/WebLibraries/three/addons/tsl/display/depthAwareBlend.js +80 -0
- package/Resources/WebLibraries/three/addons/tsl/display/depthAwareBlur.js +63 -0
- package/Resources/WebLibraries/three/addons/tsl/display/hashBlur.js +55 -0
- package/Resources/WebLibraries/three/addons/tsl/display/lensDirt.js +30 -0
- package/Resources/WebLibraries/three/addons/tsl/display/radialBlur.js +68 -0
- package/Resources/WebLibraries/three/addons/tsl/lighting/ClusteredLightsNode.js +994 -0
- package/Resources/WebLibraries/three/addons/tsl/lighting/DynamicLightsNode.js +201 -0
- package/Resources/WebLibraries/three/addons/tsl/lighting/LightProbeGridNode.js +183 -0
- package/Resources/WebLibraries/three/addons/tsl/lighting/data/AmbientLightDataNode.js +67 -0
- package/Resources/WebLibraries/three/addons/tsl/lighting/data/DirectionalLightDataNode.js +117 -0
- package/Resources/WebLibraries/three/addons/tsl/lighting/data/HemisphereLightDataNode.js +105 -0
- package/Resources/WebLibraries/three/addons/tsl/lighting/data/PointLightDataNode.js +140 -0
- package/Resources/WebLibraries/three/addons/tsl/lighting/data/SpotLightDataNode.js +167 -0
- package/Resources/WebLibraries/three/addons/tsl/math/Bayer.js +84 -0
- package/Resources/WebLibraries/three/addons/tsl/math/curlNoise.js +107 -0
- package/Resources/WebLibraries/three/addons/tsl/math/voronoiNoise.js +112 -0
- package/Resources/WebLibraries/three/addons/tsl/shadows/TileShadowNode.js +542 -0
- package/Resources/WebLibraries/three/addons/tsl/shadows/TileShadowNodeHelper.js +212 -0
- package/Resources/WebLibraries/three/addons/tsl/utils/GroundedSkybox.js +62 -0
- package/Resources/WebLibraries/three/addons/tsl/utils/RNoise.js +51 -0
- package/Resources/WebLibraries/three/addons/tsl/utils/Raymarching.js +70 -0
- package/Resources/WebLibraries/three/addons/tsl/utils/SoftParticles.js +53 -0
- package/Resources/WebLibraries/three/addons/tsl/utils/SpecularHelpers.js +302 -0
- package/Resources/WebLibraries/three/addons/tsl/utils/TAAUtils.js +184 -0
- package/Resources/WebLibraries/three/addons/utils/BufferGeometryUtils.js +1523 -0
- package/Resources/WebLibraries/three/addons/utils/CameraUtils.js +82 -0
- package/Resources/WebLibraries/three/addons/utils/ColorUtils.js +76 -0
- package/Resources/WebLibraries/three/addons/utils/GaussianSplatUtils.js +229 -0
- package/Resources/WebLibraries/three/addons/utils/GeometryCompressionUtils.js +547 -0
- package/Resources/WebLibraries/three/addons/utils/GeometryUtils.js +226 -0
- package/Resources/WebLibraries/three/addons/utils/LDrawUtils.js +211 -0
- package/Resources/WebLibraries/three/addons/utils/SceneOptimizer.js +459 -0
- package/Resources/WebLibraries/three/addons/utils/SceneUtils.js +363 -0
- package/Resources/WebLibraries/three/addons/utils/ShadowMapViewer.js +244 -0
- package/Resources/WebLibraries/three/addons/utils/ShadowMapViewerGPU.js +233 -0
- package/Resources/WebLibraries/three/addons/utils/SkeletonUtils.js +496 -0
- package/Resources/WebLibraries/three/addons/utils/SortUtils.js +175 -0
- package/Resources/WebLibraries/three/addons/utils/UVsDebug.js +173 -0
- package/Resources/WebLibraries/three/addons/utils/WebGLTextureUtils.js +115 -0
- package/Resources/WebLibraries/three/addons/utils/WebGPUTextureUtils.js +81 -0
- package/Resources/WebLibraries/three/addons/utils/WorkerPool.js +167 -0
- package/Resources/WebLibraries/three/addons/utils/unwrapUVs.js +865 -0
- package/Resources/WebLibraries/three/three.core.js +61162 -0
- package/Resources/WebLibraries/three/three.module.js +19537 -0
- package/Resources/WebLibraries/three/three.tsl.js +41903 -0
- package/Resources/WebLibraries/three/three.webgpu.js +53714 -0
- package/Resources/skills/2d-game/SKILL.md +305 -0
- package/Resources/skills/3d-game/SKILL.md +274 -0
- package/Resources/skills/accounts/SKILL.md +41 -0
- package/Resources/skills/android-design/SKILL.md +159 -0
- package/Resources/skills/app-ai/SKILL.md +154 -0
- package/Resources/skills/app-intents/SKILL.md +31 -0
- package/Resources/skills/app-kinds/SKILL.md +122 -0
- package/Resources/skills/apple-watch/SKILL.md +43 -0
- package/Resources/skills/assets/SKILL.md +183 -0
- package/Resources/skills/building-blocks/SKILL.md +97 -0
- package/Resources/skills/device-control/SKILL.md +56 -0
- package/Resources/skills/device-integrations/SKILL.md +278 -0
- package/Resources/skills/finder-files/SKILL.md +19 -0
- package/Resources/skills/game/SKILL.md +414 -0
- package/Resources/skills/game-3d/SKILL.md +12 -0
- package/Resources/skills/game-art/SKILL.md +11 -0
- package/Resources/skills/game-feel/SKILL.md +13 -0
- package/Resources/skills/game-levels/SKILL.md +12 -0
- package/Resources/skills/game-live/SKILL.md +12 -0
- package/Resources/skills/game-physics/SKILL.md +13 -0
- package/Resources/skills/game-playtest/SKILL.md +26 -0
- package/Resources/skills/game-ui/SKILL.md +278 -0
- package/Resources/skills/github-projects/SKILL.md +59 -0
- package/Resources/skills/icloud-sync/SKILL.md +30 -0
- package/Resources/skills/iphone-hardware/SKILL.md +57 -0
- package/Resources/skills/iphone-widgets/SKILL.md +53 -0
- package/Resources/skills/lg-developer-mode/SKILL.md +152 -0
- package/Resources/skills/mac-widgets/SKILL.md +60 -0
- package/Resources/skills/maps/SKILL.md +8 -0
- package/Resources/skills/menu-bar/SKILL.md +36 -0
- package/Resources/skills/more-apps/SKILL.md +53 -0
- package/Resources/skills/phone-controllers/SKILL.md +79 -0
- package/Resources/skills/phone-pages/SKILL.md +173 -0
- package/Resources/skills/roku-developer-mode/SKILL.md +106 -0
- package/Resources/skills/scene-3d/SKILL.md +27 -0
- package/Resources/skills/scripting/SKILL.md +17 -0
- package/Resources/skills/share-extension/SKILL.md +22 -0
- package/Resources/skills/tv-design/SKILL.md +72 -0
- package/Resources/skills/web-app/SKILL.md +136 -0
- package/Resources/skills/web-game/SKILL.md +95 -0
- package/Resources/skills/web-or-native/SKILL.md +103 -0
- package/Resources/skills/wish-links/SKILL.md +156 -0
- package/THIRD_PARTY_NOTICES.md +170 -0
- package/cli/android.mjs +941 -0
- package/cli/browser.mjs +268 -0
- package/cli/calls.mjs +390 -0
- package/cli/dev.mjs +773 -0
- package/cli/devices.mjs +984 -0
- package/cli/lamp.mjs +1016 -0
- package/cli/lampapp.mjs +332 -0
- package/cli/lamps/android-host/LampPageGateway.kt +345 -0
- package/cli/lamps/android-host/app/build.gradle.kts +40 -0
- package/cli/lamps/android-host/build.gradle.kts +4 -0
- package/cli/lamps/android-host/game/AndroidManifest.xml +33 -0
- package/cli/lamps/android-host/game/MainActivity.kt +173 -0
- package/cli/lamps/android-host/gradle/libs.versions.toml +39 -0
- package/cli/lamps/android-host/gradle.properties +8 -0
- package/cli/lamps/android-host/page/AndroidManifest.xml +35 -0
- package/cli/lamps/android-host/page/MainActivity.kt +193 -0
- package/cli/lamps/android-host/settings.gradle.kts +30 -0
- package/cli/lamps/android-host/tools.json +43 -0
- package/cli/lamps/lamp-controllers.js +2 -0
- package/cli/lamps/lamp-for-games.js +6863 -0
- package/cli/lamps/lamp.js +7023 -0
- package/cli/lamps/mac-starter/.lamp/meta.json +1 -0
- package/cli/lamps/mac-starter/APP.md +12 -0
- package/cli/lamps/mac-starter/Package.swift +8 -0
- package/cli/lamps/mac-starter/Sources/App.swift +11 -0
- package/cli/lamps/mac-starter/Sources/ContentView.swift +17 -0
- package/cli/lamps/mac-starter/Sources/LampSupport.swift +18097 -0
- package/cli/lamps/mac-starter/build.sh +336 -0
- package/cli/lamps/mac-starter/gitignore +3 -0
- package/cli/lamps/mac-starter/lamp-starter.json +6 -0
- package/cli/lamps/web-starter/APP.md +8 -0
- package/cli/lamps/web-starter/app.js +1 -0
- package/cli/lamps/web-starter/index.html +17 -0
- package/cli/lamps/web-starter/lamp-starter.json +5 -0
- package/cli/lamps/web-starter/lamp.js +7023 -0
- package/cli/lamps/web-starter/style.css +25 -0
- package/cli/link.mjs +138 -0
- package/cli/mac.mjs +621 -0
- package/cli/mdns.mjs +205 -0
- package/cli/msix.mjs +413 -0
- package/cli/network.mjs +209 -0
- package/cli/outside.mjs +140 -0
- package/cli/page.js +165 -0
- package/cli/place.mjs +298 -0
- package/cli/png.mjs +184 -0
- package/cli/share.mjs +202 -0
- package/cli/skill/SKILL.md +255 -0
- package/cli/skills.mjs +71 -0
- package/cli/state.mjs +173 -0
- package/cli/update.mjs +57 -0
- package/package.json +20 -4
- package/site/lib/quota.js +59 -0
- package/site/lib/shared-wishes.js +996 -0
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: 2d-game
|
|
3
|
+
description: 2D games - choosing 2D or 3D, and Phaser (a game of Lamp's game kind, played on the Mac, iPhone and iPad, Android, TVs and the Xbox) or SpriteKit (a Mac or iPhone app); scenes, the frame step, cameras, tilemaps, sprite sheets, physics, controls on every input, pixel art, TV safe areas, sound, saves, drawing the art, and keeping it smooth on the slowest place. Read before writing or changing a 2D game; a new web game on the Mac starts from the web-game guide instead, which says when to open this.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# 2D games
|
|
7
|
+
|
|
8
|
+
A 2D game is drawn flat: a platformer, a shooter seen from above or the side, a racer seen from above, a puzzle or
|
|
9
|
+
arcade game played in real time. Read the game skill too: what every game needs (moving by the time since the last
|
|
10
|
+
frame, controls, sound, saves, the look) is there, and this skill is how to do it in 2D.
|
|
11
|
+
|
|
12
|
+
## 2D or 3D
|
|
13
|
+
|
|
14
|
+
2D when the person describes something flat (a platformer, "like Mario", "like Pac-Man", a top-down shooter, a pixel-art
|
|
15
|
+
game, a puzzle with falling pieces) or a game whose look is drawings rather than a world to look around. 3D when they
|
|
16
|
+
want depth to move through (driving through a city, flying, a first-person view, a ball rolling over hills): the
|
|
17
|
+
3d-game skill. When nothing says, a game whose play is flat is better flat: it's cheaper on every device and drawings
|
|
18
|
+
read better than simple 3D shapes. Don't ask; choose, and say in a sentence which.
|
|
19
|
+
|
|
20
|
+
## Phaser or SpriteKit
|
|
21
|
+
|
|
22
|
+
- **Phaser**, in a game of Lamp's game kind: the page in Web/ on Lamp's game runtime. It's played on the Mac and goes,
|
|
23
|
+
unchanged, to an iPhone or iPad, an Android phone, tablet or TV, a Samsung or LG TV, the Xbox and a page on the web,
|
|
24
|
+
whenever the person asks for one of those later. This is the default for a 2D game.
|
|
25
|
+
- **SpriteKit**, in a Mac or iPhone app written in Swift: when the game belongs with Apple's own things (Game Center,
|
|
26
|
+
a widget, iCloud, the Apple Watch), sits inside an app of theirs (a game in a menu of their Mac app), or is for an
|
|
27
|
+
Apple TV, which runs no pages. A SpriteKit game can never go to Android, the Xbox, a Samsung or LG TV or the web: it
|
|
28
|
+
would have to be written again. Say so plainly when choosing it, and choose it only for what needs it.
|
|
29
|
+
|
|
30
|
+
The kind of app is chosen before your first turn: a game is of the game kind, and a Mac or iPhone app whose request is
|
|
31
|
+
a game is written with SpriteKit. When the choice doesn't fit (a game of the game kind that needs Game Center, or a Mac
|
|
32
|
+
app that should be playable on the person's Xbox), say why in a sentence and change it with make_app_for.
|
|
33
|
+
|
|
34
|
+
## Phaser on Lamp's runtime
|
|
35
|
+
|
|
36
|
+
Lamp's game kind runs Phaser 3 (3.90) through `Web/lamp-phaser.js`, over the runtime in `Web/lamp-game.js`. Read both,
|
|
37
|
+
all of them, before writing the game: their comments are the interface (a new game at home on the Mac has the
|
|
38
|
+
web-game guide instead). Lamp puts Phaser in `Web/phaser` once the page
|
|
39
|
+
imports it.
|
|
40
|
+
|
|
41
|
+
index.html keeps the scaffold's head (lamp.js, then the import map, whose `phaser` entry is
|
|
42
|
+
`./phaser/phaser.esm.min.js`), then `<script type="module" src="game.js">`. game.js:
|
|
43
|
+
|
|
44
|
+
```js
|
|
45
|
+
import * as Phaser from 'phaser';
|
|
46
|
+
import { phaser, pad } from './lamp-phaser.js';
|
|
47
|
+
import { BUTTON, STICK, sound, save, load, random, log, label, glyph, onInput, hot } from './lamp-game.js';
|
|
48
|
+
|
|
49
|
+
const CONTROLS = { move: STICK.LEFT, jump: BUTTON.A, back: BUTTON.B, pause: BUTTON.MENU };
|
|
50
|
+
|
|
51
|
+
class Play extends Phaser.Scene {
|
|
52
|
+
constructor() { super('play'); }
|
|
53
|
+
preload() { this.load.image('hero', 'art/hero.png'); }
|
|
54
|
+
create() { … }
|
|
55
|
+
update(time, delta) {
|
|
56
|
+
const p = pad(), dt = delta / 1000;
|
|
57
|
+
if (p.pressed & CONTROLS.jump) …
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
phaser({ scenes: [Boot, Play], physics: { default: 'arcade', arcade: { gravity: { y: 2400 } } }, touch: BUTTON.A | BUTTON.B | BUTTON.MENU });
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
As the game grows, each scene goes in a module of its own (`play.js` exporting `Play`, imported by game.js) that ends
|
|
65
|
+
with `hot(import.meta).accept()` from lamp-game.js, so a change to it goes into the running scene (the game skill,
|
|
66
|
+
"Changes while it's played"): the scene keeps its sprites and fields and runs the new `update` and the methods it
|
|
67
|
+
calls. What `create` built stays until the scene starts again, so a change to `create` or `preload` returns false from
|
|
68
|
+
`accept((next, { changed }) => !changed.some(n => /\.(create|preload|init)$/.test(n)))` and the game's code runs again
|
|
69
|
+
in place from the snapshot, with a new Phaser game that loads its pictures again.
|
|
70
|
+
|
|
71
|
+
What Lamp has changed in Phaser, so its usual code works here:
|
|
72
|
+
|
|
73
|
+
- **One frame loop, Lamp's.** Phaser's own loop is asleep; each of the runtime's frames steps Phaser once. Use a
|
|
74
|
+
scene's `update(time, delta)` (ms) and Phaser's timers and tweens freely: they all run on that one clock. Never requestAnimationFrame, setTimeout, Date or Math.random of your own:
|
|
75
|
+
`random()` from lamp-game.js, or `Phaser.Math.RND`, which Lamp seeds from it.
|
|
76
|
+
- **Input is the runtime's pad.** `pad()` is the first controller with the keyboard and the touch controls merged in
|
|
77
|
+
(lx, ly, rx, ry with up positive; buttons as BUTTON bits in down, pressed and released). Phaser's keyboard and
|
|
78
|
+
gamepad are off: `this.input.keyboard` and `this.input.gamepad` are null, so never `createCursorKeys` or
|
|
79
|
+
`addKey`. Phaser's pointer input works with the mouse on the Mac; on a touch screen Lamp's touch controls lie over
|
|
80
|
+
the canvas, so a game played by tapping the screen itself passes `touch: false` and reads `this.input.on('pointerdown')`.
|
|
81
|
+
- **Sound is the runtime's.** Phaser's sound is off: `this.load.audio` and `this.sound` play nothing. Load files from the
|
|
82
|
+
`assets` tool (the game skill, "Sound") with `await sound.load('assets/…')`, once, at the top of game.js; `sound.tone`
|
|
83
|
+
makes an effect in code where nothing there fits, in a scene's `create` or at the top of game.js.
|
|
84
|
+
- **The stage is 1920x1080**, fitted to the window or screen with black around it. Lay everything out in those units.
|
|
85
|
+
The page is never resized to the window: don't listen for resize.
|
|
86
|
+
- **Loading.** Load each scene's files in its `preload`, best all in a first Boot scene, and start play from `create`.
|
|
87
|
+
A scene that loads mid-game takes what it takes while the game goes on, so it shows a loading line rather than nothing.
|
|
88
|
+
|
|
89
|
+
### Structure
|
|
90
|
+
|
|
91
|
+
- **Scenes**: Boot, which loads, then one for each screen of the flow the game picked (the game-ui skill), which is Play
|
|
92
|
+
alone for a game that opens on play. A pause menu is a scene launched over
|
|
93
|
+
play (`this.scene.launch('pause'); this.scene.pause()`), which reads the pad itself and resumes play
|
|
94
|
+
(`this.scene.resume('play'); this.scene.stop()`). Keep the game's state (score, lives, level) in a plain object
|
|
95
|
+
passed in `this.scene.start('play', state)`, not spread over scenes.
|
|
96
|
+
- **A map as a menu**, where the game-ui skill's example suits: the stops in an array, one sprite tweened between them
|
|
97
|
+
with `this.tweens.add` as the choice changes, `this.cameras.main.pan` or `zoomTo` from the view before it, and input
|
|
98
|
+
ignored only for the tween's few frames so a fast press can go on to the next stop. The registered state keeps the stop and the
|
|
99
|
+
camera's position, and `create` sets them directly from it.
|
|
100
|
+
- **The step**: move by `delta`, and give Arcade physics the work of falling, sliding and colliding. Arcade steps its
|
|
101
|
+
world at a fixed 60 times a second whatever the screen's rate (its `fps`, the default), so jumps reach the same
|
|
102
|
+
height on a 120 Hz Mac and a 50 Hz TV. Game logic that must be exact (a puzzle's grid, a rhythm) counts in its own
|
|
103
|
+
fixed steps from `delta`.
|
|
104
|
+
- **Cameras**: `this.cameras.main.setBounds(0, 0, worldWidth, worldHeight)` and `startFollow(hero, true, 0.12, 0.12)`
|
|
105
|
+
with a deadzone (`setDeadzone(300, 200)`), so the screen doesn't jerk with every step. Shake and flash
|
|
106
|
+
(`shake(120, 0.006)`, `flash(150)`) for hits, briefly. The HUD is a second camera's, or HTML over the canvas, never
|
|
107
|
+
moved by the world's camera (`setScrollFactor(0)` on what's drawn in the scene).
|
|
108
|
+
- **Tilemaps**: write each level as rows of characters in code (`'..........#####....'`), each a tile, and build it with
|
|
109
|
+
`this.make.tilemap({ data, tileWidth: 64, tileHeight: 64 })`, `map.addTilesetImage('tiles')` and `map.createLayer(0,
|
|
110
|
+
tileset)`, with `layer.setCollision([...])` and `this.physics.add.collider(hero, layer)`. Rows in code are easy to
|
|
111
|
+
read and change; there's no level editor here. Make the tileset image in code (below) so every tile is the same size.
|
|
112
|
+
- **Sprite sheets and atlases**: frames of one character or thing in one image, `this.load.spritesheet('hero',
|
|
113
|
+
'art/hero.png', { frameWidth: 64, frameHeight: 64 })`, played with `this.anims.create({ key: 'run', frames:
|
|
114
|
+
this.anims.generateFrameNumbers('hero', { start: 0, end: 5 }), frameRate: 12, repeat: -1 })`. Many small pictures in
|
|
115
|
+
one texture draw in one batch: that's what keeps a busy screen cheap. Pictures made in code are drawn into one
|
|
116
|
+
texture too (`this.textures.createCanvas`, then `texture.add(name, 0, x, y, w, h)` for each frame).
|
|
117
|
+
- **Physics**: Arcade for nearly every game: boxes and circles, gravity, velocity, `collider` and `overlap` with a
|
|
118
|
+
callback. A platformer's player is `this.physics.add.sprite` with `setCollideWorldBounds(true)`; it may jump while
|
|
119
|
+
`body.blocked.down`, with a few frames' grace after leaving a ledge and a jump pressed just before landing still
|
|
120
|
+
taken. Matter (`physics: { default: 'matter' }`) only when things must tumble, stack, swing on joints or have real
|
|
121
|
+
shapes (a physics puzzle, a pile of crates): it costs far more a body and its bodies don't stand still as simply.
|
|
122
|
+
- **Many of a thing** (bullets, coins, enemies, sparks): a group made once with `maxSize` and taken from with
|
|
123
|
+
`group.get(x, y)`. A physics group's member is put back with `disableBody(true, true)` and taken out again with
|
|
124
|
+
`enableBody(true, x, y, true, true)`: hidden and inactive alone, its body still collides and overlaps, so a
|
|
125
|
+
collected coin is collected again and a spent bullet still hits. Sprites without bodies are put back with
|
|
126
|
+
`setActive(false).setVisible(false)`. Particles come from one emitter (`this.add.particles`) with a modest
|
|
127
|
+
`maxParticles`.
|
|
128
|
+
|
|
129
|
+
### Controls on every input
|
|
130
|
+
|
|
131
|
+
Write the controls once as actions on the pad (CONTROLS above) and read the pad through them. The runtime gives a
|
|
132
|
+
controller, the keyboard and the touch controls as that one pad, so the game needs nothing else. Show how to play, and
|
|
133
|
+
every prompt, from the actions: as HTML over the canvas inside `onInput(draw)`, with `glyph(CONTROLS.jump)` in the
|
|
134
|
+
text ("Press [A] to jump", a badge as the player's input draws it), or in a Phaser Text with `label(CONTROLS.jump)`
|
|
135
|
+
("A", "✕", "Space"), redrawn from `onInput`. Leave a line out where `label` is null. When the person asked for the arrow
|
|
136
|
+
keys, `phaser({ keyboardHints: { leftStick: 'arrows' }, … })` makes the hints name them (the game skill, "Controls"). On a phone the touch controls show
|
|
137
|
+
a left stick and the buttons `touch` names: give a platformer jump on A and perhaps one more, and keep the game whole
|
|
138
|
+
with those few. Menus move with the stick or the d-pad, choose with A and go back with B; Menu pauses.
|
|
139
|
+
|
|
140
|
+
### Pixel art or smooth
|
|
141
|
+
|
|
142
|
+
- **Pixel art**: `phaser({ pixelArt: true, … })` keeps every pixel sharp, and the camera zooms by a whole number so
|
|
143
|
+
each art pixel is the same number of screen pixels: `this.cameras.main.setZoom(4)` shows a 480x270 world on the
|
|
144
|
+
1920x1080 stage (`setZoom(6)` 320x180). Draw the art at that small size, and place things on whole pixels. Never
|
|
145
|
+
scale pixel art by a fraction, or some pixels come out wider than others.
|
|
146
|
+
- **Smooth** (drawings, painted art, vector shapes): leave `pixelArt` off, draw at the size things appear on the
|
|
147
|
+
1920x1080 stage, and scale pictures down rather than up.
|
|
148
|
+
|
|
149
|
+
### Safe areas
|
|
150
|
+
|
|
151
|
+
On a TV the edge of the picture may be cut off: keep the score, lives and prompts inside the middle 90% of the stage
|
|
152
|
+
(96 px in from the sides, 54 from the top and bottom), and anything the player must see there too. On a phone Lamp's
|
|
153
|
+
touch controls sit in the bottom corners and small buttons in the top corners: put the HUD at the top center or along
|
|
154
|
+
the top edge between them, never under a thumb.
|
|
155
|
+
|
|
156
|
+
### Drawing the art
|
|
157
|
+
|
|
158
|
+
Nothing is loaded from the network, ever: the art is in Web/ or made in code.
|
|
159
|
+
|
|
160
|
+
- **Ready-made sprite sheets and tilesets** from the `assets` tool: a platformer's characters with their walk, jump
|
|
161
|
+
and hurt frames, enemies, coins, tiles, a space shooter's ships, a top-down racer's cars and track, pixel-art towns
|
|
162
|
+
and dungeons, UI and icons, each pack in one style. Before drawing a game's characters, enemies and tiles, look
|
|
163
|
+
there (`find` with `show`): a pack's matching frames are what code and `make_art` struggle to give. Open the assets
|
|
164
|
+
skill to choose and to load an XML atlas or a tilesheet with spacing.
|
|
165
|
+
|
|
166
|
+
- **Shapes made in code** are the quickest and always match: draw once with `this.make.graphics({ add: false })`
|
|
167
|
+
(rectangles, circles, rounded boxes, polygons, a gradient of bands) and `generateTexture('coin', 48, 48)`, in a
|
|
168
|
+
Boot scene, then use the texture like any picture. A palette of four to eight colors, used everywhere, makes a game
|
|
169
|
+
look made.
|
|
170
|
+
- **Pixel art in code**: `this.textures.generate('hero', { data: ['..33..', '.3333.', …], pixelWidth: 4 })` draws a
|
|
171
|
+
picture from rows of palette digits (Phaser's 16-color palette, or give `palette`), a frame per call; it's the way to
|
|
172
|
+
get a character's frames the same size and style.
|
|
173
|
+
- **Pictures from `make_art`**: for a character, an object or a background in the style of the game's icon, cut out
|
|
174
|
+
on a transparent background (with `scene` for a whole backdrop). It saves into Assets/: copy what the game uses into
|
|
175
|
+
Web/art/ (`cp Assets/hero.png Web/art/`) and load it from there. Each picture stands alone, so it suits a hero, a
|
|
176
|
+
boss, an item or a backdrop rather than animation frames: move one with tweens (a bob, a squash on landing, a tilt as
|
|
177
|
+
it runs) instead of frames that wouldn't match. Make it once and scale it in the game (`setDisplaySize`).
|
|
178
|
+
- A file the person gave (Attachments/) is copied into Web/ the same way.
|
|
179
|
+
|
|
180
|
+
Whichever way it's drawn, make it read at a glance: characters and objects with recognizable silhouettes, one sprite
|
|
181
|
+
scale and one outline style throughout, a quieter background behind the action, hazards and rewards that never look
|
|
182
|
+
alike, and animation that shows each action (a wind-up, a landing, a hit). Simple geometric art chosen for the game is
|
|
183
|
+
fine.
|
|
184
|
+
|
|
185
|
+
### Sound and saves
|
|
186
|
+
|
|
187
|
+
The game skill's: look in the `assets` tool first for an effect for each thing the player does or meets and for the
|
|
188
|
+
music, loaded once; `sound.tone` only for what nothing there fits (a jump sliding up, a coin's two quick notes, noise
|
|
189
|
+
for a hit), made once; music quiet or none. The best score, levels reached and settings with
|
|
190
|
+
`save(name, value)` and `load(name)`, read with a default for anything new (`load('best') ?? 0`).
|
|
191
|
+
A registered state keeps the player's place through a change (the scene, its mode, which menu or DOM card is
|
|
192
|
+
open, the player's position, the score, the time), and `busy()` in phaser() holds a change that runs the game's code again during a round (the game skill,
|
|
193
|
+
"Changes while it's played"). A 2D game registers only its state: Phaser's game and cameras are made again from it. The first scene
|
|
194
|
+
goes straight to the scene the state names, and that scene's `create` builds that place with no title card or intro,
|
|
195
|
+
so a fresh start and a change's start are one path:
|
|
196
|
+
|
|
197
|
+
```js
|
|
198
|
+
const play = hot(import.meta).state('play', {
|
|
199
|
+
version: 1,
|
|
200
|
+
start: () => ({ scene: 'play', mode: 'ready', level: 1, score: 0, paused: false, player: { x: 120, y: 400 } }),
|
|
201
|
+
});
|
|
202
|
+
class Boot extends Phaser.Scene {
|
|
203
|
+
constructor() { super('boot'); }
|
|
204
|
+
preload() { … }
|
|
205
|
+
create() { this.scene.start(play.scene); }
|
|
206
|
+
}
|
|
207
|
+
class Play extends Phaser.Scene {
|
|
208
|
+
constructor() { super('play'); }
|
|
209
|
+
create() {
|
|
210
|
+
play.scene = 'play';
|
|
211
|
+
this.level = buildLevel(play.level);
|
|
212
|
+
this.hero.setPosition(play.player.x, play.player.y);
|
|
213
|
+
if (play.paused) this.showPause(); else if (play.mode === 'ready') hud.card(`Level ${play.level}`);
|
|
214
|
+
}
|
|
215
|
+
update() { play.player.x = this.hero.x; play.player.y = this.hero.y; … }
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
A picture Phaser's loader read goes in again through `assetChanged(path)` in phaser() (load it again and
|
|
220
|
+
swap the texture), or the game's code runs again in place from the state.
|
|
221
|
+
|
|
222
|
+
### Keeping it smooth on the slowest place
|
|
223
|
+
|
|
224
|
+
The slowest places are an Xbox One in Developer Mode, a cheap Android phone or TV stick and a Samsung or LG TV's
|
|
225
|
+
browser; the request's device line names the slowest this game is played on and its budget. In 2D what costs is draws and filling the screen, rarely shapes:
|
|
226
|
+
|
|
227
|
+
- few textures: sprites that share one texture (an atlas, or the shapes made into one canvas texture) are drawn in
|
|
228
|
+
one batch, and every change of texture starts another;
|
|
229
|
+
- a Text object redraws its whole picture when its text changes: change it only when the number does, never every
|
|
230
|
+
frame (or use HTML over the canvas for the HUD);
|
|
231
|
+
- a Graphics object drawn in play is redrawn every frame: make it a texture once instead;
|
|
232
|
+
- layers of big pictures over the whole screen (parallax backgrounds, a full-screen overlay) each fill the screen
|
|
233
|
+
again: two or three at most on a TV;
|
|
234
|
+
- effects (`preFX`, `postFX`, glow, blur, bloom) are full extra passes: none on a TV or an Xbox One;
|
|
235
|
+
- no allocation per frame, and pools for what comes and goes; Arcade rather than Matter.
|
|
236
|
+
|
|
237
|
+
The log on a device says every ten seconds how many frames a second it gets there: when it's short of 60, draw less.
|
|
238
|
+
|
|
239
|
+
### Checking it
|
|
240
|
+
|
|
241
|
+
`look_at_app` shows the game in its window on this Mac, where the person is in it, opening it when it's closed;
|
|
242
|
+
`use_app` plays it there with Lamp's own controller (`stick`, `button`, `wait`, and a `click` on what its screen shows), from wherever it is, a picture at the
|
|
243
|
+
end (the game skill, "Playtesting"). When the person asks you to try, play or test it, play it as they would: start
|
|
244
|
+
it, jump, die, win, pause, change a setting and come back to find it kept. Once it looks or plays right there,
|
|
245
|
+
`look_at_app` with `on_device` looks at it in the preview of where Try On points (the iPhone or iPad simulator); the
|
|
246
|
+
person's own iPhone, iPad or Xbox is only for their Play.
|
|
247
|
+
|
|
248
|
+
## SpriteKit in a Mac or iPhone app
|
|
249
|
+
|
|
250
|
+
The game is an `SKScene` subclass for each screen, shown in SwiftUI with `SpriteView`, with SwiftUI over it for the
|
|
251
|
+
score, menus and prompts (the game-ui skill says how to choose a game's screens and how they look):
|
|
252
|
+
|
|
253
|
+
```swift
|
|
254
|
+
import SwiftUI
|
|
255
|
+
import SpriteKit
|
|
256
|
+
|
|
257
|
+
struct GameScreen: View {
|
|
258
|
+
@State private var scene = PlayScene(size: CGSize(width: 1920, height: 1080))
|
|
259
|
+
var body: some View {
|
|
260
|
+
SpriteView(scene: scene, preferredFramesPerSecond: 60, options: [.ignoresSiblingOrder])
|
|
261
|
+
.ignoresSafeArea()
|
|
262
|
+
.overlay(alignment: .top) { ScoreBar(score: scene.score) }
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
Make the scene once (`@State`), never in `body`. A scene of a fixed size with `scaleMode = .aspectFit` is laid out
|
|
268
|
+
once and letterboxed on any window, like the game kind's stage; `.resizeFill` with an `SKCameraNode` following the
|
|
269
|
+
player shows more of a big world on a big window. What SwiftUI shows over it (the score, lives, a pause) comes from
|
|
270
|
+
the scene as `@Observable` (Observation), which `@State` holds as above; a scene that is an `ObservableObject` with
|
|
271
|
+
`@Published` values is held with `@StateObject` instead, or the view never sees them change.
|
|
272
|
+
|
|
273
|
+
- **The step**: `update(_ currentTime:)` works out the time since the last frame (at most 1/20 s, so a pause doesn't
|
|
274
|
+
jump) and moves by it; physics bodies move themselves. Logic that must be exact counts fixed steps of 1/60 s from it.
|
|
275
|
+
- **Cameras**: an `SKCameraNode`, `scene.camera = cam`, moved toward the player each frame (`cam.position +=
|
|
276
|
+
(target - cam.position) * min(1, dt * 8)`) and kept inside the level; the HUD is SwiftUI, or nodes added to the
|
|
277
|
+
camera.
|
|
278
|
+
- **Tiles**: for a level of rows in code, a sprite per tile is fine to a few thousand; past that, draw the level's still
|
|
279
|
+
part once into one texture (`view.texture(from: node)`) and show that. `SKTileMapNode` with a tile set made in code
|
|
280
|
+
works too, for a big grid.
|
|
281
|
+
- **Sprite sheets**: one picture cut into frames with `SKTexture(rect:in:)` (the rect in 0-1 of the sheet), played with
|
|
282
|
+
`SKAction.animate(with:timePerFrame:)`. Textures of one `SKTextureAtlas`, or cut from one sheet, draw in one batch.
|
|
283
|
+
- **Physics**: `SKPhysicsBody(rectangleOf:)` or `(circleOfRadius:)`, each with its `categoryBitMask`,
|
|
284
|
+
`collisionBitMask` and `contactTestBitMask`, and the scene as `physicsWorld.contactDelegate` hearing
|
|
285
|
+
`didBegin(_:)`. A platformer's ground is one body for each run of tiles side by side, not one per tile, so the player
|
|
286
|
+
doesn't catch on the seams; give the player `allowsRotation = false` and set its velocity rather than pushing it.
|
|
287
|
+
- **Controls**: a controller through GameController, read each frame from `GCController.current?.extendedGamepad`
|
|
288
|
+
(`leftThumbstick`, `dpad`, `buttonA`…, and `buttonMenu` to pause); on the Mac the keys with
|
|
289
|
+
`NSEvent.addLocalMonitorForEvents(matching: [.keyDown, .keyUp])`, keeping the keys held in a set and returning nil
|
|
290
|
+
for the game's keys (so they don't beep); on an iPhone, touch: `GCVirtualController` with a left stick and one or
|
|
291
|
+
two buttons draws them over the game and reads as a controller, so one reading covers both. Name a controller's
|
|
292
|
+
buttons from the controller itself (`buttonA.sfSymbolsName` as an `Image(systemName:)`, or `localizedName`), never
|
|
293
|
+
a fixed letter, and keys by the key ("Space", "←").
|
|
294
|
+
- **Pixel art**: `texture.filteringMode = .nearest` on every texture, positions rounded to whole pixels, and the scene
|
|
295
|
+
scaled by a whole number.
|
|
296
|
+
- **Sound and saves**: as the game skill says for Swift (files with AVAudioPlayer or effects made once with AVAudioEngine, `.ambient` on iPhone),
|
|
297
|
+
and the best score and settings in LampStore.
|
|
298
|
+
- **Game Center** (`GKLocalPlayer.local.authenticateHandler`, leaderboards) works only in an app signed for the
|
|
299
|
+
person's Apple team with Game Center on: keep the game whole without it, scores in LampStore as well.
|
|
300
|
+
- **Art**: as above: shapes in code (`SKShapeNode` drawn once into a texture with `view.texture(from:)`, since a shape
|
|
301
|
+
node is costly to draw), pictures from `make_art` in Assets/ loaded once with `LampAssets.image("hero.png").map(SKTexture.init(image:))`.
|
|
302
|
+
|
|
303
|
+
Look with `look_at_app` (with `motion` for what moves), and use it with `use_app`, keys and clicks, when the person
|
|
304
|
+
asks you to try it: the game skill's "Playtesting" says what to say otherwise. Its "Keeping every frame smooth" holds
|
|
305
|
+
here too.
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: 3d-game
|
|
3
|
+
description: 3D games and scenes in a web page with three.js, on any device that runs web, TVs included - which library 3D is drawn with, loading it and the renderer on Lamp's game runtime or in a TV or device page, models, light and materials, physics with Rapier (things that fall, roll, stack and collide, a player that walks on them, a car on its wheels), and what costs the most. Read before writing or changing 3D in a web page.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# 3D in a web page
|
|
7
|
+
|
|
8
|
+
three.js runs in a page wherever the device takes a page (`make_app_for`'s table) and its browser gives WebGL 2: a
|
|
9
|
+
host for the page isn't enough on its own, so check WebGL 2 first (below). What differs then is speed: a Mac draws far
|
|
10
|
+
more than a phone, and a TV's chip is often slower than a phone's. Nothing here limits what you make; it says what
|
|
11
|
+
costs the most, so you can choose what the game is worth on the devices it's played on.
|
|
12
|
+
|
|
13
|
+
Lamp ships three.js in two kinds of page: a game's page on its runtime (`make_app_for` with `kind` "game"), and a page
|
|
14
|
+
with no runtime: a Samsung or LG TV app's page, or a device integration's web app whose rules name the 3D library's folder. A page with no
|
|
15
|
+
runtime has no game runtime, no Phaser bridge and no Rapier: it loads three.js from the folder its rules give and makes
|
|
16
|
+
its own renderer and frame loop (below).
|
|
17
|
+
|
|
18
|
+
A 3D game is made this way by default, on every device Lamp has a host for a page on, the Mac included. 3D in Swift,
|
|
19
|
+
in a Mac app made natively (because the person asked for native, or the app was made so), is the scene-3d skill's
|
|
20
|
+
(RealityKit through LampSceneView); native 3D on iPhone, iPad or Apple TV is written with that device's own
|
|
21
|
+
frameworks. A game whose play is flat is better made in 2D with Phaser (the 2d-game skill), which also runs where a
|
|
22
|
+
TV's browser has no WebGL 2. The game skill holds what every game needs
|
|
23
|
+
(the frame loop, controls, sound, saves); read it too.
|
|
24
|
+
|
|
25
|
+
A menu that's a place (the game-ui skill) is a list of stops with a camera position for each, eased between in `frame`,
|
|
26
|
+
with the stop in the game's registered state and the camera registered (the game skill, "Changes while it's played").
|
|
27
|
+
|
|
28
|
+
## The renderer
|
|
29
|
+
|
|
30
|
+
### A game on Lamp's runtime
|
|
31
|
+
|
|
32
|
+
index.html loads lamp.js, then the scaffold's import map, then game.js as a module: keep that. The import map's
|
|
33
|
+
`three` and `three/addons/` come from `Web/three`: the WebGL build and its addons such as GLTFLoader. `three/webgpu`
|
|
34
|
+
and `three/tsl` (node materials, drawing with WebGL 2) are added once the page imports them, and only a heavy scene
|
|
35
|
+
needs them. Nothing else is there, so no Draco or KTX2 files (the `assets` tool brings in none), and nothing is
|
|
36
|
+
loaded from the network. Physics is
|
|
37
|
+
Rapier, mapped in the import map when a game needs it (Physics, below).
|
|
38
|
+
|
|
39
|
+
game.js registers one renderer, `live.renderer(() => new THREE.WebGLRenderer({ antialias: true }))` (with
|
|
40
|
+
`const live = hot(import.meta)`), each camera with `live.camera(id, build)` and its state with `live.state` (the game
|
|
41
|
+
skill, "Changes while it's played", has the recipe), and calls `game({ renderer, init, frame, touch })` from
|
|
42
|
+
lamp-game.js once. A `WebGPURenderer` is made and given its `await r.init()` in an async build:
|
|
43
|
+
`const renderer = await live.renderer(async () => { const r = new THREE.WebGPURenderer(); await r.init(); return r; })`.
|
|
44
|
+
|
|
45
|
+
- Lamp sizes the canvas to 1920x1080 and fits it to the window or screen.
|
|
46
|
+
- `init` runs once, before the first frame. It may be async, to load models with
|
|
47
|
+
`new GLTFLoader().loadAsync('ship.glb')` or sounds; make or load every sound here. It builds what the state says:
|
|
48
|
+
the level, the player and the open menu or overlay from the registered state, which is already back after a change
|
|
49
|
+
of the code, and the opening (a title, an intro, a briefing) only when `play.mode` says so (the game skill, "Changes
|
|
50
|
+
while it's played"; the game-ui skill says how a game opens and what its menus are). A camera's start pose goes in its `build`, never here.
|
|
51
|
+
- `frame(f)` runs about 60 times a second. It reads the controller as `f.pads[0]`, moves things by `f.dt` (and
|
|
52
|
+
`f.time`), and ends with `renderer.render(scene, camera)`.
|
|
53
|
+
- `touch` names the buttons Lamp's touch controls show on a phone or tablet.
|
|
54
|
+
- game.js only puts the parts together, and keeps the scene, the lights and the world's parts with
|
|
55
|
+
`hot(import.meta).keep` (the game skill, "Changes while it's played", has the recipe). The world is built in parts (the ground, the
|
|
56
|
+
buildings, the trees and street details, the props, the traffic), each in a module of its own and into a group of
|
|
57
|
+
its own, kept with the data it's built from as its `from`, so a change to one part or its data builds only that
|
|
58
|
+
part again, and a part that's slow to build (the city, the terrain, the traffic) with `background: true`, built in
|
|
59
|
+
steps or with a worker, so the game plays on while it's built (the game skill's recipe); each thing that moves or changes in play (the player,
|
|
60
|
+
a vehicle, the camera, enemies, the HUD's code) is a class in a module that ends with `hot(import.meta).accept()`,
|
|
61
|
+
so a change to its methods goes into the running scene with nothing built again. A uniform's value or a material's
|
|
62
|
+
color set in an `update` swaps; a material made anew compiles its shader again, which Lamp does before the
|
|
63
|
+
first frame after a change while the frame before stays on screen, so a world's materials are made in its kept parts.
|
|
64
|
+
|
|
65
|
+
A page laid out at the window's size whose HUD is its own HTML (a strategy game, a page made for phones as well as
|
|
66
|
+
TVs) passes `fill: true` to `game()` and places and sizes its canvas itself, drawing it smaller when frames run slow;
|
|
67
|
+
the rest of this section is for a game drawn at 1920x1080.
|
|
68
|
+
|
|
69
|
+
When frames run slow, Lamp draws the game smaller and scales it up to the screen by itself (`sharp: true` in `game()`
|
|
70
|
+
keeps it at 1920x1080), so size anything drawn off-screen (a render target, a post-process pass) from
|
|
71
|
+
`renderer.getSize()`, never from 1920x1080. Read lamp-game.js's comments first: they're the whole interface.
|
|
72
|
+
|
|
73
|
+
### A page with no runtime (a TV app, a device's web app)
|
|
74
|
+
|
|
75
|
+
The page's rules name the folder Lamp writes three.js in and the path the page reaches it at (`./three/` in a Samsung
|
|
76
|
+
or LG TV app's Web/). Lamp writes that folder before every build: never edit it. In index.html, after lamp.js, put the
|
|
77
|
+
import map with that path:
|
|
78
|
+
|
|
79
|
+
```html
|
|
80
|
+
<script type="importmap">{ "imports": { "three": "./three/three.module.js", "three/addons/": "./three/addons/" } }</script>
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
then the 3D code in a `<script type="module">` (or a module file of your own beside it), with
|
|
84
|
+
`import * as THREE from 'three'` and addons from `'three/addons/…'`. Use this three.js for anything 3D, never another
|
|
85
|
+
engine or one from the network. Make the renderer yourself, sized to 1920x1080 at a pixel ratio of 1 (the page is laid
|
|
86
|
+
out at that size and scaled), and draw from your own `requestAnimationFrame`, moving everything by the time since the
|
|
87
|
+
last frame. Modules and import maps need a newer TV browser than the oldest the TV rules allow for, and three.js needs
|
|
88
|
+
WebGL 2 (next).
|
|
89
|
+
|
|
90
|
+
The three.js Lamp ships asks the browser only for a WebGL 2 context and stops with an error when it gets none, so
|
|
91
|
+
WebGL 2 comes before anything else here: check on the device that a canvas gives one (log whether
|
|
92
|
+
`canvas.getContext('webgl2')` returns a context, and ask the person to try it there with Play, since Lamp's looks
|
|
93
|
+
never reach their device). Most browsers
|
|
94
|
+
have it; not every Samsung TV does. Where it's missing, make the game another way (2D with Phaser, which draws with WebGL 1 or a plain canvas, or native on that
|
|
95
|
+
device) or tell the person why 3D in a page can't run there.
|
|
96
|
+
|
|
97
|
+
## Models, light and materials
|
|
98
|
+
|
|
99
|
+
- Before building a game's buildings, vehicles, characters or trees in code, look in Lamp's free models (`assets` with
|
|
100
|
+
`find` and `show`; the assets skill): many distinct things, and anything that walks or runs, come out better
|
|
101
|
+
ready-made. Shapes made in code (boxes, spheres, cylinders, extruded outlines), well lit and colored, often look better than
|
|
102
|
+
mismatched models, and cost little. Build each character and prop as a `THREE.Group` of them in a function of its
|
|
103
|
+
own (a body, a head, eyes that blink, wheels that turn, a tail that wags), its parts named so they can move, and
|
|
104
|
+
give it a little life: a bob, a lean into a turn, a squash on landing. A model file (a .glb) is one the person
|
|
105
|
+
gave or named, or a ready-made one from the `assets` tool (buildings, vehicles, characters with animations, trees:
|
|
106
|
+
the assets skill says when they suit and how to scale, turn, copy and animate them). It goes in Web/ and loads once
|
|
107
|
+
in `init` with lamp-game.js's `model('ship.glb')` (a group holding its scene, with `group.animations`), and a picture with
|
|
108
|
+
`texture('bricks.png', t => { t.colorSpace = THREE.SRGBColorSpace; })`: a new copy of either goes into the running
|
|
109
|
+
game, inside the same group or texture. A clone of the model, or a mixer playing its animations, is made again in
|
|
110
|
+
`assetChanged(path)` (the game skill, "Changes while it's played").
|
|
111
|
+
- One or two lights computed in the shader are the safe start. MeshStandardMaterial looks good under them;
|
|
112
|
+
MeshLambertMaterial or MeshBasicMaterial cost less.
|
|
113
|
+
- Make every mesh, material and texture once in `init` and reuse it; many copies of one shape are one
|
|
114
|
+
`THREE.InstancedMesh`.
|
|
115
|
+
- Compose the view, within the same budget: a camera framing what the player needs to see ahead, things at one
|
|
116
|
+
consistent scale, light that separates the player and hazards from the ground, and contact cues (a blob shadow, a
|
|
117
|
+
dust puff) that put things on the ground instead of floating.
|
|
118
|
+
|
|
119
|
+
## Physics
|
|
120
|
+
|
|
121
|
+
**Whether a game needs it.** Most don't. A runner, a shooter, a racer on a track or a platformer on flat ground moves
|
|
122
|
+
things by its own rules and tests what touches what itself: the distance between two centers, two boxes
|
|
123
|
+
(`THREE.Box3`), a ray down to the ground over a few meshes (`THREE.Raycaster`). That costs nearly nothing and does
|
|
124
|
+
exactly what the game means. Physics is for games whose fun is things behaving like things: a stack of crates that
|
|
125
|
+
topples, marbles that roll and knock into each other, a board that tilts, a ball through pins, a car on its
|
|
126
|
+
suspension, a door on a hinge, a chain, a pile of anything. Once a game starts writing its own bounces, stacking or
|
|
127
|
+
rolling, it wants physics: hand-made physics is where objects sink, jitter and pass through walls.
|
|
128
|
+
|
|
129
|
+
**Rapier.** Lamp ships Rapier, a physics engine in WebAssembly, as one module that loads like three.js does,
|
|
130
|
+
wherever the game is played. It's for a game's page on its runtime; a TV app's page doesn't get it. Add it to the import map in index.html,
|
|
131
|
+
`"@dimforge/rapier3d-compat": "./rapier/rapier.js"`, and Lamp puts `Web/rapier/` beside the page (it's Lamp's, like
|
|
132
|
+
`Web/three/`; a game that doesn't import it doesn't carry it). Start it once, in `init`, and step the world with
|
|
133
|
+
`fixedStep` from lamp-game.js:
|
|
134
|
+
|
|
135
|
+
```js
|
|
136
|
+
import * as THREE from 'three';
|
|
137
|
+
import RAPIER from '@dimforge/rapier3d-compat';
|
|
138
|
+
import { game, fixedStep } from './lamp-game.js';
|
|
139
|
+
|
|
140
|
+
let world, events, physics, pad, presses = 0;
|
|
141
|
+
game({ renderer, async init() {
|
|
142
|
+
await RAPIER.init();
|
|
143
|
+
world = new RAPIER.World({ x: 0, y: -9.81, z: 0 });
|
|
144
|
+
events = new RAPIER.EventQueue(true);
|
|
145
|
+
physics = fixedStep(() => { steer(); world.step(events); events.drainCollisionEvents(hit); });
|
|
146
|
+
// … the ground, the bodies and their meshes
|
|
147
|
+
}, frame(f) {
|
|
148
|
+
pad = f.pads[0];
|
|
149
|
+
presses |= pad.pressed; // kept until a step uses them
|
|
150
|
+
physics(f.dt);
|
|
151
|
+
place();
|
|
152
|
+
renderer.render(scene, camera);
|
|
153
|
+
} });
|
|
154
|
+
|
|
155
|
+
function steer() {
|
|
156
|
+
// Held buttons and sticks are read as they are now, every step.
|
|
157
|
+
ball.applyImpulse({ x: pad.lx * PUSH, y: 0, z: -pad.ly * PUSH }, true);
|
|
158
|
+
// A press is used by one step only: a frame can run no step (at 120 frames a second) or two (at 30).
|
|
159
|
+
if (presses & CONTROLS.jump) ball.applyImpulse({ x: 0, y: JUMP, z: 0 }, true);
|
|
160
|
+
presses = 0;
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
- `await RAPIER.init()` goes inside `init` (never at the top of the module), so a failure is the game's start-up
|
|
165
|
+
error, which the game's log and the look report.
|
|
166
|
+
- The world steps 1/60 s at a time (`world.timestep`, its default), the step `fixedStep` runs as often as the frame
|
|
167
|
+
needs, so the game plays the same at 30, 60 or 120 frames a second. Never pass `f.dt` to the world or change `timestep` per frame. Anything that
|
|
168
|
+
pushes bodies (the controls, a motor) runs inside the step, as `steer()` does above, reading the pad the frame
|
|
169
|
+
kept. A press (`pressed`, `released`) belongs to a frame, not a step: gather it into `presses` in `frame` and let the
|
|
170
|
+
first step that runs use it and clear it, or a jump is lost at 120 frames a second and done twice at 30. Anything
|
|
171
|
+
random in the setup comes from `random()`.
|
|
172
|
+
- Meters, kilograms and seconds, y up, as three.js has them. Keep things their real size: a box a meter across, a
|
|
173
|
+
marble a few centimeters. Rapier's tolerances are tuned for things about a meter across: in a world of small things
|
|
174
|
+
(a tabletop maze, marbles, dice) set `world.lengthUnit` to their typical size in meters (0.05 for marbles), or they
|
|
175
|
+
sink into each other and jitter. Small fast things can pass through thin walls between steps: make walls thick, or
|
|
176
|
+
give the fast body `setCcdEnabled(true)`.
|
|
177
|
+
|
|
178
|
+
**Bodies and colliders.** A body is how a thing moves; its colliders are its shape.
|
|
179
|
+
|
|
180
|
+
- Fixed (`RigidBodyDesc.fixed()`): what never moves, the ground, walls, the track.
|
|
181
|
+
- Dynamic (`RigidBodyDesc.dynamic()`): moved only by physics, crates, balls, debris. Push it with `applyImpulse` (a
|
|
182
|
+
kick) or `addForce` (a steady push), never by setting its position each frame.
|
|
183
|
+
- Kinematic (`RigidBodyDesc.kinematicPositionBased()`): moved by the game, pushing dynamic bodies without being
|
|
184
|
+
pushed back: a moving platform, a tilting board, a door the game opens. Give it its place each step with
|
|
185
|
+
`setNextKinematicTranslation` and `setNextKinematicRotation`.
|
|
186
|
+
|
|
187
|
+
Colliders are simple shapes: `ColliderDesc.cuboid`, `ball`, `capsule`, `cylinder`, `roundCuboid` (a box with
|
|
188
|
+
rounded edges rolls and stacks more kindly), several on one body for a shape made of parts (a table: a top and four
|
|
189
|
+
legs). A model's look never becomes its collider: an odd shape that moves is a `convexHull` of a few of its points,
|
|
190
|
+
a level that never moves may be a `trimesh` of a simplified mesh, and land is a `heightfield`. Bounce, grip and weight
|
|
191
|
+
are the collider's: `setRestitution`, `setFriction`, `setDensity`.
|
|
192
|
+
|
|
193
|
+
**Meshes in step with bodies.** Keep each body with the mesh that shows it and copy the body onto the mesh after the
|
|
194
|
+
step, straight into three.js's own objects, so nothing is allocated per frame:
|
|
195
|
+
|
|
196
|
+
```js
|
|
197
|
+
const things = []; // { body, mesh }
|
|
198
|
+
function place() {
|
|
199
|
+
for (const t of things) { t.body.translation(t.mesh.position); t.body.rotation(t.mesh.quaternion); }
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
Many of one thing (a hundred crates) are one `THREE.InstancedMesh`: read each body into one Vector3 and Quaternion
|
|
204
|
+
kept for it, `matrix.compose(position, quaternion, scale)`, `setMatrixAt(i, matrix)`, and set `instanceMatrix.needsUpdate`.
|
|
205
|
+
|
|
206
|
+
**The player.** A character that walks, climbs steps and stands on slopes is a kinematic body with a capsule and
|
|
207
|
+
Rapier's character controller, rather than a dynamic ball pushed around:
|
|
208
|
+
|
|
209
|
+
```js
|
|
210
|
+
const controller = world.createCharacterController(0.02);
|
|
211
|
+
controller.enableAutostep(0.3, 0.2, true); // steps up to 30 cm
|
|
212
|
+
controller.enableSnapToGround(0.3);
|
|
213
|
+
controller.setMaxSlopeClimbAngle(45 * Math.PI / 180);
|
|
214
|
+
// each step: where the player wants to go this step, gravity included
|
|
215
|
+
controller.computeColliderMovement(playerCollider, wanted);
|
|
216
|
+
const moved = controller.computedMovement(), at = playerBody.translation();
|
|
217
|
+
playerBody.setNextKinematicTranslation({ x: at.x + moved.x, y: at.y + moved.y, z: at.z + moved.z });
|
|
218
|
+
const grounded = controller.computedGrounded(); // may jump
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
The game keeps the player's own vertical speed (gravity added each step, set on a jump, zeroed when grounded).
|
|
222
|
+
|
|
223
|
+
**A vehicle.** A car is a dynamic body (a box for the chassis, its weight low) and Rapier's vehicle controller, which
|
|
224
|
+
casts a ray for each wheel and gives it suspension and grip:
|
|
225
|
+
|
|
226
|
+
```js
|
|
227
|
+
const car = world.createVehicleController(chassis);
|
|
228
|
+
const down = { x: 0, y: -1, z: 0 }, axle = { x: -1, y: 0, z: 0 };
|
|
229
|
+
for (const [x, z] of [[-0.8, 1.3], [0.8, 1.3], [-0.8, -1.3], [0.8, -1.3]]) car.addWheel({ x, y: 0, z }, down, axle, 0.3, 0.35);
|
|
230
|
+
// each step: steering on the front wheels, engine and brake on the back
|
|
231
|
+
car.setWheelSteering(0, steer); car.setWheelSteering(1, steer);
|
|
232
|
+
car.setWheelEngineForce(2, gas); car.setWheelEngineForce(3, gas);
|
|
233
|
+
car.updateVehicle(world.timestep);
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
Wheels are drawn from the controller (`wheelSuspensionLength(i)`, `wheelRotation(i)`, `wheelSteering(i)`), not given
|
|
237
|
+
bodies of their own. Tune `setWheelFrictionSlip` and the suspension by driving it in its window (`use_app`).
|
|
238
|
+
|
|
239
|
+
**Joints** hold two bodies together: `RAPIER.JointData.revolute` (a hinge: a door, a wheel, a flipper),
|
|
240
|
+
`prismatic` (a slider), `spherical` (a ball joint: a chain link, a pendulum), `fixed` (welded), made with
|
|
241
|
+
`world.createImpulseJoint(data, body1, body2, true)`. A hinge can be driven with `configureMotorVelocity`.
|
|
242
|
+
|
|
243
|
+
**What touched what.** Give a collider `setActiveEvents(RAPIER.ActiveEvents.COLLISION_EVENTS)` and read
|
|
244
|
+
`events.drainCollisionEvents((a, b, started) => …)` after each step, a pair of collider handles: keep a `Map` from
|
|
245
|
+
handle to the game's thing. A sensor (`setSensor(true)`) is a zone that's felt but not hit: a goal, a pickup, a
|
|
246
|
+
checkpoint. A collision is the moment for a sound and a rumble.
|
|
247
|
+
|
|
248
|
+
**Keeping it light.** Physics runs on the CPU, before anything is drawn, so it takes from the same 16 ms. Resting
|
|
249
|
+
bodies fall asleep by themselves and then cost almost nothing: leave them be (don't set their position or velocity
|
|
250
|
+
unless something happens to them), and when the game moves a sleeping one itself, `wakeUp()` it. Balls, boxes and
|
|
251
|
+
capsules are cheapest; convex hulls cost more, trimeshes most. Where the request's device line names a slow place
|
|
252
|
+
(a TV, a streamer, an older console), keep to about 100 dynamic bodies awake at once, with a few hundred more asleep or
|
|
253
|
+
fixed; a Mac takes a thousand. Step only through `fixedStep`, never make a body or collider per frame (make them in `init` or when a level starts, and reuse them), and remove what
|
|
254
|
+
falls out of the world (`world.removeRigidBody`). The device's log says how its frames keep to time: when they're late
|
|
255
|
+
with physics in play, make fewer bodies move at once before drawing less.
|
|
256
|
+
|
|
257
|
+
## What costs the most
|
|
258
|
+
|
|
259
|
+
Roughly in order: shadow maps (each light that casts one draws the scene again), full-screen effects
|
|
260
|
+
(post-processing, bloom), many lights, a high pixel ratio (lamp-game.js already draws at 1, never at the screen's own),
|
|
261
|
+
many draw calls (each distinct mesh and material), large textures, and many triangles.
|
|
262
|
+
|
|
263
|
+
## On a TV
|
|
264
|
+
|
|
265
|
+
Some platforms might not be powerful enough to run three.js.
|
|
266
|
+
|
|
267
|
+
## Checking it
|
|
268
|
+
|
|
269
|
+
`look_at_app` shows the game in its window on this Mac, where the person is in it, opening it when it's closed; its
|
|
270
|
+
log says every ten seconds how it kept to its frames there, which says nothing of a slower device's: keep to the budget
|
|
271
|
+
in the request's device line. `use_app` plays it there with Lamp's own controller. `look_at_app` with `on_device` looks
|
|
272
|
+
at the game in the preview of where Try On points (the iPhone or iPad simulator), never the person's own device, so its
|
|
273
|
+
frame times there say nothing of a phone's either.
|
|
274
|
+
When the person asks you to try, play or test it, play it as the game skill's "Playtesting" says.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: accounts
|
|
3
|
+
description: The person's calendar, reminders, contacts, mail and cloud files through the accounts already on the device, and on a Mac what lives in another app they use (LampComputer). Read before an app reaches any of them, including when a wish names a service such as "my Google Calendar".
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# The person's own accounts and apps
|
|
7
|
+
|
|
8
|
+
When a wish needs the person's calendar, reminders, contacts, mail or files, reach first for what the device is
|
|
9
|
+
already signed in to, before any service's API, sign-in, key or connector.
|
|
10
|
+
|
|
11
|
+
## Calendar, reminders and contacts (Mac and iPhone)
|
|
12
|
+
|
|
13
|
+
They come through the accounts already on the device (iCloud, Google, Exchange, Outlook), with no sign-in or key, from
|
|
14
|
+
LampSupport.swift: `LampCalendar.events(from:to:)` / `.add(...)`, `LampReminders.open()` / `.add(...)` /
|
|
15
|
+
`.complete(id)`, `LampContacts.search("name")` / `.find(email:)`.
|
|
16
|
+
|
|
17
|
+
## Mail and files (Mac only)
|
|
18
|
+
|
|
19
|
+
- Email: `LampMail.inbox(limit:)` / `.search(text)` / `.body(of:)` / `.draft(to:subject:body:)` (a draft opens in Mail
|
|
20
|
+
for the person to send; nothing is sent by the app).
|
|
21
|
+
- Their files in Google Drive, Dropbox, OneDrive or iCloud Drive are folders on the Mac: `LampFolders.synced` lists
|
|
22
|
+
them and `LampFolders.synced("Google Drive")` gives one; read, write and watch them as files.
|
|
23
|
+
|
|
24
|
+
## What a service's name means
|
|
25
|
+
|
|
26
|
+
A wish that names a service ("my Google Calendar", "my Gmail", "my Outlook contacts", "my Dropbox") means these:
|
|
27
|
+
never call the service's web API, never ask for a key, a sign-in or a developer registration, and don't ask which
|
|
28
|
+
service they use. When their account or folder isn't on the Mac, the app says in one line where to add it (System
|
|
29
|
+
Settings → Internet Accounts, the service's own Mac app, or Mail) and works once it's there. macOS and iOS ask the
|
|
30
|
+
person once per kind of data; each call throws a plain message if refused, so show `error.localizedDescription`.
|
|
31
|
+
|
|
32
|
+
## Another app the person uses (Mac only)
|
|
33
|
+
|
|
34
|
+
When the information or the job lives in another app the person uses and the accounts and synced folders above and
|
|
35
|
+
`connectors` can't reach it (Slack, Messages, Notes, or a site in their browser such as LinkedIn or a CRM), use
|
|
36
|
+
`LampComputer.ask("…")` to read it or `LampComputer.perform("…")` to act there, from LampSupport.swift. It works with
|
|
37
|
+
what they're already signed in to, with no API keys or connectors, and returns text (ask for JSON in the task when
|
|
38
|
+
the app needs structure). Calls take 10–60 seconds, so run them when the person asks (a button), never on a timer,
|
|
39
|
+
and show progress. `perform` asks the person before every click or keystroke. Prefer files, LampText and the web when
|
|
40
|
+
they can do the job, and prefer the accounts above to LampComputer for mail, calendar, people and files: they're
|
|
41
|
+
instant and free.
|