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>