WEBLEB
Início
Editor
Entrar
Pro
Português
English
Français
Español
Português
Deutsch
Italiano
हिंदी
Jogo de pilha
1296
fastbug170
Abrir no Editor
Publique Seu Código
Recomendado
22 May 2025
Painel de administração
25 April 2025
Calculadora de Impostos Robux
27 February 2025
jogo
HTML
Copy
Javascript Stack Game
0
Click (or press the spacebar) to place the block
Game Over
You did great, you're the best.
Click or spacebar to start again
Start
CSS
Copy
@import url("https://fonts.googleapis.com/css?family=Comfortaa"); html, body { margin: 0; overflow: hidden; height: 100vh; width: 100%; position: relative; font-family: "Comfortaa", cursive; } #container { width: 100%; height: 100%; } #container #score { position: absolute; top: 20px; width: 100%; text-align: center; font-size: 10vh; transition: transform 0.5s ease; color: #333344; transform: translatey(-200px) scale(1); } #container #game { position: absolute; top: 0; right: 0; bottom: 0; left: 0; } #container .game-over { position: absolute; top: 0; left: 0; width: 100%; height: 85%; display: flex; flex-direction: column; align-items: center; justify-content: center; } #container .game-over * { transition: opacity 0.5s ease, transform 0.5s ease; opacity: 0; transform: translatey(-50px); color: #333344; } #container .game-over h2 { margin: 0; padding: 0; font-size: 40px; } #container .game-ready { position: absolute; top: 0; left: 0; width: 100%; height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: space-around; } #container .game-ready #start-button { transition: opacity 0.5s ease, transform 0.5s ease; opacity: 0; transform: translatey(-50px); border: 3px solid #333344; padding: 10px 20px; background-color: transparent; color: #333344; font-size: 30px; } #container #instructions { position: absolute; width: 100%; top: 16vh; left: 0; text-align: center; transition: opacity 0.5s ease, transform 0.5s ease; opacity: 0; } #container #instructions.hide { opacity: 0 !important; } #container.playing #score, #container.resetting #score { transform: translatey(0px) scale(1); } #container.playing #instructions { opacity: 1; } #container.ready .game-ready #start-button { opacity: 1; transform: translatey(0); } #container.ended #score { transform: translatey(6vh) scale(1.5); } #container.ended .game-over * { opacity: 1; transform: translatey(0); } #container.ended .game-over p { transition-delay: 0.3s; }
JS
Copy
"use strict"; console.clear(); class Stage { constructor() { // container this.render = function () { this.renderer.render(this.scene, this.camera); }; this.add = function (elem) { this.scene.add(elem); }; this.remove = function (elem) { this.scene.remove(elem); }; this.container = document.getElementById('game'); // renderer this.renderer = new THREE.WebGLRenderer({ antialias: true, alpha: false }); this.renderer.setSize(window.innerWidth, window.innerHeight); this.renderer.setClearColor('#D0CBC7', 1); this.container.appendChild(this.renderer.domElement); // scene this.scene = new THREE.Scene(); // camera let aspect = window.innerWidth / window.innerHeight; let d = 20; this.camera = new THREE.OrthographicCamera(-d * aspect, d * aspect, d, -d, -100, 1000); this.camera.position.x = 2; this.camera.position.y = 2; this.camera.position.z = 2; this.camera.lookAt(new THREE.Vector3(0, 0, 0)); //light this.light = new THREE.DirectionalLight(0xffffff, 0.5); this.light.position.set(0, 499, 0); this.scene.add(this.light); this.softLight = new THREE.AmbientLight(0xffffff, 0.4); this.scene.add(this.softLight); window.addEventListener('resize', () => this.onResize()); this.onResize(); } setCamera(y, speed = 0.3) { TweenLite.to(this.camera.position, speed, { y: y + 4, ease: Power1.easeInOut }); TweenLite.to(this.camera.lookAt, speed, { y: y, ease: Power1.easeInOut }); } onResize() { let viewSize = 30; this.renderer.setSize(window.innerWidth, window.innerHeight); this.camera.left = window.innerWidth / -viewSize; this.camera.right = window.innerWidth / viewSize; this.camera.top = window.innerHeight / viewSize; this.camera.bottom = window.innerHeight / -viewSize; this.camera.updateProjectionMatrix(); } } class Block { constructor(block) { // set size and position this.STATES = { ACTIVE: 'active', STOPPED: 'stopped', MISSED: 'missed' }; this.MOVE_AMOUNT = 12; this.dimension = { width: 0, height: 0, depth: 0 }; this.position = { x: 0, y: 0, z: 0 }; this.targetBlock = block; this.index = (this.targetBlock ? this.targetBlock.index : 0) + 1; this.workingPlane = this.index % 2 ? 'x' : 'z'; this.workingDimension = this.index % 2 ? 'width' : 'depth'; // set the dimensions from the target block, or defaults. this.dimension.width = this.targetBlock ? this.targetBlock.dimension.width : 10; this.dimension.height = this.targetBlock ? this.targetBlock.dimension.height : 2; this.dimension.depth = this.targetBlock ? this.targetBlock.dimension.depth : 10; this.position.x = this.targetBlock ? this.targetBlock.position.x : 0; this.position.y = this.dimension.height * this.index; this.position.z = this.targetBlock ? this.targetBlock.position.z : 0; this.colorOffset = this.targetBlock ? this.targetBlock.colorOffset : Math.round(Math.random() * 100); // set color if (!this.targetBlock) { this.color = 0x333344; } else { let offset = this.index + this.colorOffset; var r = Math.sin(0.3 * offset) * 55 + 200; var g = Math.sin(0.3 * offset + 2) * 55 + 200; var b = Math.sin(0.3 * offset + 4) * 55 + 200; this.color = new THREE.Color(r / 255, g / 255, b / 255); } // state this.state = this.index > 1 ? this.STATES.ACTIVE : this.STATES.STOPPED; // set direction this.speed = -0.1 - (this.index * 0.005); if (this.speed < -4) this.speed = -4; this.direction = this.speed; // create block let geometry = new THREE.BoxGeometry(this.dimension.width, this.dimension.height, this.dimension.depth); geometry.applyMatrix(new THREE.Matrix4().makeTranslation(this.dimension.width / 2, this.dimension.height / 2, this.dimension.depth / 2)); this.material = new THREE.MeshToonMaterial({ color: this.color, shading: THREE.FlatShading }); this.mesh = new THREE.Mesh(geometry, this.material); this.mesh.position.set(this.position.x, this.position.y + (this.state == this.STATES.ACTIVE ? 0 : 0), this.position.z); if (this.state == this.STATES.ACTIVE) { this.position[this.workingPlane] = Math.random() > 0.5 ? -this.MOVE_AMOUNT : this.MOVE_AMOUNT; } } reverseDirection() { this.direction = this.direction > 0 ? this.speed : Math.abs(this.speed); } place() { this.state = this.STATES.STOPPED; let overlap = this.targetBlock.dimension[this.workingDimension] - Math.abs(this.position[this.workingPlane] - this.targetBlock.position[this.workingPlane]); let blocksToReturn = { plane: this.workingPlane, direction: this.direction }; if (this.dimension[this.workingDimension] - overlap < 0.3) { overlap = this.dimension[this.workingDimension]; blocksToReturn.bonus = true; this.position.x = this.targetBlock.position.x; this.position.z = this.targetBlock.position.z; this.dimension.width = this.targetBlock.dimension.width; this.dimension.depth = this.targetBlock.dimension.depth; } if (overlap > 0) { let choppedDimensions = { width: this.dimension.width, height: this.dimension.height, depth: this.dimension.depth }; choppedDimensions[this.workingDimension] -= overlap; this.dimension[this.workingDimension] = overlap; let placedGeometry = new THREE.BoxGeometry(this.dimension.width, this.dimension.height, this.dimension.depth); placedGeometry.applyMatrix(new THREE.Matrix4().makeTranslation(this.dimension.width / 2, this.dimension.height / 2, this.dimension.depth / 2)); let placedMesh = new THREE.Mesh(placedGeometry, this.material); let choppedGeometry = new THREE.BoxGeometry(choppedDimensions.width, choppedDimensions.height, choppedDimensions.depth); choppedGeometry.applyMatrix(new THREE.Matrix4().makeTranslation(choppedDimensions.width / 2, choppedDimensions.height / 2, choppedDimensions.depth / 2)); let choppedMesh = new THREE.Mesh(choppedGeometry, this.material); let choppedPosition = { x: this.position.x, y: this.position.y, z: this.position.z }; if (this.position[this.workingPlane] < this.targetBlock.position[this.workingPlane]) { this.position[this.workingPlane] = this.targetBlock.position[this.workingPlane]; } else { choppedPosition[this.workingPlane] += overlap; } placedMesh.position.set(this.position.x, this.position.y, this.position.z); choppedMesh.position.set(choppedPosition.x, choppedPosition.y, choppedPosition.z); blocksToReturn.placed = placedMesh; if (!blocksToReturn.bonus) blocksToReturn.chopped = choppedMesh; } else { this.state = this.STATES.MISSED; } this.dimension[this.workingDimension] = overlap; return blocksToReturn; } tick() { if (this.state == this.STATES.ACTIVE) { let value = this.position[this.workingPlane]; if (value > this.MOVE_AMOUNT || value < -this.MOVE_AMOUNT) this.reverseDirection(); this.position[this.workingPlane] += this.direction; this.mesh.position[this.workingPlane] = this.position[this.workingPlane]; } } } class Game { constructor() { this.STATES = { 'LOADING': 'loading', 'PLAYING': 'playing', 'READY': 'ready', 'ENDED': 'ended', 'RESETTING': 'resetting' }; this.blocks = []; this.state = this.STATES.LOADING; this.stage = new Stage(); this.mainContainer = document.getElementById('container'); this.scoreContainer = document.getElementById('score'); this.startButton = document.getElementById('start-button'); this.instructions = document.getElementById('instructions'); this.scoreContainer.innerHTML = '0'; this.newBlocks = new THREE.Group(); this.placedBlocks = new THREE.Group(); this.choppedBlocks = new THREE.Group(); this.stage.add(this.newBlocks); this.stage.add(this.placedBlocks); this.stage.add(this.choppedBlocks); this.addBlock(); this.tick(); this.updateState(this.STATES.READY); document.addEventListener('keydown', e => { if (e.keyCode == 32) this.onAction(); }); document.addEventListener('click', e => { this.onAction(); }); document.addEventListener('touchstart', e => { e.preventDefault(); }); } updateState(newState) { for (let key in this.STATES) this.mainContainer.classList.remove(this.STATES[key]); this.mainContainer.classList.add(newState); this.state = newState; } onAction() { switch (this.state) { case this.STATES.READY: this.startGame(); break; case this.STATES.PLAYING: this.placeBlock(); break; case this.STATES.ENDED: this.restartGame(); break; } } startGame() { if (this.state != this.STATES.PLAYING) { this.scoreContainer.innerHTML = '0'; this.updateState(this.STATES.PLAYING); this.addBlock(); } } restartGame() { this.updateState(this.STATES.RESETTING); let oldBlocks = this.placedBlocks.children; let removeSpeed = 0.2; let delayAmount = 0.02; for (let i = 0; i < oldBlocks.length; i++) { TweenLite.to(oldBlocks[i].scale, removeSpeed, { x: 0, y: 0, z: 0, delay: (oldBlocks.length - i) * delayAmount, ease: Power1.easeIn, onComplete: () => this.placedBlocks.remove(oldBlocks[i]) }); TweenLite.to(oldBlocks[i].rotation, removeSpeed, { y: 0.5, delay: (oldBlocks.length - i) * delayAmount, ease: Power1.easeIn }); } let cameraMoveSpeed = removeSpeed * 2 + (oldBlocks.length * delayAmount); this.stage.setCamera(2, cameraMoveSpeed); let countdown = { value: this.blocks.length - 1 }; TweenLite.to(countdown, cameraMoveSpeed, { value: 0, onUpdate: () => { this.scoreContainer.innerHTML = String(Math.round(countdown.value)); } }); this.blocks = this.blocks.slice(0, 1); setTimeout(() => { this.startGame(); }, cameraMoveSpeed * 1000); } placeBlock() { let currentBlock = this.blocks[this.blocks.length - 1]; let newBlocks = currentBlock.place(); this.newBlocks.remove(currentBlock.mesh); if (newBlocks.placed) this.placedBlocks.add(newBlocks.placed); if (newBlocks.chopped) { this.choppedBlocks.add(newBlocks.chopped); let positionParams = { y: '-=30', ease: Power1.easeIn, onComplete: () => this.choppedBlocks.remove(newBlocks.chopped) }; let rotateRandomness = 10; let rotationParams = { delay: 0.05, x: newBlocks.plane == 'z' ? ((Math.random() * rotateRandomness) - (rotateRandomness / 2)) : 0.1, z: newBlocks.plane == 'x' ? ((Math.random() * rotateRandomness) - (rotateRandomness / 2)) : 0.1, y: Math.random() * 0.1, }; if (newBlocks.chopped.position[newBlocks.plane] > newBlocks.placed.position[newBlocks.plane]) { positionParams[newBlocks.plane] = '+=' + (40 * Math.abs(newBlocks.direction)); } else { positionParams[newBlocks.plane] = '-=' + (40 * Math.abs(newBlocks.direction)); } TweenLite.to(newBlocks.chopped.position, 1, positionParams); TweenLite.to(newBlocks.chopped.rotation, 1, rotationParams); } this.addBlock(); } addBlock() { let lastBlock = this.blocks[this.blocks.length - 1]; if (lastBlock && lastBlock.state == lastBlock.STATES.MISSED) { return this.endGame(); } this.scoreContainer.innerHTML = String(this.blocks.length - 1); let newKidOnTheBlock = new Block(lastBlock); this.newBlocks.add(newKidOnTheBlock.mesh); this.blocks.push(newKidOnTheBlock); this.stage.setCamera(this.blocks.length * 2); if (this.blocks.length >= 5) this.instructions.classList.add('hide'); } endGame() { this.updateState(this.STATES.ENDED); } tick() { this.blocks[this.blocks.length - 1].tick(); this.stage.render(); requestAnimationFrame(() => { this.tick(); }); } } let game = new Game();