当前位置:嗨网首页>书籍在线阅读

01-完整代码列表

  
选择背景色: 黄橙 洋红 淡粉 水蓝 草绿 白色 选择字体: 宋体 黑体 微软雅黑 楷体 选择字体大小: 恢复默认

附录 完整代码列表

1.第7章代码

例A-1 空间掠夺者,包含动态网络声音优化和状态加载器

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CH7EX9: Space Raiders With Optimized Dynamic Network
    Sound And State Loader</title>
<script src="modernizr.js"></script>
<script type="text/javascript">
window.addEventListener('load', eventWindowLoaded, false);
function eventWindowLoaded() {
  canvasApp();
}
function supportedAudioFormat(audio) {
  var returnExtension = "";
  if (audio.canPlayType("audio/ogg") =="probably" ||
    audio.canPlayType("audio/ogg") == "maybe") {
     returnExtension = "ogg"; } else if(audio.canPlayType("audio/wav")
      =="probably" ||
    audio.canPlayType("audio/wav") == "maybe") {
     returnExtension = "wav";
  } else if(audio.canPlayType("audio/mp3") == "probably" ||
    audio.canPlayType("audio/mp3") == "maybe") {
     returnExtension = "mp3";
  }
return returnExtension;
}
function canvasSupport () {
   return Modernizr.canvas;
}
function canvasApp() {
  var STATE_INIT = 10;
  var STATE_LOADING = 20;
  var STATE_RESET = 30;
  var STATE_PLAYING = 40;
  var appState = STATE_INIT;
  var loadCount= 0;
  var itemsToLoad = 0;
  var alienImage = new Image();
  var missileImage = new Image();
  var playerImage = new Image();
  var SOUND_EXPLODE = "explode1";
  var SOUND_SHOOT = "shoot1";
  var MAX_SOUNDS = 6;
  var soundPool = new Array();
  var explodeSound ;
  var explodeSound2 ;
  var explodeSound3 ;
  var shootSound;
  var shootSound2;
  var shootSound3;
  var audioType;
  var mouseX;
  var mouseY;
  var player = {x:250,y:475};
  var aliens = new Array();
  var missiles = new Array();
  var ALIEN_START_X = 25;
  var ALIEN_START_Y = 25;
  var ALIEN_ROWS = 5;
  var ALIEN_COLS = 8;
  var ALIEN_SPACING = 40;
  if (!canvasSupport()) {
     return;
     }
  var theCanvas = document.getElementById("canvasOne");
  var context = theCanvas.getContext("2d");
  function itemLoaded(event) {
  loadCount++;
  if (loadCount >= itemsToLoad) {
   shootSound.removeEventListener("canplaythrough",itemLoaded, false);
   shootSound2.removeEventListener("canplaythrough",itemLoaded, false);
   shootSound3.removeEventListener("canplaythrough",itemLoaded, false);
   explodeSound.removeEventListener("canplaythrough",itemLoaded,false);
   explodeSound2.removeEventListener("canplaythrough",itemLoaded,false);
   explodeSound3.removeEventListener("canplaythrough",itemLoaded,false);
   soundPool.push({name:"explode1", element:explodeSound, played:false});
   soundPool.push({name:"explode1", element:explodeSound2, played:false});
   soundPool.push({name:"explode1", element:explodeSound3, played:false});
   soundPool.push({name:"shoot1", element:shootSound, played:false});
   soundPool.push({name:"shoot1", element:shootSound2, played:false});
   soundPool.push({name:"shoot1", element:shootSound3, played:false});
   appState = STATE_RESET;
 }
}
function initApp() {
 loadCount=0;
 itemsToLoad = 9;
 explodeSound = document.createElement("audio");
 document.body.appendChild(explodeSound);
 audioType = supportedAudioFormat(explodeSound);
 explodeSound.addEventListener("canplaythrough",itemLoaded,false);
 explodeSound.setAttribute("src", "explode1." + audioType);
 explodeSound2 = document.createElement("audio");
 document.body.appendChild(explodeSound2);
 explodeSound2.addEventListener("canplaythrough",itemLoaded,false);
 explodeSound2.setAttribute("src", "explode1." + audioType);
 explodeSound3 = document.createElement("audio");
 document.body.appendChild(explodeSound3);
 explodeSound3.addEventListener("canplaythrough",itemLoaded,false);
 explodeSound3.setAttribute("src", "explode1." + audioType);
 shootSound = document.createElement("audio");
 document.body.appendChild(shootSound);
 shootSound.addEventListener("canplaythrough",itemLoaded,false);
 shootSound.setAttribute("src", "shoot1." + audioType);
 shootSound2 = document.createElement("audio");
 document.body.appendChild(shootSound2);
 shootSound2.addEventListener("canplaythrough",itemLoaded,false);
 shootSound2.setAttribute("src", "shoot1." + audioType);
 shootSound3 = document.createElement("audio");
 document.body.appendChild(shootSound3);
 shootSound3.addEventListener("canplaythrough",itemLoaded,false);
 shootSound3.setAttribute("src", "shoot1." + audioType);
 alienImage = new Image();
 alienImage.onload = itemLoaded;
 alienImage.src = "alien.png";
 playerImage = new Image();
 playerImage.onload = itemLoaded;
 playerImage.src = "player.png";
 missileImage = new Image();
 missileImage.onload = itemLoaded;
 missileImage.src = "missile.png";
 appState = STATE_LOADING;
 }
function startLevel() {
  for (var r = 0; r < ALIEN_ROWS; r++) {
    for( var c= 0; c < ALIEN_COLS; c++) {
     aliens.push({speed:2,x:ALIEN_START_X+c*ALIEN_SPACING, y:ALIEN_START_Y+r*
       ALIEN_SPACING,width:alienImage.width, height:alienImage.height});
    }
  }
}
function resetApp() {
 playSound(SOUND_EXPLODE,0);
 playSound(SOUND_SHOOT,0);
 startLevel();
 appState = STATE_PLAYING;
}
function drawScreen () {
  //移动导弹
  for (var i=missiles.length−1; i>= 0;i−−) {
    missiles[i].y −= missiles[i].speed;
    if (missiles[i].y < (0-missiles[i].height)) {
      missiles.splice(i,1);
    }
}
//移动外星人
for (var i=aliens.length−1; i>= 0;i−−) {
  aliens[i].x += aliens[i].speed;
  if (aliens[i].x > (theCanvas.width-aliens[i].width) || aliens[i].x < 0) {
    aliens[i].speed *= −1;
    aliens[i].y += 20;
  }
  if (aliens[i].y > theCanvas.height) {
    aliens.splice(i,1);
  }
}
//碰撞检测
missile: for (var i=missiles.length−1; i>= 0;i−−) {
  var tempMissile = missiles[i]
  for (var j=aliens.length−1; j>= 0;j−−) {
    var tempAlien =aliens[j];
    if (hitTest(tempMissile,tempAlien)) {
      playSound(SOUND_EXPLODE,.5);
      missiles.splice(i,1);
      aliens.splice(j,1);
      break missile;
    }
  }
  if (aliens.length <=0) {
    appState = STATE_RESET;
  }
}
//填充背景
context.fillStyle = "#000000";
context.fillRect(0, 0, theCanvas.width, theCanvas.height);
//边框
context.strokeStyle = "#EEEEEE";
context.strokeRect(5, 5, theCanvas.width−10, theCanvas.height−10);
//绘制玩家
context.drawImage(playerImage,player.x,player.y);
//绘制导弹
for (var i=missiles.length−1; i>= 0;i−−) {
  context.drawImage(missileImage,missiles[i].x,missiles[i].y);
}
//绘制外星人
for (var i=aliens.length−1; i>= 0;i−−) {
  context.drawImage(alienImage,aliens[i].x,aliens[i].y);
}
//绘制文字
context.fillStyle = "#FFFFFF";
context.fillText ("Active Sounds: " + soundPool.length, 200 ,480);
}
function hitTest(image1,image2) {
  r1left = image1.x;
  r1top = image1.y;
  r1right = image1.x + image1.width;
  r1bottom = image1.y + image1.height;
  r2left = image2.x;
  r2top = image2.y;
  r2right = image2.x + image2.width;
  r2bottom = image2.y + image2.height;
  retval = false;
  if ( (r1left > r2right) || (r1right < r2left) || (r1bottom < r2top) ||
     (r1top > r2bottom) ) {
    retval = false;
  } else {
    retval = true;
  }
  return retval;
}
function eventMouseMove(event) {
  var x;
  var y;
  if (event.pageX || event.pageY) {
    x = event.pageX;
    y = event.pageY;
   } else {
    x = e.clientX + document.body.scrollLeft
      + document.documentElement.scrollLeft;
    y = e.clientY + document.body.scrollTop
      + document.documentElement.scrollTop;
  }
  x -= theCanvas.offsetLeft;
  y -= theCanvas.offsetTop;
  mouseX=x;
  mouseY=y;
  player.x = mouseX;
  player.y = mouseY;
}
function eventMouseUp(event) {
missiles.push({speed:5, x: player.x+.5*playerImage.width,
  y:player.y-missileImage.height,width:missileImage.width,
  height:missileImage.height});
  playSound(SOUND_SHOOT,.5);
}
function playSound(sound,volume) {
  var soundFound = false;
  var soundIndex = 0;
  var tempSound;
  if (soundPool.length> 0) {
    while (!soundFound && soundIndex < soundPool.length) {
     var tSound = soundPool[soundIndex];
     if ((tSound.element.ended || !tSound.played) && tSound.name == sound) {
       soundFound = true;
       tSound.played = true;
     } else {
       soundIndex++;
     }
   }
  }
  if (soundFound) {
    tempSound = soundPool[soundIndex].element;
    tempSound.volume = volume;
    tempSound.play();
  } else if (soundPool.length < MAX_SOUNDS){
    tempSound = document.createElement("audio");
    tempSound.setAttribute("src", sound + "." + audioType);
    tempSound.volume = volume;
    tempSound.play();
    soundPool.push({name:sound, element:tempSound, type:audioType, played:true});
  }
}
function run() {
  switch(appState) {
  case STATE_INIT:
   initApp();
   break;
  case STATE_LOADING:
   //等待回调
   break;
  case STATE_RESET:
   resetApp();
   break;
  case STATE_PLAYING:
   drawScreen();
   break;
 }
}
theCanvas.addEventListener("mouseup",eventMouseUp, false);
theCanvas.addEventListener("mousemove",eventMouseMove, false);
function gameLoop() {
   window.setTimeout(gameLoop, 20);
   run()
 }
 gameLoop();
}
</script>
</head>
<body>
<canvas id="canvasOne" width="500" height="500"
  style="position: absolute; top: 50px; left: 50px;">>
Your browser does not support HTML5 Canvas.
</canvas>
</body>
</html>

2.第9章代码

例A-2 Geo Blaster扩展版完整代码

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CH9EX1: Geo Blaster Extended</title>
<script src="modernizr-1.6.min.js"></script>
<script type="text/javascript">
window.addEventListener('load', eventWindowLoaded, false);
function eventWindowLoaded() {
  canvasApp();
}
function canvasSupport () {
  return Modernizr.canvas;
}
function supportedAudioFormat(audio) {
  var returnExtension = "";
  if (audio.canPlayType("audio/ogg") =="probably" ||
    audio.canPlayType("audio/ogg") == "maybe") {
     returnExtension = "ogg";
  } else if(audio.canPlayType("audio/wav") =="probably" ||
    audio.canPlayType("audio/wav") == "maybe") {
     returnExtension = "wav";
} else if(audio.canPlayType("audio/wav") == "probably" ||
  audio.canPlayType("audio/wav") == "maybe") {
   returnExtension = "mp3";
  }
  return returnExtension;
 }
 function canvasApp(){
  if (!canvasSupport()) {
      return;
   }else{
    theCanvas = document.getElementById("canvas");
    context = theCanvas.getContext("2d");
  }
  //声音
  var SOUND_EXPLODE = "explode1";
  var SOUND_SHOOT = "shoot1";
  var SOUND_SAUCER_SHOOT = "saucershoot"
  var MAX_SOUNDS = 9;
  var soundPool = new Array();
  var explodeSound;
  var explodeSound2;
  var explodeSound3;
  var shootSound;
  var shootSound2;
  var shootSound3;
  var saucershootSound;
  var saucershootSound2;
  var saucershootSound3;
  var audioType;
  //应用程序状态
  var GAME_STATE_INIT = 0;
  var GAME_STATE_WAIT_FOR_LOAD = 5;
  var GAME_STATE_TITLE = 10;
  var GAME_STATE_NEW_GAME = 20;
  var GAME_STATE_NEW_LEVEL = 30;
  var GAME_STATE_PLAYER_START = 40;
  var GAME_STATE_PLAY_LEVEL = 50;
  var GAME_STATE_PLAYER_DIE = 60;
  var GAME_STATE_GAME_OVER = 70;
  var currentGameState = 0;
  var currentGameStateFunction = null;
  //标题屏
  var titleStarted = false;
  //游戏结束屏
  var gameOverStarted = false;
  //游戏对象
  //游戏环境变量
  var score = 0;
  var level = 0; var extraShipAtEach = 10000;
  var extraShipsEarned = 0;
  var playerShips = 3;
  //游戏区域
  var xMin = 0;
  var xMax = 400;
  var yMin = 0;
  var yMax = 400;
  //分值
  var bigRockScore = 50;
  var medRockScore = 75;
  var smlRockScore = 100;
  var saucerScore = 300;
  //陨石大小常量
  var ROCK_SCALE_LARGE = 1;
  var ROCK_SCALE_MEDIUM = 2;
  var ROCK_SCALE_SMALL = 3;
  //创建游戏对象和数组
  var player = {};
  var rocks = [];
  var saucers = [];
  var playerMissiles = [];
  var particles = [];
  var saucerMissiles = [];
  var particlePool = [];
  var maxParticles = 200;
  var newParticle;
  var tempParticle;
  //关卡难度相关
  var levelRockMaxSpeedAdjust = 1;
  var levelSaucerMax = 1;
  var levelSaucerOccurrenceRate = 25;
  var levelSaucerSpeed = 1;
  var levelSaucerFireDelay = 300;
  var levelSaucerFireRate = 30;
  var levelSaucerMissileSpeed = 1;
  //按键数组
  var keyPressList=[];
  //图片表
  var shipTiles;
  var shipTiles2;
  var saucerTiles;
  var largeRockTiles;
  var mediumRockTiles;
  var smallRockTiles;
  var particleTiles;
  //加载
  var loadcCount=0;
  var itemsToLoad=0;
   function itemLoaded(event) {
    loadCount++;
    //console.log("loading:" + loadCount)
    if (loadCount >= itemsToLoad) {
     shootSound.removeEventListener("canplaythrough",itemLoaded, false);
     shootSound2.removeEventListener("canplaythrough",itemLoaded, false);
     shootSound3.removeEventListener("canplaythrough",itemLoaded, false);
     explodeSound.removeEventListener("canplaythrough",itemLoaded,false);
     explodeSound2.removeEventListener("canplaythrough",itemLoaded,false);
     explodeSound3.removeEventListener("canplaythrough",itemLoaded,false);
     saucershootSound.removeEventListener("canplaythrough",itemLoaded,false);
     saucershootSound2.removeEventListener("canplaythrough",itemLoaded,false);
     saucershootSound3.removeEventListener("canplaythrough",itemLoaded,false);
     soundPool.push({name:"explode1", element:explodeSound, played:false});
     soundPool.push({name:"explode1", element:explodeSound2, played:false});
     soundPool.push({name:"explode1", element:explodeSound3, played:false});
     soundPool.push({name:"shoot1", element:shootSound, played:false});
     soundPool.push({name:"shoot1", element:shootSound2, played:false});
     soundPool.push({name:"shoot1", element:shootSound3, played:false});
     soundPool.push({name:"saucershoot", element:saucershootSound,played:false});
     soundPool.push({name:"saucershoot", element:saucershootSound2,played:false});
     soundPool.push({name:"saucershoot", element:saucershootSound3,played:false});
     switchGameState(GAME_STATE_TITLE)
    }
  }
  function playSound(sound,volume) {
   ConsoleLog.log("play sound" + sound);
   var soundFound = false;
   var soundIndex = 0;
   var tempSound;
   if (soundPool.length> 0) {
     while (!soundFound && soundIndex < soundPool.length) {
      var tSound = soundPool[soundIndex];
      if ((tSound.element.ended || !tSound.played) && tSound.name == sound) {
       soundFound = true;
       tSound.played = true;
      } else {
       soundIndex++;
      }
     }
   }
  if (soundFound) {
    ConsoleLog.log("sound found");
    tempSound = soundPool[soundIndex].element;
    //tempSound.setAttribute("src", sound + "." + audioType);
    //tempSound.loop = false;
    //tempSound.volume = volume;
    tempSound.play();
  } else if (soundPool.length < MAX_SOUNDS){
   ConsoleLog.log("sound not found");
   tempSound = document.createElement("audio");
   tempSound.setAttribute("src", sound + "." + audioType);
   tempSound.volume = volume;
   tempSound.play();
   soundPool.push({name:sound, element:tempSound, type:audioType,played:true});
  }
}
function runGame(){
  currentGameStateFunction();
}
function switchGameState(newState) {
  currentGameState = newState;
  switch (currentGameState) {
   case GAME_STATE_INIT:
     currentGameStateFunction = gameStateInit;
     break;
   case GAME_STATE_WAIT_FOR_LOAD:
     currentGameStateFunction = gameStateWaitForLoad;
     break;
   case GAME_STATE_TITLE:
     currentGameStateFunction = gameStateTitle;
     break;
  case GAME_STATE_NEW_GAME:
     currentGameStateFunction = gameStateNewGame;
     break;
  case GAME_STATE_NEW_LEVEL:
    currentGameStateFunction = gameStateNewLevel;
    break;
  case GAME_STATE_PLAYER_START:
    currentGameStateFunction = gameStatePlayerStart;
    break;
  case GAME_STATE_PLAY_LEVEL:
    currentGameStateFunction = gameStatePlayLevel;
    break;
  case GAME_STATE_PLAYER_DIE:
    currentGameStateFunction = gameStatePlayerDie;
    break;
  case GAME_STATE_GAME_OVER:
    currentGameStateFunction = gameStateGameOver;
    break;
  }
}
function gameStateWaitForLoad(){
  //加载事件发生时不做任何事情
  console.log("doing nothing...")
}
function createObjectPools(){
  for (var ctr=0;ctr<maxParticles;ctr++){
   var newParticle = {};
   particlePool.push(newParticle)
  }
  console.log("particlePool=" + particlePool.length)
}
function gameStateInit() {
  createObjectPools();
  loadCount = 0;
  itemsToLoad = 16; //如果在IE中遇到问题可以改为7
  explodeSound = document.createElement("audio");
  document.body.appendChild(explodeSound);
  audioType = supportedAudioFormat(explodeSound);
  explodeSound.setAttribute("src", "explode1." + audioType);
  explodeSound.addEventListener("canplaythrough",itemLoaded,false);
  explodeSound2 = document.createElement("audio");
  document.body.appendChild(explodeSound2);
  explodeSound2.setAttribute("src", "explode1." + audioType);
  explodeSound2.addEventListener("canplaythrough",itemLoaded,false);
  explodeSound3 = document.createElement("audio");
  document.body.appendChild(explodeSound3);
  explodeSound3.setAttribute("src", "explode1." + audioType);
  explodeSound3.addEventListener("canplaythrough",itemLoaded,false);
  shootSound = document.createElement("audio");
  audioType = supportedAudioFormat(shootSound);
  document.body.appendChild(shootSound);
  shootSound.setAttribute("src", "shoot1." + audioType);
  shootSound.addEventListener("canplaythrough",itemLoaded,false);
  shootSound2 = document.createElement("audio");
  document.body.appendChild(shootSound2);
  shootSound2.setAttribute("src", "shoot1." + audioType);
  shootSound2.addEventListener("canplaythrough",itemLoaded,false);
  shootSound3 = document.createElement("audio");
  document.body.appendChild(shootSound3);
  shootSound3.setAttribute("src", "shoot1." + audioType);
  shootSound3.addEventListener("canplaythrough",itemLoaded,false);
  saucershootSound = document.createElement("audio");
  audioType = supportedAudioFormat(saucershootSound);
  document.body.appendChild(saucershootSound);
  saucershootSound.setAttribute("src", "saucershoot." + audioType);
  saucershootSound.addEventListener("canplaythrough",itemLoaded,false);
  saucershootSound2 = document.createElement("audio");
  document.body.appendChild(saucershootSound2);
  saucershootSound2.setAttribute("src", "saucershoot." + audioType);
  saucershootSound2.addEventListener("canplaythrough",itemLoaded,false);
  saucershootSound3 = document.createElement("audio");
  document.body.appendChild(saucershootSound3);
  saucershootSound3.setAttribute("src", "saucershoot." + audioType);
  saucershootSound3.addEventListener("canplaythrough",itemLoaded,false);
  shipTiles = new Image();
  shipTiles.src = "ship_tiles.png";
  shipTiles.onload = itemLoaded;
  shipTiles2 = new Image();
  shipTiles2.src = "ship_tiles2.png";
  shipTiles2.onload = itemLoaded;
  saucerTiles= new Image();
  saucerTiles.src = "saucer.png";
  saucerTiles.onload = itemLoaded;
  largeRockTiles = new Image();
  largeRockTiles.src = "largerocks.png";
  largeRockTiles.onload = itemLoaded;
  mediumRockTiles = new Image();
  mediumRockTiles.src = "mediumrocks.png";
  mediumRockTiles.onload = itemLoaded;
  smallRockTiles = new Image();
  smallRockTiles.src = "smallrocks.png";
  smallRockTiles.onload = itemLoaded;
  particleTiles = new Image();
  particleTiles.src = "parts.png";
  particleTiles.onload = itemLoaded;
  switchGameState(GAME_STATE_WAIT_FOR_LOAD);
}
function gameStateTitle() {
  if (titleStarted !=true){
   fillBackground();
   setTextStyleTitle();
   context.fillText ("Geo Blaster X-ten-d", 120, 70);
   setTextStyle();
   context.fillText ("Press Space To Play", 130, 140);
   setTextStyleCredits();
   context.fillText ("An HTML5 Example Game", 125, 200);
   context.fillText ("From our upcoming HTML5 Canvas", 100, 215);
   context.fillText ("book on O'Reilly Press", 130, 230);
   context.fillText ("Game Code - Jeff Fulton", 130, 260);
   context.fillText ("Sound Manager - Steve Fulton", 120, 275);
   titleStarted = true;
}else{
   //等待用户按下空格键
   if (keyPressList[32]==true){
     ConsoleLog.log("space pressed");
     switchGameState(GAME_STATE_NEW_GAME);
     titleStarted = false;
   }
  }
}
function gameStateNewGame(){
  ConsoleLog.log("gameStateNewGame")
  //设置新游戏
  level = 0;
  score = 0;
  playerShips = 3;
  player.maxVelocity = 5;
  player.width = 32;
  player.height = 32;
  player.halfWidth = 16;
  player.halfHeight = 16;
  player.hitWidth = 24;
  player.hitHeight = 24;
  player.rotationalVelocity = 10; //飞船每次旋转多少度
  player.thrustAcceleration = .05;
  player.missileFrameDelay = 5;
  player.thrust = false;
  player.alpha = 1;
  player.rotation = 0;
  player.x = 0;
  player.y = 0;
  fillBackground();
  renderScoreBoard();
  switchGameState(GAME_STATE_NEW_LEVEL)
}
function gameStateNewLevel(){
  rocks = [];
  saucers = [];
  playerMissiles = [];
  particles = [];
  saucerMissiles = [];
  level++;
  levelRockMaxSpeedAdjust = level*.25;
  if (levelRockMaxSpeedAdjust > 3){
   levelRockMaxSpeed = 3;
  }
  levelSaucerMax = 1+Math.floor(level/10);
  if (levelSaucerMax > 5){
   levelSaucerMax = 5;
  }
  levelSaucerOccurrenceRate = 10+3*level;
  if (levelSaucerOccurrenceRate > 35){
   levelSaucerOccurrenceRate = 35;
  }
  levelSaucerSpeed = 1+.5*level;
  if (levelSaucerSpeed>5){
   levelSaucerSpeed = 5;
  }
  levelSaucerFireDelay = 120-10*level;
  if (levelSaucerFireDelay<20) {
   levelSaucerFireDelay = 20;
  }
  levelSaucerFireRate = 20 + 3*level;
  if (levelSaucerFireRate<50) {
   levelSaucerFireRate = 50;
  }
  levelSaucerMissileSpeed = 1+.2*level;
  if (levelSaucerMissileSpeed > 4){
   levelSaucerMissileSpeed = 4;
  }
  //创建关卡中的陨石
  for (var newRockctr=0;newRockctr<level+3;newRockctr++){
     var newRock = {};
     newRock.scale = 1;
     //大小
     //1=大型
     //2=中型
     //3=小型
     //这些值作为除数,用于计算新陨石的大小
     //50/1 = 50
     //50/2 = 25
     //50/3 = 16
     newRock.width = 64;
     newRock.height = 64;
     newRock.halfWidth = 32;
     newRock.halfHeight = 32;
     newRock.hitWidth = 48;
     newRock.hitHeight = 48;
     //为保证飞船安全,所有新陨石都设置在屏幕的左上方
     newRock.x = Math.floor(Math.random()*50);
     //ConsoleLog.log("newRock.x=" + newRock.x);
     newRock.y = Math.floor(Math.random()*50);
     //ConsoleLog.log("newRock.y=" + newRock.y);
     newRock.dx = (Math.random()*2)+levelRockMaxSpeedAdjust;
     if (Math.random()<.5){
      newRock.dx *= −1;
     }
     newRock.dy=(Math.random()*2)+levelRockMaxSpeedAdjust;
     if (Math.random()<.5){
      newRock.dy *= −1;
     }
     //旋转速度和方向
     if (Math.random()<.5){
      newRock.rotationInc = −1;
     }else{
      newRock.rotationInc = 1;
     }
     newRock.animationDelay = Math.floor(Math.random()*3+1);
     newRock.animationCount = 0;
     newRock.scoreValue = bigRockScore;
     newRock.rotation = 0;
     rocks.push(newRock);
     //ConsoleLog.log("rock created rotationInc=" + newRock.rotationInc);
   }
  resetPlayer();
  switchGameState(GAME_STATE_PLAYER_START);
}
function gameStatePlayerStart(){
  fillBackground();
  renderScoreBoard();
  if (player.alpha < 1){
   player.alpha += .01;
   ConsoleLog.log("player.alpha=" + context.globalAlpha)
  }else{
   switchGameState(GAME_STATE_PLAY_LEVEL);
   player.safe = false; // 在第9章中添加
  }
  //renderPlayerShip(player.x, player.y,270,1);
  context.globalAlpha = 1;
  //第9章的新代码
  checkKeys();
  update();
  render(); //在第9章中添加
  checkCollisions();
  checkForExtraShip();
  checkForEndOfLevel();
  frameRateCounter.countFrames();
}
function gameStatePlayLevel(){
  checkKeys();
  update();
  render();
  checkCollisions();
  checkForExtraShip();
  checkForEndOfLevel();
  frameRateCounter.countFrames();
}
function resetPlayer() {
  player.rotation = 270;
  player.x = .5*xMax;
  player.y = .5*yMax;
  player.facingX = 0;
  player.facingY = 0;
  player.movingX = 0;
  player.movingY = 0;
  player.alpha = 0;
  player.missileFrameCount = 0;
  //在第9章中添加
  player.safe = true;
}
function checkForExtraShip() {
  if (Math.floor(score/extraShipAtEach) > extraShipsEarned) {
   playerShips++
   extraShipsEarned++;
  }
}
function checkForEndOfLevel(){
  if (rocks.length==0) {
   switchGameState(GAME_STATE_NEW_LEVEL);
  }
}
function gameStatePlayerDie(){
  if (particles.length >0 || playerMissiles.length>0) {
   fillBackground();
   renderScoreBoard();
   updateRocks();
   updateSaucers();
   updateParticles();
   updateSaucerMissiles();
   updatePlayerMissiles();
   renderRocks();
   renderSaucers();
   renderParticles();
   renderSaucerMissiles();
   renderPlayerMissiles();
   frameRateCounter.countFrames();
  }else{
   playerShips--;
   if (playerShips<1) {
     switchGameState(GAME_STATE_GAME_OVER);
   }else{
     //resetPlayer();
     switchGameState(GAME_STATE_PLAYER_START);
   }
  }
}
function gameStateGameOver() {
  //ConsoleLog.log("Game Over State");
  if (gameOverStarted !=true){
   fillBackground();
   renderScoreBoard();
   setTextStyle();
   context.fillText ("Game Over!", 160, 70);
   context.fillText ("Press Space To Play", 130, 140);
   gameOverStarted = true;
  }else{
   //等待按下空格键
   if (keyPressList[32]==true){
     ConsoleLog.log("space pressed");
     switchGameState(GAME_STATE_TITLE);
     gameOverStarted = false;
   }
  }
}
function fillBackground() {
  //绘制背景和文字
  context.fillStyle = '#000000';
  context.fillRect(xMin, yMin, xMax, yMax);
}
function setTextStyle() {
  context.fillStyle = '#ffffff';
  context.font = '15px _sans';
  context.textBaseline = 'top';
}
function setTextStyleTitle() {
  context.fillStyle = '#54ebeb';
  context.font = '20px _sans';
  context.textBaseline = 'top';
}
function setTextStyleCredits() {
  context.fillStyle = '#ffffff';
  context.font = '12px _sans';
  context.textBaseline = 'top';
}
function renderScoreBoard() {
  context.fillStyle = "#ffffff";
  context.fillText('Score ' + score, 10, 20);
  renderPlayerShip(200,16,270,.75)
  context.fillText('X ' + playerShips, 220, 20);
  context.fillText('FPS: ' + frameRateCounter.lastFrameCount, 300,20)
}
function checkKeys() {
  //检查按键
  if (keyPressList[38]==true){
  //推进
   var angleInRadians = player.rotation * Math.PI / 180;
   player.facingX = Math.cos(angleInRadians);
   player.facingY = Math.sin(angleInRadians);
   var movingXNew = player.movingX+player.thrustAcceleration*player.facingX;
   var movingYNew = player.movingY+player.thrustAcceleration*player.facingY;
   var currentVelocity = Math.sqrt ((movingXNew*movingXNew) +
   (movingXNew*movingXNew));
   if (currentVelocity < player.maxVelocity) {
     player.movingX = movingXNew;
     player.movingY = movingYNew;
   }
   player.thrust = true;
  }else{
   player.thrust = false;
  }
  if (keyPressList[37]==true) {
   //逆时针旋转
   player.rotation -= player.rotationalVelocity;
   if (player.rotation <0) {
     player.rotation = 350
   }
  }
  if (keyPressList[39]==true) {
   //顺时针旋转
   player.rotation += player.rotationalVelocity;
   if (player.rotation >350) {
     player.rotation = 10
   }
  }
  if (keyPressList[32]==true) {
     if (player.missileFrameCount>player.missileFrameDelay){
     playSound(SOUND_SHOOT,.5);
     firePlayerMissile();
     player.missileFrameCount = 0;
   }
  }
}
function update() {
  updatePlayer();
  updatePlayerMissiles();
  updateRocks();
  updateSaucers();
  updateSaucerMissiles();
  updateParticles();
}
function render() {
  fillBackground();
  renderScoreBoard();
  renderPlayerShip(player.x,player.y,player.rotation,1);
  renderPlayerMissiles();
  renderRocks();
  renderSaucers();
  renderSaucerMissiles();
  renderParticles();
}
function updatePlayer() {
  player.missileFrameCount++;
  player.x += player.movingX*frameRateCounter.step;
  player.y += player.movingY*frameRateCounter.step;
  if (player.x > xMax) {
   player.x =- player.width;
  }else if (player.x<-player.width){
   player.x = xMax;
  }
  if (player.y > yMax) {
   player.y =- player.height;
  }else if (player.y<-player.height){
   player.y = yMax;
  }
}
function updatePlayerMissiles() {
  var tempPlayerMissile = {};
  var playerMissileLength=playerMissiles.length-1;
  //ConsoleLog.log("update playerMissileLength=" + playerMissileLength);
  for (var playerMissileCtr=playerMissileLength;playerMissileCtr>=0;
  playerMissileCtr--){
   //ConsoleLog.log("update player missile" + playerMissileCtr)
   tempPlayerMissile = playerMissiles[playerMissileCtr];
   tempPlayerMissile.x += tempPlayerMissile.dx*frameRateCounter.step;
   tempPlayerMissile.y += tempPlayerMissile.dy*frameRateCounter.step;
   if (tempPlayerMissile.x > xMax) {
     tempPlayerMissile.x =- tempPlayerMissile.width;
   }else if (tempPlayerMissile.x<-tempPlayerMissile.width){
     tempPlayerMissile.x = xMax;
   }
   if (tempPlayerMissile.y > yMax) {
     tempPlayerMissile.y =- tempPlayerMissile.height;
   }else if (tempPlayerMissile.y<-tempPlayerMissile.height){
     tempPlayerMissile.y = yMax;
   }
   tempPlayerMissile.lifeCtr++;
   if (tempPlayerMissile.lifeCtr > tempPlayerMissile.life){
     //ConsoleLog.log("removing player missile");
     playerMissiles.splice(playerMissileCtr,1)
     tempPlayerMissile = null;
   }
  }
}
function updateRocks(){
  var tempRock = {};
  var rocksLength = rocks.length-1;
  //ConsoleLog.log("update rocks length=" + rocksLength);
  for (var rockCtr=rocksLength;rockCtr>=0;rockCtr--){
   tempRock = rocks[rockCtr];
   tempRock.x += tempRock.dx*frameRateCounter.step;
   tempRock.y += tempRock.dy*frameRateCounter.step;
   tempRock.animationCount++;
   if (tempRock.animationCount > tempRock.animationDelay){
     tempRock.animationCount = 0;
     tempRock.rotation += tempRock.rotationInc;
     if (tempRock.rotation > 4){
      tempRock.rotation = 0;
     }else if (tempRock.rotation <0){
      tempRock.rotation = 4;
     }
   }
   if (tempRock.x > xMax) {
     tempRock.x = xMin-tempRock.width;
   }else if (tempRock.x<xMin-tempRock.width){
     tempRock.x = xMax;
   }
   if (tempRock.y > yMax) {
     tempRock.y = yMin-tempRock.width;
   }else if (tempRock.y<yMin-tempRock.width){
     tempRock.y = yMax;
   }
  }
}
function updateSaucers() {
  //首先检查是否要添加一个飞碟
  if (saucers.length< levelSaucerMax){
   if (Math.floor(Math.random()*100)<=levelSaucerOccurrenceRate){
     //ConsoleLog.log("create saucer")
     var newSaucer = {};
     newSaucer.width = 30;
     newSaucer.height = 13;
     newSaucer.halfHeight = 6.5;
     newSaucer.halfWidth = 15;
     newSaucer.hitWidth = 30;
     newSaucer.hitHeight = 13;
     newSaucer.scoreValue = saucerScore;
     newSaucer.fireRate = levelSaucerFireRate;
     newSaucer.fireDelay = levelSaucerFireDelay;
     newSaucer.fireDelayCount = 0;
     newSaucer.missileSpeed = levelSaucerMissileSpeed;
     newSaucer.dy = (Math.random()*2);
     if (Math.floor(Math.random)*2==1){
      newSaucer.dy *= −1;
     }
     //选择起始点是在左边界还是右边界
     if (Math.floor(Math.random()*2)==1){
      //从右边开始向左飞
      newSaucer.x = 450;
      newSaucer.dx = −1*levelSaucerSpeed;
     }else{
      //从左向右
      newSaucer.x = −50;
      newSaucer.dx = levelSaucerSpeed;
     }
     newSaucer.missileSpeed = levelSaucerMissileSpeed;
     newSaucer.fireDelay = levelSaucerFireDelay;
     newSaucer.fireRate = levelSaucerFireRate;
     newSaucer.y = Math.floor(Math.random()*400);
     saucers.push(newSaucer);
   }
  }
  var tempSaucer = {};
  var saucerLength = saucers.length-1;
  //ConsoleLog.log("update rocks length=" + rocksLength);
  for (var saucerCtr=saucerLength;saucerCtr>=0;saucerCtr--){
   tempSaucer = saucers[saucerCtr];
   //飞碟是否应该射击
   tempSaucer.fireDelayCount++;
   if (Math.floor(Math.random()*100) <=tempSaucer.fireRate &&
   tempSaucer.fireDelayCount>tempSaucer.fireDelay ){
     playSound(SOUND_SAUCER_SHOOT,.5);
     fireSaucerMissile(tempSaucer);
     tempSaucer.fireDelayCount=0;
   }
   var remove = false;
   tempSaucer.x += tempSaucer.dx*frameRateCounter.step;
   tempSaucer.y += tempSaucer.dy*frameRateCounter.step;
   //在左边界或右边界移除飞碟
   if (tempSaucer.dx > 0 && tempSaucer.x >xMax){
     remove = true;
   }else if (tempSaucer.dx <0 &&tempSaucer.x<xMin-tempSaucer.width){
     remove = true;
   }
   //飞碟在上下边界
   if (tempSaucer.y > yMax || tempSaucer.y<yMin-tempSaucer.width) {
     tempSaucer.dy *= −1;
   }
   if (remove==true) {
     //移除飞碟
     saucers.splice(saucerCtr,1);
     tempSaucer = null;
   }
  }
}
function updateSaucerMissiles() {
  var tempSaucerMissile = {};
  var saucerMissileLength = saucerMissiles.length-1;
  for (var saucerMissileCtr = saucerMissileLength;saucerMissileCtr>=0;
  saucerMissileCtr--){
   //ConsoleLog.log("update player missile" + playerMissileCtr)
   tempSaucerMissile = saucerMissiles[saucerMissileCtr];
   tempSaucerMissile.x += tempSaucerMissile.dx*frameRateCounter.step;
   tempSaucerMissile.y += tempSaucerMissile.dy*frameRateCounter.step;
   if (tempSaucerMissile.x > xMax) {
     tempSaucerMissile.x =- tempSaucerMissile.width;
   }else if (tempSaucerMissile.x<-tempSaucerMissile.width){
     tempSaucerMissile.x = xMax;
   }
   if (tempSaucerMissile.y > yMax) {
     tempSaucerMissile.y =- tempSaucerMissile.height;
   }else if (tempSaucerMissile.y<-tempSaucerMissile.height){
     tempSaucerMissile.y = yMax;
   }
   tempSaucerMissile.lifeCtr++;
   if (tempSaucerMissile.lifeCtr > tempSaucerMissile.life){
     //移除导弹
     saucerMissiles.splice(saucerMissileCtr,1);
     tempSaucerMissile = null;
   }
  }
}
function updateParticles() {
  var particleLength=particles.length-1;
  for (var particleCtr=particleLength;particleCtr>=0;particleCtr--){
   var remove = false;
   tempParticle = particles[particleCtr];
   tempParticle.x += tempParticle.dx*frameRateCounter.step;
   tempParticle.y += tempParticle.dy*frameRateCounter.step;
   tempParticle.lifeCtr++;
   if (tempParticle.lifeCtr > tempParticle.life){
     remove = true;
   } else if ((tempParticle.x > xMax) || (tempParticle.x<xMin)
     || (tempParticle.y > yMax) || (tempParticle.y<yMin)){
     remove=true;
   }
   if (remove) {
     particlePool.push(tempParticle)
     particles.splice(particleCtr,1)
   }
  }
}
function renderPlayerShip(x,y,rotation, scale) {
  //形状变换
  context.save(); //将当前状态保存到栈中
  context.globalAlpha = parseFloat(player.alpha);
  var angleInRadians = rotation * Math.PI / 180;
  var sourceX = Math.floor((player.rotation/10) % 10) * 32;
  var sourceY = Math.floor((player.rotation/10) /10) *32;
  if (player.thrust){
   context.drawImage(shipTiles2, sourceX, sourceY, 32,32,
   player.x,player.y,32,32);
  }else{
   context.drawImage(shipTiles, sourceX, sourceY, 32,32,
   player.x,player.y,32,32);
  }
  //恢复环境
  context.restore(); //将原有状态恢复到屏幕
  context.globalAlpha = 1;
}
function renderPlayerMissiles() {
  var tempPlayerMissile = {};
  var playerMissileLength=playerMissiles.length-1;
  //ConsoleLog.log("render playerMissileLength=" + playerMissileLength);
  for (var playerMissileCtr=playerMissileLength;playerMissileCtr>=0;
  playerMissileCtr--){
   //ConsoleLog.log("draw player missile " + playerMissileCtr)
   tempPlayerMissile = playerMissiles[playerMissileCtr];
   context.save(); //将当前状态保存到栈中
   var sourceX=Math.floor(1 % 4) * tempPlayerMissile.width;
   var sourceY=Math.floor(1 / 4) * tempPlayerMissile.height;
   context.drawImage(particleTiles, sourceX, sourceY,
   tempPlayerMissile.width,tempPlayerMissile.height,
   tempPlayerMissile.x,tempPlayerMissile.y,
   tempPlayerMissile.width,tempPlayerMissile.height);
   context.restore(); //将原有状态恢复到屏幕中
  }
}
function renderRocks() {
  var tempRock = {};
  var rocksLength = rocks.length-1;
  for (var rockCtr = rocksLength;rockCtr>=0;rockCtr--){
   context.save(); //将当前状态保存到栈中
   tempRock = rocks[rockCtr];
   var sourceX = Math.floor((tempRock.rotation) % 5) * tempRock.width;
   var sourceY = Math.floor((tempRock.rotation) /5) *tempRock.height;
   switch(tempRock.scale){
     case 1:
      context.drawImage(largeRockTiles, sourceX, sourceY,
       tempRock.width,tempRock.height,tempRock.x,tempRock.y,
       tempRock.width,tempRock.height);
      break;
     case 2:
      context.drawImage(mediumRockTiles, sourceX,
       sourceY,tempRock.width,tempRock.height,tempRock.x,tempRock.y,
       tempRock.width,tempRock.height);
      break;
     case 3:
      context.drawImage(smallRockTiles, sourceX,
       sourceY,tempRock.width,tempRock.height,tempRock.x,tempRock.y,
       tempRock.width,tempRock.height);
      break;
   }
   context.restore(); //将原有状态恢复到屏幕中
  }
}
function renderSaucers() {
  var tempSaucer = {};
  var saucerLength = saucers.length-1;
  for (var saucerCtr = saucerLength;saucerCtr>=0;saucerCtr--){
   //ConsoleLog.log("saucer: " + saucerCtr);
   tempSaucer = saucers[saucerCtr];
   context.save();//将当前状态保存到栈中
   var sourceX = 0;
   var sourceY = 0;
   context.drawImage(saucerTiles, sourceX, sourceY, 30,15,
    tempSaucer.x,tempSaucer.y,30,15);
   context.restore();//将原有状态恢复到屏幕中
  }
}
function renderSaucerMissiles() {
  var tempSaucerMissile = {};
  var saucerMissileLength = saucerMissiles.length-1;
  //ConsoleLog.log("saucerMissiles= " + saucerMissiles.length)
  for (var saucerMissileCtr=saucerMissileLength;saucerMissileCtr>=0;
  saucerMissileCtr--){
   //ConsoleLog.log("draw player missile " + playerMissileCtr)
   tempSaucerMissile = saucerMissiles[saucerMissileCtr];
   context.save();//将当前状态保存到栈中
   var sourceX = Math.floor(0 % 4) * tempSaucerMissile.width;
   var sourceY = Math.floor(0 / 4) * tempSaucerMissile.height;
   context.drawImage(particleTiles, sourceX, sourceY,
    tempSaucerMissile.width,tempSaucerMissile.height,
    tempSaucerMissile.x,tempSaucerMissile.y,tempSaucerMissile.width,
    tempSaucerMissile.height);
   context.restore();//将原有状态恢复到屏幕中
  }
}
function renderParticles() {
  var tempParticle = {};
  var particleLength = particles.length-1;
  for (var particleCtr=particleLength;particleCtr>=0;particleCtr--){
   tempParticle = particles[particleCtr];
   context.save();//将当前状态保存到栈中
   var tile;
   //console.log("part type=" + tempParticle.type)
   switch(tempParticle.type){
     case 0: //飞碟
      tile = 0;
      break;
     case 1: //大型陨石
      tile = 2
      break;
     case 2: //中型陨石
      tile = 3;
      break;
     case 3: //小型陨石
      tile = 0;
      break;
     case 4: //玩家
      tile = 1;
      break;
   }
   var sourceX = Math.floor(tile % 4) * tempParticle.width;
   var sourceY = Math.floor(tile / 4) * tempParticle.height;
   context.drawImage(particleTiles, sourceX, sourceY,
    tempParticle.width,tempParticle.height,tempParticle.x,
    tempParticle.y,tempParticle.width,tempParticle.height);
   context.restore();//将原有状态恢复到屏幕中
  }
}
function checkCollisions() {
  //先遍历陨石,然后才是导弹
  //因为屏幕上总是有陨石和飞船
  //但不一定总有导弹
  var tempRock = {};
  var tempRock = {}
  var rocksLength = rocks.length-1;
  var tempPlayerMissile = {};
  var playerMissileLength = playerMissiles.length-1;
  var saucerLength = saucers.length-1;
  var tempSaucer = {};
  var saucerMissileLength = saucerMissiles.length-1;
  rocks: for (var rockCtr=rocksLength;rockCtr>=0;rockCtr--){
   tempRock = rocks[rockCtr];
   missiles:for (var playerMissileCtr=playerMissileLength;
    playerMissileCtr>=0;playerMissileCtr--){
     tempPlayerMissile = playerMissiles[playerMissileCtr];
     if (boundingBoxCollide(tempRock,tempPlayerMissile)){
        //ConsoleLog.log("hit rock");
        createExplode(tempRock.x+tempRock.halfWidth,
        tempRock.y+tempRock.halfHeight,10,tempRock.scale);
        if (tempRock.scale<3) {
         splitRock(tempRock.scale+1, tempRock.x, tempRock.y);
        }
        addToScore(tempRock.scoreValue);
        playerMissiles.splice(playerMissileCtr,1);
        tempPlayerMissile = null;
        rocks.splice(rockCtr,1);
        tempRock = null;
        break rocks;
        break missiles;
      }
     }
   saucers:for (var saucerCtr=saucerLength;saucerCtr>=0;saucerCtr--){
     tempSaucer = saucers[saucerCtr];
     if (boundingBoxCollide(tempRock,tempSaucer)){
        //ConsoleLog.log("hit rock");
        createExplode(tempSaucer.x+tempSaucer.halfWidth,
        tempSaucer.y+tempSaucer.halfHeight,10,0);
        createExplode(tempRock.x+tempRock.halfWidth,
        tempRock.y+tempRock.halfHeight,10,tempRock.scale);
        if (tempRock.scale<3) {
         splitRock(tempRock.scale+1, tempRock.x, tempRock.y);
        }
        saucers.splice(saucerCtr,1);
        tempSaucer = null;
        rocks.splice(rockCtr,1);
        tempRock = null;
        break rocks;
        break saucers;
      }
     }
   //飞碟导弹与陨石碰撞
   //此处已完成,所以不需要再次遍历全部陨石
   //这可能是最大的数组
   saucerMissiles:for (var saucerMissileCtr=saucerMissileLength;
            saucerMissileCtr>=0;saucerMissileCtr--){
     tempSaucerMissile = saucerMissiles[saucerMissileCtr];
     if (boundingBoxCollide(tempRock,tempSaucerMissile)){
        //ConsoleLog.log("hit rock");
        createExplode(tempRock.x+tempRock.halfWidth,
        tempRock.y+tempRock.halfHeight,10,tempRock.scale);
        if (tempRock.scale<3) {
         splitRock(tempRock.scale+1, tempRock.x, tempRock.y);
        }
        saucerMissiles.splice(saucerCtr,1);
        tempSaucerMissile = null;
        rocks.splice(rockCtr,1);
        tempRock = null;
        break rocks;
        break saucerMissiles;
      }
     }
   //检查玩家飞船与陨石碰撞
   if (boundingBoxCollide(tempRock,player) && player.safe==false){
     //ConsoleLog.log("hit player");
     createExplode(tempRock.x+tempRock.halfWidth,
     tempRock.halfHeight,10,tempRock.scale);
     addToScore(tempRock.scoreValue);
     if (tempRock.scale<3) {
      splitRock(tempRock.scale+1, tempRock.x, tempRock.y);
     }
     rocks.splice(rockCtr,1);
     tempRock=null;
     playerDie();
   }
  }
  //现在检查玩家飞船与飞碟碰撞,然后检查飞碟与玩家导弹碰撞,最后检查玩家与飞碟导弹碰撞
  playerMissileLength = playerMissiles.length-1;
  saucerLength = saucers.length-1;
  saucers:for (var saucerCtr=saucerLength;saucerCtr>=0;saucerCtr--){
   tempSaucer = saucers[saucerCtr];
   missiles:for (var playerMissileCtr=playerMissileLength;
    playerMissileCtr>=0;playerMissileCtr--){
   tempPlayerMissile = playerMissiles[playerMissileCtr];
     if (boundingBoxCollide(tempSaucer,tempPlayerMissile)){
      //ConsoleLog.log("hit rock");
      createExplode(tempSaucer.x+tempSaucer.halfWidth,
       tempSaucer.y+tempSaucer.halfHeight,10,0);
      addToScore(tempSaucer.scoreValue);
      playerMissiles.splice(playerMissileCtr,1);
      tempPlayerMissile = null;
      saucers.splice(saucerCtr,1);
      tempSaucer = null;
      break saucers;
      break missiles;
     }
   }
   //玩家飞船与飞碟碰撞
   if (boundingBoxCollide(tempSaucer,player) & player.safe==false){
     ConsoleLog.log("hit player");
     createExplode(tempSaucer.x+16,tempSaucer.y+16,10,tempRock.scale);
     addToScore(tempSaucer.scoreValue);
     saucers.splice(rockCtr,1);
     tempSaucer = null;
     playerDie();
   }
  }
  //飞碟导弹与玩家碰撞
  saucerMissileLength = saucerMissiles.length-1;
  saucerMissiles:for (var saucerMissileCtr=saucerMissileLength;
          saucerMissileCtr>=0;saucerMissileCtr--){
   tempSaucerMissile = saucerMissiles[saucerMissileCtr];
   if (boundingBoxCollide(player,tempSaucerMissile) & player.safe==false){
     ConsoleLog.log("saucer missile hit player");
     playerDie();
     saucerMissiles.splice(saucerCtr,1);
     tempSaucerMissile = null;
     break saucerMissiles;
   }
  }
}
function firePlayerMissile(){
  //ConsoleLog.log("fire playerMissile");
  var newPlayerMissile = {};
  newPlayerMissile.dx = 5*Math.cos(Math.PI*(player.rotation)/180);
  newPlayerMissile.dy = 5*Math.sin(Math.PI*(player.rotation)/180);
  newPlayerMissile.x = player.x+player.halfWidth;
  newPlayerMissile.y = player.y+player.halfHeight;
  newPlayerMissile.life = 60;
  newPlayerMissile.lifeCtr = 0;
  newPlayerMissile.width = 2;
  newPlayerMissile.height = 2;
  newPlayerMissile.hitHeight = 2;
  newPlayerMissile.hitWidth = 2;
  playerMissiles.push(newPlayerMissile);
}
function fireSaucerMissile(saucer) {
  var newSaucerMissile = {};
  newSaucerMissile.x = saucer.x+.5*saucer.width;
  newSaucerMissile.y = saucer.y+.5*saucer.height;
  newSaucerMissile.width = 2;
  newSaucerMissile.height = 2;
  newSaucerMissile.hitHeight = 2;
  newSaucerMissile.hitWidth = 2;
  newSaucerMissile.speed = saucer.missileSpeed;
  //ConsoleLog.log("saucer fire");
  //从飞碟向玩家飞船射击
  var diffx = player.x-saucer.x;
  var diffy = player.y-saucer.y;
  var radians = Math.atan2(diffy, diffx);
  var degrees = 360 * radians / (2 * Math.PI);
  newSaucerMissile.dx = saucer.missileSpeed*Math.cos(Math.PI*(degrees)/180);
  newSaucerMissile.dy = saucer.missileSpeed*Math.sin(Math.PI*(degrees)/180);
  newSaucerMissile.life = 160;
  newSaucerMissile.lifeCtr = 0;
  saucerMissiles.push(newSaucerMissile);
}
function playerDie() {
  ConsoleLog.log("player die");
  createExplode(player.x+player.halfWidth, player.y+player.halfWidth,50,4);
  resetPlayer();
  switchGameState(GAME_STATE_PLAYER_DIE);
}
  function createExplode(x,y,num,type) {
   playSound(SOUND_EXPLODE,.5);
   for (var partCtr=0;partCtr<num;partCtr++){
     if (particlePool.length > 0){
      newParticle = particlePool.pop();
   newParticle.dx = Math.random()*3;
     if (Math.random()<.5){
      newParticle.dx *= −1;
     }
   newParticle.dy = Math.random()*3;
   if (Math.random()<.5){
      newParticle.dy *= −1;
   }
   newParticle.life = Math.floor(Math.random()*30+30);
   newParticle.lifeCtr = 0;
   newParticle.x = x;
   newParticle.width = 2;
   newParticle.height = 2;
   newParticle.y = y;
   newParticle.type = type;
   //ConsoleLog.log("newParticle.life=" + newParticle.life);
   particles.push(newParticle);
   }
  }
}
function boundingBoxCollide(object1, object2) {
  var left1 = object1.x;
  var left2 = object2.x;
  var right1 = object1.x + object1.hitWidth;
  var right2 = object2.x + object2.hitWidth;
  var top1 = object1.y;
  var top2 = object2.y;
  var bottom1 = object1.y + object1.hitHeight;
  var bottom2 = object2.y + object2.hitHeight;
  if (bottom1 < top2) return(false);
  if (top1 > bottom2) return(false);
  if (right1 < left2) return(false);
  if (left1 > right2) return(false);
  return(true);
};
function splitRock(scale,x,y){
  for (var newRockctr=0;newRockctr<2;newRockctr++){
   var newRock = {};
   //ConsoleLog.log("split rock");
   if (scale==2){
     newRock.scoreValue = medRockScore;
     newRock.width = 32;
     newRock.height = 32;
     newRock.halfWidth = 16;
     newRock.halfHeight = 16;
     newRock.hitWidth = 24;
     newRock.hitHeight = 24;
   }else {
     newRock.scoreValue = smlRockScore;
     newRock.width = 24;
     newRock.height = 24;
     newRock.halfWidth = 12;
     newRock.halfHeight = 12;
     newRock.hitWidth = 16;
     newRock.hitHeight = 16;
   }
   newRock.scale = scale;
   newRock.x = x;
   newRock.y = y;
   newRock.dx = Math.random()*3;
   if (Math.random()<.5){
     newRock.dx *= −1;
   }
   newRock.dy = Math.random()*3;
   if (Math.random()<.5){
     newRock.dy *= −1;
   }
   if (Math.random()<.5){
     newRock.rotationInc = −1;
   }else{
     newRock.rotationInc = 1;
   }
   newRock.animationDelay = Math.floor(Math.random()*3+1);
   newRock.animationCount = 0;
   newRock.rotation = 0;
   ConsoleLog.log("new rock scale"+(newRock.scale));
   rocks.push(newRock);
  }
}
function addToScore(value){
  score += value;
}
document.onkeydown = function(e){
  e = e?e:window.event;
  //ConsoleLog.log(e.keyCode + "down");
  keyPressList[e.keyCode] = true;
}
document.onkeyup = function(e){
//document.body.onkeyup = function(e){
  e = e?e:window.event;
  //ConsoleLog.log(e.keyCode + "up");
  keyPressList[e.keyCode] = false;
};
//*** application start
switchGameState(GAME_STATE_INIT);
var FRAME_RATE = 40;
frameRateCounter = new FrameRateCounter(FRAME_RATE);
//**** 程序开始
var intervalTime = 1000/FRAME_RATE;
setInterval(runGame, intervalTime );
}
//***** 对象原型 *****
//*** consoleLog工具对象
//创建构造器
function ConsoleLog(){
}
//创建一个函数,并添加到类中
console_log = function(message) {
  if(typeof(console) !== 'undefined' && console != null) {
   console.log(message);
  }
}
//通过赋值将类函数/静态函数添加到类
ConsoleLog.log = console_log;
//*** console log 对象结束
//*** 新的FrameRateCounter 对象原型
function FrameRateCounter(fps) {
  if (fps == undefined){
   this.fps = 40
  }else{
   this.fps = fps
  }
  this.lastFrameCount = 0;
  var dateTemp = new Date();
  this.frameLast = dateTemp.getTime();
  delete dateTemp;
  this.frameCtr = 0;
  this.lastTime = dateTemp.getTime();
  this.step = 1;
}
FrameRateCounter.prototype.countFrames = function() {
  var dateTemp = new Date();
  var timeDifference = dateTemp.getTime()-this.lastTime;
  this.step = (timeDifference/1000)*this.fps;
  this.lastTime = dateTemp.getTime();
  this.frameCtr++;
  if (dateTemp.getTime() >=this.frameLast+1000) {
   ConsoleLog.log("frame event");
     this.lastFrameCount = this.frameCtr;
     this.frameCtr = 0;
     this.frameLast = dateTemp.getTime();
  }
  delete dateTemp;
}
</script>
</head>
<body>
<div style="position: absolute; top: 50px; left: 50px;">
<canvas id="canvas" width="400" height="400">
 Your browser does not support HTML5 Canvas.
</canvas>
</div>
</body>
</html>

3.第11章代码

例A-3给出了CH11EX1.html的完整代码列表。可以注意到,在前10章中用到的许多代码风格和结构都在这个应用程序中得以保留。除了包含与WebGL直接相关的代码,这个应用程序在本质上与本书中讨论的其他应用程序是一样的。

例A-3 CH11EX1.html的完整代码列表

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CH11EX1: WebGL Test </title>
<script src="modernizr.js"></script>
<script type="text/javascript" src="sylvester.js"></script>
<script type="text/javascript" src="glUtils.js"></script>
<script id="shader-fs" type="x-shader/x-fragment">
  #ifdef GL_ES
  precision highp float;
  #endif
  varying vec4 vColor;
  void main(void) {
   gl_FragColor = vColor;
  }
</script>
<script id="shader-vs" type="x-shader/x-vertex">
  attribute vec3 aVertexPosition;
  attribute vec4 aVertexColor;
  uniform mat4 uMVMatrix;
  uniform mat4 uPMatrix;
  varying vec4 vColor; void main(void) {
   gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
   vColor = aVertexColor;
  }
</script>
<script type="text/javascript">
window.addEventListener("load", eventWindowLoaded, false);
function eventWindowLoaded () {
  canvasApp();
}
function canvasSupport () {
  return Modernizr.canvas;
}
function webglSupport() {
  return Modernizr.webgl;
}
function canvasApp () {
function drawScreen() {
   webGLContext.viewport(0, 0, webGLContext.viewportWidth,
     webGLContext.viewportHeight);
   webGLContext.clear(webGLContext.COLOR_BUFFER_BIT | webGLContext.DEPTH_BUFFER_BIT);
   perspective(25, (webGLContext.viewportWidth / webGLContext.viewportHeight),0.1, 100.0);
   loadIdentity();
   mvTranslate([0, 0.0, −10.0]);
   mvPushMatrix();
   mvRotate(rotateCube, [0, .5, .5]);
   webGLContext.bindBuffer(webGLContext.ARRAY_BUFFER, cubeVertexPositionBuffer);
   webGLContext.vertexAttribPointer(shaderProgram.vertexPositionAttribute,
     cubeVertexPositionBuffer.itemSize, webGLContext.FLOAT, false, 0, 0);
   webGLContext.bindBuffer(webGLContext.ARRAY_BUFFER, cubeVertexColorBuffer);
   webGLContext.vertexAttribPointer(shaderProgram.vertexColorAttribute,
     cubeVertexColorBuffer.itemSize, webGLContext.FLOAT, false, 0, 0);
   webGLContext.bindBuffer(webGLContext.ELEMENT_ARRAY_BUFFER,
     cubeVertexIndexBuffer);
   setMatrixUniforms();
   webGLContext.drawElements(webGLContext.TRIANGLES,
     cubeVertexIndexBuffer.numItems, webGLContext.UNSIGNED_SHORT, 0);
   mvPopMatrix();
   rotateCube += 2;
   }
   if (!canvasSupport() ) {
      alert("Unable to initialize Canvas");
      return;
     }
   if ( !webglSupport()) {
     alert("Unable to initialize WebGL");
     return;
     }
   var webGLContext;
   var rotateCube = 0;
   var theCanvas = document.getElementById("canvasOne");
   webGLContext =theCanvas.getContext("experimental-webgl");
   webGLContext.viewportWidth =theCanvas.width;
   webGLContext.viewportHeight = theCanvas.height;
   initShaders();
   initBuffers();
   webGLContext.clearColor(0.0, 0.0, 0.0, 1.0);
   webGLContext.clearDepth(1.0);
   webGLContext.enable(webGLContext.DEPTH_TEST);
   webGLContext.depthFunc(webGLContext.LEQUAL);
   setInterval(drawScreen, 33);
  function getShader(webglcontext, id) {
   var shaderScript = document.getElementById(id);
   if (!shaderScript) {
     return null;
   }
   var str = "";
   var scriptChild = shaderScript.firstChild;
   while (scriptChild) {
     if (scriptChild.nodeType == 3) {
     str += scriptChild.textContent;
     }
     scriptChild = scriptChild.nextSibling;
   }
   var shader;
   if (shaderScript.type == "x-shader/x-fragment") {
     shader = webGLContext.createShader(webGLContext.FRAGMENT_SHADER);
   } else if (shaderScript.type == "x-shader/x-vertex") {
     shader = webGLContext.createShader(webGLContext.VERTEX_SHADER);
   } else {
    return null;
   }
   webGLContext.shaderSource(shader, str);
   webGLContext.compileShader(shader);
   if (!webGLContext.getShaderParameter(shader, webGLContext.COMPILE_STATUS)) {
     alert(webGLContext.getShaderInfoLog(shader));
     return null;
   }
   return shader;
   }
   var shaderProgram;
  function initShaders() {
   var fragmentShader = getShader(webGLContext, "shader-fs");
   var vertexShader = getShader(webGLContext, "shader-vs");
   shaderProgram = webGLContext.createProgram();
   webGLContext.attachShader(shaderProgram, vertexShader);
   webGLContext.attachShader(shaderProgram, fragmentShader);
   webGLContext.linkProgram(shaderProgram);
   if (!webGLContext.getProgramParameter(shaderProgram, webGLContext.LINK_STATUS)) {
     alert("Could not initialize shaders");
   }
   webGLContext.useProgram(shaderProgram);
   shaderProgram.vertexPositionAttribute = webGLContext.getAttribLocation
     (shaderProgram, "aVertexPosition");
   webGLContext.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);
   shaderProgram.vertexColorAttribute = webGLContext.getAttribLocation
     (shaderProgram, "aVertexColor");
   webGLContext.enableVertexAttribArray(shaderProgram.vertexColorAttribute);
   shaderProgram.pMatrixUniform = webGLContext.getUniformLocation
     (shaderProgram, "uPMatrix");
   shaderProgram.mvMatrixUniform = webGLContext.getUniformLocation
     (shaderProgram, "uMVMatrix");
   }
   var mvMatrix;
   var mvMatrixStack = [];
  function mvPushMatrix(matrix) {
   if (matrix) {
     mvMatrixStack.push(matrix.dup());
     mvMatrix = matrix.dup();
   } else {
     mvMatrixStack.push(mvMatrix.dup());
   }
   }
  function mvPopMatrix() {
   if (mvMatrixStack.length == 0) {
     throw "Invalid popMatrix!";
   } mvMatrix = mvMatrixStack.pop();
   return mvMatrix;
   }
  function loadIdentity() {
   mvMatrix = Matrix.I(4);
   }
  function multMatrix(matrix) {
   mvMatrix = mvMatrix.x(matrix);
   }
  function mvTranslate(vector) {
   var matrix = Matrix.Translation($V([vector[0], vector[1],
     vector[2]])).ensure4x4();
   multMatrix(matrix);
   }
  function mvRotate(angle, vector) {
   var radians = angle * Math.PI / 180.0;
   var matrix = Matrix.Rotation(radians, $V([vector[0],
     vector[1], vector[2]])).ensure4x4();
   multMatrix(matrix);
   }
   var pMatrix;
   function perspective(fovy, aspect, znear, zfar) {
   pMatrix = makePerspective(fovy, aspect, znear, zfar);
   }
  function setMatrixUniforms() {
   webGLContext.uniformMatrix4fv(shaderProgram.pMatrixUniform, false,
     new Float32Array(pMatrix.flatten()));
   webGLContext.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false,
     new Float32Array(mvMatrix.flatten()));
   }
   var cubeVertexPositionBuffer;
   var cubeVertexColorBuffer;
   var cubeVertexIndexBuffer;
  function initBuffers() {
   cubeVertexPositionBuffer = webGLContext.createBuffer();
   webGLContext.bindBuffer(webGLContext.ARRAY_BUFFER, cubeVertexPositionBuffer);
   vertices = [
     //前面
     −1.0, −1.0, 1.0,
     1.0, −1.0, 1.0,
     1.0, 1.0, 1.0,
     −1.0, 1.0, 1.0,
     // 背面
     −1.0, −1.0, −1.0,
     −1.0, 1.0, −1.0,
     1.0, 1.0, −1.0,
     1.0, −1.0, −1.0,
     // 上面
     −1.0, 1.0, −1.0,
     −1.0, 1.0, 1.0,
     1.0, 1.0, 1.0,
     1.0, 1.0, −1.0,
     // 下面
     −1.0, −1.0, −1.0,
     1.0, −1.0, −1.0,
     1.0, −1.0, 1.0,
     −1.0, −1.0, 1.0,
     // 右面
     1.0, −1.0, −1.0,
     1.0, 1.0, −1.0,
     1.0, 1.0, 1.0,
     1.0, −1.0, 1.0,
     // 左面
     −1.0, −1.0, −1.0,
     −1.0, −1.0, 1.0,
     −1.0, 1.0, 1.0,
     −1.0, 1.0, −1.0,
    ];
    webGLContext.bufferData(webGLContext.ARRAY_BUFFER, new Float32Array(vertices),
      webGLContext.STATIC_DRAW);
    cubeVertexPositionBuffer.itemSize = 3;
    cubeVertexPositionBuffer.numItems = 24;
    cubeVertexColorBuffer = webGLContext.createBuffer();
    webGLContext.bindBuffer(webGLContext.ARRAY_BUFFER, cubeVertexColorBuffer);
    var colors = [
     [1.0, 1.0, 1.0, 1.0], // 前面
     [0.9, 0.0, 0.0, 1.0], // 背面
     [0.6, 0.6, 0.6, 1.0], // 上面
     [0.6, 0.0, 0.0, 1.0], // 下面
     [0.3 ,0.0, 0.0, 1.0], // 右面
     [0.3, 0.3, 0.3, 1.0], // 左面
    ];
    var unpackedColors = []
    for (var i in colors) {
     var color = colors[i];
     for (var j=0; j < 4; j++) {
     unpackedColors = unpackedColors.concat(color);
    }
   }
   webGLContext.bufferData(webGLContext.ARRAY_BUFFER,
    new Float32Array(unpackedColors),
    webGLContext.STATIC_DRAW);
   cubeVertexColorBuffer.itemSize = 4;
   cubeVertexColorBuffer.numItems = 24;
   cubeVertexIndexBuffer = webGLContext.createBuffer();
   webGLContext.bindBuffer(webGLContext.ELEMENT_ARRAY_BUFFER,
    cubeVertexIndexBuffer);
   var cubeVertexIndices = [
    0, 1, 2,    0, 2, 3,     // 前面
    4, 5, 6,    4, 6, 7,    // 背面
    8, 9, 10,   8, 10, 11,   // 上面
    12, 13, 14,  12, 14, 15,  // 下面
    16, 17, 18,  16, 18, 19,  // 右面
    20, 21, 22,  20, 22, 23   // 左面
   ]
   webGLContext.bufferData(webGLContext.ELEMENT_ARRAY_BUFFER,
    new Uint16Array(cubeVertexIndices), webGLContext.STATIC_DRAW);
   cubeVertexIndexBuffer.itemSize = 1;
   cubeVertexIndexBuffer.numItems = 36;
   }
  }
</script>
</head>
<body>
<div style="position: absolute; top: 50px; left: 50px;">
<canvas id="canvasOne" width="500" height="500">
 Your browser does not support HTML5 Canvas or WebGLContext.
</canvas>
</div>
</body>
</html>