From f530e61eafeda00b02e80d0f1c36d77285a97be0 Mon Sep 17 00:00:00 2001 From: Abhijit Motekar <109235675+AbhijitMotekar99@users.noreply.github.com> Date: Thu, 10 Oct 2024 15:32:03 +0530 Subject: [PATCH] Tower block added --- projects/Tower Blocks/index.html | 33 +++ projects/Tower Blocks/script.js | 419 +++++++++++++++++++++++++++++++ projects/Tower Blocks/style.css | 107 ++++++++ 3 files changed, 559 insertions(+) create mode 100644 projects/Tower Blocks/index.html create mode 100644 projects/Tower Blocks/script.js create mode 100644 projects/Tower Blocks/style.css diff --git a/projects/Tower Blocks/index.html b/projects/Tower Blocks/index.html new file mode 100644 index 000000000..abc5a631c --- /dev/null +++ b/projects/Tower Blocks/index.html @@ -0,0 +1,33 @@ + + + + + + + + + + Tower Blocks + + + + +
+
+
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
+
+
+
+ + + diff --git a/projects/Tower Blocks/script.js b/projects/Tower Blocks/script.js new file mode 100644 index 000000000..caef7f23b --- /dev/null +++ b/projects/Tower Blocks/script.js @@ -0,0 +1,419 @@ +console.clear(); +var Stage = /** @class */ (function () { + function Stage() { + // container + var _this = this; + 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 + var aspect = window.innerWidth / window.innerHeight; + var 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", function () { + return _this.onResize(); + }); + this.onResize(); + } + Stage.prototype.setCamera = function (y, speed) { + if (speed === void 0) { + 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 }); + }; + Stage.prototype.onResize = function () { + var 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(); + }; + return Stage; +})(); +var Block = /** @class */ (function () { + function Block(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 { + var 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 + var 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; + } + } + Block.prototype.reverseDirection = function () { + this.direction = this.direction > 0 ? this.speed : Math.abs(this.speed); + }; + Block.prototype.place = function () { + this.state = this.STATES.STOPPED; + var overlap = + this.targetBlock.dimension[this.workingDimension] - + Math.abs( + this.position[this.workingPlane] - + this.targetBlock.position[this.workingPlane] + ); + var 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) { + var choppedDimensions = { + width: this.dimension.width, + height: this.dimension.height, + depth: this.dimension.depth, + }; + choppedDimensions[this.workingDimension] -= overlap; + this.dimension[this.workingDimension] = overlap; + var 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 + ) + ); + var placedMesh = new THREE.Mesh(placedGeometry, this.material); + var 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 + ) + ); + var choppedMesh = new THREE.Mesh(choppedGeometry, this.material); + var 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; + }; + Block.prototype.tick = function () { + if (this.state == this.STATES.ACTIVE) { + var 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]; + } + }; + return Block; +})(); +var Game = /** @class */ (function () { + function Game() { + var _this = this; + 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", function (e) { + if (e.keyCode == 32) _this.onAction(); + }); + document.addEventListener("click", function (e) { + _this.onAction(); + }); + document.addEventListener("touchstart", function (e) { + e.preventDefault(); + // this.onAction(); + // ☝️ this triggers after click on android so you + // insta-lose, will figure it out later. + }); + } + Game.prototype.updateState = function (newState) { + for (var key in this.STATES) + this.mainContainer.classList.remove(this.STATES[key]); + this.mainContainer.classList.add(newState); + this.state = newState; + }; + Game.prototype.onAction = function () { + switch (this.state) { + case this.STATES.READY: + this.startGame(); + break; + case this.STATES.PLAYING: + this.placeBlock(); + break; + case this.STATES.ENDED: + this.restartGame(); + break; + } + }; + Game.prototype.startGame = function () { + if (this.state != this.STATES.PLAYING) { + this.scoreContainer.innerHTML = "0"; + this.updateState(this.STATES.PLAYING); + this.addBlock(); + } + }; + Game.prototype.restartGame = function () { + var _this = this; + this.updateState(this.STATES.RESETTING); + var oldBlocks = this.placedBlocks.children; + var removeSpeed = 0.2; + var delayAmount = 0.02; + var _loop_1 = function (i) { + TweenLite.to(oldBlocks[i].scale, removeSpeed, { + x: 0, + y: 0, + z: 0, + delay: (oldBlocks.length - i) * delayAmount, + ease: Power1.easeIn, + onComplete: function () { + return _this.placedBlocks.remove(oldBlocks[i]); + }, + }); + TweenLite.to(oldBlocks[i].rotation, removeSpeed, { + y: 0.5, + delay: (oldBlocks.length - i) * delayAmount, + ease: Power1.easeIn, + }); + }; + for (var i = 0; i < oldBlocks.length; i++) { + _loop_1(i); + } + var cameraMoveSpeed = removeSpeed * 2 + oldBlocks.length * delayAmount; + this.stage.setCamera(2, cameraMoveSpeed); + var countdown = { value: this.blocks.length - 1 }; + TweenLite.to(countdown, cameraMoveSpeed, { + value: 0, + onUpdate: function () { + _this.scoreContainer.innerHTML = String(Math.round(countdown.value)); + }, + }); + this.blocks = this.blocks.slice(0, 1); + setTimeout(function () { + _this.startGame(); + }, cameraMoveSpeed * 1000); + }; + Game.prototype.placeBlock = function () { + var _this = this; + var currentBlock = this.blocks[this.blocks.length - 1]; + var newBlocks = currentBlock.place(); + this.newBlocks.remove(currentBlock.mesh); + if (newBlocks.placed) this.placedBlocks.add(newBlocks.placed); + if (newBlocks.chopped) { + this.choppedBlocks.add(newBlocks.chopped); + var positionParams = { + y: "-=30", + ease: Power1.easeIn, + onComplete: function () { + return _this.choppedBlocks.remove(newBlocks.chopped); + }, + }; + var rotateRandomness = 10; + var 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(); + }; + Game.prototype.addBlock = function () { + var 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); + var 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"); + }; + Game.prototype.endGame = function () { + this.updateState(this.STATES.ENDED); + }; + Game.prototype.tick = function () { + var _this = this; + this.blocks[this.blocks.length - 1].tick(); + this.stage.render(); + requestAnimationFrame(function () { + _this.tick(); + }); + }; + return Game; +})(); +var game = new Game(); diff --git a/projects/Tower Blocks/style.css b/projects/Tower Blocks/style.css new file mode 100644 index 000000000..a7f607eda --- /dev/null +++ b/projects/Tower Blocks/style.css @@ -0,0 +1,107 @@ +@import url("https://fonts.googleapis.com/css?family=Comfortaa"); +html, +body { + margin: 0; + overflow: hidden; + height: 100%; + 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: #334; + 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: #334; +} +#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 #334; + padding: 10px 20px; + background-color: transparent; + color: #334; + 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; +}