This particular game is an iteration of the splendid Space Invaders, but surreptitiously including the most-acknowledged spaceship in the Universe - Millennium Falcon.
Advertisement:
Read Later
This particular game is an iteration of the splendid Space Invaders, but surreptitiously including the most-acknowledged spaceship in the Universe - Millennium Falcon.
Related Links :
Previous Game
I am new to developing and designing a game using JavaScript. In this project series named JavaScript Game, I will share games I develop from basic to advanced. This particular game is an iteration of the splendid Space Invaders, but surreptitiously including the most-acknowledged spaceship in the Universe - Millennium Falcon.
It is also a competitive game: when you exceed the highest scores defined as ranks, your username will be registered as the new highest rank scorer in the database.
Click 'Preview' to Play.
For more information about the code files, please visit the Source Code section below.
- Move the Millennium Falcon using arrow keys or the mobile keyboard to fire bullets to shoot Tie Fighters.
- Pause or resume the game using the escape key or the P mobile key.
- There are three different levels in this game:
- In Level 1, each Tie Fighter getting hit by a bullet increases the sum of the score by 200 points. Every 1/15 seconds, Tie Fighters are randomly generated by the game.
- In Level 2, each Tie Fighter getting hit by a bullet increases the sum of the score by 300 points. Every 2/15 seconds, Tie Fighters are randomly generated by the game.
- In Level 3, each Tie Fighter getting hit by a bullet increases the sum of the score by 500 points. Every 3/15 seconds, Tie Fighters are randomly generated by the game. Also, the Death Star emerges: you need to hit the Death Star ten times with bullets to destroy it.
- When the Millennium Falcon got hit from a Tie Fighter, the game is over. Also, in Level 3, if the Millennium Falcon crushes to the Death Star, the game is over.
- Press Enter or click the P mobile button to restart the game.
- Note: You have to sign in to join to the scoreboard on TheAmplituhedron.
- If you hit a new highest record defined as rank, you will be the new highest rank scorer in the scoreboard :)
- Import the Game class.
- Get the canvas element.
- Create the 2d context to draw.
- Define the game width and the game height.
- Define the game object.
- Get the highest scores from the score.php file as the response by making a get request in jQuery / AJAX.
- Create the loop function to update and draw the game using requestAnimationFrame(loop);.
- Import Ship, InputHandler, Fighters, and Collision classes.
- Define game states.
- Create and define game objects. And, attach the class itself to the created game objects.
- Update each game object if the game state is not equal to paused or gameover.
- Print the game score and the current game level.
- Proceed after drawing each object unless there is not a change in the game state.
- Define pause() and gameOver() functions.
- In the gameOver() function, send the new score to the score.php file and get notification messages after interpretation as the response.
- Get the information including the game width and the game height from the Game class.
- Define the Millennium Falcon's properties - width, height, speed, accelerate, position, bullet properties, score, and level.
- Define the bullets array and the bulletNumber variable.
- Define moveRight(), moveLeft(), moveUp(), moveDown(), and stop() functions.
- In the fire() function, add a new bullet to the bullets array with the current position coordinates by using the push() function and increase the bulletNumber value.
- Draw the background of the canvas.
- Draw the Millennium Falcon.
- Draw each bullet in the bullets array if it is not empty.
- Update the Millennium Falcon position.
- Update each bullet position y-axis in the bullets array.
- If a particular bullet is outside of the canvas, remove that bullet in the bullets array using splice() and indexOf() functions.
- Get the information including the game width and the game height from the Game class.
- Define the fighters' properties - width, height, and speed.
- Define the level multidimensional array to provide each level with a different Tie Fighter fleet.
- Define the fighters array and the line variable.
- Define the Death Star's properties - position, width, height, speed, and hit.
- Draw each Tie Fighter in the fighters array.
- If the current level is equal to 3, draw the Death Star.
- Create new Tie Fighters depending on the current level and the line variable using the push() function.
- Update each Tie Fighter in the fighters array.
- If a particular Tie Fighter is outside of the canvas, remove that Tie Fighter in the fighters array using splice() and indexOf() functions.
- Get objects and variables from the Game class.
- In the detect_bullet_collision(bullets, fighters) function, detect whether a particular bullet hits to a particular Tie Fighter or not.
- If it is true; remove that array elements using splice() and indexOf() functions, increase the score depending on the current level.
- Depending on the score, instigate further levels.
- In the detect_star_collision(bullets, star) function, detect whether a particular bullet hits to the Death Star.
- If it is true, increase the hit value and remove that bullet.
- In the detect_ship_collision(ship, fighters), detect whether a particular Tie Fighter hits to the Millennium Falcon.
- If it is true, initiate the gameOver() function.
- Update the mentioned functions for each related array element.
- Get objects and variables from the Game class.
- If the right arrow is pressed or the mobile right key is clicked, execute the moveRight() function.
- If the left arrow is pressed or the mobile left key is clicked, execute the moveLeft() function.
- If the up arrow is pressed or the mobile up key is clicked, execute the moveUp() function.
- If the down arrow is pressed or the mobile down key is clicked, execute the moveDown() function.
- To avoid the page navigation while pressing arrows, use event.preventDefault();
- If the escape key is pressed or the mobile P key is clicked, pause or resume the game.
- If the Enter key is pressed or the mobile P key is clicked, restart the game in the GAMEOVER state.
- If the space key is pressed or the mobile space is clicked, fire a bullet.
- On keyup, stop the ship by executing the stop() function.
- Create a database.
- Subsequently, create a database table named `spaceinvaders`.
- Insert the default rank and user name variables to the table.
- Check the table after inserting variables.
- Define the database information to connect to the database table.
- Select ranks from the database table.
- If the scores variable is set, print all the rank and username values.
- If the new score variable is set, add each rank to the scores array using the array_push() function.
- If the new score is higher than any ranks, update the database and print a congratulations notification message as the response.
- Otherwise, print a try again notification message as the response.
Please sign in to download source code files and the game, including index.css, index.html, and assets, in a zipped folder to play and try the game on your computer.
Zip Folder
Download
Index
Download
import Game from './game.js';
let canvas = document.getElementById("game");
let ctx = canvas.getContext("2d");
const GAME_WIDTH = 300;
const GAME_HEIGHT = 500;
let game = new Game(GAME_WIDTH, GAME_HEIGHT);
// GET SCORES
$.ajax({
url: 'score.php?scores=true',
type: 'GET',
async:true,
success: (result) => {if(result) $(".scoreBoard").append(result); }
});
function loop(timestamp){
// Define if needed.
let deltaTime;
ctx.clearRect(0, 0, GAME_WIDTH, GAME_HEIGHT);
game.update(deltaTime);
game.draw(ctx);
requestAnimationFrame(loop);
}
requestAnimationFrame(loop);
Game
Download
import Ship from './ship.js';
import InputHandler from './inputhandler.js';
import Fighters from './fighters.js';
import Collision from './collision.js';
export default class Game{
constructor(gameWidth, gameHeight){
this.gameWidth = gameWidth;
this.gameHeight = gameHeight;
this.STATE = {
PAUSED: 0,
RUNNING: 1,
GAMEOVER: 2
};
this.ship = new Ship(this);
this.fighters = new Fighters(this);
this.collision = new Collision(this);
new InputHandler(this);
this.objects = [this.ship, this.fighters, this.collision];
}
update(deltaTime){
if(this.gameState == this.STATE.PAUSED || this.gameState == this.STATE.GAMEOVER) return;
this.objects.forEach((object) => object.update(deltaTime));
}
draw(ctx){
this.objects.forEach((object) => object.draw(ctx));
ctx.font = "bold 20px Times";
ctx.fillStyle = "#eb2e00";
ctx.fillText("SCORE: " + this.ship.properties.score, 10, 25);
ctx.fillText("LEVEL: " + this.ship.properties.LEVEL, 10, this.gameHeight - 25);
if(this.gameState == this.STATE.PAUSED){
ctx.fillStyle = "rgba(78, 88, 81, 0.6)";
ctx.fillRect(0, 0, this.gameWidth, this.gameHeight);
ctx.font = "bold 30px Times";
ctx.fillStyle = "white";
ctx.textAlign = "center";
ctx.fillText("PAUSED", this.gameWidth / 2, this.gameHeight / 3);
ctx.textAlign = "left";
}
if(this.gameState == this.STATE.GAMEOVER){
ctx.fillStyle = "#1F2020";
ctx.fillRect(0, 0, this.gameWidth, this.gameHeight);
ctx.font = "bold 30px Times";
ctx.fillStyle = "white";
ctx.textAlign = "center";
ctx.fillText("GAME OVER !!!", this.gameWidth / 2, this.gameHeight / 2);
}
}
pause(){
if(this.gameState == this.STATE.PAUSED){
this.gameState = this.STATE.RUNNING
}else{
this.gameState = this.STATE.PAUSED;
}
}
gameOver(){
this.gameState = this.STATE.GAMEOVER;
// SEND NEW SCORE TO THE DATABASE
$.ajax({
url: 'score.php?new_score=' + this.ship.properties.score,
type: 'GET',
async:true,
success: (result) => { alert(result); }
});
}
}
Ship
Download
export default class Ship{
constructor(game){
this.game = game;
this.gameWidth = game.gameWidth;
this.gameHeight = game.gameHeight;
this.properties = {
width: 35,
height: 45,
speed: {x: 0, y: 0},
accelerate: 10,
position: {x : this.gameWidth / 2, y: this.gameHeight / 2},
bullet: {width: 3, height: 15, speed: 10},
score: 0,
LEVEL: 1
};
this.bullets = [];
this.bulletNumber = 0;
}
moveRight(){
this.properties.speed.x = -this.properties.accelerate;
this.properties.speed.y = 0;
}
moveLeft(){
this.properties.speed.x = this.properties.accelerate;
this.properties.speed.y = 0;
}
moveUp(){
this.properties.speed.x = 0;
this.properties.speed.y = -this.properties.accelerate;
}
moveDown(){
this.properties.speed.x = 0;
this.properties.speed.y = this.properties.accelerate;
}
stop(){
this.properties.speed.x = 0;
this.properties.speed.y = 0;
}
fire(){
this.bullets.push({x: this.properties.position.x + (this.properties.width / 2 - this.properties.bullet.width / 2), y: this.properties.position.y - this.properties.height / 2});
this.bulletNumber++;
}
draw(ctx){
ctx.drawImage(document.getElementById("background"), 0, 0, this.gameWidth, this.gameHeight);
ctx.drawImage(document.getElementById("millennium_falcon"), this.properties.position.x, this.properties.position.y, this.properties.width, this.properties.height);
if(this.bulletNumber > 0){
this.bullets.forEach((object) => {
ctx.fillStyle = "cyan";
ctx.fillRect(object.x, object.y, this.properties.bullet.width, this.properties.bullet.height);
});
}
}
update(deltaTime){
this.properties.position.x += this.properties.speed.x;
this.properties.position.y += this.properties.speed.y;
if(this.properties.position.x > this.gameWidth - this.properties.width) this.properties.position.x = this.gameWidth - this.properties.width;
if(this.properties.position.x < 0) this.properties.position.x = 0;
if(this.properties.position.y > this.gameHeight - this.properties.height) this.properties.position.y = this.gameHeight - this.properties.height;
if(this.properties.position.y < 0) this.properties.position.y = 0;
if(this.bulletNumber > 0){
this.bullets.forEach((object) => {
object.y -= this.properties.bullet.speed;
if(object.y < 0) this.bullets.splice(this.bullets.indexOf(object), 1);
});
}
}
}
Fighters
Download
export default class Fighters{
constructor(game){
this.game = game;
this.gameWidth = game.gameWidth;
this.gameHeight = game.gameHeight;
this.properties = {
width: 25,
height: 25,
speed: 5
};
this.level = [
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1]
];
this.fighters = [];
this.line = -1;
this.death_star = {position: {x: 0, y: -300}, width: this.gameWidth, height: 300, speed: 3, hit: 0};
}
draw(ctx){
this.fighters.forEach((object) => {
ctx.drawImage(document.getElementById("tie_fighter"), object.x, object.y, this.properties.width, this.properties.height);
});
if(this.game.ship.properties.LEVEL == 3 && this.death_star.hit < 10){
ctx.drawImage(document.getElementById("death_star"), this.death_star.position.x, this.death_star.position.y, this.death_star.width, this.death_star.height);
}
}
update(deltaTime){
this.line++;
if(this.line > this.level[0].length) this.line = 0;
if(this.game.ship.properties.LEVEL == 1){ if(this.level[0][this.line] == 1) this.fighters.push({x: Math.floor(Math.random() * 10 * 26), y: 0}); }
if(this.game.ship.properties.LEVEL == 2){ if(this.level[1][this.line] == 1) this.fighters.push({x: Math.floor(Math.random() * 10 * 26), y: 0}); }
if(this.game.ship.properties.LEVEL == 3){
if(this.level[2][this.line] == 1) this.fighters.push({x: Math.floor(Math.random() * 10 * 26), y: 0});
if(this.death_star.position.y != 0) this.death_star.position.y += this.death_star.speed;
}
this.fighters.forEach((object) => {
object.y += this.properties.speed;
if(object.y > this.gameHeight) this.fighters.splice(this.fighters.indexOf(object), 1);
});
}
}
Collision
Download
export default class Collision{
constructor(game){
// GET ALL INFO FROM THE GAME CLASS
this.game = game;
}
detect_bullet_collision(bullets, fighters){
let bottomOfFighter = fighters.y + this.game.fighters.properties.height;
let leftOfFighter = fighters.x;
let rightOfFighter = fighters.x + this.game.fighters.properties.width;
let topOfFighter = fighters.y;
let bottomOfBullet = bullets.y + this.game.ship.properties.bullet.height;
let leftOfBullet = bullets.x;
let rightOfBullet = bullets.x + this.game.ship.properties.bullet.width;
let topOfBullet = bullets.y;
if(leftOfBullet < rightOfFighter && rightOfBullet > leftOfFighter && topOfBullet < bottomOfFighter && bottomOfBullet > topOfFighter){
this.game.ship.bullets.splice(this.game.ship.bullets.indexOf(bullets), 1);
this.game.fighters.fighters.splice(this.game.fighters.fighters.indexOf(fighters), 1);
if(this.game.ship.properties.LEVEL == 1) this.game.ship.properties.score += 200;
if(this.game.ship.properties.LEVEL == 2) this.game.ship.properties.score += 300;
if(this.game.ship.properties.LEVEL == 3) this.game.ship.properties.score += 500;
if(this.game.ship.properties.score > 5000) this.game.ship.properties.LEVEL = 2;
if(this.game.ship.properties.score > 10000) this.game.ship.properties.LEVEL = 3;
}
}
detect_star_collision(bullets, star){
if(bullets.y <= star.height / 2 && star.hit < 10){
star.hit++;
this.game.ship.bullets.splice(this.game.ship.bullets.indexOf(bullets), 1);
}
if(this.game.ship.properties.position.y <= star.height / 2 && star.hit < 10){
this.game.gameOver();
}
}
detect_ship_collision(ship, fighters){
let bottomOfFighter = fighters.y + this.game.fighters.properties.height;
let leftOfFighter = fighters.x;
let rightOfFighter = fighters.x + this.game.fighters.properties.width;
let topOfFighter = fighters.y;
let bottomOfShip = ship.position.y + ship.height;
let leftOfShip = ship.position.x;
let rightOfShip = ship.position.x + ship.width;
let topOfShip = ship.position.y;
if(leftOfShip < rightOfFighter && rightOfShip > leftOfFighter && topOfShip < bottomOfFighter && bottomOfShip > topOfFighter){
this.game.gameOver();
}
}
draw(ctx){
}
update(deltaTime){
if(this.game.ship.bulletNumber > 0){
this.game.ship.bullets.forEach((bullet) => {
this.game.fighters.fighters.forEach((fighter) => { this.detect_bullet_collision(bullet, fighter); });
});
}
this.game.fighters.fighters.forEach((fighter) => {
this.detect_ship_collision(this.game.ship.properties, fighter);
});
if(this.game.ship.properties.LEVEL == 3){
this.game.ship.bullets.forEach((bullet) => {
this.detect_star_collision(bullet, this.game.fighters.death_star);
});
}
}
}
InputHandler
Download
export default class InputHandler{
constructor(game){
// GET ALL INFO FROM THE GAME CLASS
this.game = game;
document.addEventListener("keydown", (event) => {
let key = event.keyCode || event.which;
switch(key){
case 37:
this.game.ship.moveRight();
break;
case 38:
event.preventDefault(); this.game.ship.moveUp();
break;
case 39:
this.game.ship.moveLeft();
break;
case 40:
event.preventDefault(); this.game.ship.moveDown();
break;
case 32:
event.preventDefault(); this.game.ship.fire();
break;
case 27:
if(this.game.gameState != this.game.STATE.GAMEOVER) this.game.pause();
break;
case 13:
if(this.game.gameState == this.game.STATE.GAMEOVER) location.reload();
break;
}
});
document.addEventListener("keyup", (event) => {
let key = event.keyCode || event.which;
switch(key){
case 37:
this.game.ship.stop();
break;
case 38:
this.game.ship.stop();
break;
case 39:
this.game.ship.stop();
break;
case 40:
this.game.ship.stop();
break;
}
});
// MOBILE KEYBOARD
document.getElementById("RIGHT").addEventListener("click", () => {
this.game.ship.moveRight();
});
document.getElementById("UP").addEventListener("click", () => {
this.game.ship.moveUp();
});
document.getElementById("LEFT").addEventListener("click", () => {
this.game.ship.moveLeft();
});
document.getElementById("DOWN").addEventListener("click", () => {
this.game.ship.moveDown();
});
document.getElementById("SPACE").addEventListener("click", () => {
this.game.ship.fire();
});
document.getElementById("PAUSE").addEventListener("click", () => {
if(this.game.gameState != this.game.STATE.GAMEOVER){this.game.pause();}
else{location.reload();}
});
}
}
Table
Download
-- CREATE TABLE
CREATE TABLE `spaceinvaders`(
rank int(11) PRIMARY KEY NOT NULL,
username varchar(255) NOT NULL,
score varchar(255) NOT NULL
);
-- INSERT DEFAULT RANK VARIABLES TO THE TABLE
INSERT INTO `spaceinvaders`(`rank`, `username`, `score`) VALUES (1, 'Player_1', '50000');
INSERT INTO `spaceinvaders`(`rank`, `username`, `score`) VALUES (2, 'Player_2', '40000');
INSERT INTO `spaceinvaders`(`rank`, `username`, `score`) VALUES (3, 'Player_3', '30000');
INSERT INTO `spaceinvaders`(`rank`, `username`, `score`) VALUES (4, 'Player_4', '20000');
INSERT INTO `spaceinvaders`(`rank`, `username`, `score`) VALUES (5, 'Player_5', '10000');
Score
Download
<?php
$DBServerName = "[SERVER_NAME]";
$DBUserName = "[USER_NAME]";
$DBPassword = "[PASSWORD]";
$DBName = "[DATABASE_NAME]";
$conn = mysqli_connect($DBServerName, $DBUserName, $DBPassword, $DBName);
// CREATE A TABLE ON YOUR SERVER, INCLUDING USERNAME, SCORE, AND RANK VARIABLES
$sql = "SELECT * FROM `[TABLE_NAME]`";
$result = mysqli_query($conn, $sql);
if(isset($_GET['scores'])){
while($row = mysqli_fetch_assoc($result)){
echo '<p>'.$row["username"].': '.$row["score"].'</p>';
}
exit();
}
if(isset($_GET['new_score'])){
$new_score = $_GET['new_score'];
$username = "[USER_NAME]";
$scores = [];
while($row = mysqli_fetch_assoc($result)){
array_push($scores, $row['score']);
}
if($new_score > $scores[0]){
$sql = "UPDATE `[TABLE_NAME]` SET `username` = '$username', `score` = '$new_score' WHERE rank=1";
mysqli_query($conn, $sql);
echo 'Congratulations RANK_1 exceeded :)';
exit();
}else if($new_score > $scores[1]){
$sql = "UPDATE `[TABLE_NAME]` SET `username` = '$username', `score` = '$new_score' WHERE rank=2";
mysqli_query($conn, $sql);
echo 'Congratulations RANK_2 exceeded :)';
exit();
}else if($new_score > $scores[2]){
$sql = "UPDATE `[TABLE_NAME]` SET `username` = '$username', `score` = '$new_score' WHERE rank=3";
mysqli_query($conn, $sql);
echo 'Congratulations RANK_3 exceeded :)';
exit();
}else if($new_score > $scores[3]){
$sql = "UPDATE `[TABLE_NAME]` SET `username` = '$username', `score` = '$new_score' WHERE rank=4";
mysqli_query($conn, $sql);
echo 'Congratulations RANK_4 exceeded :)';
exit();
}else if($new_score > $scores[4]){
$sql = "UPDATE `[TABLE_NAME]` SET `username` = '$username', `score` = '$new_score' WHERE rank=5";
mysqli_query($conn, $sql);
echo 'Congratulations RANK_5 exceeded :)';
exit();
}else{
echo 'Try again to hit a new record :)';
exit();
}
}
?>