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> |