From dd8361cf337921f76b9a03055932d3ff3e60517b Mon Sep 17 00:00:00 2001 From: adrien Date: Wed, 13 Mar 2024 02:29:02 +0100 Subject: [PATCH] - new loading screen - huge loading time reducing by simplifiying ocean --- index.html | 10 +- main.js | 579 +++++++++++++++++++++++++++++------------------------ style.css | 85 ++++---- 3 files changed, 367 insertions(+), 307 deletions(-) diff --git a/index.html b/index.html index 14ac46a..c32f998 100644 --- a/index.html +++ b/index.html @@ -143,15 +143,17 @@ -
- -
0%
+
+
+
Construction du labyrinthe : 0%
Se déplacer : ↑←↓→, ZQSD ou clic
Sauter : ESPACE
Regarder : Souris
- +
+
+ diff --git a/main.js b/main.js index 5501bdd..c190541 100644 --- a/main.js +++ b/main.js @@ -7,48 +7,90 @@ import { GUI } from 'three/addons/libs/lil-gui.module.min.js' import { OctreeHelper } from 'three/addons/helpers/OctreeHelper.js' import Stats from 'three/addons/libs/stats.module.js' -//import 'three-hex-tiling' - import MazeMesh from './MazeMesh.js' +//import 'three-hex-tiling' -const playerHeight = 0.5; -const mazeWidth = 23 -const parameters = { - elevation: 48, - azimuth : 53, -}; +// LOADING -const waves = { - A: { direction: 0, steepness: 0.05, wavelength: 3 }, - B: { direction: 30, steepness: 0.10, wavelength: 6 }, - C: { direction: 60, steepness: 0.05, wavelength: 1.5 }, -}; +const labyWidth =23 +const labyHeight = 23 -const dev = window.location.search.includes("dev") +for(let y=0; y < labyHeight; y++) { + let tr = document.createElement("tr") + labyTable.appendChild(tr) + for(let x=0; x < labyHeight; x++) { + let td = document.createElement("td") + tr.appendChild(td) + } +} -const ambiance = new Audio("snd/ambiance.mp3") -ambiance.loop = true -const piano = new Audio("snd/waves-and-tears.mp3") -piano.loop = false +let walls -const loadMngr = new THREE.LoadingManager(); -const loader = new THREE.TextureLoader(loadMngr); +function dig(x, y) { + walls[y][x] = false + window.requestAnimationFrame(() => labyTable.children[y].children[x].className = "ground") +} + +const directions = [[0, 1], [0, -1], [1, 0], [-1, 0]] +function* build(x, y) { + for (let direction of Array.from(directions).sort(x => .5 - Math.random())) { + let [dx, dy] = direction + let x1 = x + dx + let y1 = y + dy + let x2 = x1 + dx + let y2 = y1 + dy + if (0 <= x2 && x2 < labyWidth && 0 <= y2 && y2 < labyHeight && walls[y2][x2]) { + dig(x1, y1) + yield x1, y1 + dig(x2, y2) + yield x2, y2 + yield* build(x2, y2) + } + } +} + +function* endlessLaby() { + while (true) { + for (const tr of labyTable.children) { + for (const td of tr.children) { + td.className = "wall" + } + } + + walls = Array(labyHeight).fill(true).map(row => Array(labyWidth).fill(true)) + + //let x0 = 2 * Math.floor(labyWidth * Math.random() / 2) + 1 + //let y0 = 2 * Math.floor(labyHeight * Math.random() / 2) + 1 + let x0 = Math.floor(labyWidth / 2) + let y0 = Math.floor(labyHeight / 2) + + dig(x0, y0) + yield* build(x0, y0) + } +} + +let labyIterator = endlessLaby() + +let interval = window.setInterval(() => labyIterator.next(), 200) + + +const loadMngr = new THREE.LoadingManager() +const loader = new THREE.TextureLoader(loadMngr) +loader.setPath("textures/") loadMngr.onStart = function (url, itemsLoaded, itemsTotal) { - progressCircle.innerText = "0%" - progressCircle.style.setProperty("--progress", "0deg") + progress.innerText = "0" } loadMngr.onProgress = function (url, itemsLoaded, itemsTotal) { - progressCircle.innerText = Math.floor(100 * itemsLoaded / itemsTotal) + "%" - progressCircle.style.setProperty("--progress", Math.floor(360 * itemsLoaded / itemsTotal)+"deg") + progress.innerText = Math.floor(100 * itemsLoaded / itemsTotal) } loadMngr.onError = function (url) { - message.innerHTML = `Erreur de chargement :
${url}` + loadingMessage.innerHTML = `Erreur de chargement :
${url}` } loadMngr.onLoad = function (url, itemsLoaded, itemsTotal) { - message.innerHTML = "" - message.className = "" + loading.style.display = "none" + window.clearInterval(interval) renderer.setAnimationLoop(animate) @@ -56,26 +98,45 @@ loadMngr.onLoad = function (url, itemsLoaded, itemsTotal) { let x = Math.floor(8 + camera.position.x * 16 / mazeWidth) let y = Math.floor(8 + camera.position.z * 16 / mazeWidth) favicon.href = `favicon.php?x=${x}&y=${y}` - }, 1000); -}; + }, 1000) +} -// -const container = document.getElementById('container'); +// GAME + +const playerHeight = 0.5 +const mazeWidth = 23 + +const parameters = { + elevation: 48, + azimuth : 53, +} + +const waves = { + A: { direction: 0, steepness: 0.06, wavelength: 4 }, + B: { direction: 30, steepness: 0.10, wavelength: 6 }, + C: { direction: 60, steepness: 0.05, wavelength: 1.5 }, +} + +const ambiance = new Audio("snd/ambiance.mp3") +ambiance.loop = true +const piano = new Audio("snd/waves-and-tears.mp3") +piano.loop = false + +const container = document.getElementById('container') const renderer = new THREE.WebGLRenderer({ powerPreference: "high-performance", - antialias: true, -}); -renderer.setPixelRatio(window.devicePixelRatio); -renderer.setSize(window.innerWidth, window.innerHeight); -renderer.toneMapping = THREE.ACESFilmicToneMapping; -renderer.shadowMap.enabled = true; -renderer.shadowMap.type = THREE.PCFSoftShadowMap; +}) +renderer.setPixelRatio(window.devicePixelRatio) +renderer.setSize(window.innerWidth, window.innerHeight) +renderer.toneMapping = THREE.ACESFilmicToneMapping +renderer.shadowMap.enabled = true +renderer.shadowMap.type = THREE.PCFSoftShadowMap -container.appendChild(renderer.domElement); +container.appendChild(renderer.domElement) -const scene = new THREE.Scene(); +const scene = new THREE.Scene() scene.background = new THREE.CubeTextureLoader(loadMngr) .setPath( 'textures/calm-sea-skybox/' ) @@ -86,48 +147,50 @@ scene.background = new THREE.CubeTextureLoader(loadMngr) 'dn.webp', 'rt.webp', 'lf.webp', - ] ); -scene.backgroundBlurriness = 0.03; -scene.backgroundIntensity = 1.4; -scene.environment = scene.background; + ] ) +scene.backgroundBlurriness = 0.03 +scene.backgroundIntensity = 1.4 +scene.environment = scene.background -window.scene = scene; +window.scene = scene -const camera = new THREE.PerspectiveCamera(80, window.innerWidth / window.innerHeight, 0.1, 1000); -camera.rotation.order = 'YXZ'; -camera.position.set(0, 25 + playerHeight, 0); +const camera = new THREE.PerspectiveCamera(80, window.innerWidth / window.innerHeight, 0.1, 1000) +camera.rotation.order = 'YXZ' +camera.position.set(0, 25 + playerHeight, 0) -const mazeCollisionner = new THREE.Group(); +const mazeCollisionner = new THREE.Group() // Maze const wallMaterial = new THREE.MeshStandardMaterial({ - map : loader.load('textures/Poly-cobblestone-wall/color_map.webp'), - normalMap : loader.load('textures/Poly-cobblestone-wall/normal_map_opengl.webp'), - aoMap : loader.load('textures/Poly-cobblestone-wall/ao_map.webp'), - roughnessMap : loader.load('textures/Poly-cobblestone-wall/roughness_map.webp'), + map : loader.load('Poly-cobblestone-wall/color_map.webp'), + normalMap : loader.load('Poly-cobblestone-wall/normal_map_opengl.webp'), + aoMap : loader.load('Poly-cobblestone-wall/ao_map.webp'), + roughnessMap : loader.load('Poly-cobblestone-wall/roughness_map.webp'), roughness : 1 }) -const maze = new MazeMesh(mazeWidth, mazeWidth, 1, wallMaterial); -maze.castShadow = true; -maze.receiveShadow = true; +const maze = new MazeMesh(mazeWidth, mazeWidth, 1, wallMaterial) +maze.castShadow = true +maze.receiveShadow = true maze.matrixAutoUpdate = false scene.add(maze) console.log(String(maze)) +const dev = window.location.search.includes("dev") + if (!dev) { - const invisibleWall = new THREE.Mesh(new THREE.BoxGeometry( .9, 1.8, .9 )); - invisibleWall.material.visible = false; + const invisibleWall = new THREE.Mesh(new THREE.BoxGeometry( .9, 1.8, .9 )) + invisibleWall.material.visible = false let matrix = new THREE.Matrix4() for (let i = 0; i < maze.count; i++) { maze.getMatrixAt(i, matrix) const clone = invisibleWall.clone() - clone.position.setFromMatrixPosition(matrix); - clone.position.y = 1; - mazeCollisionner.add(clone); + clone.position.setFromMatrixPosition(matrix) + clone.position.y = 1 + mazeCollisionner.add(clone) } } @@ -139,11 +202,11 @@ function repeatGroundMaterial (texture) { texture.repeat.set(mazeWidth / 4, mazeWidth / 4) } const groundMaterial = new THREE.MeshStandardMaterial({ - map : loader.load('textures/angled-blocks-vegetation/albedo.webp', repeatGroundMaterial), - aoMap : loader.load('textures/angled-blocks-vegetation/ao.webp', repeatGroundMaterial), - metalnessMap: loader.load('textures/angled-blocks-vegetation/metallic.webp', repeatGroundMaterial), - normalMap : loader.load('textures/angled-blocks-vegetation/normal-dx.webp', repeatGroundMaterial), - roughnessMap: loader.load('textures/angled-blocks-vegetation/roughness.webp', repeatGroundMaterial), + map : loader.load('angled-blocks-vegetation/albedo.webp', repeatGroundMaterial), + aoMap : loader.load('angled-blocks-vegetation/ao.webp', repeatGroundMaterial), + metalnessMap: loader.load('angled-blocks-vegetation/metallic.webp', repeatGroundMaterial), + normalMap : loader.load('angled-blocks-vegetation/normal-dx.webp', repeatGroundMaterial), + roughnessMap: loader.load('angled-blocks-vegetation/roughness.webp', repeatGroundMaterial), /*hexTiling : { patchScale: 1, useContrastCorrectedBlending: true, @@ -184,29 +247,29 @@ const ground = new THREE.Mesh( groundMaterial, ] ) -ground.rotation.x = -Math.PI / 2; +ground.rotation.x = -Math.PI / 2 ground.position.y = -10 -ground.receiveShadow = true; +ground.receiveShadow = true ground.matrixAutoUpdate = false -ground.updateMatrix(); +ground.updateMatrix() mazeCollisionner.add(ground) -scene.add(mazeCollisionner); +scene.add(mazeCollisionner) -const mazeOctree = new Octree().fromGraphNode(mazeCollisionner); +const mazeOctree = new Octree().fromGraphNode(mazeCollisionner) // Water -const waterGeometry = new THREE.PlaneGeometry(1024, 1024, 512, 512); +const waterGeometry = new THREE.PlaneGeometry(512, 512, 128, 128) const ocean = new Water(waterGeometry, { - textureWidth : 512, - textureHeight: 512, + textureWidth : 256, + textureHeight: 256, waterNormals : loader.load( - 'textures/waternormals.webp', + 'waternormals.webp', function (texture) { - texture.wrapS = texture.wrapT = THREE.RepeatWrapping; + texture.wrapS = texture.wrapT = THREE.RepeatWrapping } ), sunDirection : new THREE.Vector3(), @@ -214,12 +277,12 @@ const ocean = new Water(waterGeometry, { waterColor : 0x001e0f, distortionScale: 3.7, fog : scene.fog !== undefined, - alpha : 0.9 -}); -ocean.rotation.x = - Math.PI / 2; -ocean.position.y = -0.2; + alpha : 0.7 +}) +ocean.rotation.x = - Math.PI / 2 +ocean.position.y = -0.2 -ocean.material.transparent = true; +ocean.material.transparent = true ocean.material.onBeforeCompile = function (shader) { shader.uniforms.size = { value: 6 } @@ -231,7 +294,7 @@ ocean.material.onBeforeCompile = function (shader) { waves.A.steepness, waves.A.wavelength, ], - }; + } shader.uniforms.waveB = { value: [ Math.sin((waves.B.direction * Math.PI) / 180), @@ -239,7 +302,7 @@ ocean.material.onBeforeCompile = function (shader) { waves.B.steepness, waves.B.wavelength, ], - }; + } shader.uniforms.waveC = { value: [ Math.sin((waves.C.direction * Math.PI) / 180), @@ -247,50 +310,50 @@ ocean.material.onBeforeCompile = function (shader) { waves.C.steepness, waves.C.wavelength, ], - }; - shader.vertexShader = document.getElementById('vertexShader').textContent; - shader.fragmentShader = document.getElementById('fragmentShader').textContent; + } + shader.vertexShader = document.getElementById('vertexShader').textContent + shader.fragmentShader = document.getElementById('fragmentShader').textContent -}; +} -scene.add(ocean); -const oceanOctree = new Octree().fromGraphNode(ocean); +scene.add(ocean) +const oceanOctree = new Octree().fromGraphNode(ocean) // Lights -const sun = new THREE.Vector3(); +const sun = new THREE.Vector3() -const ambientLight = new THREE.AmbientLight(0x404040, 5); -scene.add(ambientLight); +const ambientLight = new THREE.AmbientLight(0x404040, 5) +scene.add(ambientLight) -const sunLight = new THREE.DirectionalLight(0xffffff, 1); -sunLight.castShadow = true; -sunLight.shadow.camera.near = 0.1; -sunLight.shadow.camera.far = 1.4 * mazeWidth; -sunLight.shadow.camera.left = -1.4 * mazeWidth/2; -sunLight.shadow.camera.right = 1.4 * mazeWidth/2; -sunLight.shadow.camera.bottom = -1.4 * mazeWidth/2; -sunLight.shadow.camera.top = 1.4 * mazeWidth/2; -sunLight.shadow.mapSize.width = 1024; -sunLight.shadow.mapSize.height = 1024; -//sunLight.shadow.radius = 0.01; -sunLight.shadow.bias = 0.0001; +const sunLight = new THREE.DirectionalLight(0xffffff, 1) +sunLight.castShadow = true +sunLight.shadow.camera.near = 0.1 +sunLight.shadow.camera.far = 1.4 * mazeWidth +sunLight.shadow.camera.left = -1.4 * mazeWidth/2 +sunLight.shadow.camera.right = 1.4 * mazeWidth/2 +sunLight.shadow.camera.bottom = -1.4 * mazeWidth/2 +sunLight.shadow.camera.top = 1.4 * mazeWidth/2 +sunLight.shadow.mapSize.width = 1024 +sunLight.shadow.mapSize.height = 1024 +sunLight.shadow.radius = 0.01 +sunLight.shadow.bias = 0.0001 sunLight.target = maze -scene.add(sunLight); +scene.add(sunLight) -updateSun(); +updateSun() function updateSun() { - const phi = THREE.MathUtils.degToRad(90 - parameters.elevation); - const theta = THREE.MathUtils.degToRad(parameters.azimuth); + const phi = THREE.MathUtils.degToRad(90 - parameters.elevation) + const theta = THREE.MathUtils.degToRad(parameters.azimuth) - sun.setFromSphericalCoords(1.4 * mazeWidth/2, phi, theta); - ocean.material.uniforms['sunDirection'].value.copy(sun).normalize(); + sun.setFromSphericalCoords(1.4 * mazeWidth/2, phi, theta) + ocean.material.uniforms['sunDirection'].value.copy(sun).normalize() sunLight.position.copy(sun) - //ambientLight.intensity = 5 + 5 * Math.sin(Math.max(THREE.MathUtils.degToRad(parameters.elevation), 0)); + //ambientLight.intensity = 5 + 5 * Math.sin(Math.max(THREE.MathUtils.degToRad(parameters.elevation), 0)) } @@ -302,49 +365,49 @@ function repeatRaftMaterial(texture) { texture.repeat.set(2, 1) } const raftMaterial = new THREE.MeshStandardMaterial({ - map: loader.load("textures/Poly-wood/color_map.webp", repeatRaftMaterial), - aoMap: loader.load("textures/Poly-wood/ao_map.webp", repeatRaftMaterial), - normalMap: loader.load("textures/Poly-wood/normal_map_opengl.webp", repeatRaftMaterial), + map: loader.load("Poly-wood/color_map.webp", repeatRaftMaterial), + aoMap: loader.load("Poly-wood/ao_map.webp", repeatRaftMaterial), + normalMap: loader.load("Poly-wood/normal_map_opengl.webp", repeatRaftMaterial), normalScale : new THREE.Vector2(2, 2), - roughnessMap: loader.load("textures/Poly-wood/roughness_map.webp", repeatRaftMaterial), + roughnessMap: loader.load("Poly-wood/roughness_map.webp", repeatRaftMaterial), depthFunc: 3, depthTest: true, depthWrite: true, - displacementMap: loader.load("textures/Poly-wood/displacement_map.webp", repeatRaftMaterial), + displacementMap: loader.load("Poly-wood/displacement_map.webp", repeatRaftMaterial), displacementScale: -0.3, displacementBias: 0.15, }) const raft = new THREE.Mesh(raftGeometry, raftMaterial) raft.position.set( .25, ocean.position.y, -mazeWidth/2 - 1.1 ) -raft.castShadow = true; +raft.castShadow = true -scene.add(raft); -const raftOctree = new Octree().fromGraphNode(raft); +scene.add(raft) +const raftOctree = new Octree().fromGraphNode(raft) // GUI -const stats = new Stats(); +const stats = new Stats() if (dev) { - container.appendChild(stats.dom); + container.appendChild(stats.dom) - const gui = new GUI(); + const gui = new GUI() const lightHelper = new THREE.DirectionalLightHelper(sunLight, .5) lightHelper.position.copy(maze.start) - lightHelper.visible = false; + lightHelper.visible = false - const octreeHelper = new OctreeHelper(mazeOctree); - octreeHelper.visible = false; - scene.add(octreeHelper); + const octreeHelper = new OctreeHelper(mazeOctree) + octreeHelper.visible = false + scene.add(octreeHelper) const showHelper = gui.add({ helpers: false }, "helpers") showHelper.onChange(function (value) { - lightHelper.visible = value; - octreeHelper.visible = value; + lightHelper.visible = value + octreeHelper.visible = value - }); + }) const cameraFolder = gui.addFolder("camera") cameraFolder.add(camera, "focus", 0, 200).onChange(() => camera.updateProjectionMatrix()) @@ -360,91 +423,91 @@ if (dev) { raftRotationFolder.add(raft.rotation, "x") raftRotationFolder.add(raft.rotation, "y") raftRotationFolder.add(raft.rotation, "z") - raftFolder.close(); + raftFolder.close() - const skyFolder = gui.addFolder('Sky'); - skyFolder.add(parameters, 'elevation', 0, 90, 0.1).onChange(updateSun); - skyFolder.add(parameters, 'azimuth', - 180, 180, 0.1).onChange(updateSun); - skyFolder.close(); + const skyFolder = gui.addFolder('Sky') + skyFolder.add(parameters, 'elevation', 0, 90, 0.1).onChange(updateSun) + skyFolder.add(parameters, 'azimuth', - 180, 180, 0.1).onChange(updateSun) + skyFolder.close() - const waterUniforms = ocean.material.uniforms; + const waterUniforms = ocean.material.uniforms - const waterFolder = gui.addFolder('Water'); + const waterFolder = gui.addFolder('Water') waterFolder .add(waterUniforms.distortionScale, 'value', 0, 8, 0.1) - .name('distortionScale'); - waterFolder.add(waterUniforms.size, 'value', 0.1, 10, 0.1).name('size'); - waterFolder.add(ocean.material, 'wireframe'); - waterFolder.close(); + .name('distortionScale') + waterFolder.add(waterUniforms.size, 'value', 0.1, 10, 0.1).name('size') + waterFolder.add(ocean.material, 'wireframe') + waterFolder.close() - const waveAFolder = waterFolder.addFolder('Wave A'); + const waveAFolder = waterFolder.addFolder('Wave A') waveAFolder .add(waves.A, 'direction', 0, 359) .name('Direction') .onChange((v) => { - const x = (v * Math.PI) / 180; - ocean.material.uniforms.waveA.value[0] = Math.sin(x); - ocean.material.uniforms.waveA.value[1] = Math.cos(x); - }); + const x = (v * Math.PI) / 180 + ocean.material.uniforms.waveA.value[0] = Math.sin(x) + ocean.material.uniforms.waveA.value[1] = Math.cos(x) + }) waveAFolder .add(waves.A, 'steepness', 0, 1, 0.01) .name('Steepness') .onChange((v) => { - ocean.material.uniforms.waveA.value[2] = v; - }); + ocean.material.uniforms.waveA.value[2] = v + }) waveAFolder .add(waves.A, 'wavelength', 1, 100) .name('Wavelength') .onChange((v) => { - ocean.material.uniforms.waveA.value[3] = v; - }); - waveAFolder.open(); + ocean.material.uniforms.waveA.value[3] = v + }) + waveAFolder.open() - const waveBFolder = waterFolder.addFolder('Wave B'); + const waveBFolder = waterFolder.addFolder('Wave B') waveBFolder .add(waves.B, 'direction', 0, 359) .name('Direction') .onChange((v) => { - const x = (v * Math.PI) / 180; - ocean.material.uniforms.waveB.value[0] = Math.sin(x); - ocean.material.uniforms.waveB.value[1] = Math.cos(x); - }); + const x = (v * Math.PI) / 180 + ocean.material.uniforms.waveB.value[0] = Math.sin(x) + ocean.material.uniforms.waveB.value[1] = Math.cos(x) + }) waveBFolder .add(waves.B, 'steepness', 0, 1, 0.01) .name('Steepness') .onChange((v) => { - ocean.material.uniforms.waveB.value[2] = v; - }); + ocean.material.uniforms.waveB.value[2] = v + }) waveBFolder .add(waves.B, 'wavelength', 1, 100) .name('Wavelength') .onChange((v) => { - ocean.material.uniforms.waveB.value[3] = v; - }); - waveBFolder.open(); + ocean.material.uniforms.waveB.value[3] = v + }) + waveBFolder.open() - const waveCFolder = waterFolder.addFolder('Wave C'); + const waveCFolder = waterFolder.addFolder('Wave C') waveCFolder .add(waves.C, 'direction', 0, 359) .name('Direction') .onChange((v) => { - const x = (v * Math.PI) / 180; - ocean.material.uniforms.waveC.value[0] = Math.sin(x); - ocean.material.uniforms.waveC.value[1] = Math.cos(x); - }); + const x = (v * Math.PI) / 180 + ocean.material.uniforms.waveC.value[0] = Math.sin(x) + ocean.material.uniforms.waveC.value[1] = Math.cos(x) + }) waveCFolder .add(waves.C, 'steepness', 0, 1, 0.01) .name('Steepness') .onChange((v) => { - ocean.material.uniforms.waveC.value[2] = v; - }); + ocean.material.uniforms.waveC.value[2] = v + }) waveCFolder .add(waves.C, 'wavelength', 1, 100) .name('Wavelength') .onChange((v) => { - ocean.material.uniforms.waveC.value[3] = v; - }); - waveCFolder.open(); + ocean.material.uniforms.waveC.value[3] = v + }) + waveCFolder.open() const hexTilingFolder = gui.addFolder('Hex Tiling') if (wallMaterial?.hexTiling?.patchScale) { @@ -466,66 +529,66 @@ if (dev) { // -const clock = new THREE.Clock(); +const clock = new THREE.Clock() // Controls -const GRAVITY = 30; +const GRAVITY = 30 -const STEPS_PER_FRAME = 10; +const STEPS_PER_FRAME = 10 const playerCollider = new Capsule( new THREE.Vector3(0, 25.0, 0), new THREE.Vector3(0, 25 + playerHeight, 0), 0.3 -); +) -const playerVelocity = new THREE.Vector3(); -const playerDirection = new THREE.Vector3(); +const playerVelocity = new THREE.Vector3() +const playerDirection = new THREE.Vector3() -let playerOnFloor = false; -let jumping = false; -let escaped = false; +let playerOnFloor = false +let jumping = false +let escaped = false -const pointerLockControls = new PointerLockControls(camera, document.body); -pointerLockControls.pointerSpeed = 0.7; +const pointerLockControls = new PointerLockControls(camera, document.body) +pointerLockControls.pointerSpeed = 0.7 -const keyStates = {}; +const keyStates = {} document.addEventListener('keydown', (event) => { - keyStates[event.code] = true; -}); + keyStates[event.code] = true +}) document.addEventListener('keyup', (event) => { - keyStates[event.code] = false; + keyStates[event.code] = false if (event.code == 'Space') jumping = false -}); +}) -var mouseButtonsStates = []; +let mouseButtonsStates = [] function onMouseChange(event) { - for(var i=0; i < mouseButtonsStates.length || i <= Math.log2(event.buttons); i++) { + for(let i=0; i < mouseButtonsStates.length || i <= Math.log2(event.buttons); i++) { mouseButtonsStates[i] = (event.buttons & (1 << i)) > 0 } } container.addEventListener('click', function () { - pointerLockControls.lock(); + pointerLockControls.lock() }) pointerLockControls.addEventListener('lock', function () { - ambiance.play(); + ambiance.play() document.addEventListener('mousedown', onMouseChange) document.addEventListener('mouseup', onMouseChange) }) pointerLockControls.addEventListener('unlock', function () { - ambiance.pause(); + ambiance.pause() document.removeEventListener('mousedown', onMouseChange) document.removeEventListener('mouseup', onMouseChange) }) -scene.add(pointerLockControls.getObject()); +scene.add(pointerLockControls.getObject()) function playerCollisions() { @@ -535,23 +598,23 @@ function playerCollisions() { const result = playerOnMaze || playerOnRaft || playerOnWater - playerOnFloor = false; + playerOnFloor = false if ( result ) { - playerOnFloor = result.normal.y > 0; + playerOnFloor = result.normal.y > 0 if (!playerOnFloor) { - playerVelocity.addScaledVector(result.normal, - result.normal.dot(playerVelocity)); + playerVelocity.addScaledVector(result.normal, - result.normal.dot(playerVelocity)) } - playerCollider.translate(result.normal.multiplyScalar(result.depth)); + playerCollider.translate(result.normal.multiplyScalar(result.depth)) if (playerOnRaft) { camera.position.y = playerCollider.end.y + raft.position.y if (!escaped) gameEnd() } else if (playerOnWater) { - const t = ocean.material.uniforms['time'].value; + const t = ocean.material.uniforms['time'].value const waveInfo = getWaveInfo(playerCollider.end.x, playerCollider.end.z, t) camera.position.y = ocean.position.y + waveInfo.position.y + 0.2 } @@ -561,61 +624,61 @@ function playerCollisions() { function gameEnd() { - escaped = true; - message.innerHTML = '

Libre !

Rejouer'; - message.className = "escaped"; - piano.play(); + escaped = true + message.innerHTML = '

Libre !

Rejouer' + message.className = "escaped" + piano.play() - document.exitPointerLock(); - //container.style.cursor = "default"; + document.exitPointerLock() + //container.style.cursor = "default" } function updatePlayer(deltaTime) { - let damping = Math.exp(- 4 * deltaTime) - 1; + let damping = Math.exp(- 4 * deltaTime) - 1 if (!playerOnFloor) { - playerVelocity.y -= GRAVITY * deltaTime; + playerVelocity.y -= GRAVITY * deltaTime damping *= 0.1; // small air resistance } - playerVelocity.addScaledVector(playerVelocity, damping); + playerVelocity.addScaledVector(playerVelocity, damping) - const deltaPosition = playerVelocity.clone().multiplyScalar(deltaTime); - playerCollider.translate(deltaPosition); + const deltaPosition = playerVelocity.clone().multiplyScalar(deltaTime) + playerCollider.translate(deltaPosition) - camera.position.copy(playerCollider.end); + camera.position.copy(playerCollider.end) - playerCollisions(); + playerCollisions() } function getForwardVector() { - camera.getWorldDirection(playerDirection); - playerDirection.y = 0; - playerDirection.normalize(); + camera.getWorldDirection(playerDirection) + playerDirection.y = 0 + playerDirection.normalize() - return playerDirection; + return playerDirection } function getSideVector() { - camera.getWorldDirection(playerDirection); - playerDirection.y = 0; - playerDirection.normalize(); - playerDirection.cross(camera.up); + camera.getWorldDirection(playerDirection) + playerDirection.y = 0 + playerDirection.normalize() + playerDirection.cross(camera.up) - return playerDirection; + return playerDirection } function controls(deltaTime) { // gives a bit of air control - const speedDelta = deltaTime * (playerOnFloor ? 100 : 20) / STEPS_PER_FRAME; + const speedDelta = deltaTime * (playerOnFloor ? 100 : 20) / STEPS_PER_FRAME if (keyStates["ArrowUp"] || keyStates['KeyW'] || mouseButtonsStates[0]) { playerVelocity.add(getForwardVector().multiplyScalar(speedDelta)) @@ -631,7 +694,7 @@ function controls(deltaTime) { } if (playerOnFloor && jumping == false) { if (keyStates['Space']) { - playerVelocity.y = 9; + playerVelocity.y = 9 jumping = true } } @@ -639,88 +702,88 @@ function controls(deltaTime) { function getWaveInfo(x, z, time) { - const pos = new THREE.Vector3(); - const tangent = new THREE.Vector3(1, 0, 0); - const binormal = new THREE.Vector3(0, 0, 1); + const pos = new THREE.Vector3() + const tangent = new THREE.Vector3(1, 0, 0) + const binormal = new THREE.Vector3(0, 0, 1) Object.keys(waves).forEach((wave) => { - const w = waves[wave]; - const k = (Math.PI * 2) / w.wavelength; - const c = Math.sqrt(9.8 / k); + const w = waves[wave] + const k = (Math.PI * 2) / w.wavelength + const c = Math.sqrt(9.8 / k) const d = new THREE.Vector2( Math.sin((w.direction * Math.PI) / 180), - Math.cos((w.direction * Math.PI) / 180) - ); - const f = k * (d.dot(new THREE.Vector2(x, z)) - c * time); - const a = w.steepness / k; + ) + const f = k * (d.dot(new THREE.Vector2(x, z)) - c * time) + const a = w.steepness / k - pos.x += d.y * (a * Math.cos(f)); - pos.y += a * Math.sin(f); - pos.z += d.x * (a * Math.cos(f)); + pos.x += d.y * (a * Math.cos(f)) + pos.y += a * Math.sin(f) + pos.z += d.x * (a * Math.cos(f)) - tangent.x += - d.x * d.x * (w.steepness * Math.sin(f)); - tangent.y += d.x * (w.steepness * Math.cos(f)); - tangent.z += - d.x * d.y * (w.steepness * Math.sin(f)); + tangent.x += - d.x * d.x * (w.steepness * Math.sin(f)) + tangent.y += d.x * (w.steepness * Math.cos(f)) + tangent.z += - d.x * d.y * (w.steepness * Math.sin(f)) - binormal.x += - d.x * d.y * (w.steepness * Math.sin(f)); - binormal.y += d.y * (w.steepness * Math.cos(f)); - binormal.z += - d.y * d.y * (w.steepness * Math.sin(f)); + binormal.x += - d.x * d.y * (w.steepness * Math.sin(f)) + binormal.y += d.y * (w.steepness * Math.cos(f)) + binormal.z += - d.y * d.y * (w.steepness * Math.sin(f)) }) - const normal = binormal.cross(tangent).normalize(); + const normal = binormal.cross(tangent).normalize() - return { position: pos, normal: normal }; + return { position: pos, normal: normal } } function updateRaft(delta) { - const t = ocean.material.uniforms['time'].value; + const t = ocean.material.uniforms['time'].value - const waveInfo = getWaveInfo(raft.position.x, raft.position.z, t); - raft.position.y = ocean.position.y + waveInfo.position.y; + const waveInfo = getWaveInfo(raft.position.x, raft.position.z, t) + raft.position.y = ocean.position.y + waveInfo.position.y const quat = new THREE.Quaternion().setFromEuler( new THREE.Euler().setFromVector3(waveInfo.normal) - ); - raft.quaternion.rotateTowards(quat, delta * 0.5); + ) + raft.quaternion.rotateTowards(quat, delta * 0.5) } -window.addEventListener('resize', onWindowResize); +window.addEventListener('resize', onWindowResize) function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); + camera.aspect = window.innerWidth / window.innerHeight + camera.updateProjectionMatrix() - renderer.setSize(window.innerWidth, window.innerHeight); + renderer.setSize(window.innerWidth, window.innerHeight) } function animate() { const delta = Math.min(0.05, clock.getDelta()) - const deltaTime = delta / STEPS_PER_FRAME; + const deltaTime = delta / STEPS_PER_FRAME - ocean.material.uniforms['time'].value += delta; + ocean.material.uniforms['time'].value += delta - updateRaft(delta); + updateRaft(delta) // we look for collisions in substeps to mitigate the risk of // an object traversing another too quickly for detection. for (let i = 0; i < STEPS_PER_FRAME; i++) { - controls(deltaTime); + controls(deltaTime) - updatePlayer(deltaTime); + updatePlayer(deltaTime) } if (camera.position.y > 3.5) - camera.lookAt(raft.position.x, raft.position.y, raft.position.z); + camera.lookAt(raft.position.x, raft.position.y, raft.position.z) - renderer.render(scene, camera); + renderer.render(scene, camera) - if (dev) stats.update(); + if (dev) stats.update() } \ No newline at end of file diff --git a/style.css b/style.css index cc8f170..61d1b80 100644 --- a/style.css +++ b/style.css @@ -1,9 +1,45 @@ body { margin: 0; - background-color: #000; - color: #fff; - font-family: Georgia, serif; + background-color: #041626; + font-size: 1.3em; overscroll-behavior: none; + cursor: wait; +} + +#loading { + width: fit-content; + color: #2c5c88; + font-size: 1.3em; + top: 20vh; + margin: auto; +} + +#loadingMessage { + margin-bottom: 0.5em; +} + +#labyTable { + width: 230px; + height: 230px; + margin-left: auto; + margin-right: auto; + margin-top: 20vh; + margin-bottom: 5vh; + border-collapse: collapse; +} + +td { + width: 10px; + height: 10px; + transition: background-color 1s; +} + +.wall { + background-color: transparent; +} + +.ground { + background-color: #214464; } #container { @@ -32,48 +68,7 @@ body { justify-content: center; z-index: 1; color: gray; -} - -#message.loading { - display: flex; - flex-direction: column; - top: 20vh; - width: 100%; - margin: auto; - align-items: center; - gap: 5rem; - text-align: center; - font-size: 1.7em; - cursor: progress; -} - -#progressCircle { - display: flex; - justify-content: center; - align-items: center; - position: relative; - width: 200px; - height: 200px; - border: 4px solid dimgray; - border-radius: 50%; - font-size: 0; - font-size: 3vh; - font-weight: 700; - font-family: system-ui; - text-align: center; -} - -#progressCircle::after { - content: ""; - display: flex; - position: absolute; - width: 200px; - height: 200px; - top: -4px; - left: -4px; - border: 4px solid #1da8b7; - border-radius: 50%; - mask: conic-gradient(black var(--progress), transparent var(--progress)); + font-family: Times, "Times New Roman", Georgia, serif; } #message a {