pixijs 是一个canvas 2D框架

pixi.js创建所有widget 都是有一个完整的object Display Object 他包括元素定位, 宽高,渲染方式等等方法..除了ParticleContaine这些提升性能优化的API,这个一个高性能容器,优化就是省略一些多余的object Display Object .

注:我使用的版本是V6 , 6.4.2。大版本各个API会变动,引入正确版本来开发。网上使用的是V4,略有差别

1、 引入

  1. // 引入pixi.js
  2. yarn add pixi.js

2、一些主要的API:

  1. import {
  2. Application, //渲染器,舞台(canvas)
  3. Contanier, // 容器
  4. Sprite, //精灵 是一个舞台中物体基础(display Object)
  5. Loader, // 加载器 一些资源的加载器(比如说:图片、文字之类的)
  6. Text, // 文字 (一个特殊的精灵,支持display Object
  7. TextStyle, //文字样式
  8. Texture //纹理, 给精灵加载纹理
  9. } from 'pixi.js';

3、创建一个舞台(Canvas)

  1. //创建一个宽度400,高度400 ,背景色为#000的 canvas
  2. const renderer = new Application({width:400,height:400,backgroudColor:0x000000});
  3. //将Canvas 放入 Html 中
  4. const body = document.body;
  5. body.appendChild(renderer);

4、创建一个容器(container)

容器的作用是给一些东西分组,方便管理;比如一个游戏分为:
1、开始界面
2、结束界面
3、游戏画面
三个可以分为三组,便于管理。

  1. # 创建一个舞台(容器)
  2. // 一个游戏,或者一个2d的渲染器。如果里面的内容过于复杂或者多。需要一个或者多个容器包裹管理。比如:
  3. // 比如,坦克大战,玩家控制的坦克,随机生成的敌方坦克,这里可以创建两个容器用来单独管理
  4. const contanier = new Contanier();
  5. // 将容器放入舞台(canvas) 中
  6. renderer.stage.addChild(contanier);

额外的容器(ParticleContainer)

这个 使用方法跟container 是一样的,是用来做性能优化的。有时候放入在你放入容器的sprite不需要整个display object 树 ,只需要部分关键的display object ,比如x,y,width,height等关键属性。
(粒子效果就是创建很多精灵,放入性能容器里面)

  1. # 创建一个高性能舞台(容器)
  2. const prticleCntanier = new ParticleContainer();
  3. // 将容器放入舞台(canvas) 中
  4. renderer.stage.addChild(prticleCntanier);

额外的api

删除容器内精灵

  1. //删除容器内所有的精灵
  2. container.removeChildren();
  3. //删除指定下标的精灵
  4. contanier.removeChildAt(index);
  5. //删除一个或者多个精灵
  6. contanier.removeChild(...children)

获取容器中的精灵(child)

可以使用获取容器中的display object属性,然后拿到children,比如

  1. container.children(index)

也可以使用更加优雅的方法, pixijs已经帮我封装好了一些方法

  1. //根据下标拿到容器中的指定精灵,等同于上面的方法
  2. container.getChildAt(index);
  3. //deep:boolean,name:string 别名 。根据别名找到精灵,是否开启递归搜索,默认false; 比较耗费性能
  4. container.getChildByName(name,deep);
  5. //根据精灵返回下标
  6. container.getChildIndex(child);

将容器添加精灵到指定的下标

  1. //暂时我没用过,贴上官网的注释
  2. //Adds a child to the container at a specified index. If the index is out of bounds an error will be thrown
  3. container.addChildAt(child,index)

5、创建一个精灵

  1. // 创建精灵,并给精灵设置图片
  2. let sprite = PIXI.Sprite.from('assets/image.png');
  3. //将精灵放入container(容器)或者application(舞台)当中
  4. //容器
  5. container.addchild(sprite);
  6. //高性能容器
  7. //prticleCntanier.addchild(sprite);
  8. //舞台
  9. renderer.stage.addchild(sprite)

使用texture来给精灵增加纹理

  1. //创建一段纹理
  2. const texture = PIXI.Texture.from('assets/image.png');
  3. //将纹理放入精灵中
  4. const sprite = PIXI.Sprite(texture);

通过 loader来高性能加载资源

当资源很多很且复杂的时候,可以通过loader 来加载图片、视频等资源。而当sprite使用loader的资源时候,就不能使用from。

注:通过loader可以写一个资源加载器的游戏开始页面。在刚进入游戏的时候可以写一个In progress 加载页面

  1. //使用 loader 加载器
  2. const loader = Loader.shared;
  3. loader.add('name', assetsUrl).load(loaded)
  4. const loaded = (loader, resources) => {
  5. // resources 是资源的加载对象。key是name,value 是 资源
  6. // They have a couple default properties:
  7. // - `url`: The URL that the resource was loaded from
  8. // - `error`: The error that happened when trying to load (if any)
  9. // - `data`: The raw data that was loaded
  10. // also may contain other properties based on the middleware that runs.
  11. sprites.bunny = new PIXI.TilingSprite(resources.bunny.texture);
  12. sprites.spaceship = new PIXI.TilingSprite(resources.spaceship.texture);
  13. sprites.scoreFont = new PIXI.TilingSprite(resources.scoreFont.texture);
  14. }

还能通过雪碧图,和一个确定雪碧图position的json文件来更加优化性能

  1. //异步加载图片,并使用他
  2. PIXI.Loader.shared.add("assets/spritesheet.json").load(setup);
  3. function setup() {
  4. //雪碧图,确定图片位置
  5. let sheet = Loader.shared.resources["assets/spritesheet.json"].spritesheet;
  6. let sprite = new PIXI.Sprite(sheet.textures["image.png"]);
  7. };

loader 加载资源的一些API

  1. // loader加载远程资源的过程或者生命周期
  2. loader.onProgress.add(() => {}); // called once per loaded/errored file
  3. loader.onError.add(() => {}); // called once per errored file
  4. loader.onLoad.add(() => {}); // called once per loaded file
  5. loader.onComplete.add(() => {}); // called once when the queued resources all load.

特殊的精灵,平铺精灵(TilingSprite)

在一定范围内,你需要重复一个纹理,使他们创建无线滚动的背景效果。

  1. let tilingSprite = new PIXI.extras.TilingSprite(texture, width, height);
name defluat desc
texture 纹理
width 100 平铺精灵的宽度
height 100 平铺精灵的高度

6、创建一段文字并给他样式

  1. //创建文字
  2. const mark = new Text('我创建一段文字',new TextStyle({
  3. fontFamily: "Arial",
  4. fontSize: 36,
  5. fill: "white",
  6. stroke: "#ff3300",
  7. strokeThickness: 4,
  8. dropShadow: true,
  9. dropShadowColor: "#cccccc",
  10. dropShadowBlur: 4,
  11. dropShadowAngle: Math.PI / 6,
  12. dropShadowDistance: 6,
  13. });
  14. //将文字放入容器(container)中
  15. container.addChild(mark);

当一些精灵(可以是文字,纹理,绘制,哪怕是容器)存入一个容器中时,他会有一个相对位置和绝对位置,相对位置就是widget 在容器中的位置,绝对位置是相对的canvas 左上角的位置。

  1. //使用Graphics创建一段墙壁,放入容器中,因为他drawRect 绘制要确定X,Y 这个X,Y是相对容器位置的
  2. //需要使用position.set设置绝对位置才能到canvas的实际位置
  3. const leftWall = new Graphics();
  4. leftWall.beginFill(0xde3249);
  5. leftWall.drawRect(0, 0, 10, this.height as number);
  6. leftWall.position.set(0, 0);
  7. leftWall.endFill();
  8. container.addChild(leftWall);

附录,贴上两端祖传代码(百度也能查到):

一段,键盘事件的函数代码:

  1. interface keyProps {
  2. value: string
  3. isDown:boolean
  4. isUp:boolean
  5. press?: any
  6. release?: any
  7. downHandler:(Event:KeyboardEvent)=>void
  8. upHandler:(Event:KeyboardEvent)=>void
  9. unsubscribe:()=>void
  10. }
  11. const keyboard = (value:string) => {
  12. let key:keyProps = {
  13. value: value,
  14. isDown: false,
  15. isUp: true,
  16. press: undefined,
  17. release: undefined,
  18. downHandler: event => {
  19. if (event.key === key.value) {
  20. if (key.isUp && key.press)
  21. key.press();
  22. key.isDown = true;
  23. key.isUp = false;
  24. event.preventDefault();
  25. }
  26. },
  27. upHandler: event => {
  28. if (event.key === key.value) {
  29. if (key.isDown && key.release) key.release();
  30. key.isDown = false;
  31. key.isUp = true;
  32. event.preventDefault();
  33. }
  34. },
  35. unsubscribe: () => {
  36. window.removeEventListener("keydown", downListener);
  37. window.removeEventListener("keyup", upListener);
  38. }
  39. };
  40. const downListener = key.downHandler.bind(key);
  41. const upListener = key.upHandler.bind(key);
  42. window.addEventListener(
  43. "keydown", downListener, false
  44. );
  45. window.addEventListener(
  46. "keyup", upListener, false
  47. );
  48. return key;
  49. }
  50. export {
  51. keyboard,
  52. keyProps
  53. };

一段碰撞检测:

  1. hitTestRectangle(r1: any, r2: any) {
  2. let hit, combinedHalfWidths, combinedHalfHeights, vx, vy;
  3. hit = false;
  4. r1.centerX = r1.x + r1.width / 2;
  5. r1.centerY = r1.y + r1.height / 2;
  6. r2.centerX = r2.x + r2.width / 2;
  7. r2.centerY = r2.y + r2.height / 2;
  8. r1.halfWidth = r1.width / 2;
  9. r1.halfHeight = r1.height / 2;
  10. r2.halfWidth = r2.width / 2;
  11. r2.halfHeight = r2.height / 2;
  12. vx = r1.centerX - r2.centerX;
  13. vy = r1.centerY - r2.centerY;
  14. combinedHalfWidths = r1.halfWidth + r2.halfWidth;
  15. combinedHalfHeights = r1.halfHeight + r2.halfHeight;
  16. if (Math.abs(vx) < combinedHalfWidths) {
  17. if (Math.abs(vy) < combinedHalfHeights) {
  18. hit = true;
  19. } else {
  20. hit = false;
  21. }
  22. } else {
  23. hit = false;
  24. }
  25. return hit;
  26. }