patternjavascriptModerate
Asteroids game clone
Viewed 0 times
asteroidsgameclone
Problem
I have recently been teaching a JavaScript module on a games programming undergraduate course and I made this game as a learning exercise and as an example for my students to study. I have made games before but never in JavaScript.
Could you have a look at my style and let me know if there are any obvious improvements?
the code is also on my github page
The .html file includes a canvas element, some styles and a simple game loop.
The JavaScript library is intended to be portable. I use objects and inheritance wherever possible.
```
(function(window, undefined) {
function extend(ChildClass, ParentClass) {
var parent = new ParentClass();
ChildClass.prototype = parent;
ChildClass.prototype.super = parent.constructor;
ChildClass.prototype.constructor = ChildClass;
}
function random_position(canvas) {
Could you have a look at my style and let me know if there are any obvious improvements?
the code is also on my github page
The .html file includes a canvas element, some styles and a simple game loop.
Asteroids 2
body {
text-align: center;
margin: 0;
}
canvas {
background-color: rgba(0, 0, 0, 0.9);
width: 600px;
height: 600px;
}
h1 {
margin: 0;
margin-bottom: -0.21em;
font-family: helvetica;
letter-spacing: 2.5em;
padding-left: 2.5em;
font-size: 1.5em;
font-variant: small-caps;
}
AsteroidS
Mute
var fps = 60;//frames per second
var canvas = document.getElementById("asteroids");
var game = new window.Asteroids(canvas);
var framerate = 1000 / fps;//milliseconds between frames
window.onkeydown = game.keyDown.bind(game);
window.onkeyup = game.keyUp.bind(game);
var previous;
function step(timestamp) {
if (!previous) previous = timestamp;
var elapsed = timestamp - previous;
game.update(elapsed/1000);
if (elapsed >= framerate) {
game.refresh();
previous = timestamp;
}
window.requestAnimationFrame(step);
}
window.requestAnimationFrame(step);
The JavaScript library is intended to be portable. I use objects and inheritance wherever possible.
```
(function(window, undefined) {
function extend(ChildClass, ParentClass) {
var parent = new ParentClass();
ChildClass.prototype = parent;
ChildClass.prototype.super = parent.constructor;
ChildClass.prototype.constructor = ChildClass;
}
function random_position(canvas) {
Solution
When using HTML local storage, you should always check for the possibility of failure. Some possible failure modes are:
Therefore, instead of checking
With that recommendation and one other change, I've reproduced your code verbatim as a Stack Snippet, so that you can play it live.
The other change is that I've put your `
function extend(ChildClass, ParentClass) {
var parent = new ParentClass();
ChildClass.prototype = parent;
ChildClass.prototype.super = parent.constructor;
ChildClass.prototype.constructor = ChildClass;
}
function random_position(canvas) {
return {
x: Math.random() * canvas.width,
y: Math.random() * canvas.height
}
}
function copy_coords(coords) {
return { x: coords.x, y: coords.y };
}
function middle(canvas) {
return {
x: canvas.width / 2,
y: canvas.height / 2
}
}
function random_velocity(canvas) {
//cross canvas in about 10s
return {
x: (Math.random() - 0.5) canvas.width 0.1,
y: (Math.random() - 0.5) canvas.height 0.1
}
}
function random_angle() {
return Math.random() 2 Math.PI;
}
function random_rotation() {
return (0.5 - Math.random()) Math.PI 0.5;
}
function distance_between(obj1, obj2) {
return Math.sqrt(Math.pow(obj1.position.x - obj2.position.x,2) + Math.pow(obj1.position.y - obj2.position.y,2));
}
function compare(a,b) {
if (a.score === b.score) return 0;
if (a.score > b.score) {
return -1;
} else {
return 1;
}
}
// THE GAME===========================
function Game(canvas) {
this.version = 0.3;
this.canvas = canvas;
this.c = this.canvas.getContext("2d");
this.load_scores();
this.ship = new Ship(this, this.canvas);
this.waiting = true;
this.speed = 1;
this.muted = true;
}
Game.prototype.restart = function() {
this.waiting = false;
this.asteroids = [];
this.projectiles = [];
this.powerups = [];
this.objects = [];
this.level = 1;
this.reset_asteroids();
this.ship.reset();
this.score = 0;
}
Game.prototype.reset_asteroids = function() {
this.asteroids = [];
for (var i=0;i this.scores[this.scores.length-1]['score']) this.save_score();
this.waiting = true;
}
if (Math.random() > 0.999) {
var possible = "AFMWLR"; //Ammo, Friction, Manouverability, Weapon, Life, Rate
var bonus = possible.charAt(Math.floor(Math.random() * possible.length));
this.powerups.push(new PowerUp(this, bonus));
}
}
Game.prototype.save_score = function() {
this.load_scores();
var name = window.prompt('Please enter your name');
if (name !== null && name !== "") {
this.scores.push({
name: name,
score: this.score
})
this.scores = this.scores.sort(compare);
this.scores = this.scores.slice(0, 10);
}
this.save_scores();
}
Game.prototype.load_scores = function() {
try {
var local_data = JSON.parse(localStorage.getItem("asteroids")) || {};
var version = local_data.version || 0.1;
if(version this.canvas.width) {
this.position.x -= this.canvas.width;
} else if (this.position.x this.canvas.height) {
this.position.y -= this.canvas.height;
} else if (this.position.y 2 * Math.PI) {
this.angle -= 2 * Math.PI;
}
if(this.angle = 100) {
var new_vel = copy_coords(this.velocity);
new_vel.x *= 0.95;
new_vel.y *= 0.95;
var a1 = new Asteroid(this.game, new_mass, copy_coords(this.position), new_vel, this.rotation_speed);
var a2 = new Asteroid(this.game, new_mass, copy_coords(this.position), copy_coords(new_vel), this.rotation_speed);
a1.density = th
- Local storage is not supported by the browser
- Local storage is prohibited for that domain by policy
- The storage quota has been exceeded
Therefore, instead of checking
if(typeof(Storage) !== "undefined"), you should wrap the operations in a try-catch block. Otherwise, the failures above will crash your entire application.Game.prototype.load_scores = function() {
try {
var local_data = JSON.parse(localStorage.getItem("asteroids")) || {};
var version = local_data.version || 0.1;
if(version < this.version) {
alert("New version installed, clearing scores.");
this.scores = [];
this.save_scores();
} else {
this.scores = local_data['scores'] || [];
}
} catch (e) {
// Local storage unsupported, or permission denied
this.scores = [];
}
}
Game.prototype.save_scores = function() {
try {
var local_data = JSON.parse(localStorage.getItem("asteroids")) || {};
local_data['scores'] = this.scores;
local_data['version'] = this.version;
localStorage.setItem("asteroids", JSON.stringify(local_data));
} catch (e) {
// Local storage unsupported, quota exceeded, or permission
}
}With that recommendation and one other change, I've reproduced your code verbatim as a Stack Snippet, so that you can play it live.
The other change is that I've put your `
in a window.onload function. That is not a problem with your code, but rather a consequence of the way Stack Snippets work: the JavaScript is injected at the end, so your initialization code would be called before the Game code is loaded if I didn't defer the initialization like that.
(function(window, undefined) {function extend(ChildClass, ParentClass) {
var parent = new ParentClass();
ChildClass.prototype = parent;
ChildClass.prototype.super = parent.constructor;
ChildClass.prototype.constructor = ChildClass;
}
function random_position(canvas) {
return {
x: Math.random() * canvas.width,
y: Math.random() * canvas.height
}
}
function copy_coords(coords) {
return { x: coords.x, y: coords.y };
}
function middle(canvas) {
return {
x: canvas.width / 2,
y: canvas.height / 2
}
}
function random_velocity(canvas) {
//cross canvas in about 10s
return {
x: (Math.random() - 0.5) canvas.width 0.1,
y: (Math.random() - 0.5) canvas.height 0.1
}
}
function random_angle() {
return Math.random() 2 Math.PI;
}
function random_rotation() {
return (0.5 - Math.random()) Math.PI 0.5;
}
function distance_between(obj1, obj2) {
return Math.sqrt(Math.pow(obj1.position.x - obj2.position.x,2) + Math.pow(obj1.position.y - obj2.position.y,2));
}
function compare(a,b) {
if (a.score === b.score) return 0;
if (a.score > b.score) {
return -1;
} else {
return 1;
}
}
// THE GAME===========================
function Game(canvas) {
this.version = 0.3;
this.canvas = canvas;
this.c = this.canvas.getContext("2d");
this.load_scores();
this.ship = new Ship(this, this.canvas);
this.waiting = true;
this.speed = 1;
this.muted = true;
}
Game.prototype.restart = function() {
this.waiting = false;
this.asteroids = [];
this.projectiles = [];
this.powerups = [];
this.objects = [];
this.level = 1;
this.reset_asteroids();
this.ship.reset();
this.score = 0;
}
Game.prototype.reset_asteroids = function() {
this.asteroids = [];
for (var i=0;i this.scores[this.scores.length-1]['score']) this.save_score();
this.waiting = true;
}
if (Math.random() > 0.999) {
var possible = "AFMWLR"; //Ammo, Friction, Manouverability, Weapon, Life, Rate
var bonus = possible.charAt(Math.floor(Math.random() * possible.length));
this.powerups.push(new PowerUp(this, bonus));
}
}
Game.prototype.save_score = function() {
this.load_scores();
var name = window.prompt('Please enter your name');
if (name !== null && name !== "") {
this.scores.push({
name: name,
score: this.score
})
this.scores = this.scores.sort(compare);
this.scores = this.scores.slice(0, 10);
}
this.save_scores();
}
Game.prototype.load_scores = function() {
try {
var local_data = JSON.parse(localStorage.getItem("asteroids")) || {};
var version = local_data.version || 0.1;
if(version this.canvas.width) {
this.position.x -= this.canvas.width;
} else if (this.position.x this.canvas.height) {
this.position.y -= this.canvas.height;
} else if (this.position.y 2 * Math.PI) {
this.angle -= 2 * Math.PI;
}
if(this.angle = 100) {
var new_vel = copy_coords(this.velocity);
new_vel.x *= 0.95;
new_vel.y *= 0.95;
var a1 = new Asteroid(this.game, new_mass, copy_coords(this.position), new_vel, this.rotation_speed);
var a2 = new Asteroid(this.game, new_mass, copy_coords(this.position), copy_coords(new_vel), this.rotation_speed);
a1.density = th
Code Snippets
Game.prototype.load_scores = function() {
try {
var local_data = JSON.parse(localStorage.getItem("asteroids")) || {};
var version = local_data.version || 0.1;
if(version < this.version) {
alert("New version installed, clearing scores.");
this.scores = [];
this.save_scores();
} else {
this.scores = local_data['scores'] || [];
}
} catch (e) {
// Local storage unsupported, or permission denied
this.scores = [];
}
}
Game.prototype.save_scores = function() {
try {
var local_data = JSON.parse(localStorage.getItem("asteroids")) || {};
local_data['scores'] = this.scores;
local_data['version'] = this.version;
localStorage.setItem("asteroids", JSON.stringify(local_data));
} catch (e) {
// Local storage unsupported, quota exceeded, or permission
}
}Context
StackExchange Code Review Q#84898, answer score: 10
Revisions (0)
No revisions yet.