HTML马里奥小游戏

最近,我们放寒假了,所以我可以写文章了,这次教大家做一个马里奥的游戏。

css部分:

<style type="text/css">
  *body{margin:0 auto;}
  #status{
    position: absolute;
    left: 50%;
    top: 170px;
    margin-left: 60px;
  }
.background {
  background: rgb(52, 166, 251);
  table-layout: fixed;
  border-spacing: 0;
}
.background td { padding: 0; }
.lava { background: rgb(255, 100, 100); }
.wall { background: white; }
.actor { position: absolute; }
.coin { background: rgb(241, 229, 89); }
.player { background: rgb(64, 64, 64); }
.lost .player { background: rgb(160, 64, 64); }
.won .player {
  box-shadow: -4px -7px 8px white, 4px -7px 8px white;
}
.game {
  overflow: hidden;
  max-width: 600px;
  max-height: 450px;
  position: relative;
}
</style>

js部分:

<script type="text/javascript">
  
  /*
Based on Dark Blue by Thomas Palef.

Plans are submitted as an array of strings that forms a grid.


@ = 玩家
o = 金币
x = 墙壁
! = 岩浆
= = 左右移动岩浆
v = 向下移动岩浆
| = 上下移动岩浆

*/
var simpleLevelPlan = [
  "                      ",
  "                      ",
  "  x              = x  ",
  "  x         o o    x  ",
  "  x @      xxxxx   x  ",
  "  xxxxx            x  ",
  "      x!!!!!!!!!!!!x  ",
  "      xxxxxxxxxxxxxx  ",
  "                      "
];

var GAME_LEVELS = [
  ["                                                                                ",
   "                                                                                ",
   "                                                                                ",
   "                                                                                ",
   "                                                                                ",
   "                                                                                ",
   "                                                                  xxx           ",
   "                                                   xx      xx    xx!xx          ",
   "                                    o o      xx                  x!!!x          ",
   "                                                                 xx!xx          ",
   "                                   xxxxx                          xvx           ",
   "                                                                            xx  ",
   "  xx                                      o o                                x  ",
   "  x                     o                                                    x  ",
   "  x                                      xxxxx                             o x  ",
   "  x          xxxx       o                                                    x  ",
   "  x  @       x  x                                                xxxxx       x  ",
   "  xxxxxxxxxxxx  xxxxxxxxxxxxxxx   xxxxxxxxxxxxxxxxxxxx     xxxxxxx   xxxxxxxxx  ",
   "                              x   x                  x     x                    ",
   "                              x!!!x                  x!!!!!x                    ",
   "                              x!!!x                  x!!!!!x                    ",
   "                              xxxxx                  xxxxxxx                    ",
   "                                                                                ",
   "                                                                                "],
  ["                                      x!!x                        xxxxxxx                                    x!x  ",
   "                                      x!!x                     xxxx     xxxx                                 x!x  ",
   "                                      x!!xxxxxxxxxx           xx           xx                                x!x  ",
   "                                      xx!!!!!!!!!!xx         xx             xx                               x!x  ",
   "                                       xxxxxxxxxx!!x         x                                    o   o   o  x!x  ",
   "                                                xx!x         x     o   o                                    xx!x  ",
   "                                                 x!x         x                                xxxxxxxxxxxxxxx!!x  ",
   "                                                 xvx         x     xxxxx                        !!!!!!!!!!!!!!xx  ",
   "                                                             xx             xx            xxxxxxxxxxxxxxxxxxxxx   ",
   "                                                              xx!!!!!!!!!!!xx            v                        ",
   "                                                               xxxx!!!!!xxxx                                      ",
   "                                               x     x            xxxxxxx        xxx         xxx                  ",
   "                                               x     x                           x x         x x                  ",
   "                                               x     x                             x         x                    ",
   "                                               x     x                             xx        x                    ",
   "                                               xx    x                             x         x                    ",
   "                                               x     x      o  o     x   x         x         x                    ",
   "               xxxxxxx        xxx   xxx        x     x               x   x         x         x                    ",
   "              xx     xx         x   x          x     x     xxxxxx    x   x   xxxxxxxxx       x                    ",
   "             xx       xx        x o x          x    xx               x   x   x               x                    ",
   "     @       x         x        x   x          x     x               x   x   x               x                    ",
   "    xxx      x         x        x   x          x     x               x   xxxxx   xxxxxx      x                    ",
   "    x x      x         x       xx o xx         x     x               x     o     x x         x                    ",
   "!!!!x x!!!!!!x         x!!!!!!xx     xx!!!!!!!!xx    x!!!!!!!!!!     x     =     x x         x                    ",
   "!!!!x x!!!!!!x         x!!!!!xx       xxxxxxxxxx     x!!!!!!!xx!     xxxxxxxxxxxxx xx  o o  xx                    ",
   "!!!!x x!!!!!!x         x!!!!!x    o                 xx!!!!!!xx !                    xx     xx                     ",
   "!!!!x x!!!!!!x         x!!!!!x                     xx!!!!!!xx  !                     xxxxxxx                      ",
   "!!!!x x!!!!!!x         x!!!!!xx       xxxxxxxxxxxxxx!!!!!!xx   !                                                  ",
   "!!!!x x!!!!!!x         x!!!!!!xxxxxxxxx!!!!!!!!!!!!!!!!!!xx    !                                                  ",
   "!!!!x x!!!!!!x         x!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!xx     !                                                  "],
  ["                                                                                                              ",
   "                                                                                                              ",
   "                                                                                                              ",
   "                                                                                                              ",
   "                                                                                                              ",
   "                                                                                                              ",
   "                                                                                                              ",
   "                                        x                                                                     ",
   "                                        x                                                                     ",
   "                                        x                                                                     ",
   "                                        x                                                                     ",
   "                                       xxx                                  o                                 ",
   "                                       x x                 !!!        !!!  xxx                                ",
   "                                     o x x                 !x!        !x!                                     ",
   "                                     xxx xxx                x          x        o                             ",
   "                                      x   x                 x          x       xxx                            ",
   "                                      x   x                 x          x      x!!!x                           ",
   "                                    o x   x                 xxxxxxxxxxxx       xxx                            ",
   "                                    xxx   xx      x   x      x              o                                 ",
   "                                      x   xxxxxxxxx   xxxxxxxx              x x                               ",
   "                                      x   x         o x                    x!!!x                              ",
   "                                   o  x   x           x                     xxx                               ",
   "                                   xxxx   xx          x                                                       ",
   "                                      x   x= = = =    x            xxx                                        ",
   "                                      x   x           x           x!!!x       o                               ",
   "                                  o   x   x    = = = =x            xxx       xxx                              ",
   "                                  xxxxx   xx          x                     x!!!x                             ",
   "                                      x   x           x     x                xxv        xxx                   ",
   "                                      x   x           x              x                 x!!!x                  ",
   "                             xxx xxx xxx xxx          x!!!!!!!!!!!!!!x                   vx                   ",
   "                             x xxx x x xxx x          x!!!!!!!!!!!!!!x                                        ",
   "                             x             x   xxxxxxxxxxxxxxxxxxxxxxx                o                       ",
   "                             xx     o     xx                                         xxx                      ",
   "  xxx                         x     x     x                                         x!!!x                xxx  ",
   "  x x                         x    xxx    x    o                           o         xxx                 x x  ",
   "  x                           x    xxx    xxxxxxx                        xxxxx                             x  ",
   "  x                           x           x                              x   x                             x  ",
   "  x                           xx          x          o         o        ox x x                             x  ",
   "  x                                       x       |xxxx|    |xxxx|     xxx xxx                             x  ",
   "  x                xxx                    x                              x         xxx                     x  ",
   "  x               xxxxx       xx          x                             xxx       x!!!x          x         x  ",
   "  x               oxxxo       x    xxx    x                             x x        xxx          xxx        x  ",
   "  x                xxx        xxxxxxxxxxxxx  x    x    x    x    x     xx xx                    xxx        x  ",
   "  x      @          x         x           x!!x    x!!!!x    x!!!!x    xx   xx                    x         x  ",
   "  xxxxxxxxxxxxxxxxxxxxxxxxxxxxx           xxxxxxxxxxxxxxxxxxxxxxxxxxxxx     xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx  ",
   "                                                                                                              ",
   "                                                                                                              "],
  ["                                                                                                  xxx x       ",
   "                                                                                                      x       ",
   "                                                                                                  xxxxx       ",
   "                                                                                                  x           ",
   "                                                                                                  x xxx       ",
   "                          o                                                                       x x x       ",
   "                                                                                             o o oxxx x       ",
   "                   xxx                                                                                x       ",
   "       !  o  !                                                xxxxx xxxxx xxxxx xxxxx xxxxx xxxxx xxxxx       ",
   "       x     x                                                x   x x   x x   x x   x x   x x   x x           ",
   "       x= o  x            x                                   xxx x xxx x xxx x xxx x xxx x xxx x xxxxx       ",
   "       x     x                                                  x x   x x   x x   x x   x x   x x     x       ",
   "       !  o  !            o                                  xxxx xxxxx xxxxx xxxxx xxxxx xxxxx xxxxxxx       ",
   "                                                                                                              ",
   "          o              xxx                              xx                                                  ",
   "                                                                                                              ",
   "                                                                                                              ",
   "                                                      xx                                                      ",
   "                   xxx         xxx                                                                            ",
   "                                                                                                              ",
   "                          o                                                     x      x                      ",
   "                                                          xx     xx                                           ",
   "             xxx         xxx         xxx                                 x                  x                 ",
   "                                                                                                              ",
   "                                                                 ||                                           ",
   "  xxxxxxxxxxx                                                                                                 ",
   "  x         x o xxxxxxxxx o xxxxxxxxx o xx                                                x                   ",
   "  x         x   x       x   x       x   x                 ||                  x     x                         ",
   "  x  @      xxxxx   o   xxxxx   o   xxxxx                                                                     ",
   "  xxxxxxx                                     xxxxx       xx     xx     xxx                                   ",
   "        x=                  =                =x   x                     xxx                                   ",
   "        xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx   x!!!!!!!!!!!!!!!!!!!!!xxx!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!",
   "                                                  xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
   "                                                                                                              "]
];

// Assumes the plan is valid (add validation for production)
function Level(plan) {
  this.width = plan[0].length;
  this.height = plan.length;
  // Array of arrays, each position containing null or a character
  this.grid = [];
  // Contains all of the dynamic objects (lava, coin or player),
  // along with their position and state
  this.actors = [];
  
  for (var y = 0; y < this.height; y++) {
    var line = plan[y], gridLine = [];
    for (var x = 0; x < this.width; x++) {
      var ch = line[x], fieldType = null;
      var Actor = actorChars[ch];
      if (Actor)
        // This constructs the referenced moving object in
        // actorChars and pushes it to the actors array
        this.actors.push(new Actor(new Vector(x, y), ch));
      else if (ch == 'x')
        // Wall
        fieldType = 'wall';
      else if (ch == '!')
        // Stationary lava
        fieldType = 'lava';
      gridLine.push(fieldType);
    }
    this.grid.push(gridLine);
  }
  // Find the Player actor
  this.player = this.actors.filter(function(actor) {
    return actor.type == 'player';
  })[0];
  // Track whether the player has won or lost;
  // finishDelay keeps the level active for a brief period of time
  this.status = this.finishDelay = null;
}
// Figure out if the level is finished
Level.prototype.isFinished = function() {
  return this.status != null && this.finishDelay < 0;
};
// Figure out the collision area of an actor
Level.prototype.obstacleAt = function(pos, size) {
  var xStart = Math.floor(pos.x);
  var xEnd = Math.ceil(pos.x + size.x);
  var yStart = Math.floor(pos.y);
  var yEnd = Math.ceil(pos.y + size.y);
  
  // The upper and side bounds of the level return a wall
  if (xStart < 0 || xEnd > this.width || yStart < 0)
    return 'wall';
  // The bottom bound returns lava
  if (yEnd > this.height)
    return 'lava';
  // Check what's on the grid around the collision box
  for (var y = yStart; y < yEnd; y++) {
    for (var x = xStart; x < xEnd; x++) {
      var fieldType = this.grid[y][x];
      // returns 'wall' or 'lava'
      if (fieldType) return fieldType;
    }
  }
};
// Track what actors overlap a given actor
Level.prototype.actorAt = function(actor) {
  for (var i = 0; i < this.actors.length; i++) {
    var other = this.actors[i];
    if (other != actor &&
        actor.pos.x + actor.size.x > other.pos.x &&
        actor.pos.x < other.pos.x + other.size.x &&
        actor.pos.y + actor.size.y > other.pos.y &&
        actor.pos.y < other.pos.y + other.size.y)
      return other;
  }
};
// Time increment for Level.animate()
var maxStep = 0.05;
// Animate the level
// step will be given in seconds, keys is an object that
// contains info about the arrow keys the player has pressed.
Level.prototype.animate = function(step, keys) {
  // Used for the delay at the end of a game
  if (this.status != null)
    this.finishDelay -= step;
  
  while (step > 0) {
    var thisStep = Math.min(step, maxStep);
    this.actors.forEach(function(actor) {
      actor.act(thisStep, this, keys);
    }, this);
    step -= thisStep;
  }
};
// Handle collisions between actors
Level.prototype.playerTouched = function(type, actor) {
  // Lava's been touched
  if (type == 'lava' && this.status == null) {
    this.status = 'lost';
    this.finishDelay = 1;
  } else if (type == 'coin') { // a coin's been touched
    // Remove the coin that's been collected from actors array
    this.actors = this.actors.filter(function(other) {
      return other != actor;
    });
    // No more coins = you've won
    if (!this.actors.some(function(actor) {
      return actor.type == 'coin';
    })) {
      this.status = 'won';
      this.finishDelay = 1;
    }
  }
}

var actorChars = {
  '@': Player,
  'o': Coin,
  '=': Lava, '|': Lava, 'v': Lava
};

// Vector stores the position and size of an actor
function Vector(x, y) {
  this.x = x; this.y = y;
}
Vector.prototype.plus = function(other) {
  return new Vector(this.x + other.x, this.y + other.y);
}
Vector.prototype.times = function(factor) {
  return new Vector(this.x * factor, this.y * factor);
}

// Player constructor
function Player(pos) {
  // This properly aligns the bottom of the player
  // to the square below (adjusts for player height)
  this.pos = pos.plus(new Vector(0, -0.5));
  this.size = new Vector(0.8, 1.5);
  this.speed = new Vector(0, 0);
}
Player.prototype.type = 'player';
// X and Y movement are handled independently because
// a wall shouldn't stop the up and down motion of jumping
// and a floor shouldn't stop side to side movement
var playerXSpeed = 7;
Player.prototype.moveX = function(step, level, keys) {
  this.speed.x = 0;
  if (keys.left) this.speed.x -= playerXSpeed;
  if (keys.right) this.speed.x += playerXSpeed;
  
  var motion = new Vector(this.speed.x * step, 0);
  var newPos = this.pos.plus(motion);
  var obstacle = level.obstacleAt(newPos, this.size);
  if (obstacle)
    level.playerTouched(obstacle);
  else
    this.pos = newPos;
};
var gravity = 30;
var jumpSpeed = 17;
Player.prototype.moveY = function(step, level, keys) {
  this.speed.y += step * gravity;
  var motion = new Vector(0, this.speed.y * step);
  var newPos = this.pos.plus(motion);
  var obstacle = level.obstacleAt(newPos, this.size);
  if (obstacle) {
    level.playerTouched(obstacle);
    if (keys.up && this.speed.y > 0)
      this.speed.y = -jumpSpeed;
    else
      this.speed.y = 0;
  } else {
    this.pos = newPos;
  }
};
Player.prototype.act = function(step, level, keys) {
  this.moveX(step, level, keys);
  this.moveY(step, level, keys);
  
  var otherActor = level.actorAt(this);
  if (otherActor)
    level.playerTouched(otherActor.type, otherActor);
  
  // Losing animation
  if (level.status == 'lost') {
    this.pos.y += step;
    this.size.y -=step;
  }
};

// Lava constructor
function Lava(pos, ch) {
  this.pos = pos;
  this.size = new Vector(1, 1);
  if (ch == "=") {
    // I'm guessing that speed will be added to this.pos
    // in a moving method added later
    this.speed = new Vector(2, 0);
  } else if (ch == '|') {
    this.speed = new Vector(0, 2);
  } else if (ch == 'v') {
    this.speed = new Vector(0, 3);
    this.repeatPos = pos;
  }
}
Lava.prototype.type = 'lava';
// Action
Lava.prototype.act = function(step, level) {
  var newPos = this.pos.plus(this.speed.times(step));
  if (!level.obstacleAt(newPos, this.size))
    this.pos = newPos;
  else if (this.repeatPos)
    this.pos = this.repeatPos;
  else
    this.speed = this.speed.times(-1);
};

// Coin constructor
function Coin(pos) {
  this.basePos = this.pos = pos.plus(new Vector(0.2, 0.1));
  this.size = new Vector(0.6, 0.6);
  this.wobble = Math.random() * Math.PI * 2;
}
Coin.prototype.type = 'coin';
var wobbleSpeed = 8, wobbleDist = 0.07;
// Action
Coin.prototype.act = function(step) {
  this.wobble += step * wobbleSpeed;
  var wobblePos = Math.sin(this.wobble) * wobbleDist;
  this.pos = this.basePos.plus(new Vector(0, wobblePos));
};

// Helper function that creates an element
// and gives it a class
function elt(name, className) {
  var elt = document.createElement(name);
  if (className) elt.className = className;
  return elt;
}

// Tracking keypresses for player movement
var arrowCodes = {37: 'left', 38: 'up', 39: 'right'};

function trackKeys(codes) {
  var pressed = Object.create(null);
  function handler(event) {
    if (codes.hasOwnProperty(event.keyCode)) {
      var down = event.type == 'keydown';
      pressed[codes[event.keyCode]] = down;
      event.preventDefault();
    }
  }
  addEventListener('keydown', handler);
  addEventListener('keyup', handler);
  
  pressed.unregister = function() {
    removeEventListener('keydown', handler);
    removeEventListener('keyup', handler);
  };
  
  return pressed;
}

// Run the animation
function runAnimation(frameFunc) {
  var lastTime = null;
  function frame(time) {
    var stop = false;
    if (lastTime != null) {
      var timeStep = Math.min(time - lastTime, 100) / 1000;
      stop = frameFunc(timeStep) === false;
    }
    lastTime = time;
    if (!stop)
      requestAnimationFrame(frame);
  }
  requestAnimationFrame(frame);
}

// Run the level
var arrows = trackKeys(arrowCodes);

function runLevel(level, Display, andThen) {
  var display = new Display(document.body, level);
  // Used for storing pause state of the game
  var running = 'yes';
  
  function handleKey(event) {
    if (event.keyCode == 27) {
      if (running == 'no') {
        running = 'yes';
        runAnimation(animation);
      } else if (running == 'pausing') {
        running = 'yes';
      } else if (running == 'yes') {
        running = 'pausing';
      }
    }
  }
  addEventListener('keydown', handleKey);
  
  function animation(step) {
    if (running == 'pausing') {
      running = 'no';
      return false;
    }
    
    level.animate(step, arrows);
    display.drawFrame(step);
    if (level.isFinished()) {
      display.clear();
      // Remove the watch on the esc key 
      //removeEventListener('keydown', handleKey);
      // Unregister the arrow key listeners
      //arrows.unregister();
      if (andThen)
        andThen(level.status);
      return false;
    }
  }
  
  runAnimation(animation);
}

// Run the game
function runGame(plans, Display) {
  var lives = 9999999999999999999;
  var livesSpan = document.getElementById('lives');
  var gameStatus = document.getElementById('status');
  
  function startLevel(n) {
    livesSpan.textContent = lives;
    runLevel(new Level(plans[n]), Display, function(status) {
      if (status == 'lost') {
        lives--;
        if (lives == 0) {
          gameStatus.textContent = 'Game Over';
          console.log('Game over');
        }
        else 
          startLevel(n);
      }
      else if (n < plans.length - 1)
        startLevel(n + 1);
      else
        console.log('You win!');
    });
  }
  startLevel(0);
}


// DOMDisplay uses the DOM to draw the program out
function DOMDisplay(parent, level) {
  this.wrap = parent.appendChild(elt('div', 'game'));
  this.level = level;
  
  // Background is drawn only once
  this.wrap.appendChild(this.drawBackground());
  // The actorLayer is animated in the drawFrame() method
  this.actorLayer = null;
  this.drawFrame();
}
// Set the scale of 1 grid unit
var scale = 20;
// Draw the background
DOMDisplay.prototype.drawBackground = function() {
  var table = elt('table', 'background');
  table.style.width = this.level.width * scale + 'px';
  this.level.grid.forEach(function(row) {
    var rowElt = table.appendChild(elt('tr'));
    rowElt.style.height = scale + 'px';
    row.forEach(function(type) {
      rowElt.appendChild(elt('td', type));
    });
  });
  return table;
};
// Draw the actors
DOMDisplay.prototype.drawActors = function() {
  var wrap = elt('div');
  this.level.actors.forEach(function(actor) {
    var rect = wrap.appendChild(elt('div', 'actor ' + actor.type));
    rect.style.width  = actor.size.x * scale + 'px';
    rect.style.height = actor.size.y * scale + 'px';
    rect.style.left   = actor.pos.x * scale + 'px';
    rect.style.top    = actor.pos.y * scale + 'px';
  });
  return wrap;
};
// Redraw the actors
DOMDisplay.prototype.drawFrame = function() {
  if (this.actorLayer)
    this.wrap.removeChild(this.actorLayer);
  this.actorLayer = this.wrap.appendChild(this.drawActors());
  // The status class is used to style the player based on
  // the state of the game (won or lost)
  this.wrap.className = 'game ' + (this.level.status || '');
  this.scrollPlayerIntoView();
};
// Make sure the player's always on screen
DOMDisplay.prototype.scrollPlayerIntoView = function() {
  var width = this.wrap.clientWidth;
  var height = this.wrap.clientHeight;
  var margin = width / 3;
  
  // The viewport
  var left = this.wrap.scrollLeft, right = left + width;
  var top = this.wrap.scrollTop, bottom = top + height;
  
  // center makes use of the Vector methods defined earlier
  var player = this.level.player;
  var center = player.pos.plus(player.size.times(0.5))
                  .times(scale);
  
  if (center.x < left + margin)
    this.wrap.scrollLeft = center.x - margin;
  else if (center.x > right - margin)
    this.wrap.scrollLeft = center.x + margin - width;
  if (center.y < top + margin)
    this.wrap.scrollTop = center.y - margin;
  else if (center.y > bottom - margin)
    this.wrap.scrollTop = center.y + margin - height;
}
// Clear the level
DOMDisplay.prototype.clear = function() {
  this.wrap.parentNode.removeChild(this.wrap);
};

// Canvas API helper function
function flipHorizontally(context, around) {
  context.translate(around, 0);
  context.scale(-1, 1);
  context.translate(-around, 0);
}
// Use Canvas API to draw game
function CanvasDisplay(parent, level) {
  // Set up the canvas element
  this.canvas = document.createElement('canvas');
  this.canvas.width = Math.min(1000, level.width * scale);
  this.canvas.height = Math.min(1000, level.height * scale);
  this.canvas.style.display = 'block';
  this.canvas.style.margin = '200px auto 0';
  parent.appendChild(this.canvas);
  this.cx = this.canvas.getContext('2d');
  
  this.level = level;
  // Uses the step size passed to drawFrame()
  this.animationTime = 0;
  this.flipPlayer = false;
  
  this.viewport = {
    left: 0,
    top: 0,
    width: this.canvas.width / scale,
    height: this.canvas.height / scale
  };
  
  this.drawFrame(0);
}
CanvasDisplay.prototype.clear = function() {
  this.canvas.parentNode.removeChild(this.canvas);
};
// Draw the frame
CanvasDisplay.prototype.drawFrame = function(step) {
  this.animationTime += step;
  
  // Update the current player position for the viewport
  this.updateViewport();
  // Clear current frame
  this.clearDisplay();
  // Draw the background
  this.drawBackground();
  // Draw the actors
  this.drawActors();
};
// Scrolls the viewport depending on the player position
CanvasDisplay.prototype.updateViewport = function() {
  var view = this.viewport, margin = view.width / 3;
  var player = this.level.player;
  var center = player.pos.plus(player.size.times(0.5));
  
  // Compare the center.x of player to the viewport
  // Player is less than the left of the viewport plus margin
  if (center.x < view.left + margin)
    // Scroll left until less than 0
    view.left = Math.max(center.x - margin, 0);
  // Player is further right than the right of the viewport plus margin
  else if (center.x > view.left + view.width - margin)
    // Scroll right until greater than the edge of the level
    view.left = Math.min(center.x + margin - view.width,
                        this.level.width - view.width);
  if (center.y < view.top + margin)
    view.top = Math.max(center.y - margin, 0);
  else if (center.y > view.top + view.height - margin)
    view.top = Math.min(center.y + margin - view.height,
                       this.level.height - view.height);
};
CanvasDisplay.prototype.clearDisplay = function() {
  if (this.level.status == 'won')
    this.cx.fillStyle = 'rgb(68, 191, 255)';
  else if (this.level.status == 'lost')
    this.cx.fillStyle = 'rgb(44, 136, 214)';
  else
    this.cx.fillStyle = 'rgb(52, 166, 251)';
  this.cx.fillRect(0, 0,
                  this.canvas.width, this.canvas.height);
};
var otherSprites = document.createElement('img');
otherSprites.src = 'http://eloquentjavascript.net/img/sprites.png';
// Draw the background
CanvasDisplay.prototype.drawBackground = function() {
  var view = this.viewport;
  var xStart = Math.floor(view.left);
  var xEnd = Math.ceil(view.left + view.width);
  var yStart = Math.floor(view.top);
  var yEnd = Math.ceil(view.top + view.height);
  
  for (var y = yStart; y < yEnd; y++) {
    for (var x = xStart; x < xEnd; x++) {
      var tile = this.level.grid[y][x];
      if (tile == null) continue;
      var screenX = (x - view.left) * scale;
      var screenY = (y - view.top) * scale;
      var tileX = tile == 'lava' ? scale : 0;
      this.cx.drawImage(otherSprites,
                       tileX,   0,       scale, scale,
                       screenX, screenY, scale, scale);
    }
  }
};
// Draw the player (called by drawActors())
var playerSprites = document.createElement('img');
playerSprites.src = 'http://eloquentjavascript.net/img/player.png';
var playerXOverlap = 4;
CanvasDisplay.prototype.drawPlayer = function(x, y, width, height) {
  var sprite = 8, player = this.level.player;
  width += playerXOverlap * 2;
  x -= playerXOverlap;
  if (player.speed.x != 0)
    this.flipPlayer = player.speed.x < 0;
  
  if (player.speed.y != 0)
    sprite = 9;
  else if (player.speed.x != 0)
    sprite = Math.floor(this.animationTime * 12) % 8;
  
  this.cx.save();
  if (this.flipPlayer)
    flipHorizontally(this.cx, x + width / 2);
  
  this.cx.drawImage(playerSprites,
                   sprite * width, 0, width, height,
                   x,              y, width, height);
  this.cx.restore();
}
// Draw the actors
CanvasDisplay.prototype.drawActors = function() {
  this.level.actors.forEach(function(actor) {
    var width = actor.size.x * scale;
    var height = actor.size.y * scale;
    var x = (actor.pos.x - this.viewport.left) * scale;
    var y = (actor.pos.y - this.viewport.top) * scale;
    if (actor.type == 'player') {
      this.drawPlayer(x, y, width, height);
    } else {
      var tileX = (actor.type == 'coin' ? 2 : 1) * scale;
      this.cx.drawImage(otherSprites,
                       tileX, 0, width, height,
                       x,     y, width, height);
    }
  }, this);
};


// Run game with DOMDisplay
// runGame(GAME_LEVELS, DOMDisplay);

// Run game with CanvasDisplay
runGame(GAME_LEVELS, CanvasDisplay);
</script>

完整代码:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
 <HEAD>
  <TITLE> New Document </TITLE>
  <META NAME="Generator" CONTENT="EditPlus">
  <META NAME="Author" CONTENT="">
  <META NAME="Keywords" CONTENT="">
  <META NAME="Description" CONTENT="">
  <style type="text/css">
  *body{margin:0 auto;}
  #status{
    position: absolute;
    left: 50%;
    top: 170px;
    margin-left: 60px;
  }
.background {
  background: rgb(52, 166, 251);
  table-layout: fixed;
  border-spacing: 0;
}
.background td { padding: 0; }
.lava { background: rgb(255, 100, 100); }
.wall { background: white; }
.actor { position: absolute; }
.coin { background: rgb(241, 229, 89); }
.player { background: rgb(64, 64, 64); }
.lost .player { background: rgb(160, 64, 64); }
.won .player {
  box-shadow: -4px -7px 8px white, 4px -7px 8px white;
}
.game {
  overflow: hidden;
  max-width: 600px;
  max-height: 450px;
  position: relative;
}
  </style>
</head>

<body>
 <div id='status'>How many lives you have left: <span id="lives"></span></div>
<script type="text/javascript">
  
  /*
Based on Dark Blue by Thomas Palef.

Plans are submitted as an array of strings that forms a grid.


@ = 玩家
o = 金币
x = 墙壁
! = 岩浆
= = 左右移动岩浆
v = 向下移动岩浆
| = 上下移动岩浆

*/
var simpleLevelPlan = [
  "                      ",
  "                      ",
  "  x              = x  ",
  "  x         o o    x  ",
  "  x @      xxxxx   x  ",
  "  xxxxx            x  ",
  "      x!!!!!!!!!!!!x  ",
  "      xxxxxxxxxxxxxx  ",
  "                      "
];

var GAME_LEVELS = [
  ["                                                                                ",
   "                                                                                ",
   "                                                                                ",
   "                                                                                ",
   "                                                                                ",
   "                                                                                ",
   "                                                                  xxx           ",
   "                                                   xx      xx    xx!xx          ",
   "                                    o o      xx                  x!!!x          ",
   "                                                                 xx!xx          ",
   "                                   xxxxx                          xvx           ",
   "                                                                            xx  ",
   "  xx                                      o o                                x  ",
   "  x                     o                                                    x  ",
   "  x                                      xxxxx                             o x  ",
   "  x          xxxx       o                                                    x  ",
   "  x  @       x  x                                                xxxxx       x  ",
   "  xxxxxxxxxxxx  xxxxxxxxxxxxxxx   xxxxxxxxxxxxxxxxxxxx     xxxxxxx   xxxxxxxxx  ",
   "                              x   x                  x     x                    ",
   "                              x!!!x                  x!!!!!x                    ",
   "                              x!!!x                  x!!!!!x                    ",
   "                              xxxxx                  xxxxxxx                    ",
   "                                                                                ",
   "                                                                                "],
  ["                                      x!!x                        xxxxxxx                                    x!x  ",
   "                                      x!!x                     xxxx     xxxx                                 x!x  ",
   "                                      x!!xxxxxxxxxx           xx           xx                                x!x  ",
   "                                      xx!!!!!!!!!!xx         xx             xx                               x!x  ",
   "                                       xxxxxxxxxx!!x         x                                    o   o   o  x!x  ",
   "                                                xx!x         x     o   o                                    xx!x  ",
   "                                                 x!x         x                                xxxxxxxxxxxxxxx!!x  ",
   "                                                 xvx         x     xxxxx                        !!!!!!!!!!!!!!xx  ",
   "                                                             xx             xx            xxxxxxxxxxxxxxxxxxxxx   ",
   "                                                              xx!!!!!!!!!!!xx            v                        ",
   "                                                               xxxx!!!!!xxxx                                      ",
   "                                               x     x            xxxxxxx        xxx         xxx                  ",
   "                                               x     x                           x x         x x                  ",
   "                                               x     x                             x         x                    ",
   "                                               x     x                             xx        x                    ",
   "                                               xx    x                             x         x                    ",
   "                                               x     x      o  o     x   x         x         x                    ",
   "               xxxxxxx        xxx   xxx        x     x               x   x         x         x                    ",
   "              xx     xx         x   x          x     x     xxxxxx    x   x   xxxxxxxxx       x                    ",
   "             xx       xx        x o x          x    xx               x   x   x               x                    ",
   "     @       x         x        x   x          x     x               x   x   x               x                    ",
   "    xxx      x         x        x   x          x     x               x   xxxxx   xxxxxx      x                    ",
   "    x x      x         x       xx o xx         x     x               x     o     x x         x                    ",
   "!!!!x x!!!!!!x         x!!!!!!xx     xx!!!!!!!!xx    x!!!!!!!!!!     x     =     x x         x                    ",
   "!!!!x x!!!!!!x         x!!!!!xx       xxxxxxxxxx     x!!!!!!!xx!     xxxxxxxxxxxxx xx  o o  xx                    ",
   "!!!!x x!!!!!!x         x!!!!!x    o                 xx!!!!!!xx !                    xx     xx                     ",
   "!!!!x x!!!!!!x         x!!!!!x                     xx!!!!!!xx  !                     xxxxxxx                      ",
   "!!!!x x!!!!!!x         x!!!!!xx       xxxxxxxxxxxxxx!!!!!!xx   !                                                  ",
   "!!!!x x!!!!!!x         x!!!!!!xxxxxxxxx!!!!!!!!!!!!!!!!!!xx    !                                                  ",
   "!!!!x x!!!!!!x         x!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!xx     !                                                  "],
  ["                                                                                                              ",
   "                                                                                                              ",
   "                                                                                                              ",
   "                                                                                                              ",
   "                                                                                                              ",
   "                                                                                                              ",
   "                                                                                                              ",
   "                                        x                                                                     ",
   "                                        x                                                                     ",
   "                                        x                                                                     ",
   "                                        x                                                                     ",
   "                                       xxx                                  o                                 ",
   "                                       x x                 !!!        !!!  xxx                                ",
   "                                     o x x                 !x!        !x!                                     ",
   "                                     xxx xxx                x          x        o                             ",
   "                                      x   x                 x          x       xxx                            ",
   "                                      x   x                 x          x      x!!!x                           ",
   "                                    o x   x                 xxxxxxxxxxxx       xxx                            ",
   "                                    xxx   xx      x   x      x              o                                 ",
   "                                      x   xxxxxxxxx   xxxxxxxx              x x                               ",
   "                                      x   x         o x                    x!!!x                              ",
   "                                   o  x   x           x                     xxx                               ",
   "                                   xxxx   xx          x                                                       ",
   "                                      x   x= = = =    x            xxx                                        ",
   "                                      x   x           x           x!!!x       o                               ",
   "                                  o   x   x    = = = =x            xxx       xxx                              ",
   "                                  xxxxx   xx          x                     x!!!x                             ",
   "                                      x   x           x     x                xxv        xxx                   ",
   "                                      x   x           x              x                 x!!!x                  ",
   "                             xxx xxx xxx xxx          x!!!!!!!!!!!!!!x                   vx                   ",
   "                             x xxx x x xxx x          x!!!!!!!!!!!!!!x                                        ",
   "                             x             x   xxxxxxxxxxxxxxxxxxxxxxx                o                       ",
   "                             xx     o     xx                                         xxx                      ",
   "  xxx                         x     x     x                                         x!!!x                xxx  ",
   "  x x                         x    xxx    x    o                           o         xxx                 x x  ",
   "  x                           x    xxx    xxxxxxx                        xxxxx                             x  ",
   "  x                           x           x                              x   x                             x  ",
   "  x                           xx          x          o         o        ox x x                             x  ",
   "  x                                       x       |xxxx|    |xxxx|     xxx xxx                             x  ",
   "  x                xxx                    x                              x         xxx                     x  ",
   "  x               xxxxx       xx          x                             xxx       x!!!x          x         x  ",
   "  x               oxxxo       x    xxx    x                             x x        xxx          xxx        x  ",
   "  x                xxx        xxxxxxxxxxxxx  x    x    x    x    x     xx xx                    xxx        x  ",
   "  x      @          x         x           x!!x    x!!!!x    x!!!!x    xx   xx                    x         x  ",
   "  xxxxxxxxxxxxxxxxxxxxxxxxxxxxx           xxxxxxxxxxxxxxxxxxxxxxxxxxxxx     xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx  ",
   "                                                                                                              ",
   "                                                                                                              "],
  ["                                                                                                  xxx x       ",
   "                                                                                                      x       ",
   "                                                                                                  xxxxx       ",
   "                                                                                                  x           ",
   "                                                                                                  x xxx       ",
   "                          o                                                                       x x x       ",
   "                                                                                             o o oxxx x       ",
   "                   xxx                                                                                x       ",
   "       !  o  !                                                xxxxx xxxxx xxxxx xxxxx xxxxx xxxxx xxxxx       ",
   "       x     x                                                x   x x   x x   x x   x x   x x   x x           ",
   "       x= o  x            x                                   xxx x xxx x xxx x xxx x xxx x xxx x xxxxx       ",
   "       x     x                                                  x x   x x   x x   x x   x x   x x     x       ",
   "       !  o  !            o                                  xxxx xxxxx xxxxx xxxxx xxxxx xxxxx xxxxxxx       ",
   "                                                                                                              ",
   "          o              xxx                              xx                                                  ",
   "                                                                                                              ",
   "                                                                                                              ",
   "                                                      xx                                                      ",
   "                   xxx         xxx                                                                            ",
   "                                                                                                              ",
   "                          o                                                     x      x                      ",
   "                                                          xx     xx                                           ",
   "             xxx         xxx         xxx                                 x                  x                 ",
   "                                                                                                              ",
   "                                                                 ||                                           ",
   "  xxxxxxxxxxx                                                                                                 ",
   "  x         x o xxxxxxxxx o xxxxxxxxx o xx                                                x                   ",
   "  x         x   x       x   x       x   x                 ||                  x     x                         ",
   "  x  @      xxxxx   o   xxxxx   o   xxxxx                                                                     ",
   "  xxxxxxx                                     xxxxx       xx     xx     xxx                                   ",
   "        x=                  =                =x   x                     xxx                                   ",
   "        xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx   x!!!!!!!!!!!!!!!!!!!!!xxx!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!",
   "                                                  xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
   "                                                                                                              "]
];

// Assumes the plan is valid (add validation for production)
function Level(plan) {
  this.width = plan[0].length;
  this.height = plan.length;
  // Array of arrays, each position containing null or a character
  this.grid = [];
  // Contains all of the dynamic objects (lava, coin or player),
  // along with their position and state
  this.actors = [];
  
  for (var y = 0; y < this.height; y++) {
    var line = plan[y], gridLine = [];
    for (var x = 0; x < this.width; x++) {
      var ch = line[x], fieldType = null;
      var Actor = actorChars[ch];
      if (Actor)
        // This constructs the referenced moving object in
        // actorChars and pushes it to the actors array
        this.actors.push(new Actor(new Vector(x, y), ch));
      else if (ch == 'x')
        // Wall
        fieldType = 'wall';
      else if (ch == '!')
        // Stationary lava
        fieldType = 'lava';
      gridLine.push(fieldType);
    }
    this.grid.push(gridLine);
  }
  // Find the Player actor
  this.player = this.actors.filter(function(actor) {
    return actor.type == 'player';
  })[0];
  // Track whether the player has won or lost;
  // finishDelay keeps the level active for a brief period of time
  this.status = this.finishDelay = null;
}
// Figure out if the level is finished
Level.prototype.isFinished = function() {
  return this.status != null && this.finishDelay < 0;
};
// Figure out the collision area of an actor
Level.prototype.obstacleAt = function(pos, size) {
  var xStart = Math.floor(pos.x);
  var xEnd = Math.ceil(pos.x + size.x);
  var yStart = Math.floor(pos.y);
  var yEnd = Math.ceil(pos.y + size.y);
  
  // The upper and side bounds of the level return a wall
  if (xStart < 0 || xEnd > this.width || yStart < 0)
    return 'wall';
  // The bottom bound returns lava
  if (yEnd > this.height)
    return 'lava';
  // Check what's on the grid around the collision box
  for (var y = yStart; y < yEnd; y++) {
    for (var x = xStart; x < xEnd; x++) {
      var fieldType = this.grid[y][x];
      // returns 'wall' or 'lava'
      if (fieldType) return fieldType;
    }
  }
};
// Track what actors overlap a given actor
Level.prototype.actorAt = function(actor) {
  for (var i = 0; i < this.actors.length; i++) {
    var other = this.actors[i];
    if (other != actor &&
        actor.pos.x + actor.size.x > other.pos.x &&
        actor.pos.x < other.pos.x + other.size.x &&
        actor.pos.y + actor.size.y > other.pos.y &&
        actor.pos.y < other.pos.y + other.size.y)
      return other;
  }
};
// Time increment for Level.animate()
var maxStep = 0.05;
// Animate the level
// step will be given in seconds, keys is an object that
// contains info about the arrow keys the player has pressed.
Level.prototype.animate = function(step, keys) {
  // Used for the delay at the end of a game
  if (this.status != null)
    this.finishDelay -= step;
  
  while (step > 0) {
    var thisStep = Math.min(step, maxStep);
    this.actors.forEach(function(actor) {
      actor.act(thisStep, this, keys);
    }, this);
    step -= thisStep;
  }
};
// Handle collisions between actors
Level.prototype.playerTouched = function(type, actor) {
  // Lava's been touched
  if (type == 'lava' && this.status == null) {
    this.status = 'lost';
    this.finishDelay = 1;
  } else if (type == 'coin') { // a coin's been touched
    // Remove the coin that's been collected from actors array
    this.actors = this.actors.filter(function(other) {
      return other != actor;
    });
    // No more coins = you've won
    if (!this.actors.some(function(actor) {
      return actor.type == 'coin';
    })) {
      this.status = 'won';
      this.finishDelay = 1;
    }
  }
}

var actorChars = {
  '@': Player,
  'o': Coin,
  '=': Lava, '|': Lava, 'v': Lava
};

// Vector stores the position and size of an actor
function Vector(x, y) {
  this.x = x; this.y = y;
}
Vector.prototype.plus = function(other) {
  return new Vector(this.x + other.x, this.y + other.y);
}
Vector.prototype.times = function(factor) {
  return new Vector(this.x * factor, this.y * factor);
}

// Player constructor
function Player(pos) {
  // This properly aligns the bottom of the player
  // to the square below (adjusts for player height)
  this.pos = pos.plus(new Vector(0, -0.5));
  this.size = new Vector(0.8, 1.5);
  this.speed = new Vector(0, 0);
}
Player.prototype.type = 'player';
// X and Y movement are handled independently because
// a wall shouldn't stop the up and down motion of jumping
// and a floor shouldn't stop side to side movement
var playerXSpeed = 7;
Player.prototype.moveX = function(step, level, keys) {
  this.speed.x = 0;
  if (keys.left) this.speed.x -= playerXSpeed;
  if (keys.right) this.speed.x += playerXSpeed;
  
  var motion = new Vector(this.speed.x * step, 0);
  var newPos = this.pos.plus(motion);
  var obstacle = level.obstacleAt(newPos, this.size);
  if (obstacle)
    level.playerTouched(obstacle);
  else
    this.pos = newPos;
};
var gravity = 30;
var jumpSpeed = 17;
Player.prototype.moveY = function(step, level, keys) {
  this.speed.y += step * gravity;
  var motion = new Vector(0, this.speed.y * step);
  var newPos = this.pos.plus(motion);
  var obstacle = level.obstacleAt(newPos, this.size);
  if (obstacle) {
    level.playerTouched(obstacle);
    if (keys.up && this.speed.y > 0)
      this.speed.y = -jumpSpeed;
    else
      this.speed.y = 0;
  } else {
    this.pos = newPos;
  }
};
Player.prototype.act = function(step, level, keys) {
  this.moveX(step, level, keys);
  this.moveY(step, level, keys);
  
  var otherActor = level.actorAt(this);
  if (otherActor)
    level.playerTouched(otherActor.type, otherActor);
  
  // Losing animation
  if (level.status == 'lost') {
    this.pos.y += step;
    this.size.y -=step;
  }
};

// Lava constructor
function Lava(pos, ch) {
  this.pos = pos;
  this.size = new Vector(1, 1);
  if (ch == "=") {
    // I'm guessing that speed will be added to this.pos
    // in a moving method added later
    this.speed = new Vector(2, 0);
  } else if (ch == '|') {
    this.speed = new Vector(0, 2);
  } else if (ch == 'v') {
    this.speed = new Vector(0, 3);
    this.repeatPos = pos;
  }
}
Lava.prototype.type = 'lava';
// Action
Lava.prototype.act = function(step, level) {
  var newPos = this.pos.plus(this.speed.times(step));
  if (!level.obstacleAt(newPos, this.size))
    this.pos = newPos;
  else if (this.repeatPos)
    this.pos = this.repeatPos;
  else
    this.speed = this.speed.times(-1);
};

// Coin constructor
function Coin(pos) {
  this.basePos = this.pos = pos.plus(new Vector(0.2, 0.1));
  this.size = new Vector(0.6, 0.6);
  this.wobble = Math.random() * Math.PI * 2;
}
Coin.prototype.type = 'coin';
var wobbleSpeed = 8, wobbleDist = 0.07;
// Action
Coin.prototype.act = function(step) {
  this.wobble += step * wobbleSpeed;
  var wobblePos = Math.sin(this.wobble) * wobbleDist;
  this.pos = this.basePos.plus(new Vector(0, wobblePos));
};

// Helper function that creates an element
// and gives it a class
function elt(name, className) {
  var elt = document.createElement(name);
  if (className) elt.className = className;
  return elt;
}

// Tracking keypresses for player movement
var arrowCodes = {37: 'left', 38: 'up', 39: 'right'};

function trackKeys(codes) {
  var pressed = Object.create(null);
  function handler(event) {
    if (codes.hasOwnProperty(event.keyCode)) {
      var down = event.type == 'keydown';
      pressed[codes[event.keyCode]] = down;
      event.preventDefault();
    }
  }
  addEventListener('keydown', handler);
  addEventListener('keyup', handler);
  
  pressed.unregister = function() {
    removeEventListener('keydown', handler);
    removeEventListener('keyup', handler);
  };
  
  return pressed;
}

// Run the animation
function runAnimation(frameFunc) {
  var lastTime = null;
  function frame(time) {
    var stop = false;
    if (lastTime != null) {
      var timeStep = Math.min(time - lastTime, 100) / 1000;
      stop = frameFunc(timeStep) === false;
    }
    lastTime = time;
    if (!stop)
      requestAnimationFrame(frame);
  }
  requestAnimationFrame(frame);
}

// Run the level
var arrows = trackKeys(arrowCodes);

function runLevel(level, Display, andThen) {
  var display = new Display(document.body, level);
  // Used for storing pause state of the game
  var running = 'yes';
  
  function handleKey(event) {
    if (event.keyCode == 27) {
      if (running == 'no') {
        running = 'yes';
        runAnimation(animation);
      } else if (running == 'pausing') {
        running = 'yes';
      } else if (running == 'yes') {
        running = 'pausing';
      }
    }
  }
  addEventListener('keydown', handleKey);
  
  function animation(step) {
    if (running == 'pausing') {
      running = 'no';
      return false;
    }
    
    level.animate(step, arrows);
    display.drawFrame(step);
    if (level.isFinished()) {
      display.clear();
      // Remove the watch on the esc key 
      //removeEventListener('keydown', handleKey);
      // Unregister the arrow key listeners
      //arrows.unregister();
      if (andThen)
        andThen(level.status);
      return false;
    }
  }
  
  runAnimation(animation);
}

// Run the game
function runGame(plans, Display) {
  var lives = 9999999999999999999;
  var livesSpan = document.getElementById('lives');
  var gameStatus = document.getElementById('status');
  
  function startLevel(n) {
    livesSpan.textContent = lives;
    runLevel(new Level(plans[n]), Display, function(status) {
      if (status == 'lost') {
        lives--;
        if (lives == 0) {
          gameStatus.textContent = 'Game Over';
          console.log('Game over');
        }
        else 
          startLevel(n);
      }
      else if (n < plans.length - 1)
        startLevel(n + 1);
      else
        console.log('You win!');
    });
  }
  startLevel(0);
}


// DOMDisplay uses the DOM to draw the program out
function DOMDisplay(parent, level) {
  this.wrap = parent.appendChild(elt('div', 'game'));
  this.level = level;
  
  // Background is drawn only once
  this.wrap.appendChild(this.drawBackground());
  // The actorLayer is animated in the drawFrame() method
  this.actorLayer = null;
  this.drawFrame();
}
// Set the scale of 1 grid unit
var scale = 20;
// Draw the background
DOMDisplay.prototype.drawBackground = function() {
  var table = elt('table', 'background');
  table.style.width = this.level.width * scale + 'px';
  this.level.grid.forEach(function(row) {
    var rowElt = table.appendChild(elt('tr'));
    rowElt.style.height = scale + 'px';
    row.forEach(function(type) {
      rowElt.appendChild(elt('td', type));
    });
  });
  return table;
};
// Draw the actors
DOMDisplay.prototype.drawActors = function() {
  var wrap = elt('div');
  this.level.actors.forEach(function(actor) {
    var rect = wrap.appendChild(elt('div', 'actor ' + actor.type));
    rect.style.width  = actor.size.x * scale + 'px';
    rect.style.height = actor.size.y * scale + 'px';
    rect.style.left   = actor.pos.x * scale + 'px';
    rect.style.top    = actor.pos.y * scale + 'px';
  });
  return wrap;
};
// Redraw the actors
DOMDisplay.prototype.drawFrame = function() {
  if (this.actorLayer)
    this.wrap.removeChild(this.actorLayer);
  this.actorLayer = this.wrap.appendChild(this.drawActors());
  // The status class is used to style the player based on
  // the state of the game (won or lost)
  this.wrap.className = 'game ' + (this.level.status || '');
  this.scrollPlayerIntoView();
};
// Make sure the player's always on screen
DOMDisplay.prototype.scrollPlayerIntoView = function() {
  var width = this.wrap.clientWidth;
  var height = this.wrap.clientHeight;
  var margin = width / 3;
  
  // The viewport
  var left = this.wrap.scrollLeft, right = left + width;
  var top = this.wrap.scrollTop, bottom = top + height;
  
  // center makes use of the Vector methods defined earlier
  var player = this.level.player;
  var center = player.pos.plus(player.size.times(0.5))
                  .times(scale);
  
  if (center.x < left + margin)
    this.wrap.scrollLeft = center.x - margin;
  else if (center.x > right - margin)
    this.wrap.scrollLeft = center.x + margin - width;
  if (center.y < top + margin)
    this.wrap.scrollTop = center.y - margin;
  else if (center.y > bottom - margin)
    this.wrap.scrollTop = center.y + margin - height;
}
// Clear the level
DOMDisplay.prototype.clear = function() {
  this.wrap.parentNode.removeChild(this.wrap);
};

// Canvas API helper function
function flipHorizontally(context, around) {
  context.translate(around, 0);
  context.scale(-1, 1);
  context.translate(-around, 0);
}
// Use Canvas API to draw game
function CanvasDisplay(parent, level) {
  // Set up the canvas element
  this.canvas = document.createElement('canvas');
  this.canvas.width = Math.min(1000, level.width * scale);
  this.canvas.height = Math.min(1000, level.height * scale);
  this.canvas.style.display = 'block';
  this.canvas.style.margin = '200px auto 0';
  parent.appendChild(this.canvas);
  this.cx = this.canvas.getContext('2d');
  
  this.level = level;
  // Uses the step size passed to drawFrame()
  this.animationTime = 0;
  this.flipPlayer = false;
  
  this.viewport = {
    left: 0,
    top: 0,
    width: this.canvas.width / scale,
    height: this.canvas.height / scale
  };
  
  this.drawFrame(0);
}
CanvasDisplay.prototype.clear = function() {
  this.canvas.parentNode.removeChild(this.canvas);
};
// Draw the frame
CanvasDisplay.prototype.drawFrame = function(step) {
  this.animationTime += step;
  
  // Update the current player position for the viewport
  this.updateViewport();
  // Clear current frame
  this.clearDisplay();
  // Draw the background
  this.drawBackground();
  // Draw the actors
  this.drawActors();
};
// Scrolls the viewport depending on the player position
CanvasDisplay.prototype.updateViewport = function() {
  var view = this.viewport, margin = view.width / 3;
  var player = this.level.player;
  var center = player.pos.plus(player.size.times(0.5));
  
  // Compare the center.x of player to the viewport
  // Player is less than the left of the viewport plus margin
  if (center.x < view.left + margin)
    // Scroll left until less than 0
    view.left = Math.max(center.x - margin, 0);
  // Player is further right than the right of the viewport plus margin
  else if (center.x > view.left + view.width - margin)
    // Scroll right until greater than the edge of the level
    view.left = Math.min(center.x + margin - view.width,
                        this.level.width - view.width);
  if (center.y < view.top + margin)
    view.top = Math.max(center.y - margin, 0);
  else if (center.y > view.top + view.height - margin)
    view.top = Math.min(center.y + margin - view.height,
                       this.level.height - view.height);
};
CanvasDisplay.prototype.clearDisplay = function() {
  if (this.level.status == 'won')
    this.cx.fillStyle = 'rgb(68, 191, 255)';
  else if (this.level.status == 'lost')
    this.cx.fillStyle = 'rgb(44, 136, 214)';
  else
    this.cx.fillStyle = 'rgb(52, 166, 251)';
  this.cx.fillRect(0, 0,
                  this.canvas.width, this.canvas.height);
};
var otherSprites = document.createElement('img');
otherSprites.src = 'http://eloquentjavascript.net/img/sprites.png';
// Draw the background
CanvasDisplay.prototype.drawBackground = function() {
  var view = this.viewport;
  var xStart = Math.floor(view.left);
  var xEnd = Math.ceil(view.left + view.width);
  var yStart = Math.floor(view.top);
  var yEnd = Math.ceil(view.top + view.height);
  
  for (var y = yStart; y < yEnd; y++) {
    for (var x = xStart; x < xEnd; x++) {
      var tile = this.level.grid[y][x];
      if (tile == null) continue;
      var screenX = (x - view.left) * scale;
      var screenY = (y - view.top) * scale;
      var tileX = tile == 'lava' ? scale : 0;
      this.cx.drawImage(otherSprites,
                       tileX,   0,       scale, scale,
                       screenX, screenY, scale, scale);
    }
  }
};
// Draw the player (called by drawActors())
var playerSprites = document.createElement('img');
playerSprites.src = 'http://eloquentjavascript.net/img/player.png';
var playerXOverlap = 4;
CanvasDisplay.prototype.drawPlayer = function(x, y, width, height) {
  var sprite = 8, player = this.level.player;
  width += playerXOverlap * 2;
  x -= playerXOverlap;
  if (player.speed.x != 0)
    this.flipPlayer = player.speed.x < 0;
  
  if (player.speed.y != 0)
    sprite = 9;
  else if (player.speed.x != 0)
    sprite = Math.floor(this.animationTime * 12) % 8;
  
  this.cx.save();
  if (this.flipPlayer)
    flipHorizontally(this.cx, x + width / 2);
  
  this.cx.drawImage(playerSprites,
                   sprite * width, 0, width, height,
                   x,              y, width, height);
  this.cx.restore();
}
// Draw the actors
CanvasDisplay.prototype.drawActors = function() {
  this.level.actors.forEach(function(actor) {
    var width = actor.size.x * scale;
    var height = actor.size.y * scale;
    var x = (actor.pos.x - this.viewport.left) * scale;
    var y = (actor.pos.y - this.viewport.top) * scale;
    if (actor.type == 'player') {
      this.drawPlayer(x, y, width, height);
    } else {
      var tileX = (actor.type == 'coin' ? 2 : 1) * scale;
      this.cx.drawImage(otherSprites,
                       tileX, 0, width, height,
                       x,     y, width, height);
    }
  }, this);
};


// Run game with DOMDisplay
// runGame(GAME_LEVELS, DOMDisplay);

// Run game with CanvasDisplay
runGame(GAME_LEVELS, CanvasDisplay);
</script>

</body>
</html>

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值