From 711bc739ec64a07fd8e4fac7428addedd3d09d8a Mon Sep 17 00:00:00 2001 From: Arne Keller Date: Mon, 31 Jan 2022 13:26:35 +0100 Subject: [PATCH] Cleanups + comments --- game.html | 4 +- game.js | 111 +++++++++++++++++++++++++++++++++--------------------- main.css | 67 +++----------------------------- 3 files changed, 76 insertions(+), 106 deletions(-) diff --git a/game.html b/game.html index 6d5521d..94e816e 100644 --- a/game.html +++ b/game.html @@ -4,7 +4,7 @@ three.js webgl - tunnel racer - + @@ -18,6 +18,6 @@ - + diff --git a/game.js b/game.js index c5570e8..45fdcb9 100644 --- a/game.js +++ b/game.js @@ -1,3 +1,9 @@ +// ---------------------- +// | setup. of three.js | +// ---------------------- + +// shaders for sphere outline +// https://stemkoski.github.io/Three.js/Shader-Glow.html const vertexShaderGlow = ` uniform vec3 viewVector; uniform float c; @@ -17,7 +23,7 @@ void main() vec3 vNormel = normalize( normalMatrix * viewVector ); intensity = pow( c - dot(vNormal, vNormel), p ); - gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); + gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); vColor = hsv2rgb(vec3(fract(gl_Position[2] / 270.0), 0.7, 0.7)); //vColor = vec3((sin(position[2] / 360.0) + 1.0) * 0.5, (sin(position[2] / 1000.0) + 1.0) * 0.5, (sin(position[2] / 70.0) + 1.0) * 0.5); //vColor = vec3(0.5, 0, 0.5); @@ -34,6 +40,7 @@ void main() } `; +// shaders for tunnel border const fragmentShader = ` #include @@ -80,20 +87,33 @@ const vertexShader = ` } `; -const diameterOfTunnel = 100; +// size of the tunnel +const tunnelRadius = 100; +// z interval of new obstacles const spawnInterval = 40; +// coordinate of newest obstacle let lastSpawned = 0; +// gyroscope variables var gn; let headSet = false; let gammaReference = 0.0; let leftRightMove = 0.0; let upDownMove = 0.0; -var speed = 5.0; +// current player speed +const initialSpeed = 5.0; +let speed = initialSpeed; +// score points let score = 0; +// amount of sphere objects removed let removed = 0; +// player is actively racing let running = false; +// obstacles +const spheres = []; +// wall segments +const borders = []; let frameCount = 0; function logger(text) { @@ -101,14 +121,14 @@ function logger(text) { } function init_gn() { - var args = { + const args = { logger: logger }; gn = new GyroNorm(); gn.init(args).then(function() { - var isAvailable = gn.isAvailable(); + const isAvailable = gn.isAvailable(); if (!isAvailable.deviceOrientationAvailable) { console.log({ message: 'Device orientation is not available.' @@ -196,11 +216,13 @@ document.getElementById("set_head").addEventListener("click", () => { init_gn(); +// setup three.js: materials, geometries, scene, lighting and camera let scene, renderer; const camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 10000); +// obstacle geometry and material const geometrySphere = new THREE.SphereGeometry(11, 32, 32); -const materialCube = new THREE.MeshBasicMaterial(); +const materialSphere = new THREE.MeshBasicMaterial(); const customMaterial = new THREE.ShaderMaterial({ uniforms: { "c": { @@ -217,14 +239,13 @@ const customMaterial = new THREE.ShaderMaterial({ } }, vertexShader: vertexShaderGlow, - //vertexColors: true, fragmentShader: fragmentShaderGlow, side: THREE.FrontSide, blending: THREE.AdditiveBlending, transparent: true }); - +// wall texture const loader = new THREE.TextureLoader(); const texture = loader.load('./bayer.png'); texture.minFilter = THREE.NearestFilter; @@ -249,9 +270,6 @@ const material = new THREE.ShaderMaterial({ }); const borderGeometry = new THREE.PlaneGeometry(1, 1); -const cubes = []; -const borders = []; - init(); animate(); @@ -264,12 +282,13 @@ document.getElementById("start").onclick = () => { } document.getElementById("start").style.zIndex = -10; document.getElementById("start").style.visibility = "hidden"; - speed = 5.0; + // reset variables + speed = initialSpeed; score = 0; - for (let i = 0; i < cubes.length; i++) { - scene.remove(cubes[i]); + for (let i = 0; i < spheres.length; i++) { + scene.remove(spheres[i]); } - cubes.length = 0; + spheres.length = 0; removed = 0; running = true; } @@ -284,8 +303,6 @@ function gameOver() { function init() { - // - scene = new THREE.Scene(); camera.position.set(0, 10, 25); @@ -318,6 +335,7 @@ function onWindowResize() { } +// main render/update function called once per frame function animate() { requestAnimationFrame(animate); @@ -327,18 +345,19 @@ function animate() { if (running) { - if (Math.abs(camera.position.x) > diameterOfTunnel || Math.abs(camera.position.y) > diameterOfTunnel) { + if (Math.abs(camera.position.x) > tunnelRadius || Math.abs(camera.position.y) > tunnelRadius) { // out of bounds gameOver(); } - for (let i = 0; i < cubes.length; i++) { - if (cubes[i].material !== materialCube) { + // collision checks + for (let i = 0; i < spheres.length; i++) { + if (spheres[i].material !== materialSphere) { continue; } - let x1 = cubes[i].position.x; - let y1 = cubes[i].position.y; - let z1 = cubes[i].position.z; + let x1 = spheres[i].position.x; + let y1 = spheres[i].position.y; + let z1 = spheres[i].position.z; let x2 = camera.position.x; let y2 = camera.position.y; let z2 = camera.position.z; @@ -348,7 +367,7 @@ function animate() { z2 = Math.max(z1, z2 - speed); } let dist_squared = (x1 - x2) ** 2 + (y1 - y2) ** 2 + (z1 - z2) ** 2; - if (dist_squared <= (cubes[i].geometry.parameters.radius * cubes[i].scale.x) ** 2) { + if (dist_squared <= (spheres[i].geometry.parameters.radius * spheres[i].scale.x) ** 2) { gameOver(); break; } @@ -357,6 +376,7 @@ function animate() { if (running) { + // advance player position, increase speed, handle movement input camera.position.z -= 0.5 * speed; speed += 0.01; if (!Number.isNaN(leftRightMove)) { @@ -366,16 +386,17 @@ function animate() { camera.position.y += 0.1 * upDownMove; } + // create new obstacles as needed while (camera.position.z < lastSpawned - spawnInterval) { lastSpawned -= spawnInterval; // randomly spawn large spheres let scale = Math.random() < 0.1 ? 5.0 : 1.0; - const meshCube = new THREE.Mesh(geometrySphere, materialCube); + const meshCube = new THREE.Mesh(geometrySphere, materialSphere); meshCube.position.z = camera.position.z - speed * 210; - meshCube.position.x = (2 * Math.random() - 1.0) * diameterOfTunnel; - meshCube.position.y = (2 * Math.random() - 1.0) * 0.9 * diameterOfTunnel; + meshCube.position.x = (2 * Math.random() - 1.0) * tunnelRadius; + meshCube.position.y = (2 * Math.random() - 1.0) * 0.9 * tunnelRadius; meshCube.scale.multiplyScalar(scale); const outlineMesh = new THREE.Mesh(geometrySphere, customMaterial); @@ -385,19 +406,20 @@ function animate() { outlineMesh.position.y = meshCube.position.y; meshCube.add(outlineMesh); scene.add(outlineMesh); - cubes.push(outlineMesh); + spheres.push(outlineMesh); scene.add(meshCube); - cubes.push(meshCube); + spheres.push(meshCube); } } renderer.render(scene, camera); - for (let i = 0; i < cubes.length; i++) { - if (cubes[i].position.z > camera.position.z + cubes[i].geometry.parameters.radius * cubes[i].scale.x + 20) { - scene.remove(cubes[i]); - cubes.splice(i, 1); + // clean up game elements behind the camera + for (let i = 0; i < spheres.length; i++) { + if (spheres[i].position.z > camera.position.z + spheres[i].geometry.parameters.radius * spheres[i].scale.x + 20) { + scene.remove(spheres[i]); + spheres.splice(i, 1); i--; removed++; } @@ -407,44 +429,47 @@ function animate() { removed = 0; document.getElementById("score").innerText = score; } + + // create new wall segments on demand if (borders.length == 0 || borders[borders.length - 1].position.z - camera.position.z > -1200) { - const newZ = borders.length == 0 ? -200 : borders[borders.length - 1].position.z - diameterOfTunnel * 2; + const newZ = borders.length == 0 ? -200 : borders[borders.length - 1].position.z - tunnelRadius * 2; let border = new THREE.Mesh(borderGeometry, material); //border.position.x = -70; - border.position.y = -diameterOfTunnel; + border.position.y = -tunnelRadius; border.rotation.x = -Math.PI / 2; border.position.z = newZ; - border.scale.multiplyScalar(diameterOfTunnel * 2); + border.scale.multiplyScalar(tunnelRadius * 2); scene.add(border); borders.push(border); border = new THREE.Mesh(borderGeometry, material); - border.position.y = diameterOfTunnel; + border.position.y = tunnelRadius; border.rotation.x = Math.PI / 2; border.position.z = newZ; - border.scale.multiplyScalar(diameterOfTunnel * 2); + border.scale.multiplyScalar(tunnelRadius * 2); scene.add(border); borders.push(border); border = new THREE.Mesh(borderGeometry, material); - border.position.x = -diameterOfTunnel; + border.position.x = -tunnelRadius; border.rotation.y = Math.PI / 2; border.position.z = newZ; - border.scale.multiplyScalar(diameterOfTunnel * 2); + border.scale.multiplyScalar(tunnelRadius * 2); scene.add(border); borders.push(border); border = new THREE.Mesh(borderGeometry, material); - border.position.x = diameterOfTunnel; + border.position.x = tunnelRadius; border.rotation.y = -Math.PI / 2; border.position.z = newZ; - border.scale.multiplyScalar(diameterOfTunnel * 2); + border.scale.multiplyScalar(tunnelRadius * 2); scene.add(border); borders.push(border); } + // clean up wall segments behind the camera for (let i = 0; i < borders.length; i++) { - if (borders[i].position.z > camera.position.z + diameterOfTunnel + 50) { + if (borders[i].position.z > camera.position.z + tunnelRadius + 50) { scene.remove(borders[i]); borders.splice(i, 1); i--; diff --git a/main.css b/main.css index 5f127a4..772d7b9 100644 --- a/main.css +++ b/main.css @@ -1,7 +1,7 @@ body { margin: 0; background-color: #000; - font-family: Monospace; + font-family: monospace; overscroll-behavior: none; font-size: xxx-large; @@ -12,23 +12,17 @@ body { -0.07em 0 black, 0 -0.07em black; } + #start { width: 50vw; height: 20vh; + font-size: 15vh; + + /* center button */ position: absolute; top: 50vh; left: 50vw; transform: translate(-50%, -50%); - font-size: 15vh; -} - -a { - color: #ff0; - text-decoration: none; -} - -a:hover { - text-decoration: underline; } button { @@ -43,56 +37,7 @@ button { padding: 10px; box-sizing: border-box; text-align: center; - -moz-user-select: none; - -webkit-user-select: none; - -ms-user-select: none; user-select: none; pointer-events: none; - z-index: 1; /* TODO Solve this in HTML */ + z-index: 1; } - -a, button, input, select { - pointer-events: auto; -} - -.dg.ac { - -moz-user-select: none; - -webkit-user-select: none; - -ms-user-select: none; - user-select: none; - z-index: 2 !important; /* TODO Solve this in HTML */ -} - -#overlay { - position: absolute; - font-size: 16px; - z-index: 2; - top: 0; - left: 0; - width: 100%; - height: 100%; - display: flex; - align-items: center; - justify-content: center; - flex-direction: column; - background: rgba(0,0,0,0.7); -} - - #overlay button { - background: transparent; - border: 0; - border: 1px solid rgb(255, 255, 255); - border-radius: 4px; - color: #ffffff; - padding: 12px 18px; - text-transform: uppercase; - cursor: pointer; - } - -#notSupported { - width: 50%; - margin: auto; - background-color: #f00; - margin-top: 20px; - padding: 10px; -} \ No newline at end of file