之前对createJs体验了一下,正好看到网上有一篇教程,试着也写了一个打飞机的小游戏,UI相对比较简单,主要记录下实现游戏逻辑的思路。
window.onload = function() {
canvas = document.getElementById("canvas");
stage = new createjs.Stage(canvas);
preload = new createjs.LoadQueue(false);
//如果载入声音,必须先注册createjs.Sound
preload.installPlugin(createjs.Sound);
// 导入资源创建舞台
game.init();
};
init: function() {
var progressText = new createjs.Text("", "20px Arial", "#dd4814");
progressText.x = stage.canvas.width / 2;
progressText.y = stage.canvas.height / 2;
progressText.textAlign = 'center';
stage.addChild(progressText);
function startPreload() {
var manifest = [{
id: 'sprite',
src: 'plane.png'
}, {
id: 'shot',
src: 'shot.mp3'
}, {
id: "explosion",
src: 'explosion.mp3'
}];
preload.setMaxConnections(3);
preload.maintainScriptOrder = true;
preload.on("progress", loadProgress);
preload.on("error", loadError);
preload.on("complete", loadComplete);
preload.loadManifest(manifest);
}
startPreload();
function loadProgress(e) {
progressText.text = "已加载 " + (preload.progress * 100 | 0) + " %";
console.log(progressText.text);
}
function loadError(e) {
console.log(e);
}
function loadComplete(e) {
stage.removeChild(progressText);
game.handleComplete();
}
},
加载好需要用到的库以后,先运行init方法载入游戏资源;如果要播放声音,需先preload.installPlugin(createjs.Sound)注册createjs.Sound,不然后面直接调用方法播放音频文件会出错。
// 创建游戏界面,设置按键
handleComplete: function() {
buildGame.init();
buildGame.setContorl();
game.startGame();
},
//开始游戏
startGame: function() {
createjs.Ticker.setFPS(60);
createjs.Ticker.addEventListener('tick', function() {
updateGame.init();
stage.update();
});
}
在startGame方法里,设置帧频,stage.update()游戏更新场景。
buildGame.init();创建游戏场景,飞机的精灵动画对象。
init: function() {
buildGame.buildMsg();
buildGame.buildSpriteSheet();
buildGame.buildPlayer();
buildGame.buildEnemy();
buildGame.buildSpace();
},
用到的图片素材以及相应的帧数据:
//创建精灵动画表
buildSpriteSheet: function() {
var data = {
images: [preload.getResult('sprite')],
frames: [
[0, 0, 38, 42],
[37, 0, 42, 42],
[79, 0, 37, 42],
[116, 0, 42, 42],
[158, 0, 37, 42],
[0, 70, 64, 64],
[64, 70, 64, 64],
[128, 70, 64, 64],
[192, 70, 64, 64],
[256, 70, 64, 64],
[320, 70, 64, 64],
[384, 70, 64, 64],
[448, 70, 64, 64],
[512, 70, 64, 64],
[576, 70, 64, 64],
[640, 70, 64, 64],
[704, 70, 64, 64],
[768, 70, 64, 64]
],
animations: {
ship: 0,
enemy1: 1,
enemy2: 2,
enemy3: 3,
enemy4: 4,
exp: {
frames: [5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
speed: .5
}
}
};
buildGame.spriteSheet = new createjs.SpriteSheet(data);
},
这里要吐槽一下,createjs没有合适的动画转换工具可以直接生成图片素材与帧数据,美中不足啊。
素材中有5种飞机,选1个为我们可操作的主角后,剩下4个插入敌机种类的数组中。
//创建玩家的飞机
buildPlayer: function() {
Plane.player = new createjs.Sprite(buildGame.spriteSheet, 'ship');
//getBounds方法返回当前对象宽高
console.log(Plane.player.getBounds());
Plane.width = Plane.player.getBounds().width;
Plane.height = Plane.player.getBounds().height;
Plane.player.x = (stage.canvas.width - Plane.width) / 2;
Plane.player.y = stage.canvas.height - Plane.height;
stage.addChildAt(Plane.player, 0);
},
//创建敌机
buildEnemy: function() {
var e1, e2, e3, e4;
e1 = new createjs.Sprite(buildGame.spriteSheet, "enemy1");
e2 = new createjs.Sprite(buildGame.spriteSheet, "enemy2");
e3 = new createjs.Sprite(buildGame.spriteSheet, "enemy3");
e4 = new createjs.Sprite(buildGame.spriteSheet, "enemy4");
enemyPlane.enemyClip.push(e1, e2, e3, e4);
},
游戏背景就更简单了,随机生成200个小圆点散落在场景中。
//星空背景
buildSpace: function() {
var i, star, starSky, w, h, alpha;
for (i = 0; i < 200; i++) {
starSky = new createjs.Container();
star = new createjs.Shape();
w = Math.floor(Math.random() * stage.canvas.width);
h = Math.floor(Math.random() * stage.canvas.height);
alpha = Math.random();
star.graphics.beginFill("#45D0DE").drawCircle(0, 0, 2);
star.x = w;
star.y = h;
star.alpha = alpha;
starSky.addChild(star);
updateGame.starSpace.push(star);
stage.addChild(starSky);
}
},
敌机从种类数组中随机克隆种类放入编队数组,以编队的形式进入场景。
//敌机对象
var enemyPlane = {
//敌机种类数组
enemyClip: [],
//敌机队列数组
enemysQueue: [],
//敌机增加时间(速度)
addSpeed: 1000,
//敌机移动时间(速度)
moveSpeed: 5000,
//敌机队列数量
enemynNum: 15,
//随机添加一类敌机进入战场
addEnemy: function() {
var random = Math.floor((Math.random() * enemyPlane.enemyClip.length));
//克隆随机种类敌机放入队列数组
var randomPlane = enemyPlane.enemyClip[random].clone();
var randomPlaneWidth = randomPlane.getBounds().width;
randomPlane.x = Math.floor((Math.random() * (stage.canvas.width - randomPlaneWidth * 2)));
randomPlane.y = -100;
enemyPlane.enemysQueue.push(randomPlane);
stage.addChild(randomPlane);
}
};
玩家飞机通过按键发射子弹,同时将发射的子弹插入一个数组。
//飞机对象
var Plane = {
lives: 3,
score: 0,
speed: 9,
player: {},
width: 0,
height: 0,
fires: [],
//发射子弹
playFire: function() {
if (updateGame.run) {
var fire = new createjs.Shape();
fire.graphics.beginFill('#FF0').drawRect(0, 0, 6, 6).endFill();
fire.x = Plane.player.x - 3 + Plane.width / 2;
fire.y = Plane.player.y;
createjs.Sound.play('shot');
Plane.fires.push(fire);
stage.addChild(fire);
}
}
};
游戏运行时循环子弹数组和敌机编队数组就可以实现碰撞检测的逻辑了,把发生碰撞的子弹与敌机从场景清除并播放爆炸动画与音效,同时计分等。
//子弹与敌机的碰撞检测
checkCollision: function() {
for (var i = 0; i < Plane.fires.length; i++) {
var fire = Plane.fires[i];
for (var j = 0; j < enemyPlane.enemysQueue.length; j++) {
var enemy = enemyPlane.enemysQueue[j];
var fx = fire.x;
var fy = fire.y;
var ex = enemy.x;
var ey = enemy.y;
var ew = enemy.getBounds().width;
var eh = enemy.getBounds().height;
//判断子弹有没有进入敌机矩形区域内
var pt = fy < ey + eh && fy > ey && fx > ex && fx < ex + ew && ey > 0;
if (pt) {
Plane.score += 10;
Plane.fires.splice(i, 1);
enemyPlane.enemysQueue.splice(j, 1);
stage.removeChild(fire);
stage.removeChild(enemy);
createjs.Sound.play('explosion');
var exp1 = new createjs.Sprite(buildGame.spriteSheet, 'exp');
exp1.x = enemy.x;
exp1.y = enemy.y;
stage.addChild(exp1);
//爆炸动画结束时从舞台清除
exp1.addEventListener('animationend', function(e) {
stage.removeChild(e.target);
});
};
};
};
},
由于createjs提供的碰撞检测方法并不是特别好用,所以这里实现的并不算太好。还剩下的一些逻辑代码就随便贴一下吧。
//子弹飞行
updateFire: function() {
for (var i = 0; i < Plane.fires.length; i++) {
var nextY = Plane.fires[i].y - 10;
if (nextY <= 0) { //如果子弹飞出场景,在子弹数组中去掉,并在stage中删除元素
console.log(Plane.fires[i]);
stage.removeChild(Plane.fires[i]);
Plane.fires.splice(i, 1);
continue;
}
Plane.fires[i].y = nextY;
};
},
//敌机飞行
updateEnemy: function() {
var pl = Plane.player;
var plx = Plane.player.x;
var ply = Plane.player.y;
var plw = Plane.player.getBounds().width;
var plh = Plane.player.getBounds().height;
//队列内的敌机依次出现
for (var i = 0; i < enemyPlane.enemysQueue.length; i++) {
var ep = enemyPlane.enemysQueue[i];
var randomPlaneWidth = ep.getBounds().width;
var randomPlaneHeight = ep.getBounds().height;
createjs.Tween.get(ep).wait(enemyPlane.addSpeed * i).to({
x: Math.floor((Math.random() * (stage.canvas.width - randomPlaneWidth * 2))),
y: stage.canvas.height
}, enemyPlane.moveSpeed, createjs.Ease.sineInOut(-2));
// 敌机飞出场景移除
if (ep.x >= stage.canvas.width || ep.y >= stage.canvas.height) {
enemyPlane.enemysQueue.splice(ep, 1);
stage.removeChild(ep);
console.log(enemyPlane.enemysQueue);
continue;
}
//玩家飞机与敌机碰撞处理
var pz_x = Math.abs(ep.x - plx) <= randomPlaneWidth / 2 + plw / 2;
var pz_y = Math.abs(ep.y - ply) <= randomPlaneHeight / 2 + plh / 2;
if (pz_x && pz_y && updateGame.run) {
Plane.lives--;
createjs.Sound.play('explosion');
var exp1 = new createjs.Sprite(buildGame.spriteSheet, 'exp');
exp1.x = plx;
exp1.y = ply;
stage.addChild(exp1);
//爆炸动画结束时从舞台清除
exp1.addEventListener('animationend', function(e) {
stage.removeChild(e.target);
if (Plane.lives == 0) {
createjs.Ticker.setPaused(true); //暂停游戏
alert('游戏结束');
} else {
var setTime = setTimeout(function() {
buildGame.buildPlayer();
updateGame.run = true;
clearTimeout(setTime);
}, 1000);
};
});
stage.removeChild(Plane.player);
updateGame.run = false;
break;
};
};
//如果队列内没有敌机添加敌机
if (enemyPlane.enemysQueue.length == 0) {
for (var i = 0; i < enemyPlane.enemynNum; i++) {
enemyPlane.addEnemy();
};
};
},
最终运行效果,还算勉强能玩。