likes
comments
collection
share

通过飞机大战游戏学习PixiJs系列【下】

作者站长头像
站长
· 阅读数 14

书接上回

看过上一篇的同学,想必已经对资源加载,精灵创建,容器等概念有了一定的了解,本文以上一篇文章为基础,继续实现飞机大战游戏,通过本文,你能学到如何绑定事件,如何进行碰撞检测,如何创建动态效果等。阅读本文预计需要6分半钟

飞机移动

我们需要为飞机添加事件,让飞机响应pointerMove事件,来让飞机动起来 tips 这里封装了一个onDragMove方法,如果对原生Js的事件对象有了解的话,相信一定能明白e.targete.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
评论
请登录