diff --git a/app.js b/app.js index 2261eb4..63098d6 100644 --- a/app.js +++ b/app.js @@ -1,10 +1,9 @@ import * as THREE from 'three' import { scheduler } from './jsm/scheduler.js' -import { TRANSLATION, ROTATION, environment, InstancedMino, Mino, Playfield, HoldQueue, NextQueue } from './jsm/Tetrominoes.js' +import { TRANSLATION, ROTATION, environment, Mino, HoldQueue, NextQueue } from './jsm/Tetrominoes.js' import Settings from './jsm/Settings.js' import { Stats } from './jsm/Stats.js' import { Menu } from './jsm/Menu.js' -import CameraControls from './jsm/CameraControls.js' import { TetraScene } from './jsm/TetraScene.js' import * as FPS from 'three/addons/libs/stats.module.js' @@ -35,7 +34,7 @@ let game = { nextQueue.init() holdQueue.piece = undefined holdQueue.clear() - playfield.init() + scene.playfield.init() scene.music.currentTime = 0 @@ -61,7 +60,7 @@ let game = { if (settings.musicVolume) scene.music.play() - if (playfield.piece) { + if (scene.playfield.piece) { scheduler.resetInterval(game.fall, stats.fallPeriod) } else { this.generate() @@ -70,10 +69,10 @@ let game = { generate: function(nextPiece=nextQueue.shift()) { nextPiece.lockDelay = stats.lockDelay - playfield.piece = nextPiece - playfield.piece.onLockDown = game.lockDown + scene.playfield.piece = nextPiece + scene.playfield.piece.onLockDown = game.lockDown - if (playfield.piece.canMove(TRANSLATION.NONE)) { + if (scene.playfield.piece.canMove(TRANSLATION.NONE)) { scheduler.resetInterval(game.fall, stats.fallPeriod) } else { game.over() // block out @@ -81,16 +80,16 @@ let game = { }, fall: function() { - playfield.piece.move(TRANSLATION.DOWN) + scene.playfield.piece.move(TRANSLATION.DOWN) }, lockDown: function() { scheduler.clearTimeout(game.lockDown) scheduler.clearInterval(game.fall) - if (playfield.lock(playfield.piece)) { - let tSpin = playfield.piece.tSpin - let nbClearedLines = playfield.clearLines() + if (scene.playfield.lock(scene.playfield.piece)) { + let tSpin = scene.playfield.piece.tSpin + let nbClearedLines = scene.playfield.clearLines() stats.lockDown(nbClearedLines, tSpin) if (settings.sfxVolume) { if (nbClearedLines == 4 || (tSpin && nbClearedLines)) { @@ -129,11 +128,11 @@ let game = { }, over: function() { - playfield.piece.locking = false + scene.playfield.piece.locking = false document.onkeydown = null window.onblur = null - renderer.domElement.onfocus = null + scene.renderer.domElement.onfocus = null menu.settings.domElement.onfocus = null this.playing = false scene.music.pause() @@ -170,16 +169,16 @@ function playSound(sound, note=0) { /* Handle player inputs */ let playerActions = { - moveLeft: () => playfield.piece.move(TRANSLATION.LEFT), + moveLeft: () => scene.playfield.piece.move(TRANSLATION.LEFT), - moveRight: () => playfield.piece.move(TRANSLATION.RIGHT), + moveRight: () => scene.playfield.piece.move(TRANSLATION.RIGHT), - rotateCW: () => playfield.piece.rotate(ROTATION.CW), + rotateCW: () => scene.playfield.piece.rotate(ROTATION.CW), - rotateCCW: () => playfield.piece.rotate(ROTATION.CCW), + rotateCCW: () => scene.playfield.piece.rotate(ROTATION.CCW), softDrop: function () { - if (playfield.piece.move(TRANSLATION.DOWN)) stats.score++ + if (scene.playfield.piece.move(TRANSLATION.DOWN)) stats.score++ }, hardDrop: function () { @@ -188,19 +187,19 @@ let playerActions = { scene.hardDropSound.stop() scene.hardDropSound.play() } - while (playfield.piece.move(TRANSLATION.DOWN)) stats.score += 2 + while (scene.playfield.piece.move(TRANSLATION.DOWN)) stats.score += 2 game.lockDown() - playfield.hardDropAnimation.reset() - playfield.hardDropAnimation.play() + scene.playfield.hardDropAnimation.reset() + scene.playfield.hardDropAnimation.play() }, hold: function () { - if (playfield.piece.holdEnabled) { + if (scene.playfield.piece.holdEnabled) { scheduler.clearInterval(game.fall) scheduler.clearTimeout(game.lockDown) let heldpiece = holdQueue.piece - holdQueue.piece = playfield.piece + holdQueue.piece = scene.playfield.piece game.generate(heldpiece) } }, @@ -277,22 +276,11 @@ function resumeOnKeyDown(event) { /* Scene */ -const renderer = new THREE.WebGLRenderer({ - powerPreference: "high-performance", - antialias: true, - stencil: false -}) -renderer.setSize(window.innerWidth, window.innerHeight) -renderer.setClearColor(0x000000, 10) -renderer.toneMapping = THREE.ACESFilmicToneMapping -document.body.appendChild(renderer.domElement) -renderer.domElement.tabIndex = 1 - let loadingManager = new THREE.LoadingManager( function() { loadingDiv.style.display = "none" menu.startButton.show() - renderer.setAnimationLoop(animate) + scene.renderer.setAnimationLoop(animate) }, function (url, itemsLoaded, itemsTotal) { loadingPercent.innerText = Math.floor(100 * itemsLoaded / itemsTotal) + '%' @@ -309,18 +297,13 @@ loadingManager.onStart = function (url, itemsLoaded, itemsTotal) { const settings = new Settings() const stats = new Stats(settings) const scene = new TetraScene(settings, loadingManager) -const controls = new CameraControls(scene.camera, renderer.domElement) -const minoes = new InstancedMino() -scene.add(minoes) const holdQueue = new HoldQueue() scene.add(holdQueue) -const playfield = new Playfield(loadingManager) -scene.add(playfield) const nextQueue = new NextQueue() scene.add(nextQueue) -const menu = new Menu(game, settings, stats, scene, minoes, playfield) +const menu = new Menu(game, settings, stats, scene) menu.load() let fps @@ -338,20 +321,15 @@ const clock = new THREE.Clock() function animate() { const delta = clock.getDelta() - scene.updateMatrixWorld() scene.update(delta) - playfield.update(delta) - minoes.update() - controls.update() - renderer.render(scene, scene.camera) - environment.camera.update(renderer, scene) + environment.camera.update(scene.renderer, scene) fps?.update() } window.addEventListener("resize", () => { - renderer.setSize(window.innerWidth, window.innerHeight) + scene.renderer.setSize(window.innerWidth, window.innerHeight) scene.camera.aspect = window.innerWidth / window.innerHeight scene.camera.updateProjectionMatrix() }) diff --git a/jsm/Menu.js b/jsm/Menu.js index 5f2f93c..75bd66c 100644 --- a/jsm/Menu.js +++ b/jsm/Menu.js @@ -4,7 +4,7 @@ import { environment } from './Tetrominoes.js' export class Menu extends GUI { - constructor(game, settings, stats, scene, minoes, playfield) { + constructor(game, settings, stats, scene) { super({title: "ᵀᴱTᴿᴬ"}) this.startButton = this.add(game, "start").name("Jouer").hide() @@ -28,14 +28,6 @@ export class Menu extends GUI { this.settings.add(settings, "theme", ["Plasma", "Espace", "Rétro"]).name("Thème").onChange(theme => { scene.theme = theme - minoes.theme = theme - if (theme == "Rétro") { - playfield.edge.visible = false - playfield.retroEdge.visible = true - } else { - playfield.edge.visible = true - playfield.retroEdge.visible = false - } if (dev) changeMaterial() }) @@ -76,10 +68,14 @@ export class Menu extends GUI { function changeMaterial() { material?.destroy() material = dev.addFolder("minoes material").close() - material.add(minoes.material, "constructor", ["MeshBasicMaterial", "MeshStandardMaterial", "MeshPhysicalMaterial"]).listen().onChange(type => { + material.add(scene.minoes.material, "constructor", [ + "MeshBasicMaterial", + "MeshStandardMaterial", + "MeshPhysicalMaterial" + ]).listen().onChange(type => { switch(type) { case "MeshBasicMaterial": - minoes.material = new THREE.MeshBasicMaterial({ + scene.minoes.material = new THREE.MeshBasicMaterial({ envMap: environment, side: THREE.DoubleSide, transparent: true, @@ -88,7 +84,7 @@ export class Menu extends GUI { }) break case "MeshStandardMaterial": - minoes.material = new THREE.MeshStandardMaterial({ + scene.minoes.material = new THREE.MeshStandardMaterial({ envMap: environment, side: THREE.DoubleSide, transparent: true, @@ -98,7 +94,7 @@ export class Menu extends GUI { }) break case "MeshPhysicalMaterial": - minoes.material = new THREE.MeshPhysicalMaterial({ + scene.minoes.material = new THREE.MeshPhysicalMaterial({ envMap: environment, side: THREE.DoubleSide, transparent: true, @@ -110,11 +106,11 @@ export class Menu extends GUI { }) break } - minoes.update = minoes.updateColor + scene.minoes.update = scene.minoes.updateColor changeMaterial() }) - let minoMaterial = minoes.material instanceof Array ? minoes.material[0] : minoes.material + let minoMaterial = scene.minoes.material instanceof Array ? scene.minoes.material[0] : scene.minoes.material if ("opacity" in minoMaterial) material.add(minoMaterial, "opacity" ).min(0).max(1) if ("reflectivity" in minoMaterial) material.add(minoMaterial, "reflectivity" ).min(0).max(1) if ("roughness" in minoMaterial) material.add(minoMaterial, "roughness" ).min(0).max(1) @@ -155,7 +151,7 @@ export class Menu extends GUI { vortex.add(scene.vortex.darkCylinder.material, "opacity").name("dark").min(0).max(1).listen() vortex.add(scene.vortex.colorFullCylinder.material, "opacity").name("colorFull").min(0).max(1).listen() - changeMaterial(minoes.material.constructor.name) + changeMaterial(scene.minoes.material.constructor.name) let fog = dev.addFolder("fog").close() fog.add(scene.fog, "near", 0, 200) diff --git a/jsm/TetraScene.js b/jsm/TetraScene.js index 39e3e3c..c4ea08c 100644 --- a/jsm/TetraScene.js +++ b/jsm/TetraScene.js @@ -1,13 +1,30 @@ import * as THREE from 'three' +import CameraControls from './CameraControls.js' import { Vortex } from './Vortex.js' +import { Playfield, InstancedMino } from './Tetrominoes.js' export class TetraScene extends THREE.Scene { constructor(settings, loadingManager) { super() + this.renderer = new THREE.WebGLRenderer({ + powerPreference: "high-performance", + antialias: true, + stencil: false + }) + this.renderer.setSize(window.innerWidth, window.innerHeight) + this.renderer.setClearColor(0x000000, 10) + this.renderer.toneMapping = THREE.ACESFilmicToneMapping + this.renderer.toneMappingExposure = .8 + this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)) + document.body.appendChild(this.renderer.domElement) + this.renderer.domElement.tabIndex = 1 + this.camera = new THREE.PerspectiveCamera(100, window.innerWidth / window.innerHeight, 0.1, 1000) - this.camera.position.set(5, 16, 12) + this.camera.position.set(5, 15, 12) + + this.controls = new CameraControls(this.camera, this.renderer.domElement) this.vortex = new Vortex(loadingManager) this.add(this.vortex) @@ -43,6 +60,12 @@ export class TetraScene extends THREE.Scene { this.hardDropSound.setBuffer(buffer) }.bind(this)) + + this.playfield = new Playfield(loadingManager) + this.add(this.playfield) + this.minoes = new InstancedMino() + this.add(this.minoes) + this.theme = settings.theme } @@ -54,6 +77,8 @@ export class TetraScene extends THREE.Scene { this.directionalLight.position.set(5, -20, 20) this.music.src = "audio/benevolence.m4a" this.fog.color.set(0xffffff) + this.playfield.edge.visible = true + this.playfield.retroEdge.visible = false break case "Espace": this.ambientLight.intensity = 7 @@ -61,6 +86,8 @@ export class TetraScene extends THREE.Scene { this.directionalLight.position.set(2, -3, 20) this.music.src = "audio/benevolence.m4a" this.fog.color.set(0x000000) + this.playfield.edge.visible = true + this.playfield.retroEdge.visible = false break case "Rétro": this.ambientLight.intensity = 1 @@ -68,9 +95,12 @@ export class TetraScene extends THREE.Scene { this.directionalLight.position.set(19, 120, 200) this.music.src = "audio/Tetris_MkVaffQuasi_Ultimix_OC_ReMix.mp3" this.fog.color.set(0x000000) + this.playfield.edge.visible = false + this.playfield.retroEdge.visible = true break } this.vortex.theme = theme + this.minoes.theme = theme } get fogColor() { @@ -81,6 +111,11 @@ export class TetraScene extends THREE.Scene { } update(delta) { + this.controls.update() + this.updateMatrixWorld() this.vortex.update(delta) + this.playfield.update(delta) + this.minoes.update(delta) + this.renderer.render(this, this.camera) } } \ No newline at end of file