RETRO THEME
This commit is contained in:
parent
8ed998f255
commit
7f6795109b
17
app.js
17
app.js
@ -1,6 +1,6 @@
|
||||
import * as THREE from 'three'
|
||||
import { scheduler } from './jsm/scheduler.js'
|
||||
import { TRANSLATION, ROTATION, environnement, Mino, Playfield, HoldQueue, NextQueue } from './jsm/gamelogic.js'
|
||||
import { TRANSLATION, ROTATION, environment, Mino, Playfield, HoldQueue, NextQueue } from './jsm/Tetrominoes.js'
|
||||
import { Settings } from './jsm/Settings.js'
|
||||
import { Stats } from './jsm/Stats.js'
|
||||
import { TetraGUI } from './jsm/TetraGUI.js'
|
||||
@ -29,7 +29,7 @@ let game = {
|
||||
gui.settings.close()
|
||||
gui.stats.show()
|
||||
|
||||
Mino.mesh.clear()
|
||||
Mino.meshes.clear()
|
||||
|
||||
nextQueue.init()
|
||||
holdQueue.piece = undefined
|
||||
@ -298,17 +298,18 @@ const stats = new Stats()
|
||||
const settings = new Settings()
|
||||
const scene = new TetraScene(settings, loadingManager)
|
||||
const controls = new TetraControls(scene.camera, renderer.domElement)
|
||||
const gui = new TetraGUI(game, settings, stats, scene, controls)
|
||||
|
||||
scene.add(Mino.mesh)
|
||||
|
||||
scene.add(Mino.meshes)
|
||||
const holdQueue = new HoldQueue()
|
||||
scene.add(holdQueue)
|
||||
const playfield = new Playfield()
|
||||
const playfield = new Playfield(loadingManager)
|
||||
scene.add(playfield)
|
||||
const nextQueue = new NextQueue()
|
||||
scene.add(nextQueue)
|
||||
|
||||
const gui = new TetraGUI(game, settings, stats, scene, controls, playfield)
|
||||
gui.load()
|
||||
|
||||
messagesSpan.onanimationend = function (event) {
|
||||
event.target.remove()
|
||||
}
|
||||
@ -321,12 +322,12 @@ function animate() {
|
||||
scene.updateMatrixWorld()
|
||||
scene.update(delta)
|
||||
playfield.update(delta)
|
||||
Mino.mesh.update()
|
||||
Mino.meshes.update()
|
||||
controls.update()
|
||||
gui.update()
|
||||
|
||||
renderer.render(scene, scene.camera)
|
||||
environnement.camera.update(renderer, scene)
|
||||
environment.camera.update(renderer, scene)
|
||||
}
|
||||
|
||||
window.addEventListener("resize", () => {
|
||||
|
BIN
audio/Tetris_MkVaffQuasi_Ultimix_OC_ReMix.mp3
Normal file
BIN
audio/Tetris_MkVaffQuasi_Ultimix_OC_ReMix.mp3
Normal file
Binary file not shown.
BIN
images/edge.png
Normal file
BIN
images/edge.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.8 KiB |
BIN
images/sprites.png
Normal file
BIN
images/sprites.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 792 B |
@ -6,8 +6,8 @@
|
||||
<meta charset="utf-8" />
|
||||
<title>teTra</title>
|
||||
<link rel="icon" href="favicon.ico">
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<link rel="stylesheet" href="loading.css">
|
||||
<link rel="stylesheet" href="css/style.css">
|
||||
<link rel="stylesheet" href="css/loading.css">
|
||||
<script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script>
|
||||
<script type="importmap">
|
||||
{
|
||||
@ -35,6 +35,6 @@
|
||||
<audio id="music" src="audio/benevolence.m4a" loop></audio>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ENjdO4Dr2bkBIFxQpeoTz1HIcje39Wm4jDKdf19U8gI4ddQ3GYNS7NTKfAdVQSZe" crossorigin="anonymous"></script>
|
||||
<script type="module" src="app.js"></script>
|
||||
<script>navigator?.serviceWorker.register('./jsm/service-worker.js')</script>
|
||||
<script>navigator?.serviceWorker?.register('./jsm/service-worker.js')</script>
|
||||
</body>
|
||||
</html>
|
@ -1,5 +1,5 @@
|
||||
import { Clock } from 'three'
|
||||
import { T_SPIN } from './gamelogic.js'
|
||||
import { T_SPIN } from './Tetrominoes.js'
|
||||
|
||||
|
||||
// score = AWARDED_LINE_CLEARS[tSpin][nbClearedLines]
|
||||
|
@ -10,8 +10,8 @@ class TetraControls extends OrbitControls {
|
||||
this.dampingFactor = 0.04
|
||||
this.maxDistance = 21
|
||||
this.keys = {}
|
||||
this.minPolarAngle = 0.9
|
||||
this.maxPolarAngle = 2.14
|
||||
this.minPolarAngle = 1
|
||||
this.maxPolarAngle = 2.1
|
||||
this.minAzimuthAngle = 0.9 - Math.PI / 2
|
||||
this.maxAzimuthAngle = 2.14 - Math.PI / 2
|
||||
this.target.set(5, 7, 0)
|
||||
|
159
jsm/TetraGUI.js
159
jsm/TetraGUI.js
@ -1,92 +1,15 @@
|
||||
import * as THREE from 'three'
|
||||
import { GUI } from 'three/addons/libs/lil-gui.module.min.js'
|
||||
import * as FPS from 'three/addons/libs/stats.module.js'
|
||||
import { Mino, environnement } from './gamelogic.js'
|
||||
|
||||
|
||||
let jsKeyRenamer = new Proxy({
|
||||
["←"]: "ArrowLeft",
|
||||
["→"]: "ArrowRight",
|
||||
["↑"]: "ArrowUp",
|
||||
["↓"]: "ArrowDown",
|
||||
["Espace"]: " ",
|
||||
["Échap."]: "Escape",
|
||||
["Ret. arrière"]: "Backspace",
|
||||
["Entrée"]: "Enter",
|
||||
}, {
|
||||
get(obj, keyName) {
|
||||
return keyName in obj ? obj[keyName] : keyName
|
||||
}
|
||||
})
|
||||
let friendyKeyRenamer = new Proxy({
|
||||
["ArrowLeft"]: "←",
|
||||
["ArrowRight"]: "→",
|
||||
["ArrowUp"]: "↑",
|
||||
["ArrowDown"]: "↓",
|
||||
[" "]: "Espace",
|
||||
["Escape"]: "Échap.",
|
||||
["Backspace"]: "Ret. arrière",
|
||||
["Enter"]: "Entrée",
|
||||
}, {
|
||||
get(obj, keyName) {
|
||||
return keyName in obj ? obj[keyName] : keyName.toUpperCase()
|
||||
}
|
||||
})
|
||||
import { Mino, environment } from './Tetrominoes.js'
|
||||
|
||||
|
||||
export class TetraGUI extends GUI {
|
||||
constructor(game, settings, stats, scene, controls) {
|
||||
constructor(game, settings, stats, scene, controls, playfield, loadingManager) {
|
||||
super({title: "teTra"})
|
||||
this.startLevel = 1
|
||||
|
||||
let keyMaps = {
|
||||
key: {},
|
||||
action: {}
|
||||
}
|
||||
|
||||
this.key = new Proxy(keyMaps, {
|
||||
set(km, action, key) {
|
||||
key = jsKeyRenamer[key]
|
||||
km.action[key.toLowerCase()] = action
|
||||
return km.key[action] = key
|
||||
},
|
||||
has(km, action) {
|
||||
return action in km.key
|
||||
},
|
||||
get(km, action) {
|
||||
return friendyKeyRenamer[km.key[action]]
|
||||
}
|
||||
})
|
||||
this.action = new Proxy(keyMaps, {
|
||||
set(km, key, action) {
|
||||
km.key[action] = key
|
||||
return km.action[key.toLowerCase()] = action
|
||||
},
|
||||
has(km, key) {
|
||||
return key.toLowerCase() in km.action
|
||||
},
|
||||
get(km, key) {
|
||||
return km.action[key.toLowerCase()]
|
||||
}
|
||||
})
|
||||
|
||||
this.key.moveLeft = "ArrowLeft"
|
||||
this.key.moveRight = "ArrowRight"
|
||||
this.key.rotateCCW = "w"
|
||||
this.key.rotateCW = "ArrowUp"
|
||||
this.key.softDrop = "ArrowDown"
|
||||
this.key.hardDrop = " "
|
||||
this.key.hold = "c"
|
||||
this.key.pause = "Escape"
|
||||
|
||||
this.arrDelay = 50
|
||||
this.dasDelay = 300
|
||||
|
||||
this.musicVolume = 50
|
||||
this.sfxVolume = 50
|
||||
|
||||
this.startButton = this.add(game, "start").name("Jouer").hide()
|
||||
this.pauseButton = this.add(game, "pause").name("Pause").hide()
|
||||
|
||||
this.startButton = this.add(game, "start").name("Jouer").hide()
|
||||
this.pauseButton = this.add(game, "pause").name("Pause").hide()
|
||||
this.resumeButton = this.add(game, "resume").name("Reprendre").hide()
|
||||
|
||||
this.stats = this.addFolder("Stats").hide()
|
||||
@ -104,25 +27,21 @@ export class TetraGUI extends GUI {
|
||||
this.settings = this.addFolder("Options").open()
|
||||
|
||||
this.settings.add(settings, "startLevel").name("Niveau initial").min(1).max(15).step(1)
|
||||
this.settings.add(settings, "theme", ["Plasma", "Espace"]).name("Thème").onChange(theme => {
|
||||
scene.vortex.theme = theme
|
||||
switch (theme) {
|
||||
case "Plasma":
|
||||
scene.ambientLight.intensity = 0.6
|
||||
scene.directionalLight.intensity = 5
|
||||
|
||||
Mino.mesh.material.opacity = 0.7
|
||||
Mino.mesh.material.roughness = 0.48
|
||||
Mino.mesh.material.metalness = 0.9
|
||||
break
|
||||
case "Espace":
|
||||
scene.ambientLight.intensity = 20
|
||||
scene.directionalLight.intensity = 10
|
||||
|
||||
Mino.mesh.material.opacity = 0.6
|
||||
Mino.mesh.material.roughness = 0.05
|
||||
Mino.mesh.material.metalness = 0.997
|
||||
break
|
||||
|
||||
this.settings.add(settings, "theme", ["Plasma", "Espace", "Rétro"]).name("Thème").onChange(theme => {
|
||||
scene.theme = theme
|
||||
Mino.meshes.material = Mino.materials[theme]
|
||||
if (theme == "Rétro") {
|
||||
playfield.edge.visible = false
|
||||
playfield.retroEdge.visible = true
|
||||
Mino.meshes.resetColor()
|
||||
Mino.meshes.update = Mino.meshes.updateOffset
|
||||
music.src = "audio/Tetris_MkVaffQuasi_Ultimix_OC_ReMix.mp3"
|
||||
} else {
|
||||
playfield.edge.visible = true
|
||||
playfield.retroEdge.visible = false
|
||||
Mino.meshes.update = Mino.meshes.updateColor
|
||||
music.src = "audio/benevolence.m4a"
|
||||
}
|
||||
})
|
||||
|
||||
@ -184,11 +103,11 @@ export class TetraGUI extends GUI {
|
||||
function changeMaterial(type) {
|
||||
material?.destroy()
|
||||
material = dev.addFolder("minoes material")
|
||||
material.add(Mino.mesh.material, "constructor", ["MeshBasicMaterial", "MeshStandardMaterial", "MeshPhysicalMaterial"]).name("type").onChange(changeMaterial)
|
||||
material.add(Mino.meshes.material, "constructor", ["MeshBasicMaterial", "MeshStandardMaterial", "MeshPhysicalMaterial"]).name("type").onChange(changeMaterial)
|
||||
switch(type) {
|
||||
case "MeshBasicMaterial":
|
||||
Mino.mesh.material = new THREE.MeshBasicMaterial({
|
||||
envMap: environnement,
|
||||
Mino.meshes.material = new THREE.MeshBasicMaterial({
|
||||
envMap: environment,
|
||||
side: THREE.DoubleSide,
|
||||
transparent: true,
|
||||
opacity: 0.5,
|
||||
@ -196,8 +115,8 @@ export class TetraGUI extends GUI {
|
||||
})
|
||||
break
|
||||
case "MeshStandardMaterial":
|
||||
Mino.mesh.material = new THREE.MeshStandardMaterial({
|
||||
envMap: environnement,
|
||||
Mino.meshes.material = new THREE.MeshStandardMaterial({
|
||||
envMap: environment,
|
||||
side: THREE.DoubleSide,
|
||||
transparent: true,
|
||||
opacity: 0.7,
|
||||
@ -206,8 +125,8 @@ export class TetraGUI extends GUI {
|
||||
})
|
||||
break
|
||||
case "MeshPhysicalMaterial":
|
||||
Mino.mesh.material = new THREE.MeshPhysicalMaterial({
|
||||
envMap: environnement,
|
||||
Mino.meshes.material = new THREE.MeshPhysicalMaterial({
|
||||
envMap: environment,
|
||||
side: THREE.DoubleSide,
|
||||
transparent: true,
|
||||
opacity: 0.6,
|
||||
@ -223,17 +142,17 @@ export class TetraGUI extends GUI {
|
||||
})
|
||||
break
|
||||
}
|
||||
if ("opacity" in Mino.mesh.material) material.add(Mino.mesh.material, "opacity" ).min(0).max(1).listen()
|
||||
if ("reflectivity" in Mino.mesh.material) material.add(Mino.mesh.material, "reflectivity" ).min(0).max(1).listen()
|
||||
if ("roughness" in Mino.mesh.material) material.add(Mino.mesh.material, "roughness" ).min(0).max(1).listen()
|
||||
if ("metalness" in Mino.mesh.material) material.add(Mino.mesh.material, "metalness" ).min(0).max(1).listen()
|
||||
if ("attenuationDistance" in Mino.mesh.material) material.add(Mino.mesh.material, "attenuationDistance").min(0).listen()
|
||||
if ("ior" in Mino.mesh.material) material.add(Mino.mesh.material, "ior" ).min(1).max(2).listen()
|
||||
if ("sheen" in Mino.mesh.material) material.add(Mino.mesh.material, "sheen" ).min(0).max(1).listen()
|
||||
if ("sheenRoughness" in Mino.mesh.material) material.add(Mino.mesh.material, "sheenRoughness" ).min(0).max(1).listen()
|
||||
if ("specularIntensity" in Mino.mesh.material) material.add(Mino.mesh.material, "specularIntensity" ).min(0).max(1).listen()
|
||||
if ("thickness" in Mino.mesh.material) material.add(Mino.mesh.material, "thickness" ).min(0).max(5).listen()
|
||||
if ("transmission" in Mino.mesh.material) material.add(Mino.mesh.material, "transmission" ).min(0).max(1).listen()
|
||||
if ("opacity" in Mino.meshes.material) material.add(Mino.meshes.material, "opacity" ).min(0).max(1).listen()
|
||||
if ("reflectivity" in Mino.meshes.material) material.add(Mino.meshes.material, "reflectivity" ).min(0).max(1).listen()
|
||||
if ("roughness" in Mino.meshes.material) material.add(Mino.meshes.material, "roughness" ).min(0).max(1).listen()
|
||||
if ("metalness" in Mino.meshes.material) material.add(Mino.meshes.material, "metalness" ).min(0).max(1).listen()
|
||||
if ("attenuationDistance" in Mino.meshes.material) material.add(Mino.meshes.material, "attenuationDistance").min(0).listen()
|
||||
if ("ior" in Mino.meshes.material) material.add(Mino.meshes.material, "ior" ).min(1).max(2).listen()
|
||||
if ("sheen" in Mino.meshes.material) material.add(Mino.meshes.material, "sheen" ).min(0).max(1).listen()
|
||||
if ("sheenRoughness" in Mino.meshes.material) material.add(Mino.meshes.material, "sheenRoughness" ).min(0).max(1).listen()
|
||||
if ("specularIntensity" in Mino.meshes.material) material.add(Mino.meshes.material, "specularIntensity" ).min(0).max(1).listen()
|
||||
if ("thickness" in Mino.meshes.material) material.add(Mino.meshes.material, "thickness" ).min(0).max(5).listen()
|
||||
if ("transmission" in Mino.meshes.material) material.add(Mino.meshes.material, "transmission" ).min(0).max(1).listen()
|
||||
}
|
||||
changeMaterial(this.materialType)
|
||||
material.close()
|
||||
@ -250,8 +169,6 @@ export class TetraGUI extends GUI {
|
||||
}))
|
||||
|
||||
}
|
||||
|
||||
this.load()
|
||||
}
|
||||
|
||||
load() {
|
||||
|
@ -44,6 +44,24 @@ export class TetraScene extends THREE.Scene {
|
||||
}.bind(this))
|
||||
}
|
||||
|
||||
set theme(theme) {
|
||||
switch (theme) {
|
||||
case "Plasma":
|
||||
this.ambientLight.intensity = 0.6
|
||||
this.directionalLight.intensity = 5
|
||||
break
|
||||
case "Espace":
|
||||
this.ambientLight.intensity = 20
|
||||
this.directionalLight.intensity = 10
|
||||
break
|
||||
case "Rétro":
|
||||
this.ambientLight.intensity = 1
|
||||
this.directionalLight.intensity = 10
|
||||
break
|
||||
}
|
||||
this.vortex.theme = theme
|
||||
}
|
||||
|
||||
update(delta) {
|
||||
this.vortex.update(delta)
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
import * as THREE from 'three'
|
||||
import { scheduler } from './scheduler.js'
|
||||
import { TileMaterial } from './TileMaterial.js'
|
||||
|
||||
|
||||
Array.prototype.pick = function () { return this.splice(Math.floor(Math.random() * this.length), 1)[0] }
|
||||
@ -54,10 +55,17 @@ const COLUMNS = 10
|
||||
|
||||
|
||||
const envRenderTarget = new THREE.WebGLCubeRenderTarget(256)
|
||||
const environnement = envRenderTarget.texture
|
||||
environnement.type = THREE.HalfFloatType
|
||||
environnement.camera = new THREE.CubeCamera(1, 1000, envRenderTarget)
|
||||
environnement.camera.position.set(5, 10, 0)
|
||||
const environment = envRenderTarget.texture
|
||||
environment.type = THREE.HalfFloatType
|
||||
environment.camera = new THREE.CubeCamera(1, 1000, envRenderTarget)
|
||||
environment.camera.position.set(5, 10, 0)
|
||||
|
||||
|
||||
const sideMaterial = new THREE.MeshStandardMaterial({
|
||||
color: 0x222222,
|
||||
roughness: 0.8,
|
||||
metalness: 0.8,
|
||||
})
|
||||
|
||||
|
||||
class InstancedMino extends THREE.InstancedMesh {
|
||||
@ -65,6 +73,8 @@ class InstancedMino extends THREE.InstancedMesh {
|
||||
super(geometry, material, count)
|
||||
this.instances = new Set()
|
||||
this.count = 0
|
||||
this.offsets = new Uint8Array(2*count)
|
||||
this.update = this.updateColor
|
||||
}
|
||||
|
||||
add(instance) {
|
||||
@ -79,26 +89,83 @@ class InstancedMino extends THREE.InstancedMesh {
|
||||
this.instances.clear()
|
||||
}
|
||||
|
||||
update() {
|
||||
setOffsetAt(index, offset) {
|
||||
this.offsets[index * 2] = offset
|
||||
}
|
||||
|
||||
resetColor() {
|
||||
if (this.instanceColor) {
|
||||
this.instanceColor.array.fill(0xffffff)
|
||||
this.instanceColor.needsUpdate = true
|
||||
}
|
||||
}
|
||||
|
||||
updateColor() {
|
||||
this.count = 0
|
||||
this.instances.forEach(mino => {
|
||||
if (mino.parent?.visible) {
|
||||
this.setColorAt(this.count, mino.color)
|
||||
this.setMatrixAt(this.count, mino.matrixWorld)
|
||||
this.setColorAt(this.count, mino.color)
|
||||
this.count++
|
||||
}
|
||||
})
|
||||
if (this.count) {
|
||||
this.instanceColor.needsUpdate = true
|
||||
this.instanceMatrix.needsUpdate = true
|
||||
this.instanceColor.needsUpdate = true
|
||||
}
|
||||
}
|
||||
|
||||
updateOffset() {
|
||||
this.count = 0
|
||||
this.instances.forEach(mino => {
|
||||
if (mino.parent?.visible) {
|
||||
this.setMatrixAt(this.count, mino.matrixWorld)
|
||||
this.setOffsetAt(this.count, mino.offset)
|
||||
this.count++
|
||||
}
|
||||
})
|
||||
if (this.count) {
|
||||
this.instanceMatrix.needsUpdate = true
|
||||
this.geometry.setAttribute('offset', new THREE.InstancedBufferAttribute(this.offsets, 2))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Mino extends THREE.Object3D {
|
||||
static instances = new Set()
|
||||
static mesh
|
||||
static materials = {
|
||||
Plasma: new THREE.MeshStandardMaterial({
|
||||
envMap: environment,
|
||||
side: THREE.DoubleSide,
|
||||
transparent: true,
|
||||
opacity: 0.7,
|
||||
roughness: 0.48,
|
||||
metalness: 0.67,
|
||||
}),
|
||||
Espace: new THREE.MeshStandardMaterial({
|
||||
envMap: environment,
|
||||
side: THREE.DoubleSide,
|
||||
transparent: true,
|
||||
opacity: 0.8,
|
||||
roughness: 0.05,
|
||||
metalness: 0.997,
|
||||
}),
|
||||
Rétro: [sideMaterial, sideMaterial, sideMaterial, sideMaterial, sideMaterial, sideMaterial]
|
||||
}
|
||||
static {
|
||||
new THREE.TextureLoader().load("images/sprites.png", (texture) => {
|
||||
this.materials.Rétro[0] = new TileMaterial({
|
||||
color: 0xd0d4c1,
|
||||
map: texture,
|
||||
bumpMap: texture,
|
||||
bumpScale: 4,
|
||||
roughness: 0.25,
|
||||
metalness: 0.8,
|
||||
transparent: true,
|
||||
}, 8, 8)
|
||||
})
|
||||
}
|
||||
static meshes
|
||||
static {
|
||||
let minoFaceShape = new THREE.Shape()
|
||||
minoFaceShape.moveTo(.1, .1)
|
||||
@ -116,24 +183,17 @@ class Mino extends THREE.Object3D {
|
||||
bevelSegments: 1
|
||||
}
|
||||
let minoGeometry = new THREE.ExtrudeGeometry(minoFaceShape, minoExtrudeSettings)
|
||||
let minoMaterial = new THREE.MeshStandardMaterial({
|
||||
envMap: environnement,
|
||||
side: THREE.DoubleSide,
|
||||
transparent: true,
|
||||
opacity: 0.7,
|
||||
roughness: 0.48,
|
||||
metalness: 0.67,
|
||||
})
|
||||
this.mesh = new InstancedMino(minoGeometry, minoMaterial, 2*ROWS*COLUMNS)
|
||||
this.meshes = new InstancedMino(minoGeometry, this.materials.Plasma, 2*ROWS*COLUMNS)
|
||||
}
|
||||
|
||||
constructor(color) {
|
||||
constructor(color, offset) {
|
||||
super()
|
||||
this.color = color
|
||||
this.offset = offset
|
||||
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()
|
||||
this.constructor.mesh.add(this)
|
||||
this.constructor.meshes.add(this)
|
||||
}
|
||||
|
||||
explode(delta) {
|
||||
@ -149,7 +209,7 @@ class Mino extends THREE.Object3D {
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this.constructor.mesh.delete(this)
|
||||
this.constructor.meshes.delete(this)
|
||||
}
|
||||
}
|
||||
|
||||
@ -164,7 +224,7 @@ class Tetromino extends THREE.Group {
|
||||
constructor(position) {
|
||||
super()
|
||||
if (position) this.position.copy(position)
|
||||
this.minoesPosition[FACING.NORTH].forEach(() => this.add(new Mino(this.freeColor)))
|
||||
this.minoesPosition[FACING.NORTH].forEach(() => this.add(new Mino(this.freeColor, this.offset)))
|
||||
this.facing = FACING.NORTH
|
||||
this.rotatedLast = false
|
||||
this.rotationPoint4Used = false
|
||||
@ -251,6 +311,7 @@ class Ghost extends Tetromino {
|
||||
copy(piece) {
|
||||
this.position.copy(piece.position)
|
||||
this.minoesPosition = piece.minoesPosition
|
||||
//this.children.forEach(mino => mino.offset = piece.offset)
|
||||
this.facing = piece.facing
|
||||
this.visible = true
|
||||
while (this.canMove(TRANSLATION.DOWN)) this.position.y--
|
||||
@ -260,6 +321,7 @@ 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)],
|
||||
]
|
||||
Ghost.prototype.offset = 0
|
||||
|
||||
|
||||
class I extends Tetromino { }
|
||||
@ -276,6 +338,7 @@ I.prototype.srs = [
|
||||
{ [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.offset = 1
|
||||
|
||||
class J extends Tetromino { }
|
||||
J.prototype.minoesPosition = [
|
||||
@ -285,8 +348,10 @@ J.prototype.minoesPosition = [
|
||||
[P(0, 1), P(-1, -1), P(0, 0), P(0, -1)],
|
||||
]
|
||||
J.prototype.freeColor = new THREE.Color(COLORS.J)
|
||||
J.prototype.offset = 2
|
||||
|
||||
class L extends Tetromino { }
|
||||
class L extends Tetromino {
|
||||
}
|
||||
L.prototype.minoesPosition = [
|
||||
[P(-1, 0), P(0, 0), P(1, 0), P(1, 1)],
|
||||
[P(0, 1), P(0, 0), P(0, -1), P(1, -1)],
|
||||
@ -294,6 +359,7 @@ L.prototype.minoesPosition = [
|
||||
[P(0, 1), P(0, 0), P(0, -1), P(-1, 1)],
|
||||
]
|
||||
L.prototype.freeColor = new THREE.Color(COLORS.L)
|
||||
L.prototype.offset = 3
|
||||
|
||||
class O extends Tetromino { }
|
||||
O.prototype.minoesPosition = [
|
||||
@ -303,6 +369,7 @@ O.prototype.srs = [
|
||||
{ [ROTATION.CW]: [], [ROTATION.CCW]: [] }
|
||||
]
|
||||
O.prototype.freeColor = new THREE.Color(COLORS.O)
|
||||
O.prototype.offset = 4
|
||||
|
||||
class S extends Tetromino { }
|
||||
S.prototype.minoesPosition = [
|
||||
@ -312,6 +379,7 @@ S.prototype.minoesPosition = [
|
||||
[P(-1, 1), P(0, 0), P(-1, 0), P(0, -1)],
|
||||
]
|
||||
S.prototype.freeColor = new THREE.Color(COLORS.S)
|
||||
S.prototype.offset = 5
|
||||
|
||||
class T extends Tetromino {
|
||||
get tSpin() {
|
||||
@ -339,6 +407,7 @@ T.prototype.tSlots = [
|
||||
[P(-1, -1), P(-1, 1), P(1, 1), P(1, -1)],
|
||||
]
|
||||
T.prototype.freeColor = new THREE.Color(COLORS.T)
|
||||
T.prototype.offset = 6
|
||||
|
||||
class Z extends Tetromino { }
|
||||
Z.prototype.minoesPosition = [
|
||||
@ -348,18 +417,19 @@ Z.prototype.minoesPosition = [
|
||||
[P(0, 1), P(-1, 0), P(0, 0), P(-1, -1)]
|
||||
]
|
||||
Z.prototype.freeColor = new THREE.Color(COLORS.Z)
|
||||
Z.prototype.offset = 7
|
||||
|
||||
|
||||
class Playfield extends THREE.Group {
|
||||
constructor() {
|
||||
constructor(loadingManager) {
|
||||
super()
|
||||
//this.visible = false
|
||||
|
||||
const edgeMaterial = new THREE.MeshStandardMaterial({
|
||||
color: COLORS.EDGE,
|
||||
envMap: environnement,
|
||||
envMap: environment,
|
||||
transparent: true,
|
||||
opacity: 0.2,
|
||||
opacity: 0.3,
|
||||
roughness: 0.1,
|
||||
metalness: 0.67,
|
||||
})
|
||||
@ -373,14 +443,56 @@ class Playfield extends THREE.Group {
|
||||
.lineTo(COLUMNS + .3, -.3)
|
||||
.lineTo(-.3, -.3)
|
||||
.moveTo(-.3, SKYLINE)
|
||||
const edge = new THREE.Mesh(
|
||||
this.edge = new THREE.Mesh(
|
||||
new THREE.ExtrudeGeometry(edgeShape, {
|
||||
depth: 1,
|
||||
bevelEnabled: false,
|
||||
}),
|
||||
edgeMaterial
|
||||
)
|
||||
this.add(edge)
|
||||
this.add(this.edge)
|
||||
|
||||
const retroEdgeShape = new THREE.Shape()
|
||||
.moveTo(-1, SKYLINE)
|
||||
.lineTo(0, SKYLINE)
|
||||
.lineTo(0, 0)
|
||||
.lineTo(COLUMNS, 0)
|
||||
.lineTo(COLUMNS, SKYLINE)
|
||||
.lineTo(COLUMNS + 1, SKYLINE)
|
||||
.lineTo(COLUMNS + 1, -1)
|
||||
.lineTo(-1, -1)
|
||||
.moveTo(-1, SKYLINE)
|
||||
const retroEdgeTexture = new THREE.TextureLoader(loadingManager).load("images/edge.png", (texture) => {
|
||||
texture.wrapS = THREE.RepeatWrapping
|
||||
texture.wrapT = THREE.RepeatWrapping
|
||||
})
|
||||
const retroEdgeMaterial = new THREE.MeshStandardMaterial({
|
||||
color: 0xd0d4c1,
|
||||
map: retroEdgeTexture,
|
||||
bumpMap: retroEdgeTexture,
|
||||
bumpScale: 0.5,
|
||||
roughness: 0.25,
|
||||
metalness: 0.8,
|
||||
})
|
||||
this.retroEdge = new THREE.Mesh(
|
||||
new THREE.ExtrudeGeometry(retroEdgeShape, {
|
||||
depth: 1,
|
||||
bevelEnabled: false,
|
||||
}),
|
||||
[retroEdgeMaterial, sideMaterial, sideMaterial, sideMaterial, sideMaterial, sideMaterial],
|
||||
)
|
||||
this.add(this.retroEdge)
|
||||
const back = new THREE.Mesh(
|
||||
new THREE.PlaneGeometry(COLUMNS, SKYLINE),
|
||||
new THREE.MeshStandardMaterial({
|
||||
color: 0xc5d0a1,
|
||||
roughness: 0.9,
|
||||
metalness: 0.9,
|
||||
})
|
||||
)
|
||||
back.position.set(COLUMNS/2, SKYLINE/2, 0)
|
||||
this.retroEdge.add(back)
|
||||
this.retroEdge.visible = false
|
||||
|
||||
const positionKF = new THREE.VectorKeyframeTrack('.position', [0, 1, 2], [0, 0, 0, 0, -0.2, 0, 0, 0, 0])
|
||||
const clip = new THREE.AnimationClip('HardDrop', 3, [positionKF])
|
||||
@ -515,4 +627,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)]
|
||||
|
||||
|
||||
export { T_SPIN, FACING, TRANSLATION, ROTATION, COLORS, environnement, Mino, Tetromino, Playfield, HoldQueue, NextQueue }
|
||||
export { T_SPIN, FACING, TRANSLATION, ROTATION, COLORS, environment, Mino, Tetromino, Playfield, HoldQueue, NextQueue }
|
93
jsm/TileMaterial.js
Normal file
93
jsm/TileMaterial.js
Normal file
@ -0,0 +1,93 @@
|
||||
import { MeshStandardMaterial, Texture, Vector2 } from 'three';
|
||||
|
||||
export class TileMaterial extends MeshStandardMaterial {
|
||||
constructor(params, tileSizeX, tileSizeY) {
|
||||
super(params);
|
||||
this.tileSize = { value: new Vector2(tileSizeX / this.map.image.width, tileSizeY / this.map.image.height) };
|
||||
}
|
||||
|
||||
onBeforeCompile(shader) {
|
||||
shader.uniforms.tileSize = this.tileSize
|
||||
shader.vertexShader = shader.vertexShader.replace(
|
||||
`void main() {`,
|
||||
`varying vec2 vUv;
|
||||
varying vec2 vOffset;
|
||||
attribute vec2 offset;
|
||||
|
||||
void main() {
|
||||
vUv = uv;
|
||||
vOffset = offset;
|
||||
gl_Position = projectionMatrix * modelViewMatrix * instanceMatrix * vec4(position, 1.0);`
|
||||
)
|
||||
shader.fragmentShader = `varying vec2 vUv;
|
||||
varying vec2 vOffset;
|
||||
//uniform sampler2D map;
|
||||
uniform vec2 tileSize;
|
||||
varying vec2 vBumpMapUvOffset;
|
||||
` + shader.fragmentShader
|
||||
shader.fragmentShader = shader.fragmentShader.replace(
|
||||
`#include <map_fragment>`,
|
||||
`#ifdef USE_MAP
|
||||
|
||||
vec4 sampledDiffuseColor = texture2D(map, vUv * tileSize + vOffset * tileSize);
|
||||
|
||||
#ifdef DECODE_VIDEO_TEXTURE
|
||||
|
||||
// use inline sRGB decode until browsers properly support SRGB8_ALPHA8 with video textures (#26516)
|
||||
|
||||
sampledDiffuseColor = vec4( mix( pow( sampledDiffuseColor.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), sampledDiffuseColor.rgb * 0.0773993808, vec3( lessThanEqual( sampledDiffuseColor.rgb, vec3( 0.04045 ) ) ) ), sampledDiffuseColor.w );
|
||||
|
||||
#endif
|
||||
|
||||
diffuseColor *= sampledDiffuseColor;
|
||||
|
||||
#endif`
|
||||
)
|
||||
shader.fragmentShader = shader.fragmentShader.replace(
|
||||
`#include <bumpmap_pars_fragment>`,
|
||||
`#ifdef USE_BUMPMAP
|
||||
|
||||
uniform sampler2D bumpMap;
|
||||
uniform float bumpScale;
|
||||
|
||||
// Bump Mapping Unparametrized Surfaces on the GPU by Morten S. Mikkelsen
|
||||
// https://mmikk.github.io/papers3d/mm_sfgrad_bump.pdf
|
||||
|
||||
// Evaluate the derivative of the height w.r.t. screen-space using forward differencing (listing 2)
|
||||
|
||||
vec2 dHdxy_fwd() {
|
||||
|
||||
vec2 vBumpMapUvOffset = vBumpMapUv * tileSize + vOffset * tileSize;
|
||||
|
||||
vec2 dSTdx = dFdx( vBumpMapUvOffset );
|
||||
vec2 dSTdy = dFdy( vBumpMapUvOffset );
|
||||
|
||||
float Hll = bumpScale * texture2D( bumpMap, vBumpMapUvOffset ).x;
|
||||
float dBx = bumpScale * texture2D( bumpMap, vBumpMapUvOffset + dSTdx ).x - Hll;
|
||||
float dBy = bumpScale * texture2D( bumpMap, vBumpMapUvOffset + dSTdy ).x - Hll;
|
||||
|
||||
return vec2( dBx, dBy );
|
||||
|
||||
}
|
||||
|
||||
vec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy, float faceDirection ) {
|
||||
|
||||
// normalize is done to ensure that the bump map looks the same regardless of the texture's scale
|
||||
vec3 vSigmaX = normalize( dFdx( surf_pos.xyz ) );
|
||||
vec3 vSigmaY = normalize( dFdy( surf_pos.xyz ) );
|
||||
vec3 vN = surf_norm; // normalized
|
||||
|
||||
vec3 R1 = cross( vSigmaY, vN );
|
||||
vec3 R2 = cross( vN, vSigmaX );
|
||||
|
||||
float fDet = dot( vSigmaX, R1 ) * faceDirection;
|
||||
|
||||
vec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );
|
||||
return normalize( abs( fDet ) * surf_norm - vGrad );
|
||||
|
||||
}
|
||||
|
||||
#endif`
|
||||
)
|
||||
}
|
||||
}
|
@ -15,7 +15,7 @@ export class Vortex extends THREE.Group {
|
||||
this.colorFullTextureRotation = 0.006
|
||||
this.colorFullMoveForward = 0.025
|
||||
|
||||
const commonCylinderGeometry = new THREE.CylinderGeometry(35, 35, 500, 12, 1, true)
|
||||
const commonCylinderGeometry = new THREE.CylinderGeometry(35, 35, 1000, 12, 1, true)
|
||||
|
||||
this.darkCylinder = new THREE.Mesh(
|
||||
commonCylinderGeometry,
|
||||
@ -24,6 +24,7 @@ export class Vortex extends THREE.Group {
|
||||
blending: THREE.AdditiveBlending,
|
||||
})
|
||||
)
|
||||
this.darkCylinder.position.y = -100
|
||||
this.add(this.darkCylinder)
|
||||
|
||||
this.colorFullCylinder = new THREE.Mesh(
|
||||
@ -33,6 +34,7 @@ export class Vortex extends THREE.Group {
|
||||
blending: THREE.AdditiveBlending,
|
||||
})
|
||||
)
|
||||
this.colorFullCylinder.position.y = -100
|
||||
this.add(this.colorFullCylinder)
|
||||
|
||||
this.position.set(5, 10, -10)
|
||||
@ -44,7 +46,7 @@ export class Vortex extends THREE.Group {
|
||||
new THREE.TextureLoader(this.loadingManager).load("./images/plasma.jpg", texture => {
|
||||
texture.wrapS = THREE.RepeatWrapping
|
||||
texture.wrapT = THREE.MirroredRepeatWrapping
|
||||
texture.repeat.set(1, 1)
|
||||
texture.repeat.set(1, 2)
|
||||
this.darkCylinder.material.map = texture
|
||||
})
|
||||
this.darkCylinder.material.opacity = 0.17
|
||||
@ -52,7 +54,7 @@ export class Vortex extends THREE.Group {
|
||||
new THREE.TextureLoader(this.loadingManager).load("./images/plasma2.jpg", texture => {
|
||||
texture.wrapS = THREE.RepeatWrapping
|
||||
texture.wrapT = THREE.MirroredRepeatWrapping
|
||||
texture.repeat.set(2, 1)
|
||||
texture.repeat.set(2, 2)
|
||||
this.colorFullCylinder.material.map = texture
|
||||
})
|
||||
this.colorFullCylinder.material.opacity = 0.7
|
||||
@ -62,6 +64,8 @@ export class Vortex extends THREE.Group {
|
||||
this.darkMoveForward = 0.009
|
||||
this.colorFullTextureRotation = 0.006
|
||||
this.colorFullMoveForward = 0.025
|
||||
|
||||
this.visible = true
|
||||
break
|
||||
|
||||
case "Espace":
|
||||
@ -86,17 +90,29 @@ export class Vortex extends THREE.Group {
|
||||
this.darkMoveForward = 0.03
|
||||
this.colorFullTextureRotation = 0.006
|
||||
this.colorFullMoveForward = 0.012
|
||||
|
||||
this.visible = true
|
||||
break
|
||||
|
||||
case "Rétro":
|
||||
this.visible = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
update(delta) {
|
||||
this.rotation.y += this.globalRotation * delta
|
||||
|
||||
this.darkCylinder.material.map.offset.y += this.darkMoveForward * delta
|
||||
this.darkCylinder.material.map.offset.x += this.darkTextureRotation * delta
|
||||
|
||||
this.colorFullCylinder.material.map.offset.y += this.colorFullMoveForward * delta
|
||||
this.colorFullCylinder.material.map.offset.x += this.colorFullTextureRotation * delta
|
||||
if (this.visible) {
|
||||
this.rotation.y += this.globalRotation * delta
|
||||
|
||||
if (this.darkCylinder.material.map) {
|
||||
this.darkCylinder.material.map.offset.y += this.darkMoveForward * delta
|
||||
this.darkCylinder.material.map.offset.x += this.darkTextureRotation * delta
|
||||
}
|
||||
|
||||
if (this.colorFullCylinder.material.map) {
|
||||
this.colorFullCylinder.material.map.offset.y += this.colorFullMoveForward * delta
|
||||
this.colorFullCylinder.material.map.offset.x += this.colorFullTextureRotation * delta
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -33,7 +33,7 @@ class Scheduler {
|
||||
}
|
||||
|
||||
|
||||
const scheduler = new Scheduler
|
||||
const scheduler = new Scheduler()
|
||||
|
||||
|
||||
export { scheduler }
|
Loading…
x
Reference in New Issue
Block a user