如何使用pixi.js制作'打飞机'小游戏

什么是pixi.js

Pixi.js使用WebGL,是一个超快的HTML5 2D渲染引擎。作为一个Javascript的2D渲染器,Pixi.js的目标是提供一个快速的、轻量级而且是兼任所有设备的2D库。提供无缝 Canvas 回退,支持主流浏览器,包括桌面和移动。 Pixi渲染器可以开发者享受到硬件加速,但并不需要了解WebGL。

如何引入pixi.js

1.安装模块
代码中引入:import * as PIXI from 'pixi.js';
2.cdn
<script src="https://cdnjs.cloudflare.com/ajax/libs/pixi.js/4.5.1/pixi.min.js"></script>

创建pixi实例完整流程

1.创建一个应用(application)(包含舞台stage)
2.加载资源(loader)
3.创建游戏场景
4.将场景插入舞台(addchild)
5.把画布插入dom(append)
6.创建精灵(sprite)
7.把精灵加入画布(addchild)
8.刷新舞台(ticker)
9.游戏结束,销毁应用(destroy)

1.创建应用

let gameApp = new PIXI.Application({
    width: xxxx,
    height: xxxx,
    antialiasing: true, // 抗锯齿
    transparent: false, // 背景透明
    resolution: 2 // 渲染倍数,避免模糊
});

2.加载资源

let loader = new PIXI.Loader();
loader
    .add('bg', 'img/bg.jpg')
    .....
    .load((loader, resources) => {
      // 加载完毕回调
      setUp(); //执行创建精灵等操作
    });

3/4.创建游戏场景并插入舞台

let gameScene = new PIXI.Container();
gameScene.width = xxx;
gameScene.height = xxx;
gameApp.stage.addchild(gameScene);

5.把画布插入dom

document.getElementById('xxx').appendChild(gameApp.view);

6.创建精灵并插入场景

首先,为了方便的设定精灵宽高,声明两个方法

function getWidth (precent) {
  let w = document.body.clientWidth > 720 ? 720 : document.body.clientWidth;
  return (precent / 50) * w / 2;
}
function getHeight (precent) {
  let h = document.body.clientHeight;
  return (precent / 50) * h / 2;
}

1.背景

let bg = new PIXI.Sprite(resources.bg.texture);
bg.width = xxx;
bg.height = xxx;
bg.x = xxx;
bg.y = xxx;
gameScene.addchild(bg)

2.飞机

let plane = new PIXI.Sprite(resources.plane.texture);
plane.width = xxx;
plane.height = xxx;
plane.x = xxx;
plane.y = xxx;
gameScene.addchild(plane)

给飞机添加拖动事件,让飞机跟着手指移动。
给飞机添加射击事件,在ticker中调用,使飞机一直发射子弹
3.障碍物

let obstacle  = new PIXI.Sprite(resources.obstacle.texture);
obstacle.width = xxx;
obstacle.height = xxx;
obstacle.x = xxx;
obstacle.y = xxx;
gameScene.addchild(obstacle)

这种只是最基础的做法,如果有稍微多一点的需求,例如,碰撞检测的区域,和纹理图大小不一样,就需要
将障碍物纹理、碰撞区域、爆炸动画,都放入一个container内,碰撞区域push进入obstacles数组,去和子弹飞机做碰撞检测
障碍物的飞行,使用tween.js,初始化时候,设置好起点终点,在ticker中update就可以像目的地移动

    let container = new PIXI.Container();
    // 图案
    let obstacle = new PIXI.Sprite(texture.obstacle.texture);
    obstacle.name = 'obstacle';
    obstacle.width = getWidth(30);
    obstacle.height = getWidth(30);
    obstacle.x = 0;
    obstacle.y = 0;
    obstacle.anchor.set(0.5, 0.5);
    // 碰撞区域
    let circle = new PIXI.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: obstacle.width * 0.13,
      fill: '#fff'
    });
    text.x = - text.width*0.5;
    text.y = - text.height*0.5;
    container.addChild(text);

    // 爆炸效果
    let fireClip = [
    ];
    for (let i = 0; i <= 23; i++) {
      fireClip.push(texture.boom.textures['boom' + i + '.png']);
    }
    let boom = new PIXI.AnimatedSprite(fireClip);
    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 = getWidth(Math.random()*100);
    container.y = -obstacle.height;
   
    // 位移设定
    let tween = new TWEEN.Tween(container)
      .to(
        {
          x: getWidth(Math.random() * 100),
          y: getHeight(100) + 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);
    tweens.push(tween);
    gameScene.addChild(container);

7.更新舞台

创建完游戏内所有元素后,开启pixi内置定时器ticker

app.ticker.add(function () {
    return gameLoop();
});

在ticker中更新需要调用的事件,来实现游戏的动态效果

function gameLoop(){
  // 生成子弹
  plane.shut(gameScene, bullets);
  // 生成障碍物
  createobstacle(gameScene, texture, obstacles, TWEEN, tweens);
  // 子弹逻辑处理
  bulletsEvents();
  // 障碍物逻辑处理
  obstaclesEvents();
}

8.子弹飞机障碍物的碰撞逻辑(重点)

function bulletsEvents(){
  for(let i = 0; i < bullets.length;){
    let hit = false;
    for(let o = 0; o < obstacles.length; ) {
        // 子弹与障碍物碰撞检测
      if(hitTest(obstacles[o], bullets[i])) {
        hit = true;
        // 移除障碍物
        obstaclesBoom(o)
        continue;
      }else if(hitTest(obstacles[o], plane)){
        // 飞机与障碍物碰撞检测
        let _obstacle = obstacles.splice(o,1)[0];
        _obstacle.destroy();
        gameOver();
        continue;
      }else{
        o++
      }
    }
    // 根据碰撞状态做处理
    if(hit){
      // 如果碰撞了
      // 移除当前子弹
      let _bullet = bullets.splice(i,1)[0];
      _bullet.destroy();
      // 加分
      score ++;
      scorePanel.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++
      }
    }
  }
}

首先遍历子弹池,内部遍历所有障碍物,通过hitTest做碰撞检测
如果子弹和障碍物碰撞,子弹消失,障碍物消失/爆炸,得分+1;
如果飞机和障碍物碰撞,障碍物消失/爆炸,游戏结束
如果都没有,检测下一个子弹
如果子弹自下而上,飞出屏幕,则子弹移除,否则影响性能
==============================================================
碰撞检测代码,bump通过cdn引入
hitTestCircleRectangle只能用于圆形和矩形的碰撞,更多方式查看PIXI官方文档

// 子弹词条碰撞
import * as PIXI from 'pixi.js';
export function hitTest (r1, r2) {
  let b = new Bump(PIXI);
  if (b.hitTestCircleRectangle(r1, r2, true) !== false) {
    return true;
  } else {
    return false;
  }
}

9.障碍物爆炸逻辑

碰撞之后,根据parent属性,找到container,进而找到内部的爆炸动画,执行play()方法;
爆炸的同时,使纹理隐藏,形成视觉上的碰撞爆炸效果

部分代码:

function obstaclesBoom(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;
}

总结

只是随便讲一下做法的逻辑,具体代码已上传github,地址:https://github.com/huangXin1538/pixi-aircrift-wars

demo 效果地址

http://47.104.9.195:8080/pixi-aircraft-wars/
就酱~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,042评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 89,996评论 2 384
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,674评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,340评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,404评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,749评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,902评论 3 405
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,662评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,110评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,451评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,577评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,258评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,848评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,726评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,952评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,271评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,452评论 2 348