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.