kontra
Version:
Kontra HTML5 game development library
358 lines (306 loc) • 8.25 kB
JavaScript
let { canvas, contex} = kontra.init();
kontra.initKeys();
// set default asset paths
kontra.setImagePath('imgs/');
kontra.setAudioPath('sounds/');
// load assets
kontra.load(
// images
'ship.png', 'bg.png', 'bullet_enemy.png', 'bullet.png', 'enemy.png',
// audios
'explosion.mp3', 'game_over.mp3', 'kick_shock.mp3', 'laser.mp3'
).then(function() {
document.getElementById('loading').style.display = 'none';
let score = document.getElementById('score');
let playerScore = 0;
kontra.audioAssets.kick_shock.loop = true;
/*
* Audio pools
*/
// create an audio sprite for use in an audio pool, mimicking a normal image sprite
let audioSprite = {
isAlive: function() {
return !this.audio.ended;
},
init: function(properties) {
if (properties.play !== false) {
this.audio.play();
}
},
// set required properties
render: function() {},
update: function() {}
};
// common create functions for audio pool
/**
* Create a new sound effect from the passed object.
* @param {object} properties - Properties for the audio.
* @param {Audio} properties.audio - Audio object to clone.
*/
function create(properties) {
let sprite = Object.create(audioSprite);
sprite.audio = properties.audio.cloneNode();
sprite.audio.volume = 0.25;
// kill the audio by forcing it to jump to the end of the track
sprite.audio.currentTime = properties.audio.duration;
return sprite;
}
/**
* Toggle all the audios in the pool
*/
function toggleVolume() {
for (let i = 0, obj; obj = this.objects[i]; i++) {
obj.audio.muted = !obj.audio.muted;
}
}
// create audio pools
let laserPool = kontra.Pool({
create: function() {
return create({audio: kontra.audioAssets.laser})
},
fill: true,
maxSize: 5
});
laserPool.toggleVolume = toggleVolume;
for (let i = 0; i < 5; i++) {
laserPool.get({'play': false});
}
let explosionPool = kontra.Pool({
create: function() {
return create({audio: kontra.audioAssets.explosion})
},
maxSize: 10
});
explosionPool.toggleVolume = toggleVolume;
for (let i = 0; i < 5; i++) {
explosionPool.get({'play': false});
}
/*
* object pools
*/
let bullets = kontra.Pool({
create: kontra.Sprite,
maxSize: 20
});
let enemies = kontra.Pool({
create: kontra.Sprite,
maxSize: 18
});
let enemyBullets = kontra.Pool({
create: kontra.Sprite,
maxSize: 30
});
/*
* game objects
*/
let quadtree = kontra.Quadtree();
// panning background
let background = kontra.Sprite({
dy: 1,
image: kontra.imageAssets.bg,
update: function() {
this.advance();
if (this.y >= canvas.height) {
this.y = 0;
}
},
render: function() {
this.draw();
this.context.drawImage(this.image, this.x, this.y - canvas.height);
}
});
// player ship
let player = kontra.Sprite({
x: 280,
y: 270,
image: kontra.imageAssets.ship,
counter: 15,
update: function() {
this.counter++;
if (kontra.keyPressed('left')) {
this.x -= 3;
}
else if (kontra.keyPressed('right')) {
this.x += 3;
}
if (kontra.keyPressed('up')) {
this.y -= 3;
}
else if (kontra.keyPressed('down')) {
this.y += 3;
}
if (kontra.keyPressed('space') && this.counter >= 15) {
for (let i = 0; i < 2; i++) {
bullets.get({
x: this.x + 6 + (i * 27),
y: this.y,
dy: -3,
image: kontra.imageAssets.bullet,
ttl: 130,
type: 'friendly'
});
}
laserPool.get();
this.counter = 0;
}
}
});
// clamp player position to lower third of the screen
player.position.clamp(
0, canvas.height * 2 / 3,
canvas.width - player.width, canvas.height - player.height
);
/**
* Spawn a new wave of enemies.
*/
function spawnWave() {
let width = kontra.imageAssets.enemy.width;
let x = 100;
let y = -kontra.imageAssets.enemy.height;
let spacer = y * 1.5;
for (let i = 1; i <= 18; i++) {
enemies.get({
x: x,
y: y,
dy: 2,
image: kontra.imageAssets.enemy,
ttl: Infinity,
leftEdge: x - 90,
rightEdge: x + 90 + width,
bottomEdge: y + 140,
speed: 2,
type: 'enemy',
update: function() {
this.advance();
// change enemy velocity to move back and forth
if (this.x <= this.leftEdge) {
this.dx = this.speed;
}
else if (this.x >= this.rightEdge) {
this.dx = -this.speed;
}
else if (this.y >= this.bottomEdge) {
this.dy = 0;
this.dx = -this.speed;
this.y -= 5;
}
// randomly fire bullets
if (Math.floor(Math.random()*101)/100 < .01) {
enemyBullets.get({
x: this.x + this.width / 2,
y: this.y + this.height,
dy: 2.5,
image: kontra.imageAssets.bullet_enemy,
ttl: 150,
type: 'hostile'
});
}
},
});
x += width + 25;
if (i % 6 === 0) {
x = 100;
y += spacer;
}
}
}
/*
* Game loop
*/
let loop = kontra.GameLoop({
clearCanvas: false,
update: function(dt) {
background.update();
player.update();
bullets.update();
enemies.update();
enemyBullets.update();
laserPool.update();
explosionPool.update();
let liveBullets = bullets.getAliveObjects();
quadtree.clear();
quadtree.add(enemies.getAliveObjects(), enemyBullets.getAliveObjects());
// find collisions between the player ship and enemy bullets
objects = quadtree.get(player);
for (let i = 0, obj; obj = objects[i]; i++) {
if (obj.type === 'hostile' && obj.collidesWith(player)) {
gameOver();
}
}
// find collisions between the player bullets and enemy ships
for (let i = 0, bullet; bullet = liveBullets[i]; i++) {
objects = quadtree.get(bullet);
for (let j = 0, obj; obj = objects[j]; j++) {
if (obj.type === 'enemy' && obj.collidesWith(bullet)) {
bullet.ttl = 0;
obj.ttl = 0;
explosionPool.get();
playerScore += 10;
score.innerHTML = playerScore;
}
}
}
// spawn a new wave of enemies
if (enemies.getAliveObjects().length === 0) {
spawnWave();
}
},
render: function() {
background.render();
player.render();
bullets.render();
enemies.render();
enemyBullets.render();
}
});
kontra.bindKeys('m', function() {
toggleMusic();
});
kontra.bindKeys('p', function() {
if (loop.isStopped) {
loop.start();
kontra.audioAssets.kick_shock.play();
}
else {
loop.stop();
kontra.audioAssets.kick_shock.pause();
}
});
/**
* Toggle the music on and off.
*/
function toggleMusic() {
kontra.audioAssets.kick_shock.muted = !kontra.audioAssets.kick_shock.muted;
kontra.audioAssets.game_over.muted = !kontra.audioAssets.game_over.muted
laserPool.toggleVolume();
explosionPool.toggleVolume();
}
/**
* Show the game over screen.
*/
function gameOver() {
loop.stop();
document.getElementById('game-over').style.display = 'block';
kontra.audioAssets.kick_shock.pause();
kontra.audioAssets.game_over.currentTime = 0;
kontra.audioAssets.game_over.play();
}
/**
* Start the game.
*/
window.startGame = function() {
document.getElementById('game-over').style.display = 'none';
enemies.clear();
enemyBullets.clear();
bullets.clear();
playerScore = 0;
score.innerHTML = 0;
loop.start();
kontra.audioAssets.game_over.pause();
kontra.audioAssets.kick_shock.currentTime = 0;
kontra.audioAssets.kick_shock.play();
player.position.x = 280;
player.position.y = 270;
};
startGame();
});