Pixi.js

PixiJS是一个2D渲染引擎,能自动侦测并使用WebGL或Canvas。PixiJS使用JavaScript或HTML5基础来显示媒体,创建动画或管理交互式图像,从而制作游戏或应用。PixiJS适合制作游戏但并非是游戏引擎,其核心本质是尽可能快速有效地在屏幕上移动物体。

pixi.js作为JavaScript的2D渲染器,目标是提供快速轻量且兼容所有设备的2D库。提供无缝Canvas回退、支持主流浏览器,包括桌面和移动设备。

安装测试

$ npm i pixi.js

引入

import * as PIXI from "pixi.js";
  • 开发工具 Vistual Code
  • Web服务器插件 Live Server
  • Chrome调试工具 PixiJS devtools

测试

判断当前浏览器渲染引擎类型

$ vim index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://cdn.bootcss.com/pixi.js/5.2.1/pixi.min.js"></script>
</head>
<body>
    <script>
        //判断浏览器渲染引擎
        const type = PIXI.utils.isWebGLSupported() ? "WebGL" : "canvas";
        //console.log(type);//WebGL
        PIXI.utils.sayHello(type);//PixiJS 5.2.1 - WebGL - http://www.pixijs.com/
    </script>
</body>
</html>

判断代码

//判断浏览器渲染引擎
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模块主要包括

  • Application 应用
  • Container 容器
  • Stage 舞台
  • Renderer 渲染器
  • Ticker 计时器
  • Loader 加载器
let app = new PIXI.Application({width:400, height:300, view:document.querySelector("canvas")});
app.stage = new PIXI.Container();
app.ticker = new PIXI.Ticker();
app.renderer = new PIXI.Renderer();
app.loader = new PIXI.Renderer();

Pixi应用PIXI.Application

PIXI拥有一个Pixi应用对象PIXI.Application,可自动创建一个canvas标签,用于计算图片在其中的显示。PIXI.Application会自动创建渲染器、计时器和根容器。

const app = new PIXI.Application(options);

例如:创建可显示图片的矩形显示区域

//创建一个Pixi应用
const app = new PIXI.Application({width:400, height:300});
//pixi自动创建canvas标签并添加到document文档中
document.body.appendChild(app.view);

自动创建canvas标签

<canvas width="400" height="300" style="touch-action: none; cursor: inherit;"></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);
选项类型默认值描述
widthnumber800渲染器视宽度
heightnumber600渲染视图高度
viewHTMLCanvasElement-使用canvas作为视图
transparentbooleanfalse视图是否透明
resolutionnumber1分辨率,渲染器设备像素比,R屏默认为2.
antialiasbooleanfalse是否平滑抗锯齿,取消字体平滑,抗混叠。
forceCanvasbooleanfalse是否强制使用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.loaderPIXI.Loader加载器实例用于资源加载,用于加载静态资源。
app.rendererPIXI.Renderer|PIXI.CanvasRendererWebGL渲染器,可使用CanvasRenderer。
app.resizeToWindow|HTMLElement元素或窗口尺寸更改
app.screenPIXI.Rentangle渲染器的屏幕矩形对象
app.stagePIXI.Container根显示容器,舞台,pixi创建的根容器。游戏中的精灵或容器都要添加到舞台上才能显示。
app.tickerPIXI.Ticker计时器,可理解为每次渲染一帧前执行。
app.viewHTMLCanvasElement渲染器的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);

Pixi容器 PIXI.Container()

容器是用来转载多个显示对象的,创建的Pixi应用的stage属性本质上就是一个容器对象,container对象被当作根容器使用,它将包裹所有想用Pixi显示的元素。

Pixi舞台pixi.stage

创建pixi应用对象并添加到document中后就有了一个画布,要在画布上显示东西就必须加入到一个称为舞台的pixi对象pixi.stage

pixi.stage舞台是一个pixi容器对象,可理解为将放入的东西分组并存储的空箱子。stage舞台对象是在场景总所有可见对象的根容器。所有放入到舞台的东西都会被渲染到canvas中。

stage对象是Container容器,是所有Sprite精灵的根容器。

stage舞台对象是pixi容器对象,所有的容器对象都具有相同的属性和方法。尽管stage舞台拥有width和height属性,但不能查看画图窗口的大小。stage舞台的width和height属性仅仅告诉放入东西占用的大小。

舞台内科放入什么东西呢?是称作精灵的特殊图像对象。精灵是能够使用代码控制的图像,可以控制它的位置、大小等属性来产生交互和动画。

pixi拥有一个sprite精灵类用来创建游戏精灵,创建的方式有三种。

  • 使用单图像文件创建
  • 使用雪碧图来创建
  • 从纹理图集中创建

加载

将图片加载到纹理缓存,因为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);
});

坐标

  • 绝对坐标global是以舞台stage为基准的坐标系,精灵相对于舞台的位置和方向。
  • 相对坐标local是以舞台stage中某个容器container为基准的坐标系,一般是精灵的父容器。

Pixi提供了两个函数用于坐标变换

  • toGlobal 相对坐标转换为绝对坐标
  • toLocal 绝对坐标转换为相对坐标

无论是容器还是精灵的坐标以及方向都是相对于父容器的坐标系的,坐标原点默认是容器的左上角,可改变容器的坐标原点anchor

精灵

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)
参数类型描述
namestring加载源文件别名
urlstring源文件地址
optionsobject literal加载设置
options.crossOriginboolean源文件请求跨域
options.loadTypestring原文加加载类型,默认为Resource.LOAD_TYPE.XHR
options.xhrTypestringXHR处理方式,默认为Resource.XHR_RESPONSE_TYPE.DEFAULT
callbackFunctionfunction加载完后执行的回调函数

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中心点则精灵的原点,使用像素来填充。

雪碧图

雪碧图是使用单个文件包含游戏所需的所有图片文件,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);
参数类型默认值描述
texturesarray-纹理贴图数组
autoUpdatedbooleantrue是否使用PIXI.ticker.shared自动更新动画时间

返回值:返回用于控制动画精灵的对象

返回值属性类型描述
animationSpeednumber播放速度默认为1,值越大速度越快。
currentFramenumber当前帧编号
totalFramesnumber帧总数
playingboolean当前动画是否在播放
loopboolean是否循环播放
onLoopfunctionloop为true时调用
onCompletefunction当loop为false时动画完成后调用
onFrameChangefunction但动画更改要呈现的纹理时调用
texturesarray纹理数组
返回值方法参数描述
play-播放的动画精灵
stop-停止播放动画精灵
gotoAndPlayframeNumber, numberType, frameIndex转到特定帧并开始播放动画
gotoAndStopframeNumber, numberType, frameIndex转到特定帧并停止播放动画

平铺精灵PIXI.TilingSprite

平铺精灵是一种特殊的精灵,可在一定的范围内重复一个纹理。可使用平铺精灵创建无线滚动的背景效果。

创建平铺精灵

const TilingSprite = PIXI.TilingSprite;
const tiling = new TilingSprite(texture, width, height);
参数类型默认值描述
texturePIXI.Texture-平铺精灵纹理
widthnumber100平铺精灵的宽度
heightnumber100平铺精灵的高度

平铺精灵具有和普通精灵相同的属性,与普通精灵工作方式相同。

例如:使用小图创建平铺精灵并移动

4933701-9f44f0d8df70455a.png
brick.png
//设置别名
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效果

4933701-68986cb009075306.jpg
wood.jpg
4933701-2b4eff8bd889a174.png
ground.png

在同一位置层叠多个平铺精灵,远景图片移动要慢于近景图像。

//设置别名
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);
  • 4
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值