Pen Settings

HTML

CSS

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URL's added here will be added as <link>s in order, and before the CSS in the editor. If you link to another Pen, it will include the CSS from that Pen. If the preprocessor matches, it will attempt to combine them before processing.

+ add another resource

JavaScript

Babel includes JSX processing.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Packages

Add Packages

Search for and use JavaScript packages from npm here. By selecting a package, an import statement will be added to the top of the JavaScript editor for this package.

Behavior

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.

HTML

              
                <div id="game_over" class="state-ui-container">
  <div class="console">
   <div class="ui-text ui-text--heading">GAME OVER!</div>
   <div id="final_score" class="ui-text"></div> 
   <div class="ui-text">PRESS [ENTER] TO PLAY AGAIN!</div>
   <div class="ui-text">PRESS [R] TO GO TO TITLE SCREEN</div>
  </div>
</div>
<div id="title" class="state-ui-container">
  <div class="console">
    <div class="ui-text ui-text--heading">SPACE FORCE: X-ALPHA 2 TURBO</div>
    <div class="ui-text">INSTRUCTIONS:</div>
    <div class="ui-text">PRESS [ARROW KEYS] TO MOVE!</div>
    <div class="ui-text">PRESS [SPACE] TO SHOOT!</div>
    <div class="ui-text">PRESS [ENTER] TO PLAY!</div>
    <div class="ui-text">PRESS [R] TO COME BACK TO THIS SCREEN</div>
  </div>
</div>
<div id="game" class="state-ui-container">
  <div class="banner">
    <div id="score" class="ui-text">SCORE: 0</div>
    <div id="lives" class="ui-text">LIVES: 3</div>
  </div>
</div>


              
            
!

CSS

              
                * {
  margin:  0;
  padding: 0;
}

body {
  font-size: 24px;
}

#viewport {
  background: #020408;
  display: block;
  width: 100%;
  height: 100%;
  image-rendering: crisp-edges;
  image-rendering: pixelated;
}

.state-ui-container {
  display: flex;
  flex-direction: row;
  position: absolute;
  justify-content: left;
  align-items: left;
}

.ui-text {
  user-select: none;
  font-family: arial;
  font-size: 1em;
  font-weight: bold;
  font-variant-numeric: tabular-nums;
  margin: 10px;
  color: #e4e4e4;
  text-shadow: 0 2px 2px rgba(125,0,0,.51),
    0 0 10px rgba(255,0,255,1),
    0 0 10px rgba(255,0,255,1); 
  text-align: left;
}

#score {
  left: 0;
}

#lives {
  right: 0;
}

.ui-text--heading {
  font-size: 4em;
}



.state-ui-container {
  width: 100%;
  display: none;
  height: 100%;
  justify-content: center;
}

.console {
  align-self: center;
}
.banner {
  align-self: flex-start;
}
              
            
!

JS

              
                var TOP_TURN = false;

var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
context.globalAlpha = .1;
var fps = 1000 / 60;
var then = Date.now();
var startTime = then;

/**
 * Piskel was used to create my sprites.
 * It is a free online editor for animated sprites & pixel art
 * you should check it out (it's incredible)
 * https://www.piskelapp.com/ 
 */

var speedUpSprite = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABdElEQVRYR82W0RXCIAxF0zXcpG7jnyP55xwuYDdxjXpSm/oIoQlHetA/BJLLy4N0IPxN5zkZlwbjc7CmbhOF9l9H2vZ/A3HyQuAsmbGWk2PgvYPg2g9ATXKJDHtqkst22TOYyaUUoogeA8SNnmSdXMohc3rMIfi/FACViEBM5xkBvKTWfA7AaKWTG1BagQgEqpKXwDu5UskqgQeBPmjqATRjBCL3ADp8rxQFE7YB6FqCSHKlTFsTdr+GP3oAn9+4Cbs/xcbb7nbE5s1IS+8RHNKOuSleYv18vH/7efI5cQrufxnfA5y8FFgLYq2dTjSPEHhPRFy7fA/UJN+qBcA1ybf9K/CAyaUEokRo/CDCkzPM8latalhjnEsARI0lwFpnCyKZUwBLDAcCVcgAIhBYBgIALEUEgtdkJYjKv4EqAE9+Pf9fHgiZTvujpQlrbsQhJUjMtXMTDjNh5AZkKrW8hl080P0ptt52rxs3b0bafB5Ay3b8BvT6cqvw5EPXAAAAAElFTkSuQmCC';

var powerUpSprite = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAABwCAYAAAAXB/A7AAACGklEQVRYR+1ZsXHDMAwk1/AY6egVMkHKdBkpXcpM4BWiLmNkDeVAkcoLhEjAlKy7nFxZJv75AB+QbXmHr+E6Lq7XLsKXz0vzG0dgWKgSQexEYAFn5oTxC3AtBUndcB2XBKoCQJBIYFGxj4JcVMURUv3+XQ0sJ5BqtUENuq3MvK0yY9FMSKJhENvZOfc+ONU8eAuunAcExoWaEIyNTBZwJs4YvwaW0uEKKWaVQFPLJkFLRZOgpUJFUFOhImgdZ7WIx9agtXs2YL8Puq3Mvd06e77h3925t53jZHvVzYPwIcwDAuNCLRWMjSlYwPP0Sxt6DRhTQ5X0uYpgLR0TgaTCRCCpMBNwFWYCrsJEcGwN9vVBt5W5tzXzoGgmJNEQiO0cU7ko58GPNA8ubgywUJ0HEDvNAwN4TjdhvBac00OV9JmaQOxGKwFX8XgFeNRUi8crOLYG+/mg28rc25p2Rvsvvh90tXNMxX2qfi8E91L+XiAwLlTnAcRO88AAnmuWMN4C5rF0bSIo7o33EKCKYxRg0Y9RcGwN9vFBt5W5t1XzgDcTkmgIxHaOwKdBNQ/cdxD+TyQwLFSVQOzEZAFn5oTxd4GB5CQYxrMGm9Sg28rM25p2Rvsvvh/0tXOcbTfdPAjP0vOF2+hgoZoKbZRi0/MFA3iefxPGR9nanYv7+43+Wz8Jzhps4YPcRFY3JgPCwzqDI4tmwgbRTBRQ+wsmv4hBGUclzwAAAABJRU5ErkJggg=='

var bulletSprite = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAICAYAAADwdn+XAAAAQ0lEQVQoU2NkSPr/n4FUMI+REaYFzgALfPn//z83AwPjVwYGYmlUA6DG/v9PvKswDCBFM8g+6hsAMpUUV1AhECmMRgChtzPdurGgmgAAAABJRU5ErkJggg==';

var burnSprite = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAABACAYAAAB7jnWuAAAAw0lEQVRoQ+2ZwQ3AIAwDYY7uP1rnoAIJBFXfPkt1F8A4l5CktcBfhc8vPgLu0tpVqlxQ7QfPMCAC+uFTBCJgd6CLUYsYMSdFhIHDAQsG5BCSAHbHvbLgnwy8C1EgVHOQQuTlgDoDRiW0asmI3jAMeDAw3wMkCzIbxoE4EAfiwO4AsSlZSylsSYXviGYICPs/p2P1k3wwoJ6KPPYDFgzsIjAGqL4wEPo5gEFoUYrVt1+VkErBQwD1LMv/kr170AiIA3HgARYMhEG2cflOAAAAAElFTkSuQmCC';

var popCornSprite = 'data:image/png;base64,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';

var shipSprite = 'data:image/png;base64,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';

var explosionSprite = 'data:image/png;base64,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';

canvas.id = 'viewport';
canvas.width = window.innerWidth*.5;
canvas.height = window.innerHeight*.5;
context.imageSmoothingEnabled = false;
document.getElementsByTagName('body')[0].appendChild(canvas);
WAVE =  1800;
if (TOP_TURN) {
  canvas.style.transform = 'scaleX(-1)';
  UP = 39;
  DOWN = 37;
  LEFT = 40;
  RIGHT = 38;
  SHOOT = 32;
  RESET = 82;
  START = 13;
} else {
  UP = 38;
  DOWN = 40;
  LEFT = 37;
  RIGHT = 39;
  SHOOT = 32;
  RESET = 82;
  START = 13;
}

function State(name, updater, renderer)
{
  this.name = name;
  this.updater = updater;
  this.renderer = renderer;
}

function Intersection() {
  
}

Intersection.rectanglesIntersect = function(rect1, rect2) {
  return (
    rect1.x < rect2.x + rect2.width &&
    rect1.x + rect1.width > rect2.x &&
    rect1.y < rect2.y + rect2.height &&
    rect1.height + rect1.y > rect2.y
  );
}

function Star(x, y) {
  var r = Math.max(50,Math.floor(Math.random()*256));
  var g = Math.max(125, Math.floor(Math.random()*256));
  var b = Math.max(240, Math.floor(Math.random()*256));
  this.x = Math.floor(x);
  this.y = Math.floor(y);
  var size = Math.floor(Math.random() * Star.MAX_SIZE);
  this.width = size;
  this.height = size;
  this.variance = Math.max(Math.random(), .2);
  this.color = 'rgb('+r+', '+g+', '+b+')';
}

Star.SPEED = 5;
Star.MAX_SIZE = 2;
Star.prototype.update = function() {
  if (game.overdrive) {
    var travel = Star.SPEED*this.variance*6;
    this.width = 50;
  } else {
    var travel = Star.SPEED*this.variance;
    this.width = this.height;
  }
  this.x -= travel;
  if(this.x + this.width < 0) {
    this.x = canvas.width;
  }
}
Game.prototype.updateScore = function() {
  document.getElementById('score').innerHTML = 'SCORE: ' + this.score;
}

Game.prototype.updateLives = function() {
  document.getElementById('lives').innerHTML = 'LIVES: ' + this.lives;
}
Star.prototype.render = function(context) {
  if (game.overdrive) {
    context.globalAlpha = .6;
  }
  context.fillStyle = (game.overdrive) ? 'rgba(125, 135, 255, 1)' : this.color;
  context.fillRect(this.x, this.y, this.width, this.height);
  context.globalAlpha = 1;
}

function Explosion(x, y) {
  this.currentFrame = 0;
  this.frames = 12;
  this.buffer = 4;
  this.currentBuffer = 0;
  this.alive = false;
  this.img = document.createElement('img');
  this.img.src = explosionSprite;
}

Explosion.prototype.reset = function() {
  this.currentFrame = 0;
  this.frames = 12;
  this.buffer = 4;
  this.currentBuffer = 0;
  this.alive = false;
};

Explosion.prototype.update = function() {
  if (!this.alive) {
     return;
  }
  this.x -= Popcorn.SPEED;
  if (this.currentBuffer === this.buffer) {
    this.currentBuffer = 0;
    this.currentFrame++;
    if (this.currentFrame === this.frames) {
      this.reset();
    }
  }
  this.currentBuffer++;
}

Explosion.prototype.render = function(context) {
  if (!this.alive) {
     return;
  }
  context.drawImage(
    this.img,
    0, this.currentFrame * 32, 32, 32,
    this.x-16, this.y-16, 64, 64
  );
}

function TitleState(name)
{
  State.call(this, name, this.update, this.render);
}

TitleState.prototype = new State();

TitleState.prototype.init = function() {
  game.overdrive = true;
  context.setTransform(1, 0, 0, 1, 0, 0);
  game.showUI('title');
  this.entities = [];
  this.starField = [];
  for (var i = 0; i < 1024; i++) {
    this.starField.push(
      new Star(
        Math.random()*canvas.width,
        Math.random()*canvas.height
      )
    );
  }
}

TitleState.prototype.update = function() {
  if (inputManager.start) {
    game.setState(game.states.game);
    game.currentState.init();
  }
  this.starField.forEach(function(entity) {
    entity.update();
  })
}

TitleState.prototype.render = function(context) {
  context.clearRect(0, 0, canvas.width, canvas.height);
  this.starField.forEach(function(entity) {
    entity.render(context);
  })
}

function GameOverState(name)
{
  State.call(this, name, this.update, this.render);
}

GameOverState.prototype = new State();

GameOverState.prototype.init = function() {
  context.setTransform(1, 0, 0, 1, 0, 0);
  this.timer = 0;
  console.log('Game Over');
  game.showUI('game_over');
  document.querySelector('#final_score').innerHTML = 'FINAL SCORE: ' + game.score;
}

GameOverState.prototype.update = function() {
  if (inputManager.start) {
    game.setState(game.states.game);
    game.currentState.init();
    game.reset();
  }
  this.timer++;
}

GameOverState.prototype.render = function(context) { 
  for (var i = 0; i < canvas.width; i++) {
    context.drawImage(
      canvas,
      i, 0, 1, canvas.height,
      i, Math.sin((i*20*Math.PI/180))*2, 1, canvas.height
    );
  }
  for (var i = 0; i < canvas.height; i++) {
    context.drawImage(
      canvas,
      i, 0, canvas.width, 1,
      i, Math.sin((i*12*Math.PI/180))*2, canvas.width, 1
    );
  }
  if (this.timer > 60) {
    context.globalAlpha = .3;
  }
}


function GameState(name)
{
  State.call(this, name, this.update, this.render);
}

GameState.prototype = new State();

GameState.prototype.init = function() {
  game.overdrive = true;
  this.overdriveTimer = 90;
  context.setTransform(1, 0, 0, 1, 0, 0);
  game.showUI('game');
  this.timer = 0;
  this.tier1 = 1 * WAVE;
  this.tier2 = 2 * WAVE;
  this.tier3 = 3 * WAVE;
  this.tier4 = 4 * WAVE;
  this.enemyTimerEasy = 120;
  this.enemyTimerMedium = 60;
  this.enemyTimerHard = 30;
  this.enemyTimerNightmare = 5;
  this.currentEnemyTimer = this.enemyTimerEasy;
  this.powerUpTimer = 1600;
  this.starField = [];
  for (var i = 0; i < 1024; i++) {
    this.starField.push(
      new Star(
        Math.random()*canvas.width,
        Math.random()*canvas.height
      )
    );
  }
  
  this.player = new Player(20, Math.floor(canvas.height * .5), 100, null);
  for (var i = 0; i < 256; i++) {
    this.player.bulletPool.push(new Bullet(this.player.x + 32, this.player.y + 16, 1, 15));
  }
  this.enemyList = [];
  for (var i = 0; i < 32; i++) {
    this.enemyList.push(new Popcorn(canvas.width, Math.random() * (canvas.height - 32)));
  }
  
  this.explosions = [];
  for (var i = 0; i < 32; i++) {
    this.explosions.push(new Explosion(0, 0));
  }
  
  this.powerUps = [];
  for (var i = 0; i < 32; i++) {
    this.powerUps.push(new PowerUp(0, 0));
  }
  
  this.entities = [this.player];
  this.entities = this.entities.concat(
    this.enemyList,
    this.explosions,
    this.powerUps,
    this.player.bulletPool
  );
  
  console.log('game initialised');
}

GameState.prototype.deployEnemies = function() {
  if (this.timer && (this.timer % this.currentEnemyTimer === 0) ) {
    var enemy = this.enemyList.find(function(enemy) {
      return !enemy.alive;
    });
    enemy.reset();
    enemy.x = canvas.width;
    enemy.alive = true;
    enemy.y = Math.max(4, (canvas.height * Math.random()) - 24);
  }
}

GameState.prototype.deployPowerUps = function() {
  if (this.timer && (this.timer % this.powerUpTimer === 0) ) {
    if (Math.random() >= .01) {
      var powerup = this.powerUps.find(function(powerup) {
        return !powerup.alive;
      });
      powerup.x = canvas.width;
      powerup.alive = true;
      powerup.y = (canvas.height * Math.random()) - 16;
    }
  }
}

GameState.prototype.update = function() {
  
  if (this.timer > this.tier2) {
      this.currentEnemyTimer = this.enemyTimerMedium;
  }
  
  if (this.timer > this.tier3) {
      this.currentEnemyTimer = this.enemyTimerHard;
  }
  
  if (this.timer > this.tier4) {
      this.currentEnemyTimer = this.enemyTimerNightmare;
  }
  
  if (this.overdriveTimer < this.timer) {
    game.overdrive = false;
  }
  this.timer++;
  
  this.deployEnemies();
  this.deployPowerUps();
  
  this.entities.forEach(function(entity) {
    entity.update();
  });
  
  this.player.bulletPool.forEach(function(bullet) {
    if (!bullet.alive) {
      return;
    }
    
    game.currentState.enemyList.forEach(function(enemy) {
      if (!enemy.alive) {
        return;
      }
      if (Intersection.rectanglesIntersect(bullet, enemy)) {
        enemy.takeDamage(10);
        bullet.alive = false;
        if (!enemy.alive) {
          game.score += enemy.value;
          game.updateScore();
          enemy.hit = true;
          var explosion = game.currentState.explosions.find(notAlive);
          explosion.x = enemy.x;
          explosion.y = enemy.y;
          explosion.alive = true;
        }
      }
    });
  });
  var player = game.currentState.player;
  if (player.alive) {
    this.enemyList.forEach(function(enemy) {
      if(!enemy.alive) {
        return;
      }
      var playerHitBox = player.getHitBox();
      if(Intersection.rectanglesIntersect(enemy, playerHitBox)) {
        player.takeDamage(500);
        enemy.takeDamage(500);
        game.lives--;
        game.updateLives();
        var explosion = game.currentState.explosions.find(notAlive);
        explosion.x = enemy.x;
        explosion.y = enemy.y;
        explosion.alive = true;

        var explosion = game.currentState.explosions.find(notAlive);
        explosion.x = player.x;
        explosion.y = player.y;
        explosion.alive = true;

        if (game.lives === 0) {
          game.setState(game.states.game_over);
          game.currentState.init();
        }
      }
    });
    
    this.powerUps.forEach(function(powerup) {
      if (!powerup.alive) {
        return;
      }
      if (Intersection.rectanglesIntersect(powerup, player.getHitBox())) {
        player.powerLevel++;
        powerup.reset();
        game.score += powerup.value;
        game.updateScore();
      }
    });
  }
}
notAlive = function(item){
  return !item.alive;
}
alive = function(item) {
  return alive;
}
GameState.prototype.renderBackground = function(context) {
  this.starField.forEach(function(star) {
    star.update();
    star.render(context);
  });
}

GameState.prototype.render = function(context)  {
  context.fillStyle = '#000';
  context.fillRect(0, 0, canvas.width, canvas.height);
  context.globalAlpha = 1;
  this.renderBackground(context);
  this.entities.forEach(
    function(entity){
      entity.render(context);
    }
  );
}

function Game()
{
  this.reset();
  this.states = {};
  this.states['game'] = new GameState('game');
  this.states['title']  = new TitleState('title');
  this.states['game_over'] = new GameOverState('game_over');
  this.entities = [];
}

Game.prototype.reset = function() {
  this.score = 0;
  this.lives = 3;
  this.updateScore();
  this.updateLives();
}

Game.prototype.setState = function(state) {
  this.currentState = state;
}

Game.prototype.showUI = function(name)  {
  document.querySelectorAll('.state-ui-container').forEach(function(element) {
    element.style.display = 'none';
  });
  document.querySelector('#'+name).style.display = 'flex';
}

function Ship(x, y, width, height, health)
{
  this.x = x;
  this.y = y;
  this.width = width;
  this.height = height;
  this.health = health;
  this.hit = false;
  
  this.alive = true;
  this.vx = 0;
  this.vy = 0;
}

Ship.prototype.render = function(context) {
  context.fillStyle = '#666';
  context.fillRect(this.x, this.y, this.width, this.height);
}

Ship.prototype.takeDamage = function(damage) {
  this.health -= damage;
  if (this.health <= 0) {
    this.alive = false;
  }
}

function Bullet(x, y, dir, speed) {
  this.vx = 0;
  this.vy = 0;
  this.width = 16;
  this.height = 8;
  this.x = x;
  this.y = y;
  this.dir = dir;
  this.speed = speed;
  this.alive = false;
  this.img = document.createElement('img');
  this.img.src = bulletSprite;
}

Bullet.prototype.update = function() {
  if (!this.alive) {
    return;
  }
  if (this.dir === 1) this.vx = this.speed + (Math.min(game.currentState.player.powerLevel * 2, 20));
  if (this.dir === -1) this.vx = this.speed;
  this.x += this.vx;
  if (this.x > canvas.width) {
    this.alive = false;
  }
}

Bullet.prototype.render = function(context) {
  if (!this.alive) {
    return
  }
  context.drawImage(this.img, 0, 0, 16, 8, this.x, this.y, this.width, this.height);
}

function PowerUp(x, y) {
  this.x = x;
  this.y = y;
  this.timer = Math.random() * 256;
  this.currentFrame = 0;
  this.currentBuffer = 0;
  this.buffer = 4;
  this.jump = canvas.height * .5;
  this.speed = 3;
  this.frames = 7;
  this.width = 16;
  this.height = 16;
  this.img = document.createElement('img');
  this.img.src = powerUpSprite;
  this.value = 20;
}

PowerUp.prototype.update = function() {
  this.vy = Math.cos(this.timer) * this.jump;
  this.timer += .03;
  this.y = canvas.height * .5 + this.vy;
  this.x -= this.speed;
  if (this.x + this.width <= 0) {
    this.reset();
  }
}

PowerUp.prototype.reset = function() {
  this.alive = false;
}

PowerUp.prototype.render = function(context) {
  if (!this.alive) {
    return;
  }
  if (this.currentBuffer === this.buffer) {
    this.currentBuffer = 0;
    this.currentFrame ++;
    if (this.currentFrame === this.frames) {
      this.currentFrame = 0;
    }
  }
  this.currentBuffer++;
  context.drawImage(
    this.img, 0, this.currentFrame * 16, 16, 16,
    this.x, this.y, this.width, this.height
  )
}

function Player(x, y, health, weapon)
{
  Ship.call(this, x, y, 32, 32, health);
  this.burning = false;
  this.frame = 0;
  this.burnFrame = 0;
  this.weapon = weapon;
  this.speed = 5;
  this.powerLevel = 1;
  this.bulletTimer = 5;
  this.bulletCoolDown = 5;
  this.img = document.createElement('img');
  this.burnImg = document.createElement('img');
  this.img.src = shipSprite;
  this.burnImg.src = burnSprite;
  this.bulletPool = [];
  this.hitBoxSize = [8, 8];
}

Player.prototype = new Ship();

Player.prototype.getHitBox = function() {
  return {
    x: this.x + (this.width * .5) - (this.hitBoxSize[0] * .5),
    y: this.y + (this.height * .5) - (this.hitBoxSize[1] * .5),
    width: this.hitBoxSize[0],
    height: this.hitBoxSize[1],
  }
}

Player.prototype.update = function() {
  if (!this.alive) {
    if (inputManager.start) {
      game.currentState.init();
    }
    return;
  }
  this.vx = 0;
  this.vy = 0;
  if (inputManager.upPressed) {
    this.vy -= this.speed;
  }
  
  if (inputManager.downPressed) {
    this.vy += this.speed;
  }
  
  if (inputManager.rightPressed) {
    this.vx += this.speed;
    this.burning = true;
  } else {
    this.burning = false;
  }
  
  if (inputManager.leftPressed) {
    this.vx -= this.speed;
    this.burning = false;
  }
  
  if (inputManager.shooting || game.autoShoot) {
    if (this.bulletCoolDown === this.bulletTimer) {
      this.shoot();
      this.bulletTimer = 0;
    } else {
      this.bulletTimer++;
    }
  }
  
  this.x += this.vx;
  this.y += this.vy;
  
  if (this.x < 0) {
      this.x = 0;
  }
  
  if (this.y < 0) {
    this.y = 0;
  }
  
  if (this.x+this.width > canvas.width) {
    this.x = canvas.width - this.width;
  }
  
  if (this.y+this.height > canvas.height) {
    this.y = canvas.height - this.height;
  }
}

Player.prototype.shoot = function() {
  
  if (this.powerLevel === 1) {
    var bullet = this.bulletPool.find(notAlive);
    if (!bullet) {
      return;
    }
    bullet.x = this.x + this.width - bullet.width;
    bullet.y = (this.y + (this.height * .5)) - (bullet.height * .5);
    bullet.alive = true;
  }
  
  if (this.powerLevel === 2) {
    var bullet = this.bulletPool.find(notAlive);
    if (!bullet) {
      return;
    }
    bullet.alive = true;
    
    var bullet2 = this.bulletPool.find(notAlive);
    if (!bullet) {
      return;
    }
    bullet2.alive = true;
    
    bullet.x = this.x + this.width - bullet.width;
    bullet.y = (this.y + (this.height * .5)) - (bullet.height * .5) - 6;
    
    
    bullet2.x = this.x + this.width - bullet2.width;
    bullet2.y = (this.y + (this.height * .5)) - (bullet2.height * .5) + 6;
  }
  
  if (this.powerLevel >= 3) {
    var bullet = this.bulletPool.find(notAlive);
    if (!bullet) {
      return;
    }
    bullet.alive = true;
    
    var bullet2 = this.bulletPool.find(notAlive);
    if (!bullet2) {
      return;
    }
    bullet2.alive = true;
    
    var bullet3 = this.bulletPool.find(notAlive);
    if (!bullet3) {
      return;
    }
    bullet3.alive = true;
    
    bullet.x = this.x + this.width - bullet.width;
    bullet.y = (this.y + (this.height * .5)) - (bullet.height * .5) - 12;
    
    
    bullet2.x = this.x + this.width - bullet2.width + 8;
    bullet2.y = (this.y + (this.height * .5)) - (bullet2.height * .5);
    
    bullet3.x = this.x + this.width - bullet3.width;
    bullet3.y = (this.y + (this.height * .5)) - (bullet3.height * .5) + 12;
  }
  
  if (this.powerLevel >= 5) {
    var bullet = this.bulletPool.find(notAlive);
    if (!bullet) {
      return;
    }
    bullet.alive = true;
    
    var bullet2 = this.bulletPool.find(notAlive);
    if (!bullet2) {
      return;
    }
    bullet2.alive = true;
    bullet.x = this.x + this.width - bullet.width - 8;
    bullet.y = (this.y + (this.height * .5)) - (bullet.height * .5) - 24;
    bullet2.x = this.x + this.width - bullet2.width - 8;
    bullet2.y = (this.y + (this.height * .5)) - (bullet2.height * .5) + 24;
    
  }
  
}

Player.prototype.render = function(context) {
  if (!this.alive) {
    return;
  }
  this.buffer = this.buffer ? this.buffer + 1 : 1;
  if(this.buffer && this.buffer == 4) {
    this.frame += 1;
    if (this.frame >= 10) this.frame = 0;
    this.burnFrame += 1;
    if (this.burnFrame >= 2) this.burnFrame = 0;
    this.buffer = 0;
  }
  
  context.drawImage(
    this.img, 0, this.frame * 32, 32, 32,
    this.x, this.y, this.width, this.height
  )
  
  if (this.burning) {
    context.drawImage(
      this.burnImg, 0, this.burnFrame * 32, 32, 32,
      this.x, this.y, this.width, this.height
    )
  }
  
  this.bulletPool.forEach(function(bullet) {
    if (!bullet.alive) {
      return false;
    }
    bullet.render(context);
  })
  
}

function Popcorn(x, y)
{
  Ship.call(this, x, y, 32, 32, Popcorn.HEALTH);
  this.img = document.createElement('img');
  this.img.src = popCornSprite;
  this.frames = 12; 
  this.buffer = 4;
  this.speed = Popcorn.SPEED;
  this.value = 5;
  this.healthMax = Popcorn.HEALTH;
  this.reset();
}

Popcorn.prototype = new Ship(); 
Popcorn.SPEED = 5;
Popcorn.HEALTH = 80;

Popcorn.prototype.reset = function() {
  this.x = canvas.width;
  this.y = Math.random() * canvas.height - this.height;
  this.currentFrame = 0;
  this.currentBuffer = 0;
  this.alive = false;
  this.health = Popcorn.HEALTH;
}

Popcorn.prototype.update = function() {
  this.vx = -this.speed;
  this.x += this.vx;
  if (this.x + this.width <= 0) {
    this.reset();
  }
}

Popcorn.prototype.render = function(context) {
  if (!this.alive) {
    return;
  }
  if (this.currentBuffer === this.buffer) {
    this.currentBuffer = 0;
    this.currentFrame ++;
    if (this.currentFrame === this.frames) {
      this.currentFrame = 0;
    }
  }
  context.drawImage(
    this.img, 0, this.currentFrame * 32, 32, 32,
    this.x, this.y, this.width, this.height
  )
  context.fillStyle = '#f00';
  context.fillRect(this.x, this.y - 2, this.width, 1);
  
  context.fillStyle = '#0f0';
  this.perc = this.health/ this.healthMax;
  
  context.fillRect(this.x, this.y - 2, this.width * this.perc , 1)
  
  if (this.hit) {
    var tmpOperation = context.globalCompositeOperation
    context.globalCompositeOperation = 'color-dodge';
    context.drawImage(
      this.img, 0, this.currentFrame * 32, 32, 32,
      this.x, this.y, this.width, this.height
    )
    context.drawImage(
      this.img, 0, this.currentFrame * 32, 32, 32,
      this.x, this.y, this.width, this.height
    )
    context.globalCompositeOperation = tmpOperation;
    this.hit = false;
  }
  
  this.currentBuffer++;
}

Game.prototype.update = function() {
  this.currentState.update();
}

Game.prototype.render = function(context) {
  this.currentState.render(context);
}

Game.prototype.loop = function() {
  window.requestAnimationFrame(function(){this.loop()}.bind(this));
  now = Date.now();
  dt = now - then;
  if (dt <= fps) { return }
  then = now - (dt % fps);
  this.update();
  this.render(context);
}

Game.prototype.init = function() {
  this.autoShoot = true;
  this.setState(this.states.title);
  this.currentState.init();
  window.requestAnimationFrame(function(){ this.loop() }.bind(this));
}

var game = new Game();

function InputManager() {
  this.upPressed = false;
  this.downPressed = false;
  this.leftPressed = false;
  this.rightPressed = false;
  this.shooting = false;
  this.start = false;
}

InputManager.prototype.handleKey = function(e, state) {
  switch(e.keyCode) {
    case InputManager.UP:
      this.upPressed = state;
      break;
    case InputManager.DOWN:
      this.downPressed = state;
      break;
    case InputManager.LEFT:
      this.leftPressed = state;
      break;
    case InputManager.RIGHT:
      this.rightPressed = state;
      break;
    case InputManager.SHOOT:
      this.shooting = state;
      break;
    case InputManager.RESET:
      game.reset();
      game.setState(game.states.title);
      game.currentState.init();
      break;
    case InputManager.START:
      this.start = state;
      break;
  }
}

InputManager.UP = UP;
InputManager.DOWN = DOWN;
InputManager.LEFT = LEFT;
InputManager.RIGHT = RIGHT;
InputManager.SHOOT = SHOOT;
InputManager.RESET = RESET;
InputManager.START = START;

var inputManager = new InputManager();

window.addEventListener(
  'keydown', 
  function(e) {
    game.autoShoot = false;
    this.handleKey(e, true)
  }.bind(inputManager)
);

window.addEventListener(
  'keyup', 
  function(e){ 
    this.handleKey(e, false)
  }.bind(inputManager)
);

game.init();
              
            
!
999px

Console