Spaces:
Running
Running
| <html> | |
| <head> | |
| <title>Breakout WebXR Tutorial</title><meta name="description" content="Breakout WebXR Tutorial"> | |
| <!-- Basic A-Frame library --> | |
| <script src="https://aframe.io/releases/1.2.0/aframe.min.js"></script> | |
| <script> | |
| //initialize variables | |
| //arrays to hold the blocks and their positions | |
| let gameBlocks = []; //array of objects | |
| let gameBlocksX = []; //X dimensions of the blocks | |
| let gameBlocksY = []; //Y dimensions of the blocks | |
| let gameBlocksZ = []; //Z dimensions of the blocks | |
| let gameBlocksActive = []; //whether the block is active | |
| const blockWidth = 0.8; //how wide blocks are in the X dimension | |
| const blockHeight = 0.2; //how tall blocks are in the Y dimension | |
| const blockDepth = 0.2; //how deep blocks are in the Z dimension | |
| let blockColor = '#4CC3D9'; //the default color of the blocks (later this was changed to be dynamically generated) | |
| let gameIsOn = 0; //whether the game is active, controls certain functionality | |
| const gameLoopSpeed = 15; //used for regulating the speed of the game | |
| let topBorder = 3.5; //border of game area in the Y dimension | |
| let bottomBorder = 0.25; //border of game area in the Y dimension | |
| let rightBorder = 1.8; //border of game area in the X dimension | |
| let leftBorder = -1.8; //border of game area in the X dimension | |
| let scoreValue = 0; //keeps | |
| let boxGrabbed; // whether or not the user grabbed the box (the user doesn't drag the box in the final version, but I left this in for illustration) | |
| let boxHovered; // whether or not the user is hovering over the box | |
| let livesValue = 3; // how many lives the player has | |
| let gamePaddleX = 0; //where the game paddle is in the X dimension | |
| let gamePaddleY = 0.3; //where the game paddle is in the Y dimension | |
| let gamePaddleZ = -1; //where the game paddle is in the Z dimension | |
| let gamePaddleWidth = 1; //how wide the game paddle is in the X dimension | |
| let gamePaddleHeight = 0.2; //how tall the game paddle is in the Y dimension | |
| let gamePaddleDepth = 0.2; //how deep the game paddle is in the Z dimension | |
| let gameBallX = 0; //the position of the game ball in the X dimension | |
| let gameBallY = 1.25; //the position of the game ball in the Y dimension | |
| let gameBallZ = -1; //the position of the game ball in the Z dimension | |
| let gameBallVelocityX = 0.045; //how fast the game ball is moving in the X dimension | |
| let gameBallVelocityY = 0.075; //how fast the game ball is moving in the Y dimension | |
| const gameBallRadius = 0.15; //how fast the game ball is moving in the Z dimension | |
| //initialize sound | |
| let soundWarp = new Audio('warp-sfx-6897.mp3'); | |
| let soundImpact = new Audio('electronic-impact-soft-10019.mp3'); | |
| let soundChime = new Audio('chime-sound-7143.mp3'); | |
| //stop all of the sounds | |
| function stopAllSounds(){ | |
| soundWarp.pause(); | |
| soundImpact.pause(); | |
| soundChime.pause(); | |
| soundWarp.currentTime = 0; | |
| soundImpact.currentTime = 0; | |
| soundChime.currentTime = 0; | |
| } | |
| //function to check if all blocks are broken, return true if so | |
| function checkBlocks(){ | |
| let returnValue = 1; | |
| for (i = 0; i < 12; i++){ | |
| if(gameBlocksActive[i] == "1") | |
| returnValue = 0; | |
| } | |
| return returnValue; | |
| } | |
| function moveBall(){ | |
| //move the game ball | |
| gameBallX = gameBallX + gameBallVelocityX; | |
| gameBallY = gameBallY + gameBallVelocityY; | |
| } | |
| function updatePaddle(){ | |
| if(boxGrabbed == true){ | |
| gamePaddle.setAttribute('color', "#FFFF00"); | |
| } else if(boxHovered == true){ | |
| gamePaddle.setAttribute('color', "#FF0000"); | |
| } else { | |
| gamePaddle.setAttribute('color', "#0000FF"); | |
| } | |
| } | |
| //function to reset the ball position | |
| function resetBall(){ | |
| gameBallX = Math.floor(Math.random() * ((rightBorder - 0.5) - (leftBorder + 0.5) + 1)) + (leftBorder + 0.5); | |
| gameBallY = 1.25; | |
| gameBallZ = -1; | |
| gameBallVelocityX = 0.045; | |
| gameBallVelocityY = 0.075; | |
| gameBall.setAttribute('position', gameBallX + ' ' + gameBallY + ' ' + gameBallZ); | |
| } | |
| //function to reset the blocks | |
| function resetBlocks(){ | |
| for (i = 0; i < 12; i++) | |
| { | |
| gameBlocksActive[i] = "1"; | |
| let blockColor = '#' + parseInt(Math.random() * 0xffffff).toString(16); | |
| gameBlocks[i].setAttribute('color', blockColor); | |
| gameBlocks[i].setAttribute('opacity', '1'); | |
| } | |
| } | |
| function checkCollisions(){ | |
| let startGameText = document.getElementById('startGameText'); | |
| //checking border collisions | |
| if(gameBallY >= topBorder){ | |
| gameBallVelocityY = gameBallVelocityY * -1; //make the ball bounce | |
| gameBallY = gameBallY - 0.1; //to help prevent ball getting stuck | |
| } | |
| if(gameBallY <= bottomBorder){ | |
| gameBallVelocityY = gameBallVelocityY * -1; //make the ball bounce | |
| gameBallY = gameBallY + 0.1; //to help prevent balls getting stuck | |
| if(gameIsOn == 1){ //if the user is playing | |
| livesValue = livesValue - 1; //remove a life | |
| let livesText = document.getElementById('livesText'); | |
| livesText.setAttribute('text', 'font: mozillavr; width: 5; lineHeight: 50; letterSpacing: 5; color: blue; value: Lives: ' + livesValue); //update the life text | |
| resetBall(); //reset the ball's location so it doesn't get stuck | |
| stopAllSounds(); | |
| soundImpact.play(); //play sound | |
| if(livesValue == 0){ //if the player runs out of lives | |
| //turn the game off | |
| gameIsOn = 0; | |
| //display the Game Over text by setting the opacity to 1 | |
| let gameOverText = document.getElementById('gameOverText'); | |
| gameOverText.setAttribute('text', 'opacity: 1; font: mozillavr; width: 5; lineHeight: 50; letterSpacing: 5; color: red; value: Game Over'); | |
| //reset the blocks | |
| resetBlocks(); | |
| //display the Start text by changing the opacity to 1 | |
| startGameText.setAttribute('text', 'opacity: 1; font: mozillavr; width: 5; lineHeight: 50; letterSpacing: 5; color: green; value: Start'); | |
| } | |
| } | |
| } | |
| if(gameBallX >= rightBorder){ | |
| gameBallVelocityX = gameBallVelocityX * -1; //make the ball bounce | |
| } | |
| if(gameBallX <= leftBorder){ | |
| gameBallVelocityX = gameBallVelocityX * -1; //make the ball bounce | |
| } | |
| //checking block collisions | |
| //for each block | |
| for (i = 0; i < 12; i++){ | |
| //block collisions | |
| if((((gameBallY + (gameBallRadius * .8)) >= (gameBlocksY[i] - blockHeight)) && ((gameBallY - (gameBallRadius * .8)) <= gameBlocksY[i])) && ((gameBallX + (gameBallRadius * .8)) >= (gameBlocksX[i])) && ((gameBallX - (gameBallRadius * .8)) <= (gameBlocksX[i] + blockWidth)) && (gameBlocksActive[i] == "1")){ | |
| gameBallVelocityY = gameBallVelocityY * -1; //make the ball bounce | |
| gameBlocksActive[i] = "0"; //mark the block as broken | |
| gameBlocks[i].setAttribute('opacity', '0'); //hide the block | |
| if(checkBlocks()){ //if all of the blocks are broken | |
| resetBlocks(); //reset the blocks | |
| resetBall(); //reset the ball position | |
| } | |
| if(gameIsOn == 1){ | |
| scoreValue = scoreValue + 1; //increase the player's score | |
| let scoreText = document.getElementById('scoreText'); | |
| scoreText.setAttribute('text', 'font: mozillavr; width: 5; lineHeight: 50; letterSpacing: 5; color: blue; value: Score: ' + scoreValue); //update the score text | |
| stopAllSounds(); | |
| soundChime.play(); //play sound | |
| if(scoreValue == 12){ //if the player broke all of the blocks | |
| gameIsOn = 0; //turn off the game | |
| //display the You Win text by changing the opacity to 1 | |
| let youWinText = document.getElementById('youWinText'); | |
| youWinText.setAttribute('text', 'opacity: 1; font: mozillavr; width: 5; lineHeight: 50; letterSpacing: 5; color: green; value: You Win'); | |
| //display the Start text by changing the opacity to 1 | |
| startGameText.setAttribute('text', 'opacity: 1; font: mozillavr; width: 5; lineHeight: 50; letterSpacing: 5; color: green; value: Start'); | |
| } | |
| } | |
| } | |
| } | |
| //checking paddle collisions | |
| if( ((gameBallY + (gameBallRadius * .8)) >= (gamePaddleY - gamePaddleHeight)) && ((gameBallY - (gameBallRadius * .8)) <= (gamePaddleY) && ((gameBallX + (gameBallRadius * .8)) >= (gamePaddleX - gamePaddleWidth * .5)) && ((gameBallX - (gameBallRadius * .8)) <= (gamePaddleX + gamePaddleWidth *.5)))){ | |
| gameBallVelocityY = gameBallVelocityY * -1; //make the ball bounce | |
| } | |
| } | |
| AFRAME.registerComponent('handle-ball', { | |
| init: function () { | |
| this.throttledFunction = AFRAME.utils.throttle(this.gameLoop, gameLoopSpeed, this); | |
| }, | |
| gameLoop: function () { | |
| checkCollisions(); //check to see if anything collided | |
| moveBall(); //update the X and Y coordinates of game objects | |
| gameBall.setAttribute('position', gameBallX + ' ' + gameBallY + ' ' + gameBallZ); //reposition the ball | |
| }, | |
| tick: function (t, dt) { | |
| this.throttledFunction(); // Called every frame. | |
| } | |
| }); | |
| AFRAME.registerComponent('handle-paddle', { | |
| init: function () { | |
| let el = this.el; | |
| el.addEventListener('mousedown', function (evt) { | |
| boxGrabbed = true; | |
| }); | |
| el.addEventListener('mouseup', function (evt) { | |
| boxGrabbed = false; | |
| }); | |
| el.addEventListener('raycaster-intersected', evt => { | |
| this.raycaster = evt.detail.el; | |
| }); | |
| this.el.addEventListener('raycaster-intersected-cleared', evt => { | |
| this.raycaster = null; | |
| }); | |
| }, | |
| tick: function () { | |
| if (!this.raycaster) { | |
| boxHovered = false; | |
| updatePaddle(); | |
| return; | |
| }// Not intersecting. | |
| let intersection = this.raycaster.components.raycaster.getIntersection(this.el); | |
| if (!intersection) { | |
| boxHovered = false; | |
| updatePaddle(); | |
| return; | |
| } // Not intersecting | |
| // intersecting | |
| boxHovered = true; | |
| updatePaddle(); | |
| } | |
| }); | |
| AFRAME.registerComponent('cursor-listener', { | |
| init: function () { | |
| this.el.addEventListener('raycaster-intersected', evt => { | |
| this.raycaster = evt.detail.el; | |
| }); | |
| this.el.addEventListener('raycaster-intersected-cleared', evt => { | |
| this.raycaster = null; | |
| }); | |
| }, | |
| tick: function () { | |
| if (!this.raycaster) { | |
| return; | |
| }// Not intersecting. | |
| let intersection = this.raycaster.components.raycaster.getIntersection(this.el); | |
| if (!intersection) { | |
| return; | |
| } // Not intersecting | |
| // intersecting | |
| // move box if the game is running | |
| if(gameIsOn == 1){ | |
| let gamePaddle = document.getElementById('gamePaddle'); | |
| let tempY = gamePaddle.components.position.data.y; | |
| let tempZ = gamePaddle.components.position.data.z; | |
| let tempX = intersection.point.x; | |
| if(tempX < leftBorder){ | |
| tempX = leftBorder + (gamePaddleWidth/2); | |
| } | |
| if(tempX > rightBorder){ | |
| tempX = rightBorder - (gamePaddleWidth/2); | |
| } | |
| gamePaddleX = tempX; | |
| gamePaddle.setAttribute('position', gamePaddleX + ' ' + tempY + ' ' + tempZ); | |
| } | |
| } | |
| }); | |
| AFRAME.registerComponent('handle-start', { | |
| init: function () { | |
| let el = this.el; | |
| let startGameText = document.getElementById('startGameText'); | |
| el.addEventListener('mousedown', function (evt) { | |
| if(gameIsOn == 0){ | |
| startGameText.setAttribute('text', 'opacity: 0; font: mozillavr; width: 5; lineHeight: 50; letterSpacing: 5; color: green; value: Start'); | |
| //reset all game components | |
| resetBall(); | |
| resetBlocks(); | |
| //reset the score | |
| scoreValue = 0; | |
| livesValue = 3; | |
| //update the text | |
| let scoreText = document.getElementById('scoreText'); | |
| scoreText.setAttribute('text', 'text: Score: ' + scoreValue); | |
| let livesText = document.getElementById('livesText'); | |
| livesText.setAttribute('text', 'text: Lives: ' + livesValue); | |
| //hide the Game Over text by setting the opacity to zero | |
| let gameOverText = document.getElementById('gameOverText'); | |
| gameOverText.setAttribute('text', 'opacity: 0; font: mozillavr; width: 5; lineHeight: 50; letterSpacing: 5; color: red; value: Game Over'); | |
| //hide the You Win text by setting the opacity to zero | |
| let youWinText = document.getElementById('youWinText'); | |
| youWinText.setAttribute('text', 'opacity: 0; font: mozillavr; width: 5; lineHeight: 50; letterSpacing: 5; color: green; value: You Win'); | |
| //hide the Start Game text by setting the opacity to zero | |
| startGameText.setAttribute('text', 'opacity: 0; font: mozillavr; width: 5; lineHeight: 50; letterSpacing: 5; color: green; value: Start'); | |
| //set the game on flag | |
| gameIsOn = 1; | |
| stopAllSounds(); | |
| soundWarp.play(); //play sound | |
| }; | |
| }); | |
| el.addEventListener('raycaster-intersected', evt => { | |
| startGameText.setAttribute('text', "font: mozillavr; width: 5; lineHeight: 50; letterSpacing: 5; color: #FF0000; value: Start;"); | |
| }); | |
| this.el.addEventListener('raycaster-intersected-cleared', evt => { | |
| startGameText.setAttribute('text', "font: mozillavr; width: 5; lineHeight: 50; letterSpacing: 5; color: green; value: Start"); | |
| }); | |
| } | |
| }); | |
| //wait until the page loads to perform the following | |
| window.onload = function (){ | |
| //create the game blocks | |
| //declare the blocks and their attributes | |
| for (i = 0; i < 12; i++) | |
| { | |
| gameBlocks[i] = document.createElement('a-box'); | |
| gameBlocks[i].setAttribute('width', blockWidth); | |
| gameBlocks[i].setAttribute('height', blockHeight); | |
| gameBlocks[i].setAttribute('depth', blockDepth); | |
| blockColor = '#' + parseInt(Math.random() * 0xffffff).toString(16); | |
| gameBlocks[i].setAttribute('color', blockColor); | |
| gameBlocksActive[i] = "1"; | |
| } | |
| //set the position of the blocks | |
| //Top row | |
| gameBlocksX[0] = -1.5; | |
| gameBlocksY[0] = 3.5; | |
| gameBlocksZ[0] = -1; | |
| gameBlocksX[1] = -0.5; | |
| gameBlocksY[1] = 3.5; | |
| gameBlocksZ[1] = -1; | |
| gameBlocksX[2] = 0.5 | |
| gameBlocksY[2] = 3.5; | |
| gameBlocksZ[2] = -1; | |
| gameBlocksX[3] = 1.5; | |
| gameBlocksY[3] = 3.5; | |
| gameBlocksZ[3] = -1; | |
| //Middle row | |
| gameBlocksX[4] = -1.5; | |
| gameBlocksY[4] = 3; | |
| gameBlocksZ[4] = -1; | |
| gameBlocksX[5] = -0.5; | |
| gameBlocksY[5] = 3; | |
| gameBlocksZ[5] = -1; | |
| gameBlocksX[6] = 0.5 | |
| gameBlocksY[6] = 3; | |
| gameBlocksZ[6] = -1; | |
| gameBlocksX[7] = 1.5; | |
| gameBlocksY[7] = 3; | |
| gameBlocksZ[7] = -1; | |
| //Bottom row | |
| gameBlocksX[8] = -1.5; | |
| gameBlocksY[8] = 2.5; | |
| gameBlocksZ[8] = -1; | |
| gameBlocksX[9] = -0.5; | |
| gameBlocksY[9] = 2.5; | |
| gameBlocksZ[9] = -1; | |
| gameBlocksX[10] = 0.5 | |
| gameBlocksY[10] = 2.5; | |
| gameBlocksZ[10] = -1; | |
| gameBlocksX[11] = 1.5; | |
| gameBlocksY[11] = 2.5; | |
| gameBlocksZ[11] = -1; | |
| //add the blocks to the scene | |
| let scene = document.getElementById("scene"); //assign a name to the A-Frame scene | |
| for (i = 0; i < 12; i++) | |
| { | |
| scene.appendChild(gameBlocks[i]); | |
| gameBlocks[i].setAttribute('position', gameBlocksX[i] + ' ' + gameBlocksY[i] + ' ' + gameBlocksZ[i]); | |
| } | |
| } | |
| </script> | |
| </head> | |
| <body> | |
| <a-scene id="scene"> | |
| <a-assets> | |
| <a-mixin id="red" material="color: red"></a-mixin> | |
| <a-mixin id="green" material="color: green"></a-mixin> | |
| <a-mixin id="blue" material="color: blue"></a-mixin> | |
| <a-mixin id="url-red" material="color: #d63959"></a-mixin> | |
| <a-mixin id="cube" geometry="primitive: box"></a-mixin> | |
| </a-assets> | |
| <!-- set game background planes. --> | |
| <a-plane position="0 0 -3" rotation="-90 0 0" width="4" height="8" color="#a0a0a0"></a-plane> | |
| <a-plane position="0 2 -5" rotation="0 0 0" width="4" height="4" color="#bfabce"></a-plane> | |
| <!-- set a plane to track where the user is pointing --> | |
| <a-plane id="moveTracker" color="#FFFFFF" rotation="0 0 0" position="0 0 -1.6" opacity="0" width="20" height="20" cursor-listener></a-plane> | |
| <!-- sky color. --> | |
| <a-sky src="https://cdn.aframe.io/360-image-gallery-boilerplate/img/city.jpg"></a-sky>- | |
| <!-- Set camera and controller starting position. --> | |
| <a-entity position="0 0 3.8"> | |
| <a-camera look-controls wasd-controls="enabled: false"></a-camera> | |
| <a-entity laser-controls="hand: right"></a-entity> | |
| </a-entity> | |
| <!-- Start Game text --> | |
| <a-entity id="startGameText" text="font: mozillavr; width: 5; lineHeight: 50; letterSpacing: 5; color: green; value: Start" position="2.2 2 0.5" rotation="0 0 0" handle-start></a-entity> | |
| <!-- Game Over text --> | |
| <a-entity id="gameOverText" text="opacity: 0; font: mozillavr; width: 5; lineHeight: 50; letterSpacing: 5; color: red; value: Game Over" position="1.9 2.5 0.5" rotation="0 0 0"></a-entity> | |
| <!-- You Win text --> | |
| <a-entity id="youWinText" text="opacity: 0; font: mozillavr; width: 5; lineHeight: 50; letterSpacing: 5; color: green; value: You Win" position="2 2.5 0.5" rotation="0 0 0"></a-entity> | |
| <!-- Player Lives text --> | |
| <a-entity id="livesText" text="font: mozillavr; width: 5; lineHeight: 50; letterSpacing: 5; color: blue; value: Lives: 3" position="4 3.8 -0.8" rotation="0 0 0"></a-entity> | |
| <!-- Score text --> | |
| <a-entity id="scoreText" text="font: mozillavr; width: 5; lineHeight: 50; letterSpacing: 5; color: blue; value: Score: 0" position="0 3.8 -0.8" rotation="0 0 0"></a-entity> | |
| <!-- Add the game paddle --> | |
| <a-box id="gamePaddle" color="#42f4aa" position="0 0.3 -1" depth="0.2" height="0.2" width="1" handle-paddle></a-box> | |
| <!-- Add the game ball --> | |
| <a-sphere id="gameBall" color="#FFFFFF" radius="0.15" position="0 1.25 -1" handle-ball></a-box> | |
| </a-scene> | |
| </body> | |
| </html> |