通过飞机大战游戏学习PixiJs系列【下】
书接上回
看过上一篇的同学,想必已经对资源加载,精灵创建,容器等概念有了一定的了解,本文以上一篇文章为基础,继续实现飞机大战游戏,通过本文,你能学到如何绑定事件,如何进行碰撞检测,如何创建动态效果等。阅读本文预计需要6分半钟
飞机移动
我们需要为飞机添加事件,让飞机响应pointerMove
事件,来让飞机动起来
tips 这里封装了一个onDragMove
方法,如果对原生Js的事件对象有了解的话,相信一定能明白e.target
和e.currentTarget
的区别,- e.target
是触发事件的dom对象
e.currentTarget
是绑定事件的dom对象
这里我们操作的是绑定事件的精灵
// 添加事件
plane.on('pointermove', onDragMove);
// 移动
function onDragMove(event) {
let currentTarget = event.currentTarget;
let newPosition = event.data.global; // 获取拖动到的位置
// 划分范围
// 减50%物体本身的宽度和高度,这里是移动的经典处理,原因是因为x,y都是基于物体左上角来计算的,为了基于物体中心进行偏移,所以需要减去一半的宽高
if (newPosition.x > 0 && newPosition.x < screenWidth) {
currentTarget.x = newPosition.x - currentTarget.width * 0.5;
}
if (newPosition.y > 0 && newPosition.y < screenHeight) {
currentTarget.y = newPosition.y - currentTarget.height * 0.5;
}
}
app.ticker
这是一个类似requestAnimationFrame的方法,我们通过app.ticker.add
方法,来实现我们发射子弹,自动创建障碍物,更新位置等等操作,一半来说,ticker一秒钟会自动运行60次
app.ticker.add(function () {
return gameLoop();
});
飞机发射子弹
我们需要让飞机自动发射子弹,所以,这里需要将shoot
方法, 放在gameLoop
内执行。
这里,我写了一个射击延时的设计,就是类似射速的概念,通过设计射速的概念,保留了以后我们升级或者降低游戏难度的可能,具体实现和注释在下面了
// 延迟 射击间隔
// 记录射击延时的计数变量
let fireDelay = 0;
// 射出一发子弹所需要的间隔
let firingInterval = 0.2;
function shoot(scene, bullets) { // scene场景和子弹数组
// 判断射击延时是否已经大于了射击间隔
if (fireDelay > firingInterval) {
// 重置射击延时计数
fireDelay = 0;
// 创建子弹
let bullet = new Sprite(textures.bullet.texture);
bullet.width = screenWidth * .04;
// 根据纹理大小按宽高比缩放子弹精灵
bullet.height = bullet.width * 148 / 66;
// 使射出去的子弹位置位于飞机正中心
bullet.x = plane.x + (plane.width * 0.5) - (bullet.width * 0.5);
bullet.y = plane.y;
scene.addChild(bullet);
// 维护子弹数组
bullets.push(bullet);
} else {
// 每次自增1/60,
fireDelay += 1 / 60;
}
}
...
function gameLoop() {
// 生成子弹
shoot(scene, bullets);
...
}
创建障碍物
为了给游戏添加一个结束的节点,这里障碍物创建速度会随着时间的推移,越来越快。
// 创建障碍物
let obstacleDelay = 0;
let createSpeed = 3;
// 障碍物从顶部到容器底部动画所需要的时间
let obstacleTime = 6000;
function createObstacle(scene, textures, obstacles, TWEEN, tweenArr) { // 场景 纹理集合 障碍物数组 补间动画对象 补间动画集合
// 判断是否可以创建新的障碍物
if (obstacleDelay > createSpeed) {
// 逐渐增加创建速度
createSpeed -= createSpeed * 0.05
obstacleDelay = 0;
let container = new PIXI.Container();
// 图案
// 障碍物的外层图案
let obstacle = new Sprite(textures.obstacle.texture);
obstacle.name = 'obstacle';
obstacle.width = screenWidth * .3;
obstacle.height = screenWidth * .3;
obstacle.x = 0;
obstacle.y = 0;
obstacle.anchor.set(0.5, 0.5);
// 碰撞区域
let circle = new Sprite();
circle.width = obstacle.width * 0.5;
circle.height = circle.width;
circle.name = 'circle';
circle.circular = true;
circle.x = -circle.width * 0.5;
circle.y = -circle.height * 0.5;
container.addChild(circle);
// 文字
let text = new PIXI.Text('桀桀', {
fontSize:20,
fill: '#ff0909'
});
text.x = - text.width * 0.5;
text.y = - text.height * 0.5;
container.addChild(text);
// 爆炸效果 暂时先不执行,且不循环执行,通过动画精灵类来加载
let fireArea = [
];
for (let i = 0; i <= 23; i++) {
fireArea.push(textures.boom.textures['boom' + i + '.png']);
}
let boom = new PIXI.AnimatedSprite(fireArea);
boom.width = obstacle.width * 2.5;
boom.height = obstacle.height * 2.5;
boom.x = -boom.width * 0.5;
boom.y = -boom.height * 0.5;
boom.name = 'boom';
boom.loop = false;
container.addChild(boom);
container.addChild(obstacle);
container.addChild(circle);
container.x = screenWidth * Math.random();
container.y = -obstacle.height;
// 位移设定
let tween = new TWEEN.Tween(container)
.to(
{
x: screenWidth * Math.random(),
y: screenHeight + obstacle.height,
},
obstacleTime // tween持续时间
)
.easing(TWEEN.Easing.Linear.None)
.onComplete(function () {
// 到底
container.destroy();
});
tween.start();
// 旋转设定
let tween2 = new TWEEN.Tween(obstacle)
.to(
{
rotation: -20
},
obstacleTime // tween持续时间
)
.easing(TWEEN.Easing.Linear.None)
.onComplete(function () {
});
tween2.start();
// 插入场景
container.tween = tween;
obstacles.push(circle);
tweenArr.push(tween);
scene.addChild(container);
} else {
obstacleDelay += 1 / 60;
}
}
判断碰撞
借助碰撞库来实现碰撞检测
function isHit (r1, r2) {
let b = new Bump(PIXI);
if (b.hitTestCircleRectangle(r1, r2, true) !== false) {
return true;
} else {
return false;
}
}
子弹飞行逻辑
子弹在发射后,需要向上飞,碰到障碍物需要消除障碍物,如果飞出了容器,需要移除子弹,如果在此期间飞机撞到了障碍物,则游戏结束,当前方法也需要在gameLoop
里循环执行
function bulletsEvents() {
for (let i = 0; i < bullets.length;) {
let hit = false;
for (let o = 0; o < obstacles.length;) {
// 子弹与障碍物碰撞检测
if (isHit(obstacles[o], bullets[i])) {
hit = true;
// 移除障碍物
removeObstacles(o)
continue;
} else if (isHit(obstacles[o], plane)) {
// 飞机与障碍物碰撞检测
let _obstacle = obstacles.splice(o, 1)[0];
_obstacle.destroy();
end();
continue;
} else {
o++
}
}
// 根据碰撞状态做处理
if (hit) {
// 如果碰撞了
// 移除当前子弹
let _bullet = bullets.splice(i, 1)[0];
_bullet.destroy();
// 加分
score++;
scoreTexture.text = '得分:' + score;
} else {
// 如果子弹飞出屏幕,则移除;如果没有,Y轴位移
if (bullets[i].y < -bullets[i].height) {
let _bullet = bullets.splice(i, 1)[0];
_bullet.destroy();
} else {
bullets[i].y -= 10;
i++
}
}
}
}
function removeObstacles (o) {
// 获取父容器
let container = obstacles[o].parent;
let _obstacle = obstacles.splice(o,1)[0];
_obstacle.destroy();
//爆炸效果
container.children[1].play();
//圆圈,文字
container.children[0].visible = false;
container.children[2].visible = false;
}
更新障碍物的动画
// 障碍物位置更新
function obstaclesEvents() {
// 更新位置
TWEEN.update();
}
循环执行的方法
function gameLoop() {
// 生成子弹
shoot(scene, bullets);
// 生成障碍物
createObstacle(scene, textures, obstacles, TWEEN, tweenArr);
// 子弹逻辑处理
bulletsEvents();
// 障碍物动画更新
obstaclesEvents();
}
结束游戏
function end () {
app.ticker.stop();
}
转载自:https://juejin.cn/post/7155034904244256775