Playable Canvas Asteroids
How do I make an playable canvas asteroids?
A mini "Asteroids" game coded from scratch, using some math and black magic.This is a codepen adapted version of the game, check/fork from this repo: https://github.com/everblind/js-playground at github!. What is a playable canvas asteroids? How do you make a playable canvas asteroids? This script and codes were developed by Jeff Ibacache on 23 June 2022, Thursday.
Playable Canvas Asteroids - Script Codes HTML Codes
<!DOCTYPE html>
<html >
<head> <meta charset="UTF-8"> <title>Playable Canvas Asteroids</title> <link rel="stylesheet" href="css/style.css">
</head>
<body> <!--
@2014 Jeff Ibacache | http://dev.everblind.me/
License: https://github.com/everblind/js-playground/blob/master/license.md
Source: https://github.com/everblind/js-playground/tree/master/canvas-asteroids
-->
<div id="info"> <h1>Canvas Asteroids:</h1> <p>Use [A][S][W][D] or [←][↑][↓][→] to MOVE</p> <p>Use [SPACE] or [K] to SHOOT</p>
</div>
<canvas id="canvas"></canvas> <script src="js/index.js"></script>
</body>
</html>
Playable Canvas Asteroids - Script Codes CSS Codes
* { margin: 0;
}
canvas { position: absolute; width: 100%; height: 100%; background: #262626;
}
#info { color: #FFF; font-family: "Lucida Sans Typewriter", "Lucida Console", Monaco, "Bitstream Vera Sans Mono", monospace; margin: 10px 10px 0; padding: 8px; background: #161616; position: absolute; z-index: 1;
}
#info h1 { font-size: 18px;
}
#info p { font-size: 11px;
}
Playable Canvas Asteroids - Script Codes JS Codes
//pool.js ...........................................................
var Pool = (function()
{ //exposed methods: var create = function(type, size) { var obj = Object.create(def); obj.init(type, size); return obj; }; //Ship definition: var def = { _type: null, _size: null, _pointer: null, _elements:null, init: function(type, size) { this._type = type; this._size = size; this._pointer = size; this._elements = []; var i = 0; var length = this._size; for(i; i < length; ++i) { this._elements[i] = this._type.create(); } }, getElement: function() { if(this._pointer > 0) return this._elements[--this._pointer]; return null; }, disposeElement: function(obj) { this._elements[this._pointer++] = obj; } }; return {create:create};
}());
//vec2d.js ...........................................................
var Vec2D = (function()
{ //exposed methods: var create = function(x, y) { var obj = Object.create(def); obj.setXY(x, y); return obj; }; //Vec2D definition: var def = { _x: 1, _y: 0, getX: function() { return this._x; }, setX: function(value) { this._x = value; }, getY: function() { return this._y; }, setY: function(value) { this._y = value; }, setXY: function(x, y) { this._x = x; this._y = y; }, getLength: function() { return Math.sqrt(this._x * this._x + this._y * this._y); }, setLength: function(length) { var angle = this.getAngle(); this._x = Math.cos(angle) * length; this._y = Math.sin(angle) * length; }, getAngle: function() { return Math.atan2(this._y, this._x); }, setAngle: function(angle) { var length = this.getLength(); this._x = Math.cos(angle) * length; this._y = Math.sin(angle) * length; }, add: function(vector) { this._x += vector.getX(); this._y += vector.getY(); }, sub: function(vector) { this._x -= vector.getX(); this._y -= vector.getY(); }, mul: function(value) { this._x *= value; this._y *= value; }, div: function(value) { this._x /= value; this._y /= value; } }; return {create:create};
}());
//particle.js ...........................................................
var Particle = (function()
{ //exposed methods: var create = function() { var obj = Object.create(def); obj.radius = 2; obj.color = '#FFF'; obj.lifeSpan = 0; obj.fric = 0.98; obj.pos = Vec2D.create(0, 0); obj.vel = Vec2D.create(0, 0); obj.blacklisted = false; return obj; }; //Ship definition: var def = { radius: null, color: null, lifeSpan: null, fric: null, pos: null, vel: null, blacklisted: null, update: function() { this.pos.add(this.vel); this.vel.mul(this.fric); this.radius -= 0.1; if(this.radius < 0.1) this.radius = 0.1; if(this.lifeSpan-- < 0) { this.blacklisted = true; } }, reset: function() { this.blacklisted = false; } }; return {create:create};
}());
//bullet.js ...........................................................
var Bullet = (function()
{ //exposed methods: var create = function() { var obj = Object.create(def); obj.radius = 4; obj.color = '#FFF'; obj.pos = Vec2D.create(0, 0); obj.vel = Vec2D.create(0, 0); obj.blacklisted = false; return obj; }; //Bullet definition: var def = { radius: null, color: null, pos: null, vel: null, blacklisted: null, update: function() { this.pos.add(this.vel); }, reset: function() { this.blacklisted = false; } }; return {create:create};
}());
//asteroid.js ...........................................................
var Asteroid = (function()
{ //exposed methods: var create = function() { var obj = Object.create(def); obj.radius = 40; obj.color = '#FF5900'; obj.pos = Vec2D.create(0, 0); obj.vel = Vec2D.create(0, 0); obj.blacklisted = false; obj.type = 'b'; obj.sides = (Math.random() * 2 + 7) >> 0; obj.angle = 0; obj.angleVel = (1 - Math.random() * 2) * 0.01; return obj; }; //Ship definition: var def = { radius: null, color: null, pos: null, vel: null, blacklisted: null, type: null, sides: null, angle: null, angleVel: null, update: function() { this.pos.add(this.vel); this.angle += this.angleVel; }, reset: function() { this.blacklisted = false; } }; return {create:create};
}());
//ship.js ...........................................................
var Ship = (function()
{ //exposed methods: var create = function(x, y, ref) { var obj = Object.create(def); obj.ref = ref; obj.angle = 0; obj.pos = Vec2D.create(x, y); obj.vel = Vec2D.create(0, 0); obj.thrust = Vec2D.create(0, 0); obj.idle = false; obj.radius = 8; obj.idleDelay = 0; return obj; }; //Ship definition: var def = { angle: null, pos: null, vel: null, thrust: null, ref: null, bulletDelay: null, idle: null, radius: null, update: function() { this.vel.add(this.thrust); this.pos.add(this.vel); if(this.vel.getLength() > 5) this.vel.setLength(5); ++this.bulletDelay; if(this.idle) { if(++this.idleDelay > 120) { this.idleDelay = 0; this.idle = false; this.ref.resetGame(); } } }, shoot: function() { if(this.bulletDelay > 8) { this.ref.generateShot(); this.bulletDelay = 0; } } }; return {create:create};
}());
//canvas-asteroids.js ...........................................................
//common vars
var canvas;
var context;
var screenWidth;
var screenHeight;
var doublePI = Math.PI * 2;
//game vars
var ship;
var particlePool;
var particles;
var bulletPool;
var bullets;
var asteroidPool;
var asteroids;
var hScan;
var asteroidVelFactor = 0;
//keyboard vars
var keyLeft = false;
var keyUp = false;
var keyRight = false;
var keyDown = false;
var keySpace = false;
window.getAnimationFrame =
window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(callback)
{ window.setTimeout(callback, 16.6);
};
window.onload = function()
{ canvas = document.getElementById('canvas'); context = canvas.getContext('2d'); window.onresize(); keyboardInit(); particleInit(); bulletInit(); asteroidInit(); shipInit(); loop();
};
window.onresize = function()
{ if(!canvas) return; screenWidth = canvas.clientWidth; screenHeight = canvas.clientHeight; canvas.width = screenWidth; canvas.height = screenHeight; hScan = (screenHeight / 4) >> 0;
};
function keyboardInit()
{ window.onkeydown = function(e) { switch(e.keyCode) { //key A or LEFT case 65: case 37: keyLeft = true; break; //key W or UP case 87: case 38: keyUp = true; break; //key D or RIGHT case 68: case 39: keyRight = true; break; //key S or DOWN case 83: case 40: keyDown = true; break; //key Space case 32: case 75: keySpace = true; break; } e.preventDefault(); }; window.onkeyup = function(e) { switch(e.keyCode) { //key A or LEFT case 65: case 37: keyLeft = false; break; //key W or UP case 87: case 38: keyUp = false; break; //key D or RIGHT case 68: case 39: keyRight = false; break; //key S or DOWN case 83: case 40: keyDown = false; break; //key Space case 75: case 32: keySpace = false; break; } e.preventDefault(); };
}
function particleInit()
{ particlePool = Pool.create(Particle, 100); particles = [];
}
function bulletInit()
{ bulletPool = Pool.create(Bullet, 40); bullets = [];
}
function asteroidInit()
{ asteroidPool = Pool.create(Asteroid, 30); asteroids = [];
}
function shipInit()
{ ship = Ship.create(screenWidth >> 1, screenHeight >> 1, this);
}
function loop()
{ updateShip(); updateParticles(); updateBullets(); updateAsteroids(); checkCollisions(); render(); getAnimationFrame(loop);
}
function updateShip()
{ ship.update(); if(ship.idle) return; if(keySpace) ship.shoot(); if(keyLeft) ship.angle -= 0.1; if(keyRight) ship.angle += 0.1; if(keyUp) { ship.thrust.setLength(0.1); ship.thrust.setAngle(ship.angle); generateThrustParticle(); } else { ship.vel.mul(0.94); ship.thrust.setLength(0); } if(ship.pos.getX() > screenWidth) ship.pos.setX(0); else if(ship.pos.getX() < 0) ship.pos.setX(screenWidth); if(ship.pos.getY() > screenHeight) ship.pos.setY(0); else if(ship.pos.getY() < 0) ship.pos.setY(screenHeight);
}
function generateThrustParticle()
{ var p = particlePool.getElement(); //if the particle pool doesn't have more elements, will return 'null'. if(!p) return; p.radius = Math.random() * 3 + 2; p.color = '#FFF'; p.lifeSpan = 80; p.pos.setXY(ship.pos.getX() + Math.cos(ship.angle) * -14, ship.pos.getY() + Math.sin(ship.angle) * -14); p.vel.setLength(8 / p.radius); p.vel.setAngle(ship.angle + (1 - Math.random() * 2) * (Math.PI / 18)); p.vel.mul(-1); //particles[particles.length] = p; same as: particles.push(p); particles[particles.length] = p;
}
function updateParticles()
{ var i = particles.length - 1; for(i; i > -1; --i) { var p = particles[i]; if(p.blacklisted) { p.reset(); particles.splice(particles.indexOf(p), 1); particlePool.disposeElement(p); continue; } p.update(); }
}
function updateBullets()
{ var i = bullets.length - 1; for(i; i > -1; --i) { var b = bullets[i]; if(b.blacklisted) { b.reset(); bullets.splice(bullets.indexOf(b), 1); bulletPool.disposeElement(b); continue; } b.update(); if(b.pos.getX() > screenWidth) b.blacklisted = true; else if(b.pos.getX() < 0) b.blacklisted = true; if(b.pos.getY() > screenHeight) b.blacklisted = true; else if(b.pos.getY() < 0) b.blacklisted = true; }
}
function updateAsteroids()
{ var i = asteroids.length - 1; for(i; i > -1; --i) { var a = asteroids[i]; if(a.blacklisted) { a.reset(); asteroids.splice(asteroids.indexOf(a), 1); asteroidPool.disposeElement(a); continue; } a.update(); if(a.pos.getX() > screenWidth + a.radius) a.pos.setX(-a.radius); else if(a.pos.getX() < -a.radius) a.pos.setX(screenWidth + a.radius); if(a.pos.getY() > screenHeight + a.radius) a.pos.setY(-a.radius); else if(a.pos.getY() < -a.radius) a.pos.setY(screenHeight + a.radius); } if(asteroids.length < 5) { var factor = (Math.random() * 2) >> 0; generateAsteroid(screenWidth * factor, screenHeight * factor, 60 , 'b'); }
}
function generateAsteroid(x, y, radius, type)
{ var a = asteroidPool.getElement(); //if the bullet pool doesn't have more elements, will return 'null'. if(!a) return; a.radius = radius; a.type = type; a.pos.setXY(x, y); a.vel.setLength(1 + asteroidVelFactor); a.vel.setAngle(Math.random() * (Math.PI * 2)); //bullets[bullets.length] = b; same as: bullets.push(b); asteroids[asteroids.length] = a; asteroidVelFactor += 0.025;
}
function checkCollisions()
{ checkBulletAsteroidCollisions(); checkShipAsteroidCollisions();
}
function checkBulletAsteroidCollisions()
{ var i = bullets.length - 1; var j; for(i; i > -1; --i) { j = asteroids.length - 1; for(j; j > -1; --j) { var b = bullets[i]; var a = asteroids[j]; if(checkDistanceCollision(b, a)) { b.blacklisted = true; destroyAsteroid(a); } } }
}
function checkShipAsteroidCollisions()
{ var i = asteroids.length - 1; for(i; i > -1; --i) { var a = asteroids[i]; var s = ship; if(checkDistanceCollision(a, s)) { if(s.idle) return; s.idle = true; generateShipExplosion(); destroyAsteroid(a); } }
}
function generateShipExplosion()
{ var i = 18; for(i; i > -1; --i) { var p = particlePool.getElement(); //if the particle pool doesn't have more elements, will return 'null'. if(!p) return; p.radius = Math.random() * 6 + 2; p.lifeSpan = 80; p.color = '#FFF'; p.vel.setLength(20 / p.radius); p.vel.setAngle(ship.angle + (1 - Math.random() * 2) * doublePI); p.pos.setXY(ship.pos.getX() + Math.cos(p.vel.getAngle()) * (ship.radius * 0.8), ship.pos.getY() + Math.sin(p.vel.getAngle()) * (ship.radius * 0.8)); //particles[particles.length] = p; same as: particles.push(p); particles[particles.length] = p; }
}
function checkDistanceCollision(obj1, obj2)
{ var vx = obj1.pos.getX() - obj2.pos.getX(); var vy = obj1.pos.getY() - obj2.pos.getY(); var vec = Vec2D.create(vx, vy); if(vec.getLength() < obj1.radius + obj2.radius) { return true; } return false;
}
function destroyAsteroid(asteroid)
{ asteroid.blacklisted = true; generateAsteroidExplosion(asteroid); resolveAsteroidType(asteroid);
}
function generateAsteroidExplosion(asteroid)
{ var i = 18; for(i; i > -1; --i) { var p = particlePool.getElement(); //if the particle pool doesn't have more elements, will return 'null'. if(!p) return; p.radius = Math.random() * (asteroid.radius >> 2) + 2; p.lifeSpan = 80; p.color = '#FF5900'; p.vel.setLength(20 / p.radius); p.vel.setAngle(ship.angle + (1 - Math.random() * 2) * doublePI); p.pos.setXY(asteroid.pos.getX() + Math.cos(p.vel.getAngle()) * (asteroid.radius * 0.8), asteroid.pos.getY() + Math.sin(p.vel.getAngle()) * (asteroid.radius * 0.8)); //particles[particles.length] = p; same as: particles.push(p); particles[particles.length] = p; }
}
function resolveAsteroidType(asteroid)
{ switch(asteroid.type) { case 'b': generateAsteroid(asteroid.pos.getX(), asteroid.pos.getY(), 40, 'm'); generateAsteroid(asteroid.pos.getX(), asteroid.pos.getY(), 40, 'm'); break; case 'm': generateAsteroid(asteroid.pos.getX(), asteroid.pos.getY(), 20, 's'); generateAsteroid(asteroid.pos.getX(), asteroid.pos.getY(), 20, 's'); break; }
}
function render()
{ context.fillStyle = '#262626'; context.globalAlpha = 0.4; context.fillRect(0, 0, screenWidth, screenHeight); context.globalAlpha = 1; renderShip(); renderParticles(); renderBullets(); renderAsteroids(); renderScanlines();
}
function renderShip()
{ if(ship.idle) return; context.save(); context.translate(ship.pos.getX() >> 0, ship.pos.getY() >> 0); context.rotate(ship.angle); context.strokeStyle = '#FFF'; context.lineWidth = (Math.random() > 0.9) ? 2 : 1; context.beginPath(); context.moveTo(10, 0); context.lineTo(-10, -10); context.lineTo(-10, 10); context.lineTo(10, 0); context.stroke(); context.closePath(); context.restore();
}
function renderParticles()
{ //inverse for loop = more performance. var i = particles.length - 1; for(i; i > -1; --i) { var p = particles[i]; context.beginPath(); context.strokeStyle = p.color; context.arc(p.pos.getX() >> 0, p.pos.getY() >> 0, p.radius, 0, doublePI); if(Math.random() > 0.4) context.stroke(); context.closePath(); }
}
function renderBullets()
{ //inverse for loop = more performance. var i = bullets.length - 1; for(i; i > -1; --i) { var b = bullets[i]; context.beginPath(); context.strokeStyle = b.color; context.arc(b.pos.getX() >> 0, b.pos.getY() >> 0, b.radius, 0, doublePI); if(Math.random() > 0.2) context.stroke(); context.closePath(); }
}
function renderAsteroids()
{ //inverse for loop = more performance. var i = asteroids.length - 1; for(i; i > -1; --i) { var a = asteroids[i]; context.beginPath(); context.lineWidth = (Math.random() > 0.2) ? 4 : 3; context.strokeStyle = a.color; var j = a.sides; context.moveTo((a.pos.getX() + Math.cos(doublePI * (j / a.sides) + a.angle) * a.radius) >> 0, (a.pos.getY() + Math.sin(doublePI * (j / a.sides) + a.angle) * a.radius) >> 0); for(j; j > -1; --j) { context.lineTo((a.pos.getX() + Math.cos(doublePI * (j / a.sides) + a.angle) * a.radius) >> 0, (a.pos.getY() + Math.sin(doublePI * (j / a.sides) + a.angle) * a.radius) >> 0); } if(Math.random() > 0.2) context.stroke(); context.closePath(); }
}
function renderScanlines()
{ //inverse for loop = more performance. var i = hScan; context.globalAlpha = 0.05; context.lineWidth = 1; for(i; i > -1; --i) { context.beginPath(); context.moveTo(0, i * 4); context.lineTo(screenWidth, i * 4); context.strokeStyle = (Math.random() > 0.0001) ? '#FFF' : '#222'; context.stroke(); } context.globalAlpha = 1;
}
function generateShot()
{ var b = bulletPool.getElement(); //if the bullet pool doesn't have more elements, will return 'null'. if(!b) return; b.radius = 1; b.pos.setXY(ship.pos.getX() + Math.cos(ship.angle) * 14, ship.pos.getY() + Math.sin(ship.angle) * 14); b.vel.setLength(10); b.vel.setAngle(ship.angle); //bullets[bullets.length] = b; same as: bullets.push(b); bullets[bullets.length] = b;
}
function resetGame()
{ asteroidVelFactor = 0; ship.pos.setXY(screenWidth >> 1, screenHeight >> 1); ship.vel.setXY(0, 0); resetAsteroids();
}
function resetAsteroids()
{ var i = asteroids.length - 1; for(i; i > -1; --i) { var a = asteroids[i]; a.blacklisted = true; }
}
Developer | Jeff Ibacache |
Username | jeffibacache |
Uploaded | June 23, 2022 |
Rating | 4.5 |
Size | 5,711 Kb |
Views | 24,288 |
Find the perfect freelance services for your business! Fiverr's mission is to change how the world works together. Fiverr connects businesses with freelancers offering digital services in 500+ categories. Find Developer!
Name | Size |
Astral Spiral Playground | 2,764 Kb |
Electric worm | 2,377 Kb |
Wave Physics Playground | 3,267 Kb |
Fake 3D Blob | 2,823 Kb |
Trippy Pac-Man | 2,552 Kb |
Stardust | 2,629 Kb |
Majesty of waves | 3,174 Kb |
A Pen by Jeff Ibacache | 2,359 Kb |
Wave | 1,997 Kb |
Majesty of waves II | 3,342 Kb |
Jasper is the AI Content Generator that helps you and your team break through creative blocks to create amazing, original content 10X faster. Discover all the ways the Jasper AI Content Platform can help streamline your creative workflows. Start For Free!
Name | Username | Size |
Popup Modal | Aldlevine | 3,696 Kb |
Bootstrap 3 Price Table | Honglio | 2,655 Kb |
Blog Concept - Single Post | Marionebl | 9,603 Kb |
Scroll to top button | DominicFrancois | 3,743 Kb |
Multicolumns 2 | Raphaelgoetter | 1,857 Kb |
Preloader | Rikki_Sixx | 2,815 Kb |
A Pen by Brendan Skousen | Bskousen | 2,954 Kb |
Clock | Martingrand | 3,520 Kb |
SVG Circle Progress | JMChristensen | 3,368 Kb |
React Vote Component | Souporserious | 5,465 Kb |
Surf anonymously, prevent hackers from acquiring your IP address, send anonymous email, and encrypt your Internet connection. High speed, ultra secure, and easy to use. Instant setup. Hide Your IP Now!