Instructions: Shoot incoming asteroids before they hit the ship. Use the mouse to aim and the left mouse button to fire at asteroids. Press any key to begin.
<!DOCTYPE html> <html> <head> <title>XoaX.net HTML</title> <style> .cAsteroid { position:absolute; animation: kfExpandWidth linear 2.5s 1, kfMoveDown linear 2.5s 1, kfExpandHeight linear 2.5s 1, kfMoveRight linear 2.5s 1; } @keyframes kfExpandWidth { from {width: 10%;} to {width: 100%;} } @keyframes kfMoveDown { from {top: 45%;} to {top: 0%;} } @keyframes kfExpandHeight { from {height: 10%;} to {height: 100%;} } @keyframes kfMoveRight { from {left: 45%;} to {left: 0%;} } .cExplosion { position:absolute; animation: kfDisappear linear .5s 1; } @keyframes kfDisappear { from {opacity: 100%;} to {opacity: 0%;} } </style> <script> var gqOutside = null; var giScore = 0; var giDamage = 0; var gqaAsteroids = null; var gqaExplosions = null; var giCollisionFrame = 20; var gbGameOver = true; function fnStart() { // Prevent multiple starts from key presses if (gbGameOver) { gbGameOver = false; giScore = 0; giDamage = 0; gqOutside = document.getElementById("idOutside"); // Allocate the asteroid and explosion arrays gqaAsteroids = new Array(); gqaExplosions = new Array(); // Remove the start message var qMessageElement = document.getElementById("idStartMessage"); qMessageElement.style.display = "none"; // Start the timed game loop fnGameLoop(); } } function fnGameLoop() { if (!gbGameOver) { setTimeout(fnGameLoop, 50); // Increment the asteroid frame counts for(var i = 0; i < gqaAsteroids.length; ++i) { gqaAsteroids[i].miFrame += 1; } // Place an asteroid if (Math.floor(Math.random() * 100) < (3 + giScore/10)) { fnPlaceAsteroid(); } // Move the screen during a collision if (giCollisionFrame < 20) { gqOutside.style.left = Math.floor((Math.random() * 6) - 3) + "px"; gqOutside.style.top = Math.floor((Math.random() * 6) - 3) + "px"; ++giCollisionFrame; } else { gqOutside.style.left = 0 + "px"; gqOutside.style.top = 0 + "px"; giCollisionFrame = 20; } } else { // Remove the remaining asteroids and stop their collisions for(var i = 0; i < gqaAsteroids.length; ++i) { gqOutside.removeChild(gqaAsteroids[i].mqDiv); clearTimeout(gqaAsteroids[i].mqTimeOut); } // Reset the collision shake giCollisionFrame = 20; var qMessageElement = document.getElementById("idStartMessage"); qMessageElement.style.display = "block"; } // Update the score and damage var qScore = document.getElementById("idScore"); qScore.innerHTML = giScore; var qDamage = document.getElementById("idDamage"); qDamage.innerHTML = giDamage; } function CAsteroid() { // The frame counter is used to determine how big the asteroid is this.miFrame = 0; // x between 30 and 640 - 30 - 28 = 582 this.miCenterX = Math.floor((Math.random() * 552) + 30); // y between 30 and 388 - 30 - 28 = 330 this.miCenterY = Math.floor((Math.random() * 300) + 30); // Create the container div for the image this.mqDiv = document.createElement("div"); this.mqDiv.style.position = "absolute"; this.mqDiv.style.left = this.miCenterX - 28 + "px"; this.mqDiv.style.top = this.miCenterY - 28 + "px"; this.mqDiv.style.width = "56px"; this.mqDiv.style.height = "56px"; this.mqDiv.style.zIndex = "1"; this.mqDiv.innerHTML = "<img class='cAsteroid' src='Rock.png' />"; gqOutside.appendChild(this.mqDiv); // Create the collision event function. Asteroids collide after 2.5 seconds this.mqTimeOut = setTimeout(fnAsteroidCollision, 2500); } function fnPlaceAsteroid() { var qNewAsteroid = new CAsteroid(); // Add the asteroid at the end of the array gqaAsteroids.push(qNewAsteroid); fnAsteroidResetZ(); } function fnAsteroidCollision() { // Make the collision sound and shake the screen var qCollisionAudio = document.getElementById("idCollision"); qCollisionAudio.currentTime = 0; qCollisionAudio.play(); // Initialize the screen shake giCollisionFrame = 0; // Increase the damage ++giDamage; // Remove the first asteroid in the list. This one hit the ship. var qRemoved = gqaAsteroids.shift(); // Remove the asteroid image from the HTML gqOutside.removeChild(qRemoved.mqDiv); fnAsteroidResetZ(); if (giDamage >= 3) { gbGameOver = true; } } function fnAsteroidResetZ() { for(var i = 0; i < gqaAsteroids.length; ++i) { gqaAsteroids[i].mqDiv.style.zIndex = gqaAsteroids.length - i; } } function fnAsteroidShot(iIndex, dPercent) { // Create the explosion image fnCreateExplosion(dPercent, gqaAsteroids[iIndex]); // Stop the collision event clearTimeout(gqaAsteroids[iIndex].mqTimeOut); // Remove element first because it is accessed via the array gqOutside.removeChild(gqaAsteroids[iIndex].mqDiv); // Remove the asteroid from the array gqaAsteroids.splice(iIndex, 1); // Reset the z values fnAsteroidResetZ(); ++giScore; } function fnCreateExplosion(dPercent, qAsteroid) { // Create the explosion image. The image is 56x56 var iImageSize = Math.round(dPercent*56); var qImageElement = document.createElement("img"); qImageElement.className = "cExplosion"; qImageElement.src = "ExplodingRock.png"; qImageElement.style.left = qAsteroid.miCenterX - (iImageSize/2) + "px"; qImageElement.style.top = qAsteroid.miCenterY - (iImageSize/2) + "px"; qImageElement.style.width = iImageSize + "px"; qImageElement.style.height = iImageSize + "px"; qImageElement.style.zIndex = qAsteroid.mqDiv.style.zIndex; gqOutside.appendChild(qImageElement); gqaExplosions.push(qImageElement); // Play the explosion shot sound var qAsteroidShotAudio = document.getElementById("idAsteroidShot"); qAsteroidShotAudio.currentTime = 0; qAsteroidShotAudio.play(); setTimeout(fnRemoveExplosion, 500); } function fnRemoveExplosion() { // Get the first explosion and remove it from the outside and array var qExplosionImage = gqaExplosions.shift(); gqOutside.removeChild(qExplosionImage); } function fnOnClick(e, qElement) { // Play the shot sound var qShotAudio = document.getElementById("idShot"); qShotAudio.currentTime = 0; qShotAudio.play(); // This is all to calculate the click coordinates relative to the div var dElementOffsetX = 0; var dElementOffsetY = 0; do{ dElementOffsetX += qElement.offsetLeft - qElement.scrollLeft; dElementOffsetY += qElement.offsetTop - qElement.scrollTop; qElement = qElement.offsetParent } while(qElement != document.body); var dRelX = e.pageX - dElementOffsetX; var dRelY = e.pageY - dElementOffsetY; // Check each asteroid for a hit for (var i = gqaAsteroids.length - 1; i >= 0; --i) { // Percentage complete - there are 2500/50 = 50 frames per asteroid var dPC = (gqaAsteroids[i].miFrame/50); // The max radius is 50 pixels. The min radius is 10% or 5 pixels var dHitRadius = ((50*dPC) + 5*(1 - dPC)); var dDeltaX = dRelX - gqaAsteroids[i].miCenterX; var dDeltaY = dRelY - gqaAsteroids[i].miCenterY; var dClickDistance = Math.sqrt(dDeltaX*dDeltaX + dDeltaY*dDeltaY); if (dHitRadius > dClickDistance) { fnAsteroidShot(i, dPC); return; } } } </script> </head> <body onkeydown="fnStart()"> <h1 id="idStartMessage" style="position:absolute;left:155px;top:130px;z-index:5001;color:white;text-align: center;"> GAME OVER<br />Press any key to begin!</h1> <audio id="idCollision" src="Collision.mp3"></audio> <audio id="idAsteroidShot" src="AsteroidShot.mp3"></audio> <audio id="idShot" src="Shot.mp3"></audio> <!-- The background stars and asteroids --> <div id="idOutside" style="position:absolute;left:0px;top:0px;"> <img style="position:absolute;left:0px;top:0px;z-index:-1;" src="Space.png" /> </div> <!-- The window click region --> <div style="position:absolute;left:0px;top:0px;z-index:5000;width:640px;height:388px;cursor:crosshair;" onclick="fnOnClick(event, this)"></div> <!-- The ship's window, console, and scores --> <img style="position:absolute;left:0px;top:0px;z-index:1000;" src="Windshield.png" /> <h1 style="position:absolute;left:20px;top:395px;z-index:5000;">Score: <span id="idScore">0</span></h1> <h1 style="position:absolute;left:340px;top:395px;z-index:5000;">Damage: <span id="idDamage">0</span></h1> </body> </html>
© 20072025 XoaX.net LLC. All rights reserved.