Compare commits
No commits in common. "eadae0205f283c52fa10bc8320b6345603535d2a" and "a7eff47b97f6d51ff6a1fd6d1a27d166cadffbbc" have entirely different histories.
eadae0205f
...
a7eff47b97
57
app.js
57
app.js
@ -1,6 +1,6 @@
|
|||||||
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, environnement, Playfield, HoldQueue, NextQueue } from './jsm/gamelogic.js'
|
import { TRANSLATION, ROTATION, environnement, Matrix, HoldQueue, NextQueue } from './jsm/gamelogic.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 { TetraGUI } from './jsm/TetraGUI.js'
|
import { TetraGUI } from './jsm/TetraGUI.js'
|
||||||
@ -31,13 +31,12 @@ let game = {
|
|||||||
holdQueue.remove(holdQueue.piece)
|
holdQueue.remove(holdQueue.piece)
|
||||||
holdQueue.piece = undefined
|
holdQueue.piece = undefined
|
||||||
if (nextQueue.pieces) nextQueue.pieces.forEach(piece => nextQueue.remove(piece))
|
if (nextQueue.pieces) nextQueue.pieces.forEach(piece => nextQueue.remove(piece))
|
||||||
playfield.init()
|
matrix.init()
|
||||||
|
|
||||||
scene.remove(playfield.piece)
|
scene.remove(matrix.piece)
|
||||||
if (playfield.piece) playfield.remove(playfield.piece)
|
matrix.piece = null
|
||||||
playfield.piece = null
|
|
||||||
scene.music.currentTime = 0
|
scene.music.currentTime = 0
|
||||||
playfield.visible = true
|
matrix.visible = true
|
||||||
|
|
||||||
this.playing = true
|
this.playing = true
|
||||||
stats.clock.start()
|
stats.clock.start()
|
||||||
@ -65,16 +64,16 @@ let game = {
|
|||||||
stats.clock.elapsedTime = stats.elapsedTime
|
stats.clock.elapsedTime = stats.elapsedTime
|
||||||
scene.music.play()
|
scene.music.play()
|
||||||
|
|
||||||
if (playfield.piece) scheduler.setInterval(game.fall, stats.fallPeriod)
|
if (matrix.piece) scheduler.setInterval(game.fall, stats.fallPeriod)
|
||||||
else this.generate()
|
else this.generate()
|
||||||
},
|
},
|
||||||
|
|
||||||
generate: function(nextPiece=nextQueue.shift()) {
|
generate: function(nextPiece=nextQueue.shift()) {
|
||||||
nextPiece.lockDelay = stats.lockDelay
|
nextPiece.lockDelay = stats.lockDelay
|
||||||
playfield.piece = nextPiece
|
matrix.piece = nextPiece
|
||||||
playfield.piece.onLockDown = game.lockDown
|
matrix.piece.onLockDown = game.lockDown
|
||||||
|
|
||||||
if (playfield.piece.canMove(TRANSLATION.NONE)) {
|
if (matrix.piece.canMove(TRANSLATION.NONE)) {
|
||||||
scheduler.setInterval(game.fall, stats.fallPeriod)
|
scheduler.setInterval(game.fall, stats.fallPeriod)
|
||||||
} else {
|
} else {
|
||||||
game.over() // block out
|
game.over() // block out
|
||||||
@ -82,17 +81,17 @@ let game = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
fall: function() {
|
fall: function() {
|
||||||
playfield.piece.move(TRANSLATION.DOWN)
|
matrix.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 (matrix.lock(matrix.piece)) {
|
||||||
let tSpin = playfield.piece.tSpin
|
let tSpin = matrix.piece.tSpin
|
||||||
let nbClearedLines = playfield.clearLines()
|
let nbClearedLines = matrix.clearLines()
|
||||||
playfield.remove(playfield.piece)
|
matrix.remove(matrix.piece)
|
||||||
if (settings.sfxVolume) {
|
if (settings.sfxVolume) {
|
||||||
if (nbClearedLines == 4 || (tSpin && nbClearedLines)) {
|
if (nbClearedLines == 4 || (tSpin && nbClearedLines)) {
|
||||||
scene.tetrisSound.currentTime = 0
|
scene.tetrisSound.currentTime = 0
|
||||||
@ -130,7 +129,7 @@ let game = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
over: function() {
|
over: function() {
|
||||||
playfield.piece.locking = false
|
matrix.piece.locking = false
|
||||||
|
|
||||||
document.onkeydown = null
|
document.onkeydown = null
|
||||||
window.onblur = null
|
window.onblur = null
|
||||||
@ -151,16 +150,16 @@ let game = {
|
|||||||
/* Handle player inputs */
|
/* Handle player inputs */
|
||||||
|
|
||||||
let playerActions = {
|
let playerActions = {
|
||||||
moveLeft: () => playfield.piece.move(TRANSLATION.LEFT),
|
moveLeft: () => matrix.piece.move(TRANSLATION.LEFT),
|
||||||
|
|
||||||
moveRight: () => playfield.piece.move(TRANSLATION.RIGHT),
|
moveRight: () => matrix.piece.move(TRANSLATION.RIGHT),
|
||||||
|
|
||||||
rotateCW: () => playfield.piece.rotate(ROTATION.CW),
|
rotateCW: () => matrix.piece.rotate(ROTATION.CW),
|
||||||
|
|
||||||
rotateCCW: () => playfield.piece.rotate(ROTATION.CCW),
|
rotateCCW: () => matrix.piece.rotate(ROTATION.CCW),
|
||||||
|
|
||||||
softDrop: function () {
|
softDrop: function () {
|
||||||
if (playfield.piece.move(TRANSLATION.DOWN)) stats.score++
|
if (matrix.piece.move(TRANSLATION.DOWN)) stats.score++
|
||||||
},
|
},
|
||||||
|
|
||||||
hardDrop: function () {
|
hardDrop: function () {
|
||||||
@ -170,19 +169,19 @@ let playerActions = {
|
|||||||
scene.hardDropSound.currentTime = 0
|
scene.hardDropSound.currentTime = 0
|
||||||
scene.hardDropSound.play()
|
scene.hardDropSound.play()
|
||||||
}
|
}
|
||||||
while (playfield.piece.move(TRANSLATION.DOWN)) stats.score += 2
|
while (matrix.piece.move(TRANSLATION.DOWN)) stats.score += 2
|
||||||
game.lockDown()
|
game.lockDown()
|
||||||
playfield.hardDropAnimation.reset()
|
matrix.hardDropAnimation.reset()
|
||||||
playfield.hardDropAnimation.play()
|
matrix.hardDropAnimation.play()
|
||||||
},
|
},
|
||||||
|
|
||||||
hold: function () {
|
hold: function () {
|
||||||
if (playfield.piece.holdEnabled) {
|
if (matrix.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 = matrix.piece
|
||||||
game.generate(heldpiece)
|
game.generate(heldpiece)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -290,8 +289,8 @@ const clock = new THREE.Clock()
|
|||||||
|
|
||||||
const holdQueue = new HoldQueue()
|
const holdQueue = new HoldQueue()
|
||||||
scene.add(holdQueue)
|
scene.add(holdQueue)
|
||||||
const playfield = new Playfield()
|
const matrix = new Matrix()
|
||||||
scene.add(playfield)
|
scene.add(matrix)
|
||||||
const nextQueue = new NextQueue()
|
const nextQueue = new NextQueue()
|
||||||
scene.add(nextQueue)
|
scene.add(nextQueue)
|
||||||
|
|
||||||
@ -306,7 +305,7 @@ function animate() {
|
|||||||
|
|
||||||
const delta = clock.getDelta()
|
const delta = clock.getDelta()
|
||||||
scene.update(delta)
|
scene.update(delta)
|
||||||
playfield.update(delta)
|
matrix.update(delta)
|
||||||
controls.update()
|
controls.update()
|
||||||
gui.update()
|
gui.update()
|
||||||
|
|
||||||
|
170
jsm/TetraGUI.js
170
jsm/TetraGUI.js
@ -1,10 +1,10 @@
|
|||||||
import * as THREE from 'three'
|
import * as THREE from 'three'
|
||||||
import { GUI } from 'three/addons/libs/lil-gui.module.min.js'
|
import { GUI } from 'three/addons/libs/lil-gui.module.min.js'
|
||||||
import * as FPS from 'three/addons/libs/stats.module.js'
|
import * as FPS from 'three/addons/libs/stats.module.js'
|
||||||
import { COLORS, environnement, minoMaterial, I, J, L, O, S, T, Z, Tetromino } from './gamelogic.js'
|
import { COLORS, environnement, I, J, L, O, S, T, Z } from './gamelogic.js'
|
||||||
|
|
||||||
|
|
||||||
export class TetraGUI extends GUI {
|
class TetraGUI extends GUI {
|
||||||
constructor(game, settings, stats, scene) {
|
constructor(game, settings, stats, scene) {
|
||||||
super({title: "teTra"})
|
super({title: "teTra"})
|
||||||
this.domElement.tabIndex = 1
|
this.domElement.tabIndex = 1
|
||||||
@ -33,27 +33,30 @@ export class TetraGUI extends GUI {
|
|||||||
let darkTexture, colorfullTexture
|
let darkTexture, colorfullTexture
|
||||||
switch (background) {
|
switch (background) {
|
||||||
case "Plasma":
|
case "Plasma":
|
||||||
darkTexture = new THREE.TextureLoader(loadingManager).load("./images/plasma2.jpg", texture => {
|
darkTexture = new THREE.TextureLoader(loadingManager).load("./images/plasma.jpg", texture => {
|
||||||
texture.wrapS = THREE.RepeatWrapping
|
|
||||||
texture.wrapT = THREE.MirroredRepeatWrapping
|
|
||||||
texture.repeat.set(2, 1)
|
|
||||||
})
|
|
||||||
colorfullTexture = new THREE.TextureLoader(loadingManager).load("./images/plasma.jpg", texture => {
|
|
||||||
texture.wrapS = THREE.RepeatWrapping
|
texture.wrapS = THREE.RepeatWrapping
|
||||||
texture.wrapT = THREE.MirroredRepeatWrapping
|
texture.wrapT = THREE.MirroredRepeatWrapping
|
||||||
texture.repeat.set(1, 1)
|
texture.repeat.set(1, 1)
|
||||||
})
|
})
|
||||||
|
colorfullTexture = new THREE.TextureLoader(loadingManager).load("./images/plasma2.jpg", texture => {
|
||||||
|
texture.wrapS = THREE.RepeatWrapping
|
||||||
|
texture.wrapT = THREE.MirroredRepeatWrapping
|
||||||
|
texture.repeat.set(2, 1)
|
||||||
|
})
|
||||||
loadingManager.onLoad = function() {
|
loadingManager.onLoad = function() {
|
||||||
scene.vortex.darkCylinder.material.map = darkTexture
|
scene.vortex.darkCylinder.material.map = darkTexture
|
||||||
scene.vortex.darkCylinder.material.opacity = 0.75
|
scene.vortex.darkCylinder.material.opacity = 0.1
|
||||||
scene.vortex.colorFullCylinder.material.map = colorfullTexture
|
scene.vortex.colorFullCylinder.material.map = colorfullTexture
|
||||||
scene.vortex.colorFullCylinder.material.opacity = 0.075
|
scene.vortex.colorFullCylinder.material.opacity = 0.6
|
||||||
|
|
||||||
scene.vortex.globalRotation = 0.028
|
scene.vortex.globalRotation = 0.028
|
||||||
scene.vortex.darkTextureRotation = 0.005
|
scene.vortex.darkTextureRotation = 0.006
|
||||||
scene.vortex.darkMoveForward = 0.012
|
scene.vortex.darkMoveForward = 0.007
|
||||||
scene.vortex.colorFullTextureRotation = 0.006
|
scene.vortex.colorFullTextureRotation = 0.006
|
||||||
scene.vortex.colorFullMoveForward = 0.016
|
scene.vortex.colorFullMoveForward = 0.02
|
||||||
|
|
||||||
|
scene.ambientLight.intensity = 0.1
|
||||||
|
scene.directionalLight.intensity = 15
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case "Espace":
|
case "Espace":
|
||||||
@ -110,9 +113,9 @@ export class TetraGUI extends GUI {
|
|||||||
|
|
||||||
this.settings.volume = this.settings.addFolder("Volume").open()
|
this.settings.volume = this.settings.addFolder("Volume").open()
|
||||||
this.settings.volume.add(settings,"musicVolume").name("Musique").min(0).max(100).step(1).onChange(volume => {
|
this.settings.volume.add(settings,"musicVolume").name("Musique").min(0).max(100).step(1).onChange(volume => {
|
||||||
|
if (volume) {
|
||||||
scene.music.setVolume(volume/100)
|
scene.music.setVolume(volume/100)
|
||||||
if (game.playing) {
|
if (game.playing) scene.music.play()
|
||||||
if (volume) scene.music.play()
|
|
||||||
} else {
|
} else {
|
||||||
scene.music.pause()
|
scene.music.pause()
|
||||||
}
|
}
|
||||||
@ -136,11 +139,6 @@ export class TetraGUI extends GUI {
|
|||||||
directionalLightPosition.add(scene.directionalLight.position, "y")
|
directionalLightPosition.add(scene.directionalLight.position, "y")
|
||||||
directionalLightPosition.add(scene.directionalLight.position, "z")
|
directionalLightPosition.add(scene.directionalLight.position, "z")
|
||||||
|
|
||||||
let directionalLightTargetPosition = this.debug.addFolder("directionalLight.target").close()
|
|
||||||
directionalLightTargetPosition.add(scene.directionalLight.target.position, "x")
|
|
||||||
directionalLightTargetPosition.add(scene.directionalLight.target.position, "y")
|
|
||||||
directionalLightTargetPosition.add(scene.directionalLight.target.position, "z")
|
|
||||||
|
|
||||||
let light = this.debug.addFolder("lights intensity").close()
|
let light = this.debug.addFolder("lights intensity").close()
|
||||||
light.add(scene.ambientLight, "intensity").name("ambient").min(0).max(20)
|
light.add(scene.ambientLight, "intensity").name("ambient").min(0).max(20)
|
||||||
light.add(scene.directionalLight, "intensity").name("directional").min(0).max(20)
|
light.add(scene.directionalLight, "intensity").name("directional").min(0).max(20)
|
||||||
@ -149,18 +147,125 @@ export class TetraGUI extends GUI {
|
|||||||
vortex.add(scene.vortex.darkCylinder.material, "opacity").name("dark").min(0).max(1)
|
vortex.add(scene.vortex.darkCylinder.material, "opacity").name("dark").min(0).max(1)
|
||||||
vortex.add(scene.vortex.colorFullCylinder.material, "opacity").name("colorFull").min(0).max(1)
|
vortex.add(scene.vortex.colorFullCylinder.material, "opacity").name("colorFull").min(0).max(1)
|
||||||
|
|
||||||
|
let materialParams = {
|
||||||
|
type: "MeshBasicMaterial",
|
||||||
|
opacity: 0.95,
|
||||||
|
reflectivity: 0.8,
|
||||||
|
roughness: 0.1,
|
||||||
|
metalness: 0.5,
|
||||||
|
attenuationDistance: 0.5,
|
||||||
|
ior: 2,
|
||||||
|
sheen: 0,
|
||||||
|
sheenRoughness: 1,
|
||||||
|
specularIntensity: 1,
|
||||||
|
thickness: 5,
|
||||||
|
transmission: 1,
|
||||||
|
}
|
||||||
let material = this.debug.addFolder("minoes material").close()
|
let material = this.debug.addFolder("minoes material").close()
|
||||||
material.add(minoMaterial, "opacity").min(0).max(1)
|
let type = material.add(materialParams, "type", ["MeshBasicMaterial", "MeshStandardMaterial", "MeshPhysicalMaterial"])
|
||||||
//material.add(minoMaterial, "reflectivity").min(0).max(1)
|
let opacity = material.add(materialParams, "opacity").min(0).max(1)
|
||||||
material.add(minoMaterial, "roughness").min(0).max(1)
|
let reflectivity = material.add(materialParams, "reflectivity").min(0).max(1)
|
||||||
material.add(minoMaterial, "metalness").min(0).max(1)
|
let roughness = material.add(materialParams, "roughness").min(0).max(1).hide()
|
||||||
//material.add(minoMaterial, "attenuationDistance").min(0).max(1).hide()
|
let metalness = material.add(materialParams, "metalness").min(0).max(1).hide()
|
||||||
//material.add(minoMaterial, "ior").min(1).max(2).hide()
|
let attenuationDistance = material.add(materialParams, "attenuationDistance").min(0).max(1).hide()
|
||||||
//material.add(minoMaterial, "sheen").min(0).max(1).hide()
|
let ior = material.add(materialParams, "ior").min(1).max(2).hide()
|
||||||
//material.add(minoMaterial, "sheenRoughness").min(0).max(1).hide()
|
let sheen = material.add(materialParams, "sheen").min(0).max(1).hide()
|
||||||
//material.add(minoMaterial, "specularIntensity").min(0).max(1).hide()
|
let sheenRoughness = material.add(materialParams, "sheenRoughness").min(0).max(1).hide()
|
||||||
//material.add(minoMaterial, "thickness").min(0).max(5).hide()
|
let specularIntensity = material.add(materialParams, "specularIntensity").min(0).max(1).hide()
|
||||||
//material.add(minoMaterial, "transmission").min(0).max(1).hide()
|
let thickness = material.add(materialParams, "thickness").min(0).max(5).hide()
|
||||||
|
let transmission = material.add(materialParams, "transmission").min(0).max(1).hide()
|
||||||
|
type.onChange(type => {
|
||||||
|
switch(type) {
|
||||||
|
case "MeshBasicMaterial":
|
||||||
|
reflectivity.show()
|
||||||
|
roughness.hide()
|
||||||
|
metalness.hide()
|
||||||
|
attenuationDistance.hide()
|
||||||
|
ior.hide()
|
||||||
|
sheen.hide()
|
||||||
|
sheenRoughness.hide()
|
||||||
|
specularIntensity.hide()
|
||||||
|
thickness.hide()
|
||||||
|
transmission.hide()
|
||||||
|
break
|
||||||
|
case "MeshStandardMaterial":
|
||||||
|
reflectivity.hide()
|
||||||
|
roughness.show()
|
||||||
|
metalness.show()
|
||||||
|
attenuationDistance.hide()
|
||||||
|
ior.hide()
|
||||||
|
sheen.hide()
|
||||||
|
sheenRoughness.hide()
|
||||||
|
specularIntensity.hide()
|
||||||
|
thickness.hide()
|
||||||
|
transmission.hide()
|
||||||
|
break
|
||||||
|
case "MeshPhysicalMaterial":
|
||||||
|
reflectivity.hide()
|
||||||
|
roughness.show()
|
||||||
|
metalness.show()
|
||||||
|
attenuationDistance.show()
|
||||||
|
ior.show()
|
||||||
|
sheen.show()
|
||||||
|
sheenRoughness.show()
|
||||||
|
specularIntensity.show()
|
||||||
|
thickness.show()
|
||||||
|
transmission.show()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
})
|
||||||
|
material.onChange(() => {
|
||||||
|
let minoMaterialFactory
|
||||||
|
switch(materialParams.type) {
|
||||||
|
case "MeshBasicMaterial":
|
||||||
|
minoMaterialFactory = color => new THREE.MeshBasicMaterial({
|
||||||
|
color : color,
|
||||||
|
envMap : environnement,
|
||||||
|
reflectivity: materialParams.reflectivity,
|
||||||
|
transparent : true,
|
||||||
|
opacity : materialParams.opacity,
|
||||||
|
side : THREE.DoubleSide,
|
||||||
|
})
|
||||||
|
break
|
||||||
|
case "MeshStandardMaterial":
|
||||||
|
minoMaterialFactory = color => new THREE.MeshStandardMaterial({
|
||||||
|
color : color,
|
||||||
|
envMap : environnement,
|
||||||
|
transparent: true,
|
||||||
|
opacity : materialParams.opacity,
|
||||||
|
side : THREE.DoubleSide,
|
||||||
|
roughness : materialParams.roughness,
|
||||||
|
metalness : materialParams.metalness,
|
||||||
|
})
|
||||||
|
break
|
||||||
|
case "MeshPhysicalMaterial":
|
||||||
|
minoMaterialFactory = color => new THREE.MeshPhysicalMaterial({
|
||||||
|
color : "white",
|
||||||
|
envMap : environnement,
|
||||||
|
transparent : true,
|
||||||
|
opacity : materialParams.opacity,
|
||||||
|
side : THREE.DoubleSide,
|
||||||
|
roughness : materialParams.roughness,
|
||||||
|
metalness : materialParams.metalness,
|
||||||
|
attenuationColor : color,
|
||||||
|
attenuationDistance: materialParams.attenuationDistance,
|
||||||
|
ior : materialParams.ior,
|
||||||
|
sheen : materialParams.sheen,
|
||||||
|
sheenRoughness : materialParams.sheenRoughness,
|
||||||
|
specularIntensity : materialParams.specularIntensity,
|
||||||
|
thickness : materialParams.thickness,
|
||||||
|
transmission : materialParams.transmission,
|
||||||
|
})
|
||||||
|
break
|
||||||
|
}
|
||||||
|
I.prototype.material = minoMaterialFactory(COLORS.I)
|
||||||
|
J.prototype.material = minoMaterialFactory(COLORS.J)
|
||||||
|
L.prototype.material = minoMaterialFactory(COLORS.L)
|
||||||
|
O.prototype.material = minoMaterialFactory(COLORS.O)
|
||||||
|
S.prototype.material = minoMaterialFactory(COLORS.S)
|
||||||
|
T.prototype.material = minoMaterialFactory(COLORS.T)
|
||||||
|
Z.prototype.material = minoMaterialFactory(COLORS.Z)
|
||||||
|
})
|
||||||
|
|
||||||
this.fps = new FPS.default()
|
this.fps = new FPS.default()
|
||||||
document.body.appendChild(this.fps.dom)
|
document.body.appendChild(this.fps.dom)
|
||||||
@ -193,3 +298,6 @@ export class TetraGUI extends GUI {
|
|||||||
this.fps?.update()
|
this.fps?.update()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export { TetraGUI }
|
@ -2,7 +2,7 @@ import * as THREE from 'three'
|
|||||||
import { Vortex } from './Vortex.js'
|
import { Vortex } from './Vortex.js'
|
||||||
|
|
||||||
|
|
||||||
export class TetraScene extends THREE.Scene {
|
class TetraScene extends THREE.Scene {
|
||||||
constructor(loadingManager, settings) {
|
constructor(loadingManager, settings) {
|
||||||
super()
|
super()
|
||||||
|
|
||||||
@ -12,15 +12,12 @@ export class TetraScene extends THREE.Scene {
|
|||||||
this.vortex = new Vortex(loadingManager)
|
this.vortex = new Vortex(loadingManager)
|
||||||
this.add(this.vortex)
|
this.add(this.vortex)
|
||||||
|
|
||||||
this.ambientLight = new THREE.AmbientLight(0xffffff, .5)
|
this.ambientLight = new THREE.AmbientLight(0xffffff, 1)
|
||||||
this.add(this.ambientLight)
|
this.add(this.ambientLight)
|
||||||
|
|
||||||
this.directionalLight = new THREE.DirectionalLight(0xffffff, 6)
|
this.directionalLight = new THREE.DirectionalLight(0xffffff, 20)
|
||||||
this.directionalLight.position.set(5, -100, 0)
|
this.directionalLight.position.set(5, -100, -20)
|
||||||
this.add(this.directionalLight)
|
this.add(this.directionalLight)
|
||||||
this.directionalLight.target = new THREE.Object3D()
|
|
||||||
this.directionalLight.target.position.set(5, -50, 20)
|
|
||||||
this.add(this.directionalLight.target)
|
|
||||||
|
|
||||||
|
|
||||||
/* Sounds */
|
/* Sounds */
|
||||||
@ -57,3 +54,6 @@ export class TetraScene extends THREE.Scene {
|
|||||||
this.vortex.update(delta)
|
this.vortex.update(delta)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export { TetraScene }
|
@ -1,40 +1,26 @@
|
|||||||
import * as THREE from 'three'
|
import * as THREE from 'three'
|
||||||
|
|
||||||
|
|
||||||
export class Vortex extends THREE.Group {
|
class Vortex extends THREE.Group {
|
||||||
constructor(loadingManager) {
|
constructor(loadingManager) {
|
||||||
super()
|
super()
|
||||||
|
|
||||||
|
const commonCylinderGeometry = new THREE.CylinderGeometry(35, 35, 500, 12, 1, true)
|
||||||
|
|
||||||
|
|
||||||
this.globalRotation = 0.028
|
this.globalRotation = 0.028
|
||||||
|
|
||||||
this.darkTextureRotation = 0.006
|
this.darkTextureRotation = 0.006
|
||||||
this.darkMoveForward = 0.012
|
this.darkMoveForward = 0.007
|
||||||
|
|
||||||
this.colorFullTextureRotation = 0.006
|
this.colorFullTextureRotation = 0.006
|
||||||
this.colorFullMoveForward = 0.016
|
this.colorFullMoveForward = 0.02
|
||||||
|
|
||||||
const commonCylinderGeometry = new THREE.CylinderGeometry(35, 35, 500, 12, 1, true)
|
|
||||||
|
|
||||||
this.background = "Plasma"
|
this.background = "Plasma"
|
||||||
|
|
||||||
this.darkCylinder = new THREE.Mesh(
|
this.darkCylinder = new THREE.Mesh(
|
||||||
commonCylinderGeometry,
|
commonCylinderGeometry,
|
||||||
new THREE.MeshLambertMaterial({
|
new THREE.MeshLambertMaterial({
|
||||||
side: THREE.BackSide,
|
|
||||||
map: new THREE.TextureLoader(loadingManager).load("./images/plasma2.jpg", (texture) => {
|
|
||||||
texture.wrapS = THREE.RepeatWrapping
|
|
||||||
texture.wrapT = THREE.MirroredRepeatWrapping
|
|
||||||
texture.repeat.set(2, 1)
|
|
||||||
}),
|
|
||||||
blending: THREE.AdditiveBlending,
|
|
||||||
opacity: 0.75
|
|
||||||
})
|
|
||||||
)
|
|
||||||
this.add(this.darkCylinder)
|
|
||||||
|
|
||||||
this.colorFullCylinder = new THREE.Mesh(
|
|
||||||
commonCylinderGeometry,
|
|
||||||
new THREE.MeshBasicMaterial({
|
|
||||||
side: THREE.BackSide,
|
side: THREE.BackSide,
|
||||||
map: new THREE.TextureLoader(loadingManager).load("./images/plasma.jpg", (texture) => {
|
map: new THREE.TextureLoader(loadingManager).load("./images/plasma.jpg", (texture) => {
|
||||||
texture.wrapS = THREE.RepeatWrapping
|
texture.wrapS = THREE.RepeatWrapping
|
||||||
@ -42,21 +28,39 @@ export class Vortex extends THREE.Group {
|
|||||||
texture.repeat.set(1, 1)
|
texture.repeat.set(1, 1)
|
||||||
}),
|
}),
|
||||||
blending: THREE.AdditiveBlending,
|
blending: THREE.AdditiveBlending,
|
||||||
opacity: 0.075
|
opacity: 0.1
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
this.add(this.colorFullCylinder)
|
this.darkCylinder.position.set(5, 10, -10)
|
||||||
|
this.add(this.darkCylinder)
|
||||||
|
|
||||||
this.position.set(5, 10, -10)
|
this.colorFullCylinder = new THREE.Mesh(
|
||||||
|
commonCylinderGeometry,
|
||||||
|
new THREE.MeshBasicMaterial({
|
||||||
|
side: THREE.BackSide,
|
||||||
|
map: new THREE.TextureLoader(loadingManager).load("./images/plasma2.jpg", (texture) => {
|
||||||
|
texture.wrapS = THREE.RepeatWrapping
|
||||||
|
texture.wrapT = THREE.MirroredRepeatWrapping
|
||||||
|
texture.repeat.set(2, 1)
|
||||||
|
}),
|
||||||
|
blending: THREE.AdditiveBlending,
|
||||||
|
opacity: 0.6
|
||||||
|
})
|
||||||
|
)
|
||||||
|
this.colorFullCylinder.position.set(5, 10, -10)
|
||||||
|
this.add(this.colorFullCylinder)
|
||||||
}
|
}
|
||||||
|
|
||||||
update(delta) {
|
update(delta) {
|
||||||
this.rotation.y += this.globalRotation * delta
|
this.darkCylinder.rotation.y += this.globalRotation * delta
|
||||||
|
|
||||||
this.darkCylinder.material.map.offset.y += this.darkMoveForward * delta
|
this.darkCylinder.material.map.offset.y += this.darkMoveForward * delta
|
||||||
this.darkCylinder.material.map.offset.x += this.darkTextureRotation * delta
|
this.darkCylinder.material.map.offset.x += this.darkTextureRotation * delta
|
||||||
|
|
||||||
|
this.colorFullCylinder.rotation.y += this.globalRotation * delta
|
||||||
this.colorFullCylinder.material.map.offset.y += this.colorFullMoveForward * delta
|
this.colorFullCylinder.material.map.offset.y += this.colorFullMoveForward * delta
|
||||||
this.colorFullCylinder.material.map.offset.x += this.colorFullTextureRotation * delta
|
this.colorFullCylinder.material.map.offset.x += this.colorFullTextureRotation * delta
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export { Vortex }
|
296
jsm/gamelogic.js
296
jsm/gamelogic.js
@ -16,8 +16,6 @@ const COLORS = {
|
|||||||
S: 0xC8FBA8,
|
S: 0xC8FBA8,
|
||||||
T: 0xedb2ff,
|
T: 0xedb2ff,
|
||||||
Z: 0xffb8c5,
|
Z: 0xffb8c5,
|
||||||
LOCKING: "white",
|
|
||||||
GHOST: "white",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const TRANSLATION = {
|
const TRANSLATION = {
|
||||||
@ -54,6 +52,20 @@ environnement.camera = new THREE.CubeCamera(1, 1000, envRenderTarget)
|
|||||||
environnement.camera.position.set(5, 10)
|
environnement.camera.position.set(5, 10)
|
||||||
|
|
||||||
|
|
||||||
|
class Mino extends THREE.Mesh {
|
||||||
|
constructor() {
|
||||||
|
super(Mino.prototype.geometry)
|
||||||
|
this.velocity = P(50 - 100 * Math.random(), 50 - 100 * Math.random(), 50 - 100 * Math.random())
|
||||||
|
this.rotationAngle = P(Math.random(), Math.random(), Math.random()).normalize()
|
||||||
|
this.angularVelocity = 5 - 10 * Math.random()
|
||||||
|
}
|
||||||
|
|
||||||
|
update(delta) {
|
||||||
|
this.velocity.y += delta * GRAVITY
|
||||||
|
this.position.addScaledVector(this.velocity, delta)
|
||||||
|
this.rotateOnWorldAxis(this.rotationAngle, delta * this.angularVelocity)
|
||||||
|
}
|
||||||
|
}
|
||||||
const minoFaceShape = new THREE.Shape()
|
const minoFaceShape = new THREE.Shape()
|
||||||
minoFaceShape.moveTo(.1, .1)
|
minoFaceShape.moveTo(.1, .1)
|
||||||
minoFaceShape.lineTo(.1, .9)
|
minoFaceShape.lineTo(.1, .9)
|
||||||
@ -69,46 +81,67 @@ const minoExtrudeSettings = {
|
|||||||
bevelOffset: 0,
|
bevelOffset: 0,
|
||||||
bevelSegments: 1
|
bevelSegments: 1
|
||||||
}
|
}
|
||||||
let minoGeometry = new THREE.ExtrudeGeometry(minoFaceShape, minoExtrudeSettings)
|
Mino.prototype.geometry = new THREE.ExtrudeGeometry(minoFaceShape, minoExtrudeSettings)
|
||||||
|
|
||||||
let minoMaterial = new THREE.MeshStandardMaterial({
|
|
||||||
|
class MinoMaterial extends THREE.MeshBasicMaterial {
|
||||||
|
constructor(color) {
|
||||||
|
super({
|
||||||
|
color: color,
|
||||||
envMap: environnement,
|
envMap: environnement,
|
||||||
side: THREE.DoubleSide,
|
reflectivity: 0.9,
|
||||||
transparent: true,
|
transparent: true,
|
||||||
opacity: 0.8,
|
opacity: 0.8,
|
||||||
//reflectivity: 0.8,
|
side: THREE.DoubleSide,
|
||||||
roughness: 0.1,
|
})
|
||||||
metalness: 0.9,
|
|
||||||
//attenuationDistance: 0.5,
|
|
||||||
//ior: 2,
|
|
||||||
//sheen: 0,
|
|
||||||
//sheenRoughness: 1,
|
|
||||||
//specularIntensity: 1,
|
|
||||||
//thickness: 5,
|
|
||||||
//transmission: 1,
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
class Mino extends THREE.Object3D {
|
|
||||||
constructor(color, x, y, z=0) {
|
|
||||||
super()
|
|
||||||
this.color = color
|
|
||||||
this.position.set(x, y, z)
|
|
||||||
this.velocity = P(50 - 100 * Math.random(), 50 - 100 * Math.random(), 50 - 100 * Math.random())
|
|
||||||
this.rotationAngle = P(Math.random(), Math.random(), Math.random()).normalize()
|
|
||||||
this.angularVelocity = 5 - 10 * Math.random()
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
update(delta) {
|
class GhostMaterial extends THREE.MeshBasicMaterial {
|
||||||
this.velocity.y += delta * GRAVITY
|
constructor(color) {
|
||||||
this.position.addScaledVector(this.velocity, delta)
|
super({
|
||||||
this.rotateOnWorldAxis(this.rotationAngle, delta * this.angularVelocity)
|
color: color,
|
||||||
this.updateMatrix()
|
envMap: environnement,
|
||||||
|
reflectivity: 0.9,
|
||||||
|
transparent: true,
|
||||||
|
opacity: 0.15,
|
||||||
|
side: THREE.DoubleSide,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class Tetromino extends THREE.InstancedMesh {
|
class AbstractTetromino extends THREE.Group {
|
||||||
|
constructor() {
|
||||||
|
super()
|
||||||
|
for (let i = 0; i < 4; i++) {
|
||||||
|
this.add(new Mino())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
set facing(facing) {
|
||||||
|
this._facing = facing
|
||||||
|
this.minoesPosition[this.facing].forEach(
|
||||||
|
(position, i) => this.children[i].position.copy(position)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
get facing() {
|
||||||
|
return this._facing
|
||||||
|
}
|
||||||
|
|
||||||
|
canMove(translation, facing=this.facing) {
|
||||||
|
let testPosition = this.position.clone().add(translation)
|
||||||
|
return this.minoesPosition[facing].every(minoPosition => this.parent.cellIsEmpty(minoPosition.clone().add(testPosition)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Ghost extends AbstractTetromino {}
|
||||||
|
Ghost.prototype.minoesPosition = [
|
||||||
|
[P(0, 0, 0), P(0, 0, 0), P(0, 0, 0), P(0, 0, 0)],
|
||||||
|
]
|
||||||
|
|
||||||
|
class Tetromino extends AbstractTetromino {
|
||||||
static randomBag = []
|
static randomBag = []
|
||||||
static get random() {
|
static get random() {
|
||||||
if (!this.randomBag.length) this.randomBag = [I, J, L, O, S, T, Z]
|
if (!this.randomBag.length) this.randomBag = [I, J, L, O, S, T, Z]
|
||||||
@ -116,49 +149,14 @@ class Tetromino extends THREE.InstancedMesh {
|
|||||||
}
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super(minoGeometry, undefined, 4)
|
super()
|
||||||
this.material = this.minoMaterial
|
|
||||||
this.facing = FACING.NORTH
|
|
||||||
this.rotatedLast = false
|
this.rotatedLast = false
|
||||||
this.rotationPoint4Used = false
|
this.rotationPoint4Used = false
|
||||||
this.holdEnabled = true
|
this.holdEnabled = true
|
||||||
|
this.facing = 0
|
||||||
this.locking = false
|
this.locking = false
|
||||||
}
|
}
|
||||||
|
|
||||||
set facing(facing) {
|
|
||||||
this._facing = facing
|
|
||||||
let matrix4 = new THREE.Matrix4()
|
|
||||||
this.minoesPosition[this.facing].forEach((position, i) => {
|
|
||||||
matrix4.setPosition(position)
|
|
||||||
this.setMatrixAt(i, matrix4)
|
|
||||||
})
|
|
||||||
this.instanceMatrix.needsUpdate = true
|
|
||||||
}
|
|
||||||
|
|
||||||
get facing() {
|
|
||||||
return this._facing
|
|
||||||
}
|
|
||||||
|
|
||||||
set locking(locking) {
|
|
||||||
if (locking) {
|
|
||||||
this.color = this.lockingColor
|
|
||||||
} else {
|
|
||||||
this.color = this.freeColor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
set color(color) {
|
|
||||||
for (let i = 0; i < this.count; i++) {
|
|
||||||
this.setColorAt(i, color)
|
|
||||||
}
|
|
||||||
this.instanceColor.needsUpdate = true
|
|
||||||
}
|
|
||||||
|
|
||||||
canMove(translation, facing=this.facing) {
|
|
||||||
let testPosition = this.position.clone().add(translation)
|
|
||||||
return this.minoesPosition[facing].every(minoPosition => this.parent.cellIsEmpty(minoPosition.clone().add(testPosition)))
|
|
||||||
}
|
|
||||||
|
|
||||||
move(translation, rotatedFacing, rotationPoint) {
|
move(translation, rotatedFacing, rotationPoint) {
|
||||||
if (this.canMove(translation, rotatedFacing)) {
|
if (this.canMove(translation, rotatedFacing)) {
|
||||||
this.position.add(translation)
|
this.position.add(translation)
|
||||||
@ -169,16 +167,14 @@ class Tetromino extends THREE.InstancedMesh {
|
|||||||
}
|
}
|
||||||
if (this.canMove(TRANSLATION.DOWN)) {
|
if (this.canMove(TRANSLATION.DOWN)) {
|
||||||
this.locking = false
|
this.locking = false
|
||||||
this.parent.ghost.visible = true
|
|
||||||
this.parent.ghost.copy(this)
|
|
||||||
scheduler.clearTimeout(this.onLockDown)
|
scheduler.clearTimeout(this.onLockDown)
|
||||||
} else {
|
} else {
|
||||||
scheduler.resetTimeout(this.onLockDown, this.lockDelay)
|
scheduler.resetTimeout(this.onLockDown, this.lockDelay)
|
||||||
this.locking = true
|
this.locking = true
|
||||||
this.parent.ghost.visible = false
|
|
||||||
}
|
}
|
||||||
|
if (this.ghost.visible) this.updateGhost()
|
||||||
return true
|
return true
|
||||||
} else if (translation == TRANSLATION.DOWN) {
|
} else {
|
||||||
this.locked = true
|
this.locked = true
|
||||||
if (!scheduler.timeoutTasks.has(this.onLockDown))
|
if (!scheduler.timeoutTasks.has(this.onLockDown))
|
||||||
scheduler.setTimeout(this.onLockDown, this.lockDelay)
|
scheduler.setTimeout(this.onLockDown, this.lockDelay)
|
||||||
@ -193,19 +189,27 @@ class Tetromino extends THREE.InstancedMesh {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
set locking(locking) {
|
||||||
|
if (locking) {
|
||||||
|
this.children.forEach(mino => mino.material = this.lockedMaterial)
|
||||||
|
this.ghost.visible = false
|
||||||
|
} else {
|
||||||
|
this.children.forEach(mino => mino.material = this.material)
|
||||||
|
this.ghost.visible = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateGhost() {
|
||||||
|
this.ghost.position.copy(this.position)
|
||||||
|
this.ghost.minoesPosition = this.minoesPosition
|
||||||
|
this.ghost.facing = this.facing
|
||||||
|
while (this.ghost.canMove(TRANSLATION.DOWN)) this.ghost.position.y--
|
||||||
|
}
|
||||||
|
|
||||||
get tSpin() {
|
get tSpin() {
|
||||||
return T_SPIN.NONE
|
return T_SPIN.NONE
|
||||||
}
|
}
|
||||||
|
|
||||||
copy(piece) {
|
|
||||||
this.position.copy(piece.position)
|
|
||||||
this.minoesPosition = piece.minoesPosition
|
|
||||||
this.facing = piece.facing
|
|
||||||
while (this.canMove(TRANSLATION.DOWN)) this.position.y--
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Tetromino.prototype.minoMaterial = minoMaterial
|
|
||||||
Tetromino.prototype.lockingColor = new THREE.Color(COLORS.LOCKING)
|
|
||||||
// Super Rotation System
|
// Super Rotation System
|
||||||
// freedom of movement = srs[this.parent.piece.facing][rotation]
|
// freedom of movement = srs[this.parent.piece.facing][rotation]
|
||||||
Tetromino.prototype.srs = [
|
Tetromino.prototype.srs = [
|
||||||
@ -214,21 +218,9 @@ Tetromino.prototype.srs = [
|
|||||||
{ [ROTATION.CW]: [P(0, 0), P(1, 0), P(1, 1), P(0, -2), P(1, -2)], [ROTATION.CCW]: [P(0, 0), P(-1, 0), P(-1, 1), P(0, -2), P(-1, -2)] },
|
{ [ROTATION.CW]: [P(0, 0), P(1, 0), P(1, 1), P(0, -2), P(1, -2)], [ROTATION.CCW]: [P(0, 0), P(-1, 0), P(-1, 1), P(0, -2), P(-1, -2)] },
|
||||||
{ [ROTATION.CW]: [P(0, 0), P(-1, 0), P(-1, -1), P(0, 2), P(-1, 2)], [ROTATION.CCW]: [P(0, 0), P(-1, 0), P(-1, -1), P(0, 2), P(-1, 2)] },
|
{ [ROTATION.CW]: [P(0, 0), P(-1, 0), P(-1, -1), P(0, 2), P(-1, 2)], [ROTATION.CCW]: [P(0, 0), P(-1, 0), P(-1, -1), P(0, 2), P(-1, 2)] },
|
||||||
]
|
]
|
||||||
|
Tetromino.prototype.lockedMaterial = new MinoMaterial(0xffffff)
|
||||||
Tetromino.prototype.lockDelay = 500
|
Tetromino.prototype.lockDelay = 500
|
||||||
|
Tetromino.prototype.ghost = new Ghost()
|
||||||
|
|
||||||
class Ghost extends Tetromino {}
|
|
||||||
Ghost.prototype.minoMaterial = new THREE.MeshBasicMaterial({
|
|
||||||
envMap: environnement,
|
|
||||||
reflectivity: 0.9,
|
|
||||||
transparent: true,
|
|
||||||
opacity: 0.15,
|
|
||||||
side: THREE.DoubleSide,
|
|
||||||
})
|
|
||||||
Ghost.prototype.freeColor = new THREE.Color(COLORS.GHOST)
|
|
||||||
Ghost.prototype.minoesPosition = [
|
|
||||||
[P(0, 0, 0), P(0, 0, 0), P(0, 0, 0), P(0, 0, 0)],
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class I extends Tetromino { }
|
class I extends Tetromino { }
|
||||||
@ -244,7 +236,8 @@ I.prototype.srs = [
|
|||||||
{ [ROTATION.CW]: [P(0, 0), P(2, 0), P(-1, 0), P(2, 1), P(-1, -2)], [ROTATION.CCW]: [P(0, 0), P(1, 0), P(-2, 0), P(1, -2), P(-2, 1)] },
|
{ [ROTATION.CW]: [P(0, 0), P(2, 0), P(-1, 0), P(2, 1), P(-1, -2)], [ROTATION.CCW]: [P(0, 0), P(1, 0), P(-2, 0), P(1, -2), P(-2, 1)] },
|
||||||
{ [ROTATION.CW]: [P(0, 0), P(1, 0), P(-2, 0), P(1, -2), P(-2, 1)], [ROTATION.CCW]: [P(0, 0), P(-2, 0), P(1, 0), P(-2, -1), P(1, 2)] },
|
{ [ROTATION.CW]: [P(0, 0), P(1, 0), P(-2, 0), P(1, -2), P(-2, 1)], [ROTATION.CCW]: [P(0, 0), P(-2, 0), P(1, 0), P(-2, -1), P(1, 2)] },
|
||||||
]
|
]
|
||||||
I.prototype.freeColor = new THREE.Color(COLORS.I)
|
I.prototype.material = new MinoMaterial(COLORS.I)
|
||||||
|
I.prototype.ghostMaterial = new GhostMaterial(COLORS.I)
|
||||||
|
|
||||||
class J extends Tetromino { }
|
class J extends Tetromino { }
|
||||||
J.prototype.minoesPosition = [
|
J.prototype.minoesPosition = [
|
||||||
@ -253,7 +246,8 @@ J.prototype.minoesPosition = [
|
|||||||
[P(1, -1), P(-1, 0), P(0, 0), P(1, 0)],
|
[P(1, -1), P(-1, 0), P(0, 0), P(1, 0)],
|
||||||
[P(0, 1), P(-1, -1), P(0, 0), P(0, -1)],
|
[P(0, 1), P(-1, -1), P(0, 0), P(0, -1)],
|
||||||
]
|
]
|
||||||
J.prototype.freeColor = new THREE.Color(COLORS.J)
|
J.prototype.material = new MinoMaterial(COLORS.J)
|
||||||
|
J.prototype.ghostMaterial = new GhostMaterial(COLORS.J)
|
||||||
|
|
||||||
class L extends Tetromino { }
|
class L extends Tetromino { }
|
||||||
L.prototype.minoesPosition = [
|
L.prototype.minoesPosition = [
|
||||||
@ -262,7 +256,8 @@ L.prototype.minoesPosition = [
|
|||||||
[P(-1, 0), P(0, 0), P(1, 0), P(-1, -1)],
|
[P(-1, 0), P(0, 0), P(1, 0), P(-1, -1)],
|
||||||
[P(0, 1), P(0, 0), P(0, -1), P(-1, 1)],
|
[P(0, 1), P(0, 0), P(0, -1), P(-1, 1)],
|
||||||
]
|
]
|
||||||
L.prototype.freeColor = new THREE.Color(COLORS.L)
|
L.prototype.material = new MinoMaterial(COLORS.L)
|
||||||
|
L.prototype.ghostMaterial = new GhostMaterial(COLORS.L)
|
||||||
|
|
||||||
class O extends Tetromino { }
|
class O extends Tetromino { }
|
||||||
O.prototype.minoesPosition = [
|
O.prototype.minoesPosition = [
|
||||||
@ -271,7 +266,8 @@ O.prototype.minoesPosition = [
|
|||||||
O.prototype.srs = [
|
O.prototype.srs = [
|
||||||
{ [ROTATION.CW]: [], [ROTATION.CCW]: [] }
|
{ [ROTATION.CW]: [], [ROTATION.CCW]: [] }
|
||||||
]
|
]
|
||||||
O.prototype.freeColor = new THREE.Color(COLORS.O)
|
O.prototype.material = new MinoMaterial(COLORS.O)
|
||||||
|
O.prototype.ghostMaterial = new GhostMaterial(COLORS.O)
|
||||||
|
|
||||||
class S extends Tetromino { }
|
class S extends Tetromino { }
|
||||||
S.prototype.minoesPosition = [
|
S.prototype.minoesPosition = [
|
||||||
@ -280,7 +276,8 @@ S.prototype.minoesPosition = [
|
|||||||
[P(-1, -1), P(0, 0), P(1, 0), P(0, -1)],
|
[P(-1, -1), P(0, 0), P(1, 0), P(0, -1)],
|
||||||
[P(-1, 1), P(0, 0), P(-1, 0), P(0, -1)],
|
[P(-1, 1), P(0, 0), P(-1, 0), P(0, -1)],
|
||||||
]
|
]
|
||||||
S.prototype.freeColor = new THREE.Color(COLORS.S)
|
S.prototype.material = new MinoMaterial(COLORS.S)
|
||||||
|
S.prototype.ghostMaterial = new GhostMaterial(COLORS.S)
|
||||||
|
|
||||||
class T extends Tetromino {
|
class T extends Tetromino {
|
||||||
get tSpin() {
|
get tSpin() {
|
||||||
@ -307,7 +304,8 @@ T.prototype.tSlots = [
|
|||||||
[P(1, -1), P(-1, -1), P(-1, 1), P(1, 1)],
|
[P(1, -1), P(-1, -1), P(-1, 1), P(1, 1)],
|
||||||
[P(-1, -1), P(-1, 1), P(1, 1), P(1, -1)],
|
[P(-1, -1), P(-1, 1), P(1, 1), P(1, -1)],
|
||||||
]
|
]
|
||||||
T.prototype.freeColor = new THREE.Color(COLORS.T)
|
T.prototype.material = new MinoMaterial(COLORS.T)
|
||||||
|
T.prototype.ghostMaterial = new GhostMaterial(COLORS.T)
|
||||||
|
|
||||||
class Z extends Tetromino { }
|
class Z extends Tetromino { }
|
||||||
Z.prototype.minoesPosition = [
|
Z.prototype.minoesPosition = [
|
||||||
@ -316,7 +314,8 @@ Z.prototype.minoesPosition = [
|
|||||||
[P(-1, 0), P(0, 0), P(0, -1), P(1, -1)],
|
[P(-1, 0), P(0, 0), P(0, -1), P(1, -1)],
|
||||||
[P(0, 1), P(-1, 0), P(0, 0), P(-1, -1)]
|
[P(0, 1), P(-1, 0), P(0, 0), P(-1, -1)]
|
||||||
]
|
]
|
||||||
Z.prototype.freeColor = new THREE.Color(COLORS.Z)
|
Z.prototype.material = new MinoMaterial(COLORS.Z)
|
||||||
|
Z.prototype.ghostMaterial = new GhostMaterial(COLORS.Z)
|
||||||
|
|
||||||
|
|
||||||
const ROWS = 24
|
const ROWS = 24
|
||||||
@ -324,7 +323,7 @@ const SKYLINE = 20
|
|||||||
const COLUMNS = 10
|
const COLUMNS = 10
|
||||||
|
|
||||||
|
|
||||||
class Playfield extends THREE.Group {
|
class Matrix extends THREE.Group {
|
||||||
constructor() {
|
constructor() {
|
||||||
super()
|
super()
|
||||||
this.visible = false
|
this.visible = false
|
||||||
@ -365,24 +364,13 @@ class Playfield extends THREE.Group {
|
|||||||
this.hardDropAnimation.loop = THREE.LoopOnce
|
this.hardDropAnimation.loop = THREE.LoopOnce
|
||||||
this.hardDropAnimation.setDuration(0.2)
|
this.hardDropAnimation.setDuration(0.2)
|
||||||
|
|
||||||
this.ghost = new Ghost()
|
|
||||||
this.add(this.ghost)
|
|
||||||
this.ghost.visible = false
|
|
||||||
|
|
||||||
this.lockedMeshes = new THREE.InstancedMesh(minoGeometry, minoMaterial, 200)
|
|
||||||
this.add(this.lockedMeshes)
|
|
||||||
|
|
||||||
this.freedMinoes = []
|
|
||||||
this.freedMeshes = new THREE.InstancedMesh(minoGeometry, minoMaterial, 200)
|
|
||||||
this.freedMeshes.count = 0
|
|
||||||
this.add(this.freedMeshes)
|
|
||||||
|
|
||||||
this.init()
|
this.init()
|
||||||
}
|
}
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
|
while(this.children.length > 1 ) this.remove(this.children[1])
|
||||||
this.cells = Array(ROWS).fill().map(() => Array(COLUMNS))
|
this.cells = Array(ROWS).fill().map(() => Array(COLUMNS))
|
||||||
this.lockedMeshes.count = 0
|
this.unlockedMinoes = new Set()
|
||||||
}
|
}
|
||||||
|
|
||||||
cellIsEmpty(p) {
|
cellIsEmpty(p) {
|
||||||
@ -395,9 +383,11 @@ class Playfield extends THREE.Group {
|
|||||||
if (piece) {
|
if (piece) {
|
||||||
this.add(piece)
|
this.add(piece)
|
||||||
piece.position.set(4, SKYLINE)
|
piece.position.set(4, SKYLINE)
|
||||||
this.ghost.color = piece.freeColor
|
this.add(piece.ghost)
|
||||||
this.ghost.copy(piece)
|
piece.ghost.children.forEach((mino) => {
|
||||||
this.ghost.visible = true
|
mino.material = piece.ghostMaterial
|
||||||
|
})
|
||||||
|
piece.updateGhost()
|
||||||
}
|
}
|
||||||
this._piece = piece
|
this._piece = piece
|
||||||
}
|
}
|
||||||
@ -407,66 +397,50 @@ class Playfield extends THREE.Group {
|
|||||||
}
|
}
|
||||||
|
|
||||||
lock() {
|
lock() {
|
||||||
this.piece.minoesPosition[this.piece.facing].forEach(position => {
|
this.piece.locking = false
|
||||||
position = position.clone()
|
let minoes = Array.from(this.piece.children)
|
||||||
position.add(this.piece.position)
|
minoes.forEach(mino => {
|
||||||
if (this.cellIsEmpty(position)) {
|
mino.position.add(this.piece.position)
|
||||||
this.cells[position.y][position.x] = this.piece.freeColor
|
this.add(mino)
|
||||||
|
if (this.cellIsEmpty(mino.position)) {
|
||||||
|
this.cells[mino.position.y][mino.position.x] = mino
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
this.updateLockedMinoes()
|
return minoes.some(mino => mino.position.y < SKYLINE)
|
||||||
return this.piece.minoesPosition[this.piece.facing].every(position => position.y + this.piece.position.y < SKYLINE)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
clearLines() {
|
clearLines() {
|
||||||
let nbClearedLines = this.cells.reduceRight((nbClearedLines, row, y) => {
|
let nbClearedLines = this.cells.reduceRight((nbClearedLines, row, y) => {
|
||||||
if (row.filter(color => color).length == COLUMNS) {
|
if (row.filter(mino => mino).length == COLUMNS) {
|
||||||
row.forEach((color, x) => {
|
row.forEach(mino => this.unlockedMinoes.add(mino))
|
||||||
this.freedMinoes.push(new Mino(color, x, y))
|
|
||||||
})
|
|
||||||
this.cells.splice(y, 1)
|
this.cells.splice(y, 1)
|
||||||
this.cells.push(Array(COLUMNS))
|
this.cells.push(Array(COLUMNS))
|
||||||
return ++nbClearedLines
|
return ++nbClearedLines
|
||||||
}
|
}
|
||||||
return nbClearedLines
|
return nbClearedLines
|
||||||
}, 0)
|
}, 0)
|
||||||
this.updateLockedMinoes()
|
if (nbClearedLines) {
|
||||||
|
this.cells.forEach((rows, y) => {
|
||||||
|
rows.forEach((mino, x) => {
|
||||||
|
mino.position.set(x, y)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
return nbClearedLines
|
return nbClearedLines
|
||||||
}
|
}
|
||||||
|
|
||||||
updateLockedMinoes() {
|
updateUnlockedMinoes(delta) {
|
||||||
let i = 0
|
this.unlockedMinoes.forEach(mino => {
|
||||||
let matrix4 = new THREE.Matrix4()
|
mino.update(delta)
|
||||||
this.cells.forEach((row, y) => row.forEach((color, x) => {
|
if (Math.sqrt(mino.position.x * mino.position.x + mino.position.z * mino.position.z) > 40 || mino.position.y < -50) {
|
||||||
matrix4.setPosition(x, y, 0)
|
this.remove(mino)
|
||||||
this.lockedMeshes.setMatrixAt(i, matrix4)
|
this.unlockedMinoes.delete(mino)
|
||||||
this.lockedMeshes.setColorAt(i, color)
|
|
||||||
i++
|
|
||||||
}))
|
|
||||||
this.lockedMeshes.count = i
|
|
||||||
this.lockedMeshes.instanceMatrix.needsUpdate = true
|
|
||||||
this.lockedMeshes.instanceColor.needsUpdate = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateFreedMinoes(delta) {
|
|
||||||
this.freedMinoes.forEach(mino => mino.update(delta))
|
|
||||||
this.freedMinoes = this.freedMinoes.filter(mino =>
|
|
||||||
Math.sqrt(mino.position.x * mino.position.x + mino.position.z * mino.position.z) <= 40 && mino.position.y > -50
|
|
||||||
) || []
|
|
||||||
|
|
||||||
this.freedMeshes.count = this.freedMinoes.length
|
|
||||||
if (this.freedMeshes.count) {
|
|
||||||
this.freedMinoes.forEach((mino, i) => {
|
|
||||||
this.freedMeshes.setMatrixAt(i, mino.matrix)
|
|
||||||
this.freedMeshes.setColorAt(i, mino.color)
|
|
||||||
})
|
})
|
||||||
this.freedMeshes.instanceMatrix.needsUpdate = true
|
|
||||||
this.freedMeshes.instanceColor.needsUpdate = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
update(delta) {
|
update(delta) {
|
||||||
this.updateFreedMinoes(delta)
|
this.updateUnlockedMinoes(delta)
|
||||||
this.mixer?.update(delta)
|
this.mixer?.update(delta)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -525,4 +499,4 @@ class NextQueue extends THREE.Group {
|
|||||||
NextQueue.prototype.positions = [P(0, 0), P(0, -3), P(0, -6), P(0, -9), P(0, -12), P(0, -15), P(0, -18)]
|
NextQueue.prototype.positions = [P(0, 0), P(0, -3), P(0, -6), P(0, -9), P(0, -12), P(0, -15), P(0, -18)]
|
||||||
|
|
||||||
|
|
||||||
export { T_SPIN, FACING, TRANSLATION, ROTATION, COLORS, environnement, minoMaterial, Tetromino, I, J, L, O, S, T, Z, Playfield, HoldQueue, NextQueue }
|
export { T_SPIN, FACING, TRANSLATION, ROTATION, COLORS, environnement, Tetromino, I, J, L, O, S, T, Z, Matrix, HoldQueue, NextQueue }
|
Loading…
x
Reference in New Issue
Block a user