SINGLE INSTANCEDMESH
This commit is contained in:
parent
d2a0e241c8
commit
cfa73565f0
5
app.js
5
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, Mino, Playfield, 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'
|
||||||
@ -288,6 +288,8 @@ const gui = new TetraGUI(game, settings, stats, scene)
|
|||||||
|
|
||||||
const clock = new THREE.Clock()
|
const clock = new THREE.Clock()
|
||||||
|
|
||||||
|
scene.add(Mino.mesh)
|
||||||
|
|
||||||
const holdQueue = new HoldQueue()
|
const holdQueue = new HoldQueue()
|
||||||
scene.add(holdQueue)
|
scene.add(holdQueue)
|
||||||
const playfield = new Playfield()
|
const playfield = new Playfield()
|
||||||
@ -307,6 +309,7 @@ function animate() {
|
|||||||
const delta = clock.getDelta()
|
const delta = clock.getDelta()
|
||||||
scene.update(delta)
|
scene.update(delta)
|
||||||
playfield.update(delta)
|
playfield.update(delta)
|
||||||
|
Mino.update()
|
||||||
controls.update()
|
controls.update()
|
||||||
gui.update()
|
gui.update()
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
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 { Mino } from './gamelogic.js'
|
||||||
|
|
||||||
|
|
||||||
export class TetraGUI extends GUI {
|
export class TetraGUI extends GUI {
|
||||||
@ -148,17 +148,17 @@ export class TetraGUI extends GUI {
|
|||||||
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 material = this.debug.addFolder("minoes material").close()
|
let material = this.debug.addFolder("minoes material").close()
|
||||||
if ("opacity" in minoMaterial) material.add(minoMaterial, "opacity").min(0).max(1)
|
if ("opacity" in Mino.mesh.material) material.add(Mino.mesh.material, "opacity").min(0).max(1)
|
||||||
if ("reflectivity" in minoMaterial) material.add(minoMaterial, "reflectivity").min(0).max(1)
|
if ("reflectivity" in Mino.mesh.material) material.add(Mino.mesh.material, "reflectivity").min(0).max(1)
|
||||||
if ("roughness" in minoMaterial) material.add(minoMaterial, "roughness").min(0).max(1)
|
if ("roughness" in Mino.mesh.material) material.add(Mino.mesh.material, "roughness").min(0).max(1)
|
||||||
if ("metalness" in minoMaterial) material.add(minoMaterial, "metalness").min(0).max(1)
|
if ("metalness" in Mino.mesh.material) material.add(Mino.mesh.material, "metalness").min(0).max(1)
|
||||||
if ("attenuationDistance" in minoMaterial) material.add(minoMaterial, "attenuationDistance").min(0).max(1)
|
if ("attenuationDistance" in Mino.mesh.material) material.add(Mino.mesh.material, "attenuationDistance").min(0).max(1)
|
||||||
if ("ior" in minoMaterial) material.add(minoMaterial, "ior").min(1).max(2)
|
if ("ior" in Mino.mesh.material) material.add(Mino.mesh.material, "ior").min(1).max(2)
|
||||||
if ("sheen" in minoMaterial) material.add(minoMaterial, "sheen").min(0).max(1)
|
if ("sheen" in Mino.mesh.material) material.add(Mino.mesh.material, "sheen").min(0).max(1)
|
||||||
if ("sheenRoughness" in minoMaterial) material.add(minoMaterial, "sheenRoughness").min(0).max(1)
|
if ("sheenRoughness" in Mino.mesh.material) material.add(Mino.mesh.material, "sheenRoughness").min(0).max(1)
|
||||||
if ("specularIntensity" in minoMaterial) material.add(minoMaterial, "specularIntensity").min(0).max(1)
|
if ("specularIntensity" in Mino.mesh.material) material.add(Mino.mesh.material, "specularIntensity").min(0).max(1)
|
||||||
if ("thickness" in minoMaterial) material.add(minoMaterial, "thickness").min(0).max(5)
|
if ("thickness" in Mino.mesh.material) material.add(Mino.mesh.material, "thickness").min(0).max(5)
|
||||||
if ("transmission" in minoMaterial) material.add(minoMaterial, "transmission").min(0).max(1)
|
if ("transmission" in Mino.mesh.material) material.add(Mino.mesh.material, "transmission").min(0).max(1)
|
||||||
|
|
||||||
this.fps = new FPS.default()
|
this.fps = new FPS.default()
|
||||||
document.body.appendChild(this.fps.dom)
|
document.body.appendChild(this.fps.dom)
|
||||||
|
231
jsm/gamelogic.js
231
jsm/gamelogic.js
@ -60,85 +60,118 @@ environnement.camera = new THREE.CubeCamera(1, 1000, envRenderTarget)
|
|||||||
environnement.camera.position.set(5, 10)
|
environnement.camera.position.set(5, 10)
|
||||||
|
|
||||||
|
|
||||||
const minoFaceShape = new THREE.Shape()
|
|
||||||
minoFaceShape.moveTo(.1, .1)
|
|
||||||
minoFaceShape.lineTo(.1, .9)
|
|
||||||
minoFaceShape.lineTo(.9, .9)
|
|
||||||
minoFaceShape.lineTo(.9, .1)
|
|
||||||
minoFaceShape.lineTo(.1, .1)
|
|
||||||
const minoExtrudeSettings = {
|
|
||||||
steps: 1,
|
|
||||||
depth: .8,
|
|
||||||
bevelEnabled: true,
|
|
||||||
bevelThickness: .1,
|
|
||||||
bevelSize: .1,
|
|
||||||
bevelOffset: 0,
|
|
||||||
bevelSegments: 1
|
|
||||||
}
|
|
||||||
let minoGeometry = new THREE.ExtrudeGeometry(minoFaceShape, minoExtrudeSettings)
|
|
||||||
|
|
||||||
/*let minoMaterial = new THREE.MeshBasicMaterial({
|
|
||||||
envMap: environnement,
|
|
||||||
side: THREE.DoubleSide,
|
|
||||||
transparent: true,
|
|
||||||
opacity: 0.8,
|
|
||||||
reflectivity: 0.9,
|
|
||||||
})*/
|
|
||||||
let minoMaterial = new THREE.MeshStandardMaterial({
|
|
||||||
envMap: environnement,
|
|
||||||
side: THREE.DoubleSide,
|
|
||||||
transparent: true,
|
|
||||||
opacity: 0.6,
|
|
||||||
roughness: 0.1,
|
|
||||||
metalness: 0.95,
|
|
||||||
})
|
|
||||||
/*
|
|
||||||
let minoMaterial = new THREE.MeshPhysicalMaterial({
|
|
||||||
envMap: environnement,
|
|
||||||
side: THREE.DoubleSide,
|
|
||||||
transparent: true,
|
|
||||||
opacity: 0.6,
|
|
||||||
roughness: 0.1,
|
|
||||||
metalness: 0.90,
|
|
||||||
attenuationDistance: 0.5,
|
|
||||||
ior: 2,
|
|
||||||
sheen: 0,
|
|
||||||
sheenRoughness: 1,
|
|
||||||
specularIntensity: 1,
|
|
||||||
thickness: 5,
|
|
||||||
transmission: 1,
|
|
||||||
})*/
|
|
||||||
|
|
||||||
|
|
||||||
class Mino extends THREE.Object3D {
|
class Mino extends THREE.Object3D {
|
||||||
constructor(color, x, y, z=0) {
|
static instances = new Set()
|
||||||
|
static mesh
|
||||||
|
static {
|
||||||
|
let minoFaceShape = new THREE.Shape()
|
||||||
|
minoFaceShape.moveTo(.1, .1)
|
||||||
|
minoFaceShape.lineTo(.1, .9)
|
||||||
|
minoFaceShape.lineTo(.9, .9)
|
||||||
|
minoFaceShape.lineTo(.9, .1)
|
||||||
|
minoFaceShape.lineTo(.1, .1)
|
||||||
|
let minoExtrudeSettings = {
|
||||||
|
steps: 1,
|
||||||
|
depth: .8,
|
||||||
|
bevelEnabled: true,
|
||||||
|
bevelThickness: .1,
|
||||||
|
bevelSize: .1,
|
||||||
|
bevelOffset: 0,
|
||||||
|
bevelSegments: 1
|
||||||
|
}
|
||||||
|
let minoGeometry = new THREE.ExtrudeGeometry(minoFaceShape, minoExtrudeSettings)
|
||||||
|
|
||||||
|
/*let minoMaterial = new THREE.MeshBasicMaterial({
|
||||||
|
envMap: environnement,
|
||||||
|
side: THREE.DoubleSide,
|
||||||
|
transparent: true,
|
||||||
|
opacity: 0.8,
|
||||||
|
reflectivity: 0.9,
|
||||||
|
})*/
|
||||||
|
let minoMaterial = new THREE.MeshStandardMaterial({
|
||||||
|
envMap: environnement,
|
||||||
|
side: THREE.DoubleSide,
|
||||||
|
transparent: true,
|
||||||
|
opacity: 0.6,
|
||||||
|
roughness: 0.1,
|
||||||
|
metalness: 0.95,
|
||||||
|
})
|
||||||
|
/*
|
||||||
|
let minoMaterial = new THREE.MeshPhysicalMaterial({
|
||||||
|
envMap: environnement,
|
||||||
|
side: THREE.DoubleSide,
|
||||||
|
transparent: true,
|
||||||
|
opacity: 0.6,
|
||||||
|
roughness: 0.1,
|
||||||
|
metalness: 0.90,
|
||||||
|
attenuationDistance: 0.5,
|
||||||
|
ior: 2,
|
||||||
|
sheen: 0,
|
||||||
|
sheenRoughness: 1,
|
||||||
|
specularIntensity: 1,
|
||||||
|
thickness: 5,
|
||||||
|
transmission: 1,
|
||||||
|
})*/
|
||||||
|
|
||||||
|
this.mesh = new THREE.InstancedMesh(minoGeometry, minoMaterial, 2*ROWS*COLUMNS)
|
||||||
|
}
|
||||||
|
|
||||||
|
static update(delta) {
|
||||||
|
let i = 0
|
||||||
|
this.instances.forEach(mino => {
|
||||||
|
if (mino.parent.visible) {
|
||||||
|
mino.updateMatrixWorld()
|
||||||
|
this.mesh.setColorAt(i, mino.color)
|
||||||
|
this.mesh.setMatrixAt(i, mino.matrixWorld)
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.mesh.count = i
|
||||||
|
if (this.mesh.count) {
|
||||||
|
this.mesh.instanceColor.needsUpdate = true
|
||||||
|
this.mesh.instanceMatrix.needsUpdate = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(color) {
|
||||||
super()
|
super()
|
||||||
this.color = color
|
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.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.rotationAngle = P(Math.random(), Math.random(), Math.random()).normalize()
|
||||||
this.angularVelocity = 5 - 10 * Math.random()
|
this.angularVelocity = 5 - 10 * Math.random()
|
||||||
|
Mino.instances.add(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
update(delta) {
|
explode(delta) {
|
||||||
this.velocity.y += delta * GRAVITY
|
this.velocity.y += delta * GRAVITY
|
||||||
this.position.addScaledVector(this.velocity, delta)
|
this.position.addScaledVector(this.velocity, delta)
|
||||||
this.rotateOnWorldAxis(this.rotationAngle, delta * this.angularVelocity)
|
this.rotateOnWorldAxis(this.rotationAngle, delta * this.angularVelocity)
|
||||||
this.updateMatrix()
|
if (Math.sqrt(mino.position.x * mino.position.x + mino.position.z * mino.position.z) > 40 || mino.position.y < -50) {
|
||||||
|
this.dispose()
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
this.updateMatrix()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dispose() {
|
||||||
|
Mino.instances.delete(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class Tetromino extends THREE.InstancedMesh {
|
class Tetromino extends THREE.Group {
|
||||||
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]
|
||||||
return this.randomBag.pick()
|
return this.randomBag.pick()
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor(position) {
|
||||||
super(minoGeometry, undefined, 4)
|
super()
|
||||||
this.material = this.minoMaterial
|
if (position) this.position.copy(position)
|
||||||
|
this.minoesPosition[FACING.NORTH].forEach(position => this.add(new Mino(this.freeColor)))
|
||||||
this.facing = FACING.NORTH
|
this.facing = FACING.NORTH
|
||||||
this.rotatedLast = false
|
this.rotatedLast = false
|
||||||
this.rotationPoint4Used = false
|
this.rotationPoint4Used = false
|
||||||
@ -148,12 +181,7 @@ class Tetromino extends THREE.InstancedMesh {
|
|||||||
|
|
||||||
set facing(facing) {
|
set facing(facing) {
|
||||||
this._facing = facing
|
this._facing = facing
|
||||||
let matrix4 = new THREE.Matrix4()
|
this.children.forEach((mino, i) => mino.position.copy(this.minoesPosition[facing][i]))
|
||||||
this.minoesPosition[this.facing].forEach((position, i) => {
|
|
||||||
matrix4.setPosition(position)
|
|
||||||
this.setMatrixAt(i, matrix4)
|
|
||||||
})
|
|
||||||
this.instanceMatrix.needsUpdate = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get facing() {
|
get facing() {
|
||||||
@ -169,10 +197,7 @@ class Tetromino extends THREE.InstancedMesh {
|
|||||||
}
|
}
|
||||||
|
|
||||||
set color(color) {
|
set color(color) {
|
||||||
for (let i = 0; i < this.count; i++) {
|
this.children.forEach((mino) => mino.color = color)
|
||||||
this.setColorAt(i, color)
|
|
||||||
}
|
|
||||||
this.instanceColor.needsUpdate = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
canMove(translation, facing=this.facing) {
|
canMove(translation, facing=this.facing) {
|
||||||
@ -190,7 +215,6 @@ 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)
|
this.parent.ghost.copy(this)
|
||||||
scheduler.clearTimeout(this.onLockDown)
|
scheduler.clearTimeout(this.onLockDown)
|
||||||
} else {
|
} else {
|
||||||
@ -238,6 +262,7 @@ class Ghost extends Tetromino {
|
|||||||
this.minoesPosition = piece.minoesPosition
|
this.minoesPosition = piece.minoesPosition
|
||||||
this.facing = piece.facing
|
this.facing = piece.facing
|
||||||
while (this.canMove(TRANSLATION.DOWN)) this.position.y--
|
while (this.canMove(TRANSLATION.DOWN)) this.position.y--
|
||||||
|
this.visible = true
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -388,13 +413,7 @@ class Playfield extends THREE.Group {
|
|||||||
this.add(this.ghost)
|
this.add(this.ghost)
|
||||||
this.ghost.visible = false
|
this.ghost.visible = false
|
||||||
|
|
||||||
this.lockedMeshes = new THREE.InstancedMesh(minoGeometry, minoMaterial, 200)
|
this.freedMinoes = new Set()
|
||||||
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()
|
||||||
}
|
}
|
||||||
@ -414,9 +433,7 @@ 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.ghost.copy(piece)
|
this.ghost.copy(piece)
|
||||||
this.ghost.visible = true
|
|
||||||
}
|
}
|
||||||
this._piece = piece
|
this._piece = piece
|
||||||
}
|
}
|
||||||
@ -426,62 +443,37 @@ class Playfield extends THREE.Group {
|
|||||||
}
|
}
|
||||||
|
|
||||||
lock() {
|
lock() {
|
||||||
this.piece.minoesPosition[this.piece.facing].forEach(position => {
|
this.piece.locking = false
|
||||||
position = position.clone()
|
return Array.from(this.piece.children).every(mino => {
|
||||||
position.add(this.piece.position)
|
this.add(mino)
|
||||||
if (this.cellIsEmpty(position)) {
|
mino.position.add(this.piece.position)
|
||||||
this.cells[position.y][position.x] = this.piece.freeColor
|
if (this.cellIsEmpty(mino.position)) {
|
||||||
|
this.cells[mino.position.y][mino.position.x] = mino
|
||||||
|
return mino.position.y < SKYLINE
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
this.updateLockedMinoes()
|
|
||||||
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(color => color).length == COLUMNS) {
|
||||||
row.forEach((color, x) => {
|
row.forEach(mino => this.freedMinoes.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((row, y) => row.forEach((mino, x) => mino.position.set(x, y, 0)))
|
||||||
return nbClearedLines
|
return nbClearedLines
|
||||||
}
|
}
|
||||||
|
|
||||||
updateLockedMinoes() {
|
|
||||||
let i = 0
|
|
||||||
let matrix4 = new THREE.Matrix4()
|
|
||||||
this.cells.forEach((row, y) => row.forEach((color, x) => {
|
|
||||||
matrix4.setPosition(x, y, 0)
|
|
||||||
this.lockedMeshes.setMatrixAt(i, matrix4)
|
|
||||||
this.lockedMeshes.setColorAt(i, color)
|
|
||||||
i++
|
|
||||||
}))
|
|
||||||
this.lockedMeshes.count = i
|
|
||||||
this.lockedMeshes.instanceMatrix.needsUpdate = true
|
|
||||||
this.lockedMeshes.instanceColor.needsUpdate = true
|
|
||||||
}
|
|
||||||
|
|
||||||
updateFreedMinoes(delta) {
|
updateFreedMinoes(delta) {
|
||||||
this.freedMinoes.forEach(mino => mino.update(delta))
|
this.freedMinoes.forEach(mino => {
|
||||||
this.freedMinoes = this.freedMinoes.filter(mino =>
|
if (mino.explode(delta)) this.freedMinoes.delete(this)
|
||||||
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) {
|
||||||
@ -522,8 +514,7 @@ class NextQueue extends THREE.Group {
|
|||||||
|
|
||||||
init() {
|
init() {
|
||||||
this.pieces = this.positions.map((position) => {
|
this.pieces = this.positions.map((position) => {
|
||||||
let piece = new Tetromino.random()
|
let piece = new Tetromino.random(position)
|
||||||
piece.position.copy(position)
|
|
||||||
this.add(piece)
|
this.add(piece)
|
||||||
return piece
|
return piece
|
||||||
})
|
})
|
||||||
@ -544,4 +535,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, Mino, Tetromino, Playfield, HoldQueue, NextQueue }
|
Loading…
x
Reference in New Issue
Block a user