Compare commits

...

4 Commits

Author SHA1 Message Date
90eb3247e0 new 3d loading animation 2024-09-30 21:23:44 +02:00
2a25dbe4b0 new 3d loading animation 2024-09-30 21:21:50 +02:00
d9397c4bcb textured ghost 2024-09-30 21:21:25 +02:00
ca93423bf8 hide vortex hole 2024-09-30 21:18:38 +02:00
8 changed files with 110 additions and 302 deletions

8
app.js
View File

@ -1,10 +1,10 @@
import * as THREE from 'three'
import { scheduler } from './jsm/scheduler.js'
import { TRANSLATION, ROTATION, environment, Mino, Playfield, HoldQueue, NextQueue } from './jsm/Tetrominoes.js'
import { Settings } from './jsm/Settings.js'
import Settings from './jsm/Settings.js'
import { Stats } from './jsm/Stats.js'
import { TetraGUI } from './jsm/TetraGUI.js'
import { TetraControls } from './jsm/TetraControls.js'
import TetraControls from './jsm/TetraControls.js'
import { TetraScene } from './jsm/TetraScene.js'
@ -277,7 +277,7 @@ renderer.domElement.tabIndex = 1
let loadingManager = new THREE.LoadingManager(
function() {
loaddingCircle.style.display = "none"
loadingDiv.style.display = "none"
gui.startButton.show()
renderer.setAnimationLoop(animate)
},
@ -290,7 +290,7 @@ let loadingManager = new THREE.LoadingManager(
)
loadingManager.onStart = function (url, itemsLoaded, itemsTotal) {
loadingPercent.innerText = "0%"
loaddingCircle.style.display = "block"
loadingDiv.style.display = "block"
}
const stats = new Stats()

View File

@ -1,279 +1,64 @@
@-webkit-keyframes outerRotate1 {
0% {
transform: translate(-50%, -50%) rotate(0);
}
body { background-color: black; }
100% {
transform: translate(-50%, -50%) rotate(360deg);
}
#loadingDiv {
box-sizing: border-box;
font-family: "Open Sans", sans-serif;
font-size: 1.4rem;
color: hsla(240, 100%, 70%, 0.6);
text-align: center;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
@-moz-keyframes outerRotate1 {
0% {
transform: translate(-50%, -50%) rotate(0);
}
100% {
transform: translate(-50%, -50%) rotate(360deg);
}
.scene {
width: 200px;
height: 200px;
margin: auto;
perspective: 200px;
}
@-o-keyframes outerRotate1 {
0% {
transform: translate(-50%, -50%) rotate(0);
}
100% {
transform: translate(-50%, -50%) rotate(360deg);
}
.cube {
width: 40px;
height: 40px;
position: relative;
transform-style: preserve-3d;
transform: translateZ(20px);
left: 80px;
top: 80px;
}
@keyframes outerRotate1 {
0% {
transform: translate(-50%, -50%) rotate(0);
}
100% {
transform: translate(-50%, -50%) rotate(360deg);
}
.cube.is-spinning {
animation: spinCube 5s infinite ease-in-out;
}
@-webkit-keyframes outerRotate2 {
0% {
transform: translate(-50%, -50%) rotate(0);
}
100% {
transform: translate(-50%, -50%) rotate(-360deg);
}
@keyframes spinCube {
0% { transform: translateZ(20px) rotateX( 0deg) rotateY( 0deg) rotateZ( 0deg); }
100% { transform: translateZ(20px) rotateX(360deg) rotateY( 0deg) rotateZ(360deg); }
}
@-moz-keyframes outerRotate2 {
0% {
transform: translate(-50%, -50%) rotate(0);
}
100% {
transform: translate(-50%, -50%) rotate(-360deg);
}
.face {
position: absolute;
width: 40px;
height: 40px;
padding: 0;
background: hsla(240, 100%, 0%, 0.4);
border: 1px solid hsla(240, 100%, 70%, 0.6);
}
@-o-keyframes outerRotate2 {
0% {
transform: translate(-50%, -50%) rotate(0);
}
.front { transform: rotateY( 0deg) translateZ(20px); }
.right { transform: rotateY( 90deg) translateZ(20px); }
.back { transform: rotateY(180deg) translateZ(20px); }
.left { transform: rotateY(-90deg) translateZ(20px); }
.top { transform: rotateX( 90deg) translateZ(20px); }
.bottom { transform: rotateX(-90deg) translateZ(20px); }
100% {
transform: translate(-50%, -50%) rotate(-360deg);
}
.cube.is-backface-hidden .face {
backface-visibility: hidden;
}
@keyframes outerRotate2 {
0% {
transform: translate(-50%, -50%) rotate(0);
}
100% {
transform: translate(-50%, -50%) rotate(-360deg);
}
}
@-webkit-keyframes textColour {
0% {
color: #fff;
}
100% {
color: #3BB2D0;
}
}
@-moz-keyframes textColour {
0% {
color: #fff;
}
100% {
color: #3BB2D0;
}
}
@-o-keyframes textColour {
0% {
color: #fff;
}
100% {
color: #3BB2D0;
}
}
@keyframes textColour {
0% {
color: #fff;
}
100% {
color: #3BB2D0;
}
}
body {
background-color: #222;
}
#loaddingCircle {
margin: 0;
padding: 0;
width: 100vw;
height: 100vh;
cursor: progress;
}
.e-loadholder {
position: absolute;
top: 50%;
left: 50%;
-webkit-transform: translate(-51%, -50%);
-moz-transform: translate(-51%, -50%);
-ms-transform: translate(-51%, -50%);
-o-transform: translate(-51%, -50%);
transform: translate(-51%, -50%);
width: 240px;
height: 240px;
border: 5px solid #1B5F70;
border-radius: 120px;
box-sizing: border-box;
}
.e-loadholder:after {
position: absolute;
top: 50%;
left: 50%;
-webkit-transform: translate(-51%, -50%);
-moz-transform: translate(-51%, -50%);
-ms-transform: translate(-51%, -50%);
-o-transform: translate(-51%, -50%);
transform: translate(-51%, -50%);
content: " ";
display: block;
background: #222;
transform-origin: center;
z-index: 0;
}
.e-loadholder:after {
width: 100px;
height: 200%;
-webkit-animation: outerRotate2 30s infinite linear;
-moz-animation: outerRotate2 30s infinite linear;
-o-animation: outerRotate2 30s infinite linear;
animation: outerRotate2 30s infinite linear;
}
.e-loadholder .m-loader {
position: absolute;
top: 50%;
left: 50%;
-webkit-transform: translate(-51%, -50%);
-moz-transform: translate(-51%, -50%);
-ms-transform: translate(-51%, -50%);
-o-transform: translate(-51%, -50%);
transform: translate(-51%, -50%);
width: 200px;
height: 200px;
color: #888;
text-align: center;
border: 5px solid #2a93ae;
border-radius: 100px;
box-sizing: border-box;
z-index: 20;
text-transform: uppercase;
}
.e-loadholder .m-loader:after {
position: absolute;
top: 50%;
left: 50%;
-webkit-transform: translate(-51%, -50%);
-moz-transform: translate(-51%, -50%);
-ms-transform: translate(-51%, -50%);
-o-transform: translate(-51%, -50%);
transform: translate(-51%, -50%);
content: " ";
display: block;
background: #222;
transform-origin: center;
z-index: -1;
}
.e-loadholder .m-loader:after {
width: 100px;
height: 106%;
-webkit-animation: outerRotate1 15s infinite linear;
-moz-animation: outerRotate1 15s infinite linear;
-o-animation: outerRotate1 15s infinite linear;
animation: outerRotate1 15s infinite linear;
}
.e-loadholder .m-loader .e-text {
font-family: "Open Sans", sans-serif;
font-size: 10px;
font-size: 1rem;
position: absolute;
top: 50%;
left: 50%;
-webkit-transform: translate(-51%, -50%);
-moz-transform: translate(-51%, -50%);
-ms-transform: translate(-51%, -50%);
-o-transform: translate(-51%, -50%);
transform: translate(-51%, -50%);
-webkit-animation: textColour 1s alternate linear infinite;
-moz-animation: textColour 1s alternate linear infinite;
-o-animation: textColour 1s alternate linear infinite;
animation: textColour 1s alternate linear infinite;
display: flex;
flex-direction: column;
justify-content: center;
width: 140px;
height: 140px;
text-align: center;
border: 5px solid #3bb2d0;
border-radius: 70px;
box-sizing: border-box;
z-index: 20;
}
.e-loadholder .m-loader .e-text:before, .e-loadholder .m-loader .e-text:after {
position: absolute;
top: 50%;
left: 50%;
-webkit-transform: translate(-51%, -50%);
-moz-transform: translate(-51%, -50%);
-ms-transform: translate(-51%, -50%);
-o-transform: translate(-51%, -50%);
transform: translate(-51%, -50%);
content: " ";
display: block;
background: #222;
transform-origin: center;
z-index: -1;
}
.e-loadholder .m-loader .e-text:before {
width: 110%;
height: 40px;
-webkit-animation: outerRotate2 3.5s infinite linear;
-moz-animation: outerRotate2 3.5s infinite linear;
-o-animation: outerRotate2 3.5s infinite linear;
animation: outerRotate2 3.5s infinite linear;
}
.e-loadholder .m-loader .e-text:after {
width: 40px;
height: 110%;
-webkit-animation: outerRotate1 8s infinite linear;
-moz-animation: outerRotate1 8s infinite linear;
-o-animation: outerRotate1 8s infinite linear;
animation: outerRotate1 8s infinite linear;
}
.mino0 { left: -40px; top: -20px; }
.mino1 { left: 0px; top: -20px; }
.mino2 { left: 40px; top: -20px; }
.mino3 { left: 0px; top: 20px; }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 792 B

After

Width:  |  Height:  |  Size: 761 B

View File

@ -20,15 +20,39 @@
</head>
<body>
<div id="loaddingCircle">
<div class="e-loadholder">
<div class="m-loader">
<span class="e-text">
<div id="loadingDiv">
<div class="scene">
<div class="cube is-spinning">
<div class="mino0 front face"></div>
<div class="mino0 back face"></div>
<div class="mino0 right face"></div>
<div class="mino0 left face"></div>
<div class="mino0 top face"></div>
<div class="mino0 bottom face"></div>
<div class="mino1 front face"></div>
<div class="mino1 back face"></div>
<div class="mino1 right face"></div>
<div class="mino1 left face"></div>
<div class="mino1 top face"></div>
<div class="mino1 bottom face"></div>
<div class="mino2 front face"></div>
<div class="mino2 back face"></div>
<div class="mino2 right face"></div>
<div class="mino2 left face"></div>
<div class="mino2 top face"></div>
<div class="mino2 bottom face"></div>
<div class="mino3 front face"></div>
<div class="mino3 back face"></div>
<div class="mino3 right face"></div>
<div class="mino3 left face"></div>
<div class="mino3 top face"></div>
<div class="mino3 bottom face"></div>
</div>
</div>
<div>
<div>Chargement</div>
<div id="loadingPercent">0%</div>
</span>
</div>
</div>
</div>
</div>
<span id="messagesSpan"></span>
<span id="pauseSpan" tabindex="1">II</span>

View File

@ -27,7 +27,7 @@ let friendyKeyRenamer = new Proxy({
}
})
class Settings {
export default class Settings {
constructor() {
this.startLevel = 1
@ -79,7 +79,4 @@ class Settings {
this.theme = "Plasma"
}
}
export { Settings }
}

View File

@ -1,8 +1,7 @@
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
class TetraControls extends OrbitControls {
export default class TetraControls extends OrbitControls {
constructor(camera, domElement) {
super(camera, domElement)
this.autoRotate
@ -10,7 +9,7 @@ class TetraControls extends OrbitControls {
this.dampingFactor = 0.04
this.maxDistance = 21
this.keys = {}
this.minPolarAngle = 1
this.minPolarAngle = 1.05
this.maxPolarAngle = 2.1
this.minAzimuthAngle = 0.9 - Math.PI / 2
this.maxAzimuthAngle = 2.14 - Math.PI / 2
@ -19,6 +18,4 @@ class TetraControls extends OrbitControls {
this.addEventListener("start", () => domElement.style.cursor = "grabbing")
this.addEventListener("end", () => domElement.style.cursor = "grab")
}
}
export { TetraControls }
}

View File

@ -91,7 +91,8 @@ class InstancedMino extends THREE.InstancedMesh {
}
setOffsetAt(index, offset) {
this.offsets[index * 2] = offset
this.offsets[2*index] = offset.x
this.offsets[2*index + 1] = offset.y
}
resetColor() {
@ -136,9 +137,9 @@ class Mino extends THREE.Object3D {
envMap: environment,
side: THREE.DoubleSide,
transparent: true,
opacity: 0.8,
roughness: 0.48,
metalness: 0.67,
opacity: 0.7,
roughness: 0.5,
metalness: 1,
}),
Espace: new THREE.MeshStandardMaterial({
envMap: environment,
@ -295,7 +296,7 @@ class Tetromino extends THREE.Group {
}
Tetromino.prototype.lockingColor = new THREE.Color(COLORS.LOCKING)
// Super Rotation System
// freedom of movement = srs[this.parent.piece.facing][rotation]
// freedom of movement = srs[this.facing][rotation]
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)] },
@ -309,7 +310,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.children.forEach(mino => {mino.offset = piece.ghostOffset})
this.facing = piece.facing
this.visible = true
while (this.canMove(TRANSLATION.DOWN)) this.position.y--
@ -319,7 +320,6 @@ 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 { }
@ -336,7 +336,8 @@ 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
I.prototype.offset = P(0, 1)
I.prototype.ghostOffset = P(0, 0)
class J extends Tetromino { }
J.prototype.minoesPosition = [
@ -346,7 +347,8 @@ 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
J.prototype.offset = P(1, 1)
J.prototype.ghostOffset = P(1, 0)
class L extends Tetromino {
}
@ -357,7 +359,8 @@ 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
L.prototype.offset = P(2, 1)
L.prototype.ghostOffset = P(2, 0)
class O extends Tetromino { }
O.prototype.minoesPosition = [
@ -367,7 +370,8 @@ O.prototype.srs = [
{ [ROTATION.CW]: [], [ROTATION.CCW]: [] }
]
O.prototype.freeColor = new THREE.Color(COLORS.O)
O.prototype.offset = 4
O.prototype.offset = P(3, 1)
O.prototype.ghostOffset = P(3, 0)
class S extends Tetromino { }
S.prototype.minoesPosition = [
@ -377,7 +381,8 @@ 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
S.prototype.offset = P(4, 1)
S.prototype.ghostOffset = P(4, 0)
class T extends Tetromino {
get tSpin() {
@ -405,7 +410,8 @@ 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
T.prototype.offset = P(5, 1)
T.prototype.ghostOffset = P(5, 0)
class Z extends Tetromino { }
Z.prototype.minoesPosition = [
@ -415,7 +421,8 @@ 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
Z.prototype.offset = P(6, 1)
Z.prototype.ghostOffset = P(6, 0)
class Playfield extends THREE.Group {

View File

@ -33,6 +33,4 @@ class Scheduler {
}
const scheduler = new Scheduler()
export { scheduler }
export const scheduler = new Scheduler()