PixiJS是一个2D渲染引擎,能自动侦测并使用WebGL或Canvas。
PixiJS使用JavaScript或HTML5基础来显示媒体,创建动画或管理交互式图像,从而制作游戏或应用。
PixiJS适合制作游戏但并非是游戏引擎,其核心本质是尽可能快速有效地在屏幕上移动物体。
PixiJS作为JavaScript的2D渲染器,目标是提供快速轻量且兼容所有设备的2D库。提供无缝Canvas回退、支持主流浏览器,包括桌面和移动设备。
PIXI只做三件事
- Loading and displaying of assets
- interactivity
- game loop
安装测试
$ npm i pixi.js
引入
import * as PIXI from "pixi.js";
- 开发工具 Vistual Code
- Web服务器插件 Live Server
- Chrome调试工具 PixiJS devtools
测试
判断当前浏览器渲染引擎类型
$ vim index.html
Document
判断代码
//判断浏览器渲染引擎
const type = PIXI.utils.isWebGLSupported() ? "WebGL" : "canvas";
//console.log(type);//WebGL
PIXI.utils.sayHello(type);//PixiJS 5.2.1 - WebGL - http://www.pixijs.com/
概念
使用PIXI的基本流程通常分为以下几步
- 创建舞台
- 创建画布
- 将画布添加到DOM
- 创建精灵
- 将精灵添加到画布
- 将画布添加舞台
- 定时刷新舞台
PIXI常用模块包括
- Application 应用
- Container 容器
- Stage 舞台
- Renderer 渲染器
- Ticker 计时器
- Loader 加载器
舞台stage
- 所有需要渲染的对象都必须添加到舞台中才能被显示出来
- 舞台处于整个树形展示结构的对底层可理解为背景
画布renderer
画布是指使用WebGL或Canvas绘图引擎进行渲染的一块区域
纹理texture
纹理可理解为承载图片的结构,纹理本身不能直接用于显示,需要通过精灵才能显示,类似DOM中的临时碎片。
精灵sprite
精灵是可以直接用于舞台显示的对象,可理解为DOM中的元素。精灵可直接食用图片创建,也可以先创建纹理在使用纹理创建精灵。
事件event
事件用于交互
应用PIXI.Application
PIXI拥有一个Pixi应用对象PIXI.Application
,可自动创建一个canvas
标签,用于计算图片在其中的显示。PIXI.Application
会自动创建渲染器、计时器和根容器。
const app = new PIXI.Application(options);
PIXI.Application建立的同时会自动建立render、ticker、root container。
const app = new PIXI.Application({width:400, height:300, view:document.querySelector("canvas")});
- app.renderer 渲染器,优先使用WebGL渲染若不支持则使用Canvas渲染。
- app.ticker 渲染的更新频率,计时器。
- app.stage 根容器
例如:创建可显示图片的矩形显示区域
//创建一个Pixi应用
const app = new PIXI.Application({width:400, height:300});
//pixi自动创建canvas标签并添加到document文档中
document.body.appendChild(app.view);
自动创建canvas
标签
使用PIXI.Application
创建的pixi应用计算并选择渲染引擎,这取决于浏览器自身所支持的渲染引擎的类型。创建pixi应用时需传入options选项对象参数,options对象中可设置pixi应用的宽高、透明等属性。若options对象中未设置view属性,则会自动创建一个canvas元素,创建出来的canvas元素就在pixi应用的view属性中。也可以使用view属性手动指定。
const app = new PIXI.Application({width:400, height:300, view:document.querySelector("canvas")});
document.body.appendChild(app.view);
选项 | 类型 | 默认值 | 描述 |
---|---|---|---|
width | number | 800 | 渲染器视宽度 |
height | number | 600 | 渲染视图高度 |
view | HTMLCanvasElement | - | 使用canvas作为视图 |
transparent | boolean | false | 视图是否透明 |
resolution | number | 1 | 分辨率,渲染器设备像素比,R屏默认为2. |
antialias | boolean | false | 是否平滑抗锯齿,取消字体平滑,抗混叠。 |
forceCanvas | boolean | false | 是否强制使用canvas渲染模式,默认使用WebGL。 |
- antialias可使字体的边界和集合界面更为圆滑,WebGL的anti-aliasing在所有平台都不可用。
- transparent会将整个canvas标签的透明度进行设置
- resolution会使pixi在不同分辨率和像素密度的设备上显示正常
- pixi的canvas画布对象将会默认选择WebGL引擎渲染模式,若需使用canvas引擎绘制可设置 forceCanvas。
适配分辨率
pixi会自动调整像素密度以匹配运行内容的设备的分辨率,开发人员所要做的是为高分辨率和低分辨率提供不同的图像。pixi将帮助你根据当前的设备像素比devicePixelRatio
选择对应的图像。当创建高分辨率图像时,可以将@2x
添加到图像文件名称后,以说明图像是支持高分辨率的屏幕,例如Retina屏幕。
获取当前设备设备像素比,dpr是一个描述设备像素比的数字,由运行应用程序的设备自动提供,其中1表示为标准分辨率,2表示高密度分辨率,3表示超高密度显示器。
const dpr= window.devicePixelRatio;
const width = window.innerWidth;
const height = window.innerHeight;
const resolution = window.devicePixelRatio;
let app = new Application({width:width, height:height, resolution:resolution, transparent:true, antialias:true});
document.body.appendChild(app.view);
pixi应用对象的成员
成员 | 类型 | 描述 |
---|---|---|
app.loader | PIXI.Loader | 加载器实例用于资源加载,用于加载静态资源。 |
app.renderer | PIXI.Renderer|PIXI.CanvasRenderer | WebGL渲染器,可使用CanvasRenderer。 |
app.resizeTo | Window|HTMLElement | 元素或窗口尺寸更改 |
app.screen | PIXI.Rentangle | 渲染器的屏幕矩形对象 |
app.stage | PIXI.Container | 根显示容器,舞台,pixi创建的根容器。游戏中的精灵或容器都要添加到舞台上才能显示。 |
app.ticker | PIXI.Ticker | 计时器,可理解为每次渲染一帧前执行。 |
app.view | HTMLCanvasElement | 渲染器的canvas元素,pixi创建的DOM元素即用于渲染游戏的canvas。 |
修改canvas标签背景色
app.renderer.backgroundColor = 0xff0000;
获取画布的宽度和高度
console.log(app.renderer.view.width);//400
console.log(app.renderer.view.height);//300
重新设置画布的canvas宽高尺寸
app.renderer.autoResize = true;
app.renderer.resize(512, 512);
console.log(app.renderer.view.width);//512
console.log(app.renderer.view.height);//512
设置canvas占满整个窗口,需配置CSS重置padding和margin为0。
*{padding:0;margin:0;}
app.renderer.view.style.position = "absolute";
app.renderer.view.style.display = "block";
app.renderer.autoResize = true;
app.renderer.resize(window.innerWidth, window.innerHeight);
渲染器app.renderer
获取当前应用渲染器的类型
const rendererType = app.renderer.type;
const rendererType = PIXI.RENDERER_TYPE;
渲染模式 | 取值 |
---|---|
UNKNOWN | 0 |
WEBGL | 1 |
CANVAS | 2 |
渲染器选项参数
名称 | 类型 | 默认值 | 描述 |
---|---|---|---|
width | number | 800 | 屏幕宽度 |
height | number | 600 | 屏幕长度 |
view | HTMLCanvasElement | - | canvas渲染模式需使用的视图与选项 |
transparent | boolean | false | 渲染器视图是否透明 |
容器 PIXI.Container()
容器是用来转载多个显示对象的,创建的Pixi应用的stage属性本质上就是一个容器对象,stage对象被当作根容器使用,它将包裹所有想用Pixi显示的元素。
let container = new PIXI.Container();
container.addChild(sprite);
自定义的Container通常用来做分组使用,分组的好处在于修改Container的属性,位于其中的子节点都会受到影响。
常见的作法是创建一个最顶层的rootContainer,之后所有的内容都添加到rootContainer中,rootContainer作为顶级元素可进行缩放来适配不同的分辨率。
Container与Sprite有何异同
- Container表示显示对象的集合,是作为其他对象的容器,是所有显示对象的基类。
- Container是用来转载多个显示对象,也包括自身Container。
- Sprite表示基于纹理对象存在,是能将纹理显示到舞台的显示对象。
- Container和Sprite的继承关系是PIXI.Sprite继承自PIXI.Container。
Sprite是继承自Container的,Container又是继承自什么对象呢?
Container继承自DisplayObject,DisplayObject是最基本的显示对象。
PIXI.Sprite -> PIXI.Container -> PIXI.DisplayObject
Sprite和Container共享DisplayObject的函数和方法
显示对象 PIXI.DisplayObject
- DisplayObject是所有渲染到屏幕上的对象的基类
- DisplayObject是一个抽象类
成员属性 | 类型 | 描述 |
---|---|---|
x | number | X轴坐标值 |
y | number | Y轴坐标值 |
position | PIXI.Point / PIXI.ObservablePoint | 位移坐标 |
alpha | number | 透明度 |
visible | boolean | 显示隐藏 |
rotation | number | 旋转角度 |
angle | number | 旋转角度 |
scale | PIXI.Point / PIXI.ObservablePoint | 缩放值 |
skew | PIXI.ObsevablePoint | 斜切值 |
transform | PIXI.TransformBase | 矩阵对象 |
cacheAsBitmap | boolean | 是否将显示对象转换为位图 |
renderable | boolean | 是否能被渲染到舞台 |
舞台app.stage
创建pixi应用对象并添加到document中后就有了一个画布,要在画布上显示东西就必须加入到一个称为舞台的pixi对象pixi.stage
。
app.stage是一个Container的实例,作为最底层的舞台,所有需要渲染的图形都应放到stage的内部。
pixi.stage
舞台是一个pixi容器对象,可理解为将放入的东西分组并存储的空箱子。stage舞台对象是在场景总所有可见对象的根容器。所有放入到舞台的东西都会被渲染到canvas中。
stage对象是Container容器,是所有Sprite精灵的根容器。
stage舞台对象是pixi容器对象,所有的容器对象都具有相同的属性和方法。尽管stage舞台拥有width和height属性,但不能查看画图窗口的大小。stage舞台的width和height属性仅仅告诉放入东西占用的大小。
舞台内科放入什么东西呢?是称作精灵的特殊图像对象。精灵是能够使用代码控制的图像,可以控制它的位置、大小等属性来产生交互和动画。
坐标
- 绝对坐标
global
是以舞台stage
为基准的坐标系,精灵相对于舞台的位置和方向。 - 相对坐标
local
是以舞台stage
中某个容器container
为基准的坐标系,一般是精灵的父容器。
Pixi提供了两个函数用于坐标变换
-
toGlobal
相对坐标转换为绝对坐标 -
toLocal
绝对坐标转换为相对坐标
无论是容器还是精灵的坐标以及方向都是相对于父容器的坐标系的,坐标原点默认是容器的左上角,可改变容器的坐标原点anchor
。
加载 PIXI.Loader
将图片加载到纹理缓存,因为pixi使用WebGL和GPU渲染图像,所以图像需转化成GPU可以处理的格式。可以被GPU处理的图像称为纹理Texture。使用精灵显示图片之前,需将图片转化成WebGL纹理,pixi使用纹理缓存存储和应用精灵所需的纹理。纹理的名字是图像的地址。
从纹理缓存中获取图片
PIXI.utils.TextureCache["images/cat.png"];
纹理被以WebGL兼容的格式存储起来,可使Pixi渲染更加高效。
例如:从纹理缓存中获取图片并转化为纹理
//将图像加载到纹理缓存
let texture = PIXI.utils.TextureCache["images/avatar.jpg"];
//使用纹理创建精灵
let sprite = new PIXI.Sprite(texture);
如何加载图像并将其转化为纹理呢?可使用pixi的Loader对象,pixi的Loader对象可加载任何种类的图像资源,使用load方法加载图像完成时可使用回调方法使用它。
const imgpath = "images/avatar.jpg";
PIXI.loader.add(imgpath).load(function(){
let texture = PIXI.loader.resources[imgpath].texture;
let sprite = new PIXI.Sprite(texture);
app.stage.addChild(sprite);
});
纹理 PIXI.Texture
PixiJS使用WebGL或Canvas渲染图像,所以图像需要转化成GPU能够处理的格式,可以被GPU处理的图像被称为纹理Texture
。比如,当让精灵显示图片之前,需要将普通的图片转化成WebGL纹理。为了让工作执行的快速有效率,PixiJS使用纹理缓存来存储和应用精灵所需的纹理。纹理的名称也就是图像的地址。
纹理存储着图像或图像的部分信息,图像是不能直接添加到显示列表中,必须将图像转化为精灵的纹理。
从图片获取纹理
const texture = PIXI.Texture.from(imgPath);
游戏是一种很耗资源的应用,特别是在移动设备中的游戏。纹理图集(Texture Atlas)又称为精灵表(Sprite Sheet)是将许多小的精灵图片组合到一张大图中。
精灵 PIXI.Sprite
PIXI的核心是Sprite,PIXI.Sprite
继承自PIXI.Container
,PIXI.Container
继承自PIXI.DisplayObject
。
Sprite对象是渲染到屏幕的所有纹理对象的基础,它能轻松的改变纹理。
Sprite默认是不会响应点击等事件的,必须需要Interactive。
pixi拥有一个sprite精灵类用来创建游戏精灵,创建的方式有三种。
- 使用单图像文件创建
- 使用雪碧图来创建
- 从纹理图集中创建
创建Sprite的方式分为
- 常用方式
const sprite = new PIXI.Sprite(texture?);
- 快捷方式
const sprite = PIXI.Sprite.from(source);
参数source可以是URL地址、Canvas、纹理。
- 快捷方式
const sprite = PIXI.Sprite.fromFrame(key);
参数key是JSON格式中的key或加载时定义的key。
- 快捷方式
const sprite = PIXI.Sprite.fromImage(url, crossorigin?;
例如:
const imgpath = "images/avatar.jpg";
//将图片添加到纹理缓存
PIXI.loader.add(imgpath).load(function(){
//从纹理缓存中获取纹理
let texture = PIXI.loader.resources[imgpath].texture;
//使用纹理创建精灵
let sprite = new PIXI.Sprite(texture);
//隐藏精灵
sprite.visible = false;
//将精灵添加舞台
app.stage.addChild(sprite);
//将精灵从舞台中删除
app.stage.removeChild(sprite);
});
使用别名
//设置别名
let Application = PIXI.Application;
let loader = PIXI.loader;
let resources = PIXI.loader.resources;
let Sprite = PIXI.Sprite;
//创建应用
let app = new Application({width:256, height:256, resolution:1, transparent:false, antialias:true});
//添加canvas到document
document.body.appendChild(app.view);
//添加图片
const img = "images/avatar.jpg";
loader.add(img).load(function(){
let texture = resources[img].texture;
let sprite = new Sprite(texture);
app.stage.addChild(sprite);
});
监视加载进程
const name = "avatar";
const url = "images/avatar.jpg";
loader.add(name, url).load(function(){
let texture = resources[name].texture;
let sprite = new Sprite(texture);
app.stage.addChild(sprite);
}).on("progress", function(loader, resource){
console.log("loading", loader.progress, resource.name, resource.url);
});
add方法参数
add(name, url, optionObject, callbackFunction)
参数 | 类型 | 描述 |
---|---|---|
name | string | 加载源文件别名 |
url | string | 源文件地址 |
options | object literal | 加载设置 |
options.crossOrigin | boolean | 源文件请求跨域 |
options.loadType | string | 原文加加载类型,默认为Resource.LOAD_TYPE.XHR |
options.xhrType | string | XHR处理方式,默认为Resource.XHR_RESPONSE_TYPE.DEFAULT |
callbackFunction | function | 加载完后执行的回调函数 |
add方法可传入对象数组,也可使用链式加载。
const imgs = [
{name:"avatar", url:"images/avatar.jpg"},
{name:"person", url:"images/person.png"},
{name:"plane", url:"images/plane.bmp"},
];
loader.add(imgs).load(function(){
let texture = resources["plane"].texture;
let sprite = new Sprite(texture);
app.stage.addChild(sprite);
}).on("progress", function(loader, resource){
console.log("loading", loader.progress, resource.name, resource.url);
});
精灵位置
默认精灵sprite对象保存x和y属性用来定位
let sprite = new Sprite(texture);
sprite.x = 100;
sprite.y = 100;
sprite.position.set(10, 10);
设置精灵的宽高大小
sprite.width = 150;
sprite.height = 120;
设置精灵的缩放比例
sprite.scale.x = 0.5;
sprite.scale.y = 0.5;
sprite.scale.set(0.5, 0.5);
scale的值从0到1表示对原精灵大小的百分比,1表示100%原来大小,0.5表示50%一般大小。
设置精灵的旋转角度
可设置精灵的rotation来设定角度来旋转,旋转是针对锚点anchor发生的,默认在精灵的左上角,锚点是精灵旋转的中心点。
sprite.rotation = 0.5;
设置纹理锚点anchor
sprite.anchor.x = 0.5;
sprite.anchor.y = 0.5;
锚点anchor取值范围从0到1,是纹理长度或宽度的百分比。
设置精灵中心点pivot
精灵提供和anchor锚点类似的pivot属性来设置精灵的原点,若改变pivot中心点的值后旋转精灵,将会围绕设置的原点来旋转。
sprite.pivot.x = 100;
sprite.pivot.y = 100;
sprite.pivot.set(100, 100);
若精灵的尺寸为200x200,设置pivot(100, 100)后,精灵将围绕其中心点旋转。
锚点pivot和中心点pivot的区别在于
- anchor锚点改变了纹理的原点,使用0到1填充。
- pivot中心点则精灵的原点,使用像素来填充。
雪碧图SpriteSheet
雪碧图是使用单个文件包含游戏所需的所有图片文件,pixi中使用雪碧图中的小图片的方式是使用一个矩形去截取纹理缓存中的对应位置来创建精灵。
//设置别名
let Application = PIXI.Application;
let loader = PIXI.loader;
let resources = PIXI.loader.resources;
let Sprite = PIXI.Sprite;
let TextureCache = PIXI.utils.TextureCache;
let Rectangle = PIXI.Rectangle;
//创建应用
let app = new Application({width:256, height:256, resolution:1, transparent:false, antialias:true});
//添加canvas到document
document.body.appendChild(app.view);
//设置满屏
app.renderer.view.style.position = "absolute";
app.renderer.view.style.display = "block";
app.renderer.autoResize = true;
app.renderer.resize(window.innerWidth, window.innerHeight);
//添加图片
const imgs = [
{name:"altas", url:"images/altas.png"},
];
loader.add(imgs).load(function(){
let texture = TextureCache["altas"];
let rectangle = new Rectangle(0, 0, 64, 64);
texture.frame = rectangle;
let sprite = new Sprite(texture);
sprite.position.set(150, 150);
app.stage.addChild(sprite);
}).on("progress", function(loader, resource){
console.log("loading", loader.progress, resource.name, resource.url);
});
Pixi内置通用Rectangle对象PIXI.Rectangle
,用来定义矩形对象,矩形对象仅仅是一个数据对象。
let rectangle = new PIXI.Rectangle(x, y, width, height);
Pixi的纹理中存在frame属性可被设置为任何Rectangle矩形对象,frame将纹理映射到Rectangle的维度。
从雪碧图创建精灵的纹理是一个使用频繁的操作,Pixi提供更为合适的方式来处理。
纹理贴图
纹理贴图集是一个JSON数据文件,包含了匹配的PNG雪碧图的子图像的大小和位置。使用纹理贴图集时只需要知道对应名称即可。修改或删除图片只需要修改JSON数据文件即可,游戏内会自动给程序内所有数据应用更新纹理贴图集。
Pixi可使用Texture Packer软件输出标准纹理贴图集格式
const imgs = [
{name:"man", url:"texture/man.json"},
];
loader.add(imgs).load(function(){
//let texture = TextureCache["man1.jpg"];
let texture = resources["man"].textures["man1.jpg"];
let sprite = new Sprite(texture);
sprite.position.set(150, 150);
app.stage.addChild(sprite);
}).on("progress", function(loader, resource){
console.log("loading", loader.progress, resource.name, resource.url);
});
例如:加载并创建纹理贴图集并获取其一居中显示
//设置别名
let Application = PIXI.Application;
let loader = PIXI.loader;
let resources = PIXI.loader.resources;
let Sprite = PIXI.Sprite;
let TextureCache = PIXI.utils.TextureCache;
let Rectangle = PIXI.Rectangle;
//创建应用
const width = window.innerWidth;
const height = window.innerHeight;
let app = new Application({width:width, height:height, resolution:1, transparent:false, antialias:true});
//添加canvas到document
document.body.appendChild(app.view);
//设置满屏
app.renderer.view.style.position = "absolute";
app.renderer.view.style.display = "block";
app.renderer.autoResize = true;
app.renderer.resize(window.innerWidth, window.innerHeight);
//资源位置
const urls = [
{name:"man", url:"texture/man.json"},
];
//加载纹理贴图集
loader.add(urls).load(function(){
//获取纹理创建精灵
let texture = resources["man"].textures["man1.jpg"];
let sprite = new Sprite(texture);
//水平垂直居中定位
const x = (width - sprite.width)/2;
const y = (height - sprite.height)/2;
sprite.position.set(x, y);
//精灵添加到舞台
app.stage.addChild(sprite);
}).on("progress", function(loader, resource){
console.log("loading", loader.progress, resource.name, resource.url);
});
精灵移动
使用pixi的ticker实现游戏循环,任何在游戏循环中的代码都会1秒更新60次。
例如:让hero精灵以每帧1像素的速率移动
//加载纹理贴图集
loader.add(urls).load(function(){
//获取纹理创建精灵
let texture = resources["man"].textures["hero1.png"];
let sprite = new Sprite(texture);
//水平垂直居中定位
const x = (width - sprite.width)/2;
const y = (height - sprite.height)/2;
sprite.position.set(x, y);
sprite.scale.set(0.5, 0.5);
sprite.vx = 1;
sprite.vy = 1;
//精灵添加到舞台
app.stage.addChild(sprite);
//精灵移动
app.ticker.add(delay=>{
console.log(sprite.y, sprite.vy);
if(sprite.x < height/2){
sprite.x += sprite.vx + delay;
sprite.y += sprite.vy + delay;
}
});
}).on("progress", function(loader, resource){
console.log("loading", loader.progress, resource.name, resource.url);
});
动画精灵 PIXI.AnimatedSprite
- 动画精灵就是逐帧动画,是通过 一帧一帧的播放图像来产生运动的错觉。
- 动画精灵使按一系列略不相同的图像创建精灵,然后一帧一帧播放这些图像产生运动的幻觉。
- 制作动画精灵需使用PIXI的AnimatedSprite方法
例如:将序列帧图片使用Texture Packer制作成纹理贴图集并导出JSON文件后制作精灵动画
//设置别名
let Application = PIXI.Application;
let loader = PIXI.loader;
let resources = PIXI.loader.resources;
let Sprite = PIXI.Sprite;
let TextureCache = PIXI.utils.TextureCache;
let Rectangle = PIXI.Rectangle;
let AnimatedSprite = PIXI.AnimatedSprite;
//创建应用
const width = window.innerWidth;
const height = window.innerHeight;
let app = new Application({width:width, height:height, resolution:1, transparent:false, antialias:true});
//添加canvas到document
document.body.appendChild(app.view);
//设置满屏
app.renderer.view.style.position = "absolute";
app.renderer.view.style.display = "block";
app.renderer.autoResize = true;
app.renderer.resize(window.innerWidth, window.innerHeight);
//资源位置
const urls = [
{name:"effect", url:"texture/effect.json"},
];
//加载纹理贴图集
loader.add(urls).load(setup).on("progress", function(loader, resource){
console.log("loading", loader.progress, resource.name, resource.url);
});
let sprite;
//启动代码
function setup(){
//获取纹理创建精灵
let textures = resources["effect"].textures;
//对象转数组
const arrTextures = Object.values(textures);
//创建精灵动画
sprite = new AnimatedSprite(arrTextures);
sprite.animationSpeed = 1;
//水平垂直居中定位
const x = (width - sprite.width)/2;
const y = (height - sprite.height)/2;
sprite.position.set(x, y);
//精灵添加到舞台
app.stage.addChild(sprite);
//播放精灵动画
sprite.play();
};
PIXI.AnimatedSpirte
使用纹理数组创建动画精灵
const sprite = new PIXI.AnimatedSprite(textures, autoUpdated);
参数 | 类型 | 默认值 | 描述 |
---|---|---|---|
textures | array | - | 纹理贴图数组 |
autoUpdated | boolean | true | 是否使用PIXI.ticker.shared自动更新动画时间 |
返回值:返回用于控制动画精灵的对象
返回值属性 | 类型 | 描述 |
---|---|---|
animationSpeed | number | 播放速度默认为1,值越大速度越快。 |
currentFrame | number | 当前帧编号 |
totalFrames | number | 帧总数 |
playing | boolean | 当前动画是否在播放 |
loop | boolean | 是否循环播放 |
onLoop | function | loop为true时调用 |
onComplete | function | 当loop为false时动画完成后调用 |
onFrameChange | function | 但动画更改要呈现的纹理时调用 |
textures | array | 纹理数组 |
返回值方法 | 参数 | 描述 |
---|---|---|
play | - | 播放的动画精灵 |
stop | - | 停止播放动画精灵 |
gotoAndPlay | frameNumber, numberType, frameIndex | 转到特定帧并开始播放动画 |
gotoAndStop | frameNumber, numberType, frameIndex | 转到特定帧并停止播放动画 |
平铺精灵PIXI.TilingSprite
平铺精灵是一种特殊的精灵,可在一定的范围内重复一个纹理。可使用平铺精灵创建无线滚动的背景效果。
创建平铺精灵
const TilingSprite = PIXI.TilingSprite;
const tiling = new TilingSprite(texture, width, height);
参数 | 类型 | 默认值 | 描述 |
---|---|---|---|
texture | PIXI.Texture | - | 平铺精灵纹理 |
width | number | 100 | 平铺精灵的宽度 |
height | number | 100 | 平铺精灵的高度 |
平铺精灵具有和普通精灵相同的属性,与普通精灵工作方式相同。
例如:使用小图创建平铺精灵并移动
//设置别名
let Application = PIXI.Application;
let loader = PIXI.loader;
let resources = PIXI.loader.resources;
let TextureCache = PIXI.utils.TextureCache;
let Rectangle = PIXI.Rectangle;
let Container = PIXI.Container;
let Sprite = PIXI.Sprite;
let AnimatedSprite = PIXI.AnimatedSprite;
let TilingSprite = PIXI.TilingSprite;
//创建应用
const width = window.innerWidth;
const height = window.innerHeight;
let app = new Application({width:width, height:height, resolution:1, transparent:false, antialias:true});
//添加canvas到document
document.body.appendChild(app.view);
//设置满屏
app.renderer.view.style.position = "absolute";
app.renderer.view.style.display = "block";
app.renderer.autoResize = true;
app.renderer.resize(window.innerWidth, window.innerHeight);
//资源位置
const urls = [
{name:"brick", url:"images/brick.png"},
];
//加载纹理
loader.add(urls).load(setup).on("progress", function(loader, resource){
console.log("loading", loader.progress, resource.name, resource.url);
});
//启动代码
let tiling;
function setup(){
//创建平铺精灵
const texture = resources["brick"].texture;
tiling = new TilingSprite(texture, width, height);
tiling.tileScale.set(0.5, 0.5);
app.stage.addChild(tiling);
app.ticker.add(delay=>loop(delay));
};
let state = play;
function loop(delay){
state(delay);
}
function play(delay){
tiling.tilePosition.x -= 1;
}
例如:视差滚动的伪3D效果
在同一位置层叠多个平铺精灵,远景图片移动要慢于近景图像。
//设置别名
let Application = PIXI.Application;
let loader = PIXI.loader;
let resources = PIXI.loader.resources;
let TextureCache = PIXI.utils.TextureCache;
let Rectangle = PIXI.Rectangle;
let Container = PIXI.Container;
let Sprite = PIXI.Sprite;
let AnimatedSprite = PIXI.AnimatedSprite;
let TilingSprite = PIXI.TilingSprite;
//创建应用
const width = window.innerWidth;
const height = window.innerHeight;
let app = new Application({width:width, height:height, resolution:1, transparent:true, antialias:true});
//添加canvas到document
document.body.appendChild(app.view);
//设置满屏
app.renderer.view.style.position = "absolute";
app.renderer.view.style.display = "block";
app.renderer.autoResize = true;
app.renderer.resize(window.innerWidth, window.innerHeight);
//资源位置
const urls = [
{name:"wood", url:"images/wood.jpg"},
{name:"ground", url:"images/ground.png"},
];
//加载纹理
loader.add(urls).load(setup).on("progress", function(loader, resource){
console.log("loading", loader.progress, resource.name, resource.url);
});
//启动代码
let wood, ground;
function setup(){
const woodTexture = resources["wood"].texture;
wood = new TilingSprite(woodTexture, width, height);
const groundTexture = resources["ground"].texture;
ground = new TilingSprite(groundTexture, width, groundTexture.height);
ground.y = height - groundTexture.height;
app.stage.addChild(wood);
app.stage.addChild(ground);
app.ticker.add(delay=>loop(delay));
};
let state = play;
function loop(delay){
state(delay);
}
function play(delay){
wood.tilePosition.x -= 1;
ground.tilePosition.x += 2;
}
精灵状态SpriteUtilities
当具有复杂的游戏角色或交互式对象时,可能希望角色根据游戏环境中发生的动作,以不同的方式运行。每个单独的行为称为状态,如果在精灵上定义状态,只要游戏中出现与该状态相应的事件时,就可以触发这些状态。比如,通过键盘控制角色移动。
使用精灵状态,首先需要状态播放器,状态播放器用于控制精灵的状态。pixi精灵没有自己的状态播放器,可使用SpriteUtilities库中的sprite方法,sprite方法将创建一个内置状态播放器的精灵。
游戏状态
游戏状态作为一种代码风格,帮助模块组织代码,结构化游戏循环代码,使场景切换和关卡类操作变得更加简单。
//设置别名
let Application = PIXI.Application;
let loader = PIXI.loader;
let resources = PIXI.loader.resources;
let Sprite = PIXI.Sprite;
let TextureCache = PIXI.utils.TextureCache;
let Rectangle = PIXI.Rectangle;
//创建应用
const width = window.innerWidth;
const height = window.innerHeight;
let app = new Application({width:width, height:height, resolution:1, transparent:false, antialias:true});
//添加canvas到document
document.body.appendChild(app.view);
//设置满屏
app.renderer.view.style.position = "absolute";
app.renderer.view.style.display = "block";
app.renderer.autoResize = true;
app.renderer.resize(window.innerWidth, window.innerHeight);
//资源位置
const urls = [
{name:"man", url:"texture/man.json"},
];
let state;//游戏状态
//加载纹理贴图集
loader.add(urls).load(setup).on("progress", function(loader, resource){
console.log("loading", loader.progress, resource.name, resource.url);
});
let sprite;
//启动代码
function setup(){
//获取纹理创建精灵
let texture = resources["man"].textures["hero1.png"];
sprite = new Sprite(texture);
//水平垂直居中定位
const x = (width - sprite.width)/2;
const y = (height - sprite.height)/2;
sprite.position.set(x, y);
sprite.scale.set(0.5, 0.5);
sprite.vx = 1;
sprite.vy = 1;
//精灵添加到舞台
app.stage.addChild(sprite);
//精灵移动
app.ticker.add(delay=>loop(delay));
};
//设置游戏状态
state = play;
//游戏环迅
function loop(delay){
state(delay);
};
//运行状态
function play(delay){
console.log(sprite.y, sprite.vy);
if(sprite.x < height/2){
sprite.x += sprite.vx + delay;
sprite.y += sprite.vy + delay;
}
};
游戏状态机
游戏状态机是用于设计游戏的基本状态逻辑
例如
const GAME_STATUS = {UNINIT:0, INIT:1, LOADED:2, PLAYING:3, GAMEOVER:4};
消息总线
监听器模式是一个减少组件之间耦合的办法,不同组件之间通过消息总线收发消息,可以有效地避免组件之间的复杂调用。游戏是游戏互动多状态多的情况。比如游戏装状态机从LOADED转变为PLAYING时广播这个消息给相关组件游戏开始了。
键盘控制
自定义keyboard键盘函数用于监听和捕获键盘事件
编写键盘方法
const keycode = {
left:37,
up:38,
right:39,
down:40
};
function keyboard(code){
let key = {};
key.code = code;
//key status
key.isDown = false;
key.isUp = true;
//key action
key.press = undefined;
key.release = undefined;
//key down handler
key.down = evt=>{
if(evt.keyCode === key.code){
if(key.isUp && key.press){
key.press();
}
key.isDown = true;
key.isUp = false;
}
evt.preventDefault();
};
// key up handler
key.up = evt=>{
if(evt.keyCode===key.code){
if(key.isDown && key.release){
key.release();
}
key.isUp = true;
key.isDown = false;
}
evt.preventDefault();
};
//attach event listeners
window.addEventListener("keydown", key.down.bind(key), false);
window.addEventListener("keyup", key.up.bind(key), false);
return key;
}
使用键盘控制角色的移动
//设置别名
let Application = PIXI.Application;
let loader = PIXI.loader;
let resources = PIXI.loader.resources;
let Sprite = PIXI.Sprite;
let TextureCache = PIXI.utils.TextureCache;
let Rectangle = PIXI.Rectangle;
let AnimatedSprite = PIXI.extras.AnimatedSprite;
//创建应用
const width = window.innerWidth;
const height = window.innerHeight;
let app = new Application({width:width, height:height, resolution:1, transparent:false, antialias:true});
//添加canvas到document
document.body.appendChild(app.view);
//设置满屏
app.renderer.view.style.position = "absolute";
app.renderer.view.style.display = "block";
app.renderer.autoResize = true;
app.renderer.resize(window.innerWidth, window.innerHeight);
//资源位置
const urls = [
{name:"effect", url:"texture/effect.json"},
];
//加载纹理贴图集
loader.add(urls).load(setup).on("progress", function(loader, resource){
console.log("loading", loader.progress, resource.name, resource.url);
});
let sprite;
//启动代码
function setup(){
//获取纹理创建精灵
let textures = resources["effect"].textures;
//对象转数组
const arrTextures = Object.values(textures);
//创建精灵动画
sprite = new AnimatedSprite(arrTextures);
sprite.animationSpeed = 1;
//水平垂直居中定位
const x = (width - sprite.width)/2;
const y = (height - sprite.height)/2;
sprite.position.set(x, y);
//移动速度
sprite.vx = 0;
sprite.vy = 0;
//精灵添加到舞台
app.stage.addChild(sprite);
//播放精灵动画
sprite.play();
//设置游戏状态
state = play;
//循环播放
app.ticker.add(delta=>loop(delta));
};
function loop(delta){
state(delta);
}
function play(delta){
sprite.x += sprite.vx;
sprite.y += sprite.vy;
}
//键盘方向键
const keyUp = keyboard(keycode.up);
const keyDown = keyboard(keycode.down);
const keyLeft = keyboard(keycode.left);
const keyRight = keyboard(keycode.right);
//键盘控制
keyUp.press = function(){
sprite.vx = 0;
sprite.vy = -5;
};
keyUp.release = function(){
if(sprite.vx===0 && !keyDown.isDown){
sprite.vy = 0;
}
};
keyDown.press = function(){
sprite.vx = 0;
sprite.vy = 5;
};
keyDown.release = function(){
if(sprite.vx===0 && !keyUp.isDown){
sprite.vy = 0;
}
};
keyLeft.press = function(){
sprite.vx = -5;
sprite.vy = 0;
};
keyLeft.release = function(){
if(sprite.vy===0 && !keyRight.isDown){
sprite.vx = 0;
}
};
keyRight.press = function(){
sprite.vx = 5;
sprite.vy = 0;
};
keyRight.release = function(){
if(sprite.vy === 0 && !keyLeft.isDown){
sprite.vx = 0;
}
};
容器PIXI.Container
容器可以用来管理相似的多个精灵
例如:将不同精灵放入容器后添加到舞台
//设置别名
let Application = PIXI.Application;
let loader = PIXI.loader;
let resources = PIXI.loader.resources;
let Sprite = PIXI.Sprite;
let TextureCache = PIXI.utils.TextureCache;
let Rectangle = PIXI.Rectangle;
let AnimatedSprite = PIXI.AnimatedSprite;
let Container = PIXI.Container;
//创建应用
const width = window.innerWidth;
const height = window.innerHeight;
let app = new Application({width:width, height:height, resolution:1, transparent:false, antialias:true});
//添加canvas到document
document.body.appendChild(app.view);
//设置满屏
app.renderer.view.style.position = "absolute";
app.renderer.view.style.display = "block";
app.renderer.autoResize = true;
app.renderer.resize(window.innerWidth, window.innerHeight);
//资源位置
const urls = [
{name:"hero1", url:"images/hero1.png"},
{name:"hero2", url:"images/hero2.png"},
];
//加载纹理
loader.add(urls).load(setup).on("progress", function(loader, resource){
console.log("loading", loader.progress, resource.name, resource.url);
});
//启动代码
function setup(){
//从纹理缓存中创建精灵
let hero1 = new Sprite(resources["hero1"].texture);
let hero2 = new Sprite(resources["hero2"].texture);
//缩放精灵
hero1.scale.set(0.5, 0.5);
hero2.scale.set(0.5, 0.5);
//创建容器并添加元素
let container = new Container();
container.addChild(hero1);
container.addChild(hero2);
//设置容器在场景中的位置
const x = (width - container.width)/2;
const y = (height - container.height)/2;
container.position.set(x, y);
//将容器添加到舞台
app.stage.addChild(container);
};
当往一个Container容器中添加精灵时,其x和y的位置是相对于分组左上角,这是精灵的局部位置。精灵还具有一个全局位置,全局位置是舞台左上角到精灵锚点(通常是精灵左上角)的距离,可通过toGlobal方法获取精灵图的全局位置。
粒子容器PIXI.ParticleContainer
Pixi有一个额外的、高性能的容器称为ParticleContainer,任何在ParticleContainer中的精灵都会比在普通Container的渲染速度快2到5倍,这是用于提升游戏性能的一个很不错的方式。
为什么ParticleContainer中的精灵图能处理的这么快呢?因为精灵的位置是直接在GPU上计算的。Pixi开发团队正努力让更多的雪碧图在GPU上处理。
创建粒子容器
const ParticleContainer = PIXI.ParticleContainer;
const particleContainer = new ParticleContainer(options);
创建ParticleContainer时可以传递四个参数size、properties、batchSize、autoResize。
- ParticleContainer中的精灵只有Container中少部分属性x、y、width、height、scale、pivot、alpha、visible。
- ParticleContainer包含的精灵不能再继续嵌套。
- ParticleContainer不能使用Pixi中的视觉效果,比如过滤器和混合模式。
- 每个ParticleContainer只能使用一个纹理,如果想让精灵有不同的表现形式,则必须更换雪碧图。
几何图形PIXI.Graphics
使用图片纹理制作精灵使最有效的方式之一,Pixi提供了自己的绘图工具用来创造矩形、线段、多边形以及文本。
Pixi的绘图工具和Canvas Drawing API几乎一致,不同之处在于Pixi的绘图API是通过WebGL在GPU上渲染的。
所有图形的初始化都需要先创建一个Pixi的Graphics类的实例
const Graphics = PIXI.Graphics;
const graphics = new Graphics();
矩形
绘制矩形
graphics.drawRect(x, y, width, height);
例如:绘制100x100,背景黑色,3像素红色边框的矩形。
//设置别名
let Application = PIXI.Application;
let Graphics = PIXI.Graphics;
//创建应用
const width = window.innerWidth;
const height = window.innerHeight;
const resolution = window.devicePixelRatio;
let app = new Application({width:width, height:height, resolution:resolution, transparent:true, antialias:true});
//添加canvas到document
document.body.appendChild(app.view);
//设置满屏
app.renderer.view.style.position = "absolute";
app.renderer.view.style.display = "block";
app.renderer.autoResize = true;
app.renderer.resize(window.innerWidth, window.innerHeight);
//创建矩形
let graphics = new Graphics();
graphics.beginFill(0x000000);
graphics.lineStyle(3, 0xff0000, 1);
graphics.drawRect(0, 0, 100, 100);
graphics.endFill();
graphics.x = (width - graphics.width)/2;
graphics.y = (height - graphics.height)/2;
app.stage.addChild(graphics);
圆角矩形
graphics.drawRoundedRect(x, y, width, height, cornerRadius);
圆形
graphics.drawCircle(x, y, radius);
椭圆
graphics.drawEllipse(x, y, width, height);
显示文本PIXI.Text
Pixi使用Text对象在舞台上显示文本,Text文本对象继承自Sprite类,可以像处理精灵一样在舞台上定位和调整文本。
//设置别名
let Application = PIXI.Application;
let Text = PIXI.Text;
let TextStyle = PIXI.TextStyle;
//创建应用
const width = window.innerWidth;
const height = window.innerHeight;
const resolution = 1;
let app = new Application({width:width, height:height, resolution:resolution, transparent:true, antialias:true});
//添加canvas到document
document.body.appendChild(app.view);
//设置满屏
app.renderer.view.style.position = "absolute";
app.renderer.view.style.display = "block";
app.renderer.autoResize = true;
app.renderer.resize(window.innerWidth, window.innerHeight);
//创建文本并设置样式
const style = new TextStyle({
fontFamily:"Arial",
fontSize:36,
fill:"white",
stroke:"#ff3300",
strokeThickness:4,
dropShadow:true,
dropShadowColor:"#000000",
dropShadowBlur:4,
dropShadowAngle:Math.PI/6,
dropShadowDistance:6
});
let str = "Hello World";
const text = new Text(str, style);
text.text = "HELLO WORLD";
let x = (width - text.width)/2;
let y = (height - text.height)/2;
text.position.set(x, y);
app.stage.addChild(text);
碰撞检测hitTestRectangle
自定义hitTestRectangle
函数用于检测两个矩形是否接触
const boolean = hitTestRectangle(sprite1, sprite2);
若精灵1和精灵2发生重叠则返回true,反之为false。
//矩形碰撞检查
function hitTestRectangle(r1, r2){
let hit = false;//是否发生碰撞
//获取矩形中心点
r1.centerX = r1.x + r1.width/2;
r1.centerY = r1.y + r1.height/2;
r2.centerX = r2.x + r2.width/2;
r2.centerY = r2.y + r2.height/2;
//计算矩形中心点距离
const vx = Math.abs(r1.centerX - r2.centerX);
const vy = Math.abs(r1.centerY - r2.centerY);
//计算矩形合并时尺寸
const combinedHalfWidth = (r1.width + r2.width)/2;
const combinedHalfHeight = (r1.height + r2.height)/2;
//检测矩形是否发生碰撞
if(combinedHalfWidth>vx && combinedHalfHeight>vy){
hit = true;
}
return hit;
}
例如:绘制两个矩形,测试是否发生碰撞。
//设置别名
let Application = PIXI.Application;
let Rectangle = PIXI.Rectangle;
let Graphics = PIXI.Graphics;
//创建应用
const width = window.innerWidth;
const height = window.innerHeight;
const resolution = 1;
let app = new Application({width:width, height:height, resolution:resolution, transparent:true, antialias:true});
//添加canvas到document
document.body.appendChild(app.view);
//设置别名
const renderer = app.renderer;
const stage = app.stage;
//设置满屏
renderer.view.style.position = "absolute";
renderer.view.style.display = "block";
renderer.autoResize = true;
renderer.resize(window.innerWidth, window.innerHeight);
//创建矩形
const r1 = new Graphics();
r1.beginFill(0x00ffff);
r1.drawRect(0, 0, 100, 100);
r1.endFill();
r1.position.set((width-r1.width)/2, (height - r1.height)/2);
stage.addChild(r1);
const r2 = new Graphics();
r2.beginFill(0x0000ff);
r2.drawRect(0, 0, 100, 100);
r2.endFill();
r2.position.set((width-r2.width)/2, (height - r2.height)/2+10);
stage.addChild(r2);
//判断举行是否发生碰撞
const hit = hitTestRectangle(r1, r2);
console.log(hit);