前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【H5游戏】 pixijs 需求级入门

【H5游戏】 pixijs 需求级入门

作者头像
神仙朱
发布2021-11-11 14:24:43
2.7K0
发布2021-11-11 14:24:43
举报

小东西快快学快快记,大知识按计划学,不拖延

最近我们有几个H5小游戏的需求,一个是人物换装,一个是红包雨,我们都是用pixijs来做的

本来主要目的是写人物换装这个H5游戏的具体实现,但是基础是要会用pixi,需要了解基本的api

所以另起一文从需求使用角度介绍下pixi,记录下使用 pixi 踩得坑,保证能用pixi完成一个东西

本文目录

1、pixi简介

2、简单说明

3、api介绍

本文基于 pixi v6.1.2

pixi 简介

pixijs 是一个

1、最快的 2D 渲染引擎

2、拥有丰富简洁的api,可以便捷地渲染图形(缩放旋转等),操作图形(各种事件交互)

3、用于替代flash,比flash性能更好,能实现更多细节

4、基于canvas,优先使用webgl

webgl

1、webgl 利用硬件加速,高性能图形渲染

2、集成在canvas中,无需引入任何插件,原生支持

5、用处

1、h5游戏

2、复杂交互的活动页

3、数据可视化

1比较竞品

除此之外还有几个游戏渲染引擎,比如常用的three.js,cocos2d,createjs,playcanvas 等

选择一个框架,通常要考虑,开发便利性(是否支持ts,文档是否齐全,是否有中文文档,难易程度),性能(包大小),生态(是否有团队维护,维护更新频率高低)

下面来看下比较

我们要做的是 2D 游戏,比较之下,在2D渲染层面,pixi是性能最优,并且支持ts,上手成本较低,而且维护迭代很好,无疑是做2D游戏最优之选

简单说明

游戏都是由各种元素组成的,人物,道具,场景 等等,所以做一个游戏,最主要的就是创建元素,操控元素。

创建元素,就是设定元素显示的内容,大小,位置,形状变化等等

操控元素,就是让元素变化和动起来

而元素创建出来肯定需要挂载的,就像DOM 一样,所以这里有一个 容器的 概念,相当于是 元素组合。

看一下 pixi 一个简单的实现

在页面上显示一张图片(关闭按钮),大概就是这样

简单看看 pixi 的实现

代码语言:javascript
复制
// 创建容器,也是创建 canvas
let app = new PIXI.Application();
// 创建元素
const del = PIXI.Sprite.from('./img/del.png');
// 添加元素进容器
app.stage.addChild(del);
// 挂载到页面
document.body.appendChild(app.view);

其实一个游戏简单流程就是

1、创建容器

2、创建元素

3、设置元素样式(大小,位置 等)

4、添加元素进容器

5、操控元素(动画、按键移动 等)

只是里面具体实现的细节会有些复杂罢了,大致的流程就是这样的

api介绍

下面就来详细介绍下 pixi 的 api 了,我是以做需求的角度去介绍的,看完就能做自己的游戏了

主要分三部分介绍

容器、资源、精灵元素

不会介绍太多,主要介绍够完成一个需求的内容

1

容器

首先我们需要创建整个应用的根容器,后面所有创建的元素都需要添加进来

代码语言:javascript
复制
let app = new PIXI.Application({
    width: 256, // default: 800 宽度
    height: 256, // default: 600 高度
    antialias: true, // default: false 反锯齿,使字体和图形边缘更加平滑
    transparent: false, // default: false 透明度,使canvas背景透明
    resolution: 1, // default: 1 分辨率
});
document.body.appendChild(app.view);

因为是h5应用,所以创建完需要挂载到页面上,其中它会自动判断是使用canvas或者webgl来渲染

查看官网

https://pixijs.huashengweilai.com/guide/start/3.stage.html#%E5%88%9B%E5%BB%BApixi%E5%BA%94%E7%94%A8%E5%92%8Cstage

如果后期想修改整个画布的大小

代码语言:javascript
复制
app.renderer.resize(512, 512);

2

资源

做一个游戏肯定需要用到很多资源的,比如图片那些,基本游戏都是由图片组成的,所以提前把图片加载完是必须的,相当于作为游戏初始化过程

主要4个问题

1、怎么预加载

2、怎么一次性加载多个

3、怎么获取缓存

4、怎么知道加载进度

怎么预加载

需要新建loader实例

代码语言:javascript
复制
const loader= new PIXI.Loader();   

或者我们可以使用新建的 app 实例中的loader 实例

代码语言:javascript
复制
app.loader

loader 加载图片,主要是 add 方法,有三个参数

1. 图片链接

2. 缓存key(为了后面更加方便得获取这个图片缓存)

3. 完成回调

支持各种参数的形式

代码语言:javascript
复制
loader   
.add('key', 'http://...', function ({})   
.add('http://...', function () {})    
.add('http://...')
.add({ name: 'key2',url: 'http://...'}, function () {})
.add({ name: 'key3',url:'http://...'onComplete: function () {}})

上面只是添加资源,只要调用了 load 方法才开始加载资源,全部加载的时候会调用传入的回调

代码语言:javascript
复制
loader.load(()=>{
    // all resource loaded 
})

一次性加载多个

传入一个数组,就可以一次性加载多个

代码语言:javascript
复制
loader.add([
  { name: "sceen", url: "./img/materials/sceen/1.png" },
  { name: "accessories", url: "./img/materials/accessories/1.png" },
  { name: "props", url: "./img/materials/props/1.png" },
  { name: "trousers", url: "./img/materials/trousers/5.png" },
])

加载进度

一般我们会在应用初始化的时候一次性加载完所有图片,这个过程中,我们会在页面中显示一个加载百分比

这时候我们就需要监听资源加载的进度,监听进度要 放在 load 方法之前

代码语言:javascript
复制
loader.onProgress.add((loader) => {
  console.log("progress", loader.progress);
});

图片的个数作为加载的百分比,比如上面加载了4个图片

获取图片缓存

每个 loader 实例加载的资源,都会存在各自实例下,不会相互影响

比如下面app自带loader和我们新建loader加载的资源列表

其中 key 是 每个资源设置的别名

我们需要使用这个缓存的资源去 创建元素,点开其中一个资源

其中 texture 属性,主要是使用它去创建元素(后面会说)

还有一个 error 属性,如果图片加载出错,它才会有值,否则会null

所以通常通过error判断这个资源加载是否成功

虽然每个loader只保存自己加载的资源,但是我们还可以一次性看到所有 loader 实例加载资源

PIXI.utilize.TextureCache

我们以设置的别名key 或者图片的路径 都可以直接获取到 缓存

还有更简便获取缓存的方式

代码语言:javascript
复制
PIXI.Texture.from("./img/materials/accessories/2.png")

直接传入图片路径,就可以拿到 缓存,毕竟有的图片是请求回来的,不会所有图片一开始都拿到走一遍loader 流程

图片是可能加载失败的,所以考虑到应用的容错性,我们会封装一个方法去获取图片的缓存

先从缓存中获取,如果 缓存的图片不存在或者加载使用,再重建缓存

代码语言:javascript
复制
function getTextureFromCache(
  app,
  name,
  url
) {
  const { loader } = app;
  const cacheResource = loader.resources[name];
  let texture = cacheResource?.texture;
  // 资源不存在或preload出现error,因为preload失败对象仍然存在,仅仅有error对象
  if (!texture || cacheResource?.error) {
    const newUrl = `${url}?retry=1`; // newUrl是由于同一链接loader会拿失败的那个继续用
    texture = PIXI.Texture.from(newUrl);
  }
  return texture;
}

3

精灵元素

容器创建好了,资源也加载好了,现在就需要创建游戏中的元素了,一般也叫做精灵 Sprite,总之游戏中 人物,道具,背景,装饰 都叫做元素

游戏主要部分就是精灵元素,所以元素涉及的内容很多

内容分为5部分

1、CRUD

2、元素内容

3、显示效果

4、精灵元素分组

5、事件

1CRUD

创建

支持两种方式

1、只支持传入缓存资源

代码语言:javascript
复制
const sceen = PIXI.utils.TextureCache.sceen
const sp1=new PIXI.Sprite(sceen)

2、支持传入缓存或者图片链接

通过 from 的方式,可以快捷地通过一个图片链接创建一个元素

代码语言:javascript
复制
const sceen = PIXI.utils.TextureCache.sceen
const sp2 = PIXI.Sprite.from(sceen) 
const sp3 = PIXI.Sprite.from("./img/xxxx.png")

精灵创建完之后,需要添加进容器才会显示

代码语言:javascript
复制
// app是前面创建的容器
app.stage.addChild(sp1)
app.stage.addChild(sp2)

app.stage.addChild(sp1,sp2)

修改

精灵都是直接修改属性就ok了,pixi 会完成动态更新,类似于Vue 修改属性

比如修改图片,直接替换缓存资源

代码语言:javascript
复制
sprite.texture = Loader.resources.accessories2.texture

比如修改宽高

代码语言:javascript
复制
sprite.width = 10
sprite.height = 10

移除

移除具体元素,可以同时移除多个

代码语言:javascript
复制
app.stage.removeChild(sprite,sprite,sptite)

还有 removeChildAt(index) 移除第几个,removeChildren(beginIdx, endIdx) 移除索引范围

不过这两个方法做需求的时候没用过

查找

根据元素名字查找子元素或者后代元素

代码语言:javascript
复制
app.stage.getChildByName(sprite.name)

创建的元素默认是没有名字的

如果要用这个方法,就需要手动给 这个元素一个名字

当然也可以获取第几个 getChildAt(index)

显隐

有时想要去掉一个元素,并不一定要移除,可以通过显隐的方式,毕竟显隐比移除再重建成本低

代码语言:javascript
复制
sprite.visible = true | false

2元素内容

元素绘制的内容,主要分为 三类,图片,图形,文字

图片

绘制图片已经说过了,比较简单

通过 new Sprite() 或者 Sprite.from() 的创建方式,图片数据通过 PIXI.Loader 加载成纹理缓存,或者直接使用图片链接的方式

图形

和 Canvas 绘制图形的api 几乎相同,不过 Pixi 可以通过 webgl 进行高性能绘制,简单贴几个例子

代码语言:javascript
复制
// 矩形
let rectangle = new PIXI.Graphics();
rectangle.beginFill(0x66ccff);
rectangle.drawRect(0, 0, 64, 64);
rectangle.endFill();
rectangle.x = 170;
rectangle.y = 170;
app.stage.addChild(rectangle)

// 圆形
let circle = new PIXI.Graphics();
circle.beginFill(0x996687);
circle.drawCircle(0, 0, 32);
circle.endFill();
circle.x = 64;
circle.y = 130;
app.stage.addChild(circle)

更多demo可以看 官网 https://pixijs.io/guides/basics/graphics.html

文字

文字绘制主要是自定义style,在 canvas 中自定义样式十分麻烦,这里就挺简单的

代码语言:javascript
复制
let style = new PIXI.TextStyle({
  fontSize: 36,
  fill: 'white',
});
// 注入创建的 style
let message = new PIXI.Text('Pixi 文字', style);
app.stage.addChild(message)

后期修改 内容和 样式,直接修改对应的属性就行

代码语言:javascript
复制
message.text="修改了"
message.style={ fontSize:100 }

更多信息:

https://pixijs.io/guides/basics/text.html

https://pixijs.huashengweilai.com/guide/start/14.graphic-primitive.html#%E6%98%BE%E7%A4%BA%E6%96%87%E5%AD%97

3显示效果

既然绘制元素肯定有这些基础的绘制效果,控制精灵元素的大小,位置,旋转,缩放,原点,层叠 等等

所有显示效果都可以简单地设置属性

宽高大小

代码语言:javascript
复制
sprite.width =10
sprite.height =10

位置

设置xy两个坐标

代码语言:javascript
复制
sprite.x=10
sprite.y=10

缩放、旋转

缩放就是scale,有两种修改方法

代码语言:javascript
复制
sprite.scale.x=1
sprite.scale.y=2

或者调用方法

代码语言:javascript
复制
sprite.scale.set(1 /*x*/,1 /*y*/)
sprite.scale.set(1) // 只传一个参数表示xy同样值

注意点

1、scale 是基于图片原始大小缩放

如果图片原始大小是 100*100,使用这个图片创建精灵,手动设置宽高为 64*64

这时候再设置 sprite.scale = 0.5 的话,是基于原始大小缩放,而不是设置的64

所以最终渲染大小是 50*50,不是32*32

2、设置 scale 可能无效

如果在图片没有加载完成的时候,就直接给精灵元素设置了 宽高和 scale,那么此时 scale 无效

比如 使用 Sprite.from("https://图片链接")创建精灵

代码语言:javascript
复制
const sprite = PIXI.Sprite.from("./img/materials/blue.png");
sprite.height = 64;
sprite.width = 64;
sprite.scale.set(0.1); // scale无效
app.stage.addChild(sprite);

最好显示出来的精灵元素宽高还是 64*64,并不会缩小成0.1倍

最好的办法是把图片放进缓存,在 图片加载完毕后 设置属性,或者 保证设置 scale 在 图片loaded 之后

旋转则是修改rotation属性

代码语言:javascript
复制
sprite.rotation = 0.5

值的单位是弧度,一圈的弧度是 2π,1 弧度 约为57.3°,所以如果转半圈,那么就应该设置为 π,在js 中就是 Math.PI

代码语言:javascript
复制
sprite.rotation = Math.PI;

如图

基点

元素渲染 和 旋转变化 是有一个基点的,就像 css 属性 background-origin 一样。

默认的基点是 元素的左上角,基点 x = 0,基点y = 0,渲染出来的 x y 和 旋转都基于这个基点

比如设置了 xy为 100,元素的左上角就向右100,向下100,渲染出来就是这样

图形的大小是 100*100,默认基点x=0,基点 y = 0,如果设置基点是 图形正中间

那么就应该设置成 基点 x = 50, 基点y = 50,那么效果就是

对比一下就是设置了基点会 整体往左上移动了

PIXI 官方没有 基点这个概念,只是我为了统一叫的,而是分为 原点 和 锚点

原点属性叫做 sprite.pivot,锚点属性叫做 sprite.anchor ,他们都包含为 x 和 y 两个坐标

这两个属性作用都是设置基点,他们不同点是设置的值单位不一样

sprite.pivot 设置的是 像素,sprite.anchor 设置的 百分比

比如 元素的 大小是 100*100,我们要设置基点为元素中心点

两种属性的写法是

代码语言:javascript
复制
sprite.anchor.x = 0.5; // 百分比50%
sprite.anchor.y = 0.5; // 百分比50%

sprite.pivot.x = 50; // 像素50px
sprite.pivot.y = 50; // 像素50px

还可以简写成

代码语言:javascript
复制
sprite.anchor.set(0.5, 0.5)
sprite.anchor.set(0.5) // 单值表示 x 和 y 一样

sprite.pivot.set(50, 50)
sprite.pivot.set(50) // 单值表示 x 和 y 一样

平常设置得比较多的属性是 pivot ,因为不是任何元素都可以设置 anchor

比如 Sprite 创建的元素就可以设置 pivot 和 anchor 属性

但是创建的容器 和 图形 就只有 pivot 属性

4精灵元素分组

游戏是会创建很多元素的,我们不可能创建一个就添加一个进根容器

这样元素关系就十分混乱不利于管理

所以会把元素分组,也就是创建一个新容器存放一类元素精灵

比如一个人物,通常就需要创建多个元素,头部,身部,腿部,手部,服装,武器等等

所以就会创建一个人物容器,把所有所属于它的元素都 加进去,这样我控制整个任务容器就可以一次性控制它的所有元素

比如人物移动,所有元素都需要移动,只会控制人物容器移动,而不会每个元素都移动一次

很简单,代码如下

代码语言:javascript
复制
const personContainer = new PIXI.Container();
const sprite = PIXI.Sprite.from("./img/head.png");
personContainer.addChild(sprite); // 添加进容器
const sprite2 = PIXI.Sprite.from("./img/body.png");
personContainer.addChild(sprite2);// 添加进容器
app.stage.addChild(personContainer);// 创建的容器加入 根容器

容器大小

容器默认的大小是包含所有子元素情况下最小矩形面积(红色框起来的地方)

容器位置

容器默认的起始位置 x =0,y = 0,但是看上面容器并不在 (0,0) 位置开始(没有修改容器 x y)

因为上面的红色框起来的部分,只能叫做大小,但是实际整个容器包含

囊括子元素最小矩形面积 + 子元素坐标偏移量

比如黑色元素坐标设置为了 x = 64 ,y = 64,那么容器需要把黑色子元素的坐标偏移量 64*64 计算进去

所以最外层红色框才是整个容器

所以容器的左上角仍然在 (0,0) ,而不是面积左上角

子元素坐标

把 元素添加进容器之后,元素设置的坐标就是相对于容器的左上角

比如子元素的大小和位置不变,修改容器起始坐标 xy,就得到这样

这里子元素坐标相对于容器左上角是固定的,修改容器的基点不会影响

容器缩放

容器缩放,子元素也会跟着缩放,但是获取子元素的宽高还是缩放前的宽高

比如下面这样

代码语言:javascript
复制
let r1 = new Graphics();
r1.beginFill(0x66ccff);
r1.drawRect(0, 0, 64, 64);
r1.endFill();
r1.x = 64 
r1.y = 64 

const container = new PIXI.Container();
container.addChild(r1);
container.scale.set(0.5, 0.5);

蓝色方块跟着容器缩小后应该是 32*32,但是实际获取数据还是64*64

如果你想获取到实际的大小,就需要用到 getBounds 方法

所以 getBounds 获取元素数据是以实际渲染为准,而直接从元素上获取 width height 的数据,则是保留最初的设置

并且 getBounds 获取到的坐标,是以元素左上角为基准进行计算,而不是 元素的基点

如下图,给 元素 r1 设置基点为中心之后,元素就往左上跑了,所以左上角就越靠近(0,0)

所以 getBounds 获取的坐标就从 (64,64) 变成 (32,32)

总结出来就是

1、元素获取自身 x、y 是固定的,元素 width、height 只受自身 scale 影响

2、getBounds 获取 x、y、width、height 则以渲染结果为准

5事件

canvas 添加事件非常麻烦,不过 PIXI 把这个事情做好了,我们使用起来就和 dom 监听事件一样简单

暂时使用起来还没有发现什么坑点

怎么添加事件

主要是给 元素设置 interactive 为true,然后再监听事件

代码语言:javascript
复制
let r1 = new PIXI.Graphics();
r1.beginFill(0x66ccff);
r1.drawRect(0, 0, 64, 64);
r1.endFill();
r1.x = 64;
r1.y = 64;

app.stage.addChild(r1);

r1.interactive = true;
r1.on("pointerdown", (e) => {
  console.log("e", e);
});

有什么事件

mouse 类(mousedown、mousemove)

touch 类(touchmove、touchstart)

自身事件(子元素添加 added,子元素移除 remove ....)

更多请看

https://pixijs.download/dev/docs/PIXI.Container.html#added

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-11-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 神仙朱 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档