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