pixijs 是一个canvas 2D框架
pixi.js创建所有widget 都是有一个完整的object Display Object 他包括元素定位, 宽高,渲染方式等等方法..除了ParticleContaine
这些提升性能优化的API,这个一个高性能容器,优化就是省略一些多余的object Display Object .
注:我使用的版本是V6 , 6.4.2。大版本各个API会变动,引入正确版本来开发。网上使用的是V4,略有差别
1、 引入
// 引入pixi.js
yarn add pixi.js
2、一些主要的API:
import {
Application, //渲染器,舞台(canvas)
Contanier, // 容器
Sprite, //精灵 是一个舞台中物体基础(display Object)
Loader, // 加载器 一些资源的加载器(比如说:图片、文字之类的)
Text, // 文字 (一个特殊的精灵,支持display Object
TextStyle, //文字样式
Texture //纹理, 给精灵加载纹理
} from 'pixi.js';
3、创建一个舞台(Canvas)
//创建一个宽度400,高度400 ,背景色为#000的 canvas
const renderer = new Application({width:400,height:400,backgroudColor:0x000000});
//将Canvas 放入 Html 中
const body = document.body;
body.appendChild(renderer);
4、创建一个容器(container)
容器的作用是给一些东西分组,方便管理;比如一个游戏分为:
1、开始界面
2、结束界面
3、游戏画面
三个可以分为三组,便于管理。
# 创建一个舞台(容器)
// 一个游戏,或者一个2d的渲染器。如果里面的内容过于复杂或者多。需要一个或者多个容器包裹管理。比如:
// 比如,坦克大战,玩家控制的坦克,随机生成的敌方坦克,这里可以创建两个容器用来单独管理
const contanier = new Contanier();
// 将容器放入舞台(canvas) 中
renderer.stage.addChild(contanier);
额外的容器(ParticleContainer)
这个 使用方法跟container
是一样的,是用来做性能优化的。有时候放入在你放入容器的sprite
不需要整个display object 树 ,只需要部分关键的display object ,比如x,y,width,height等关键属性。
(粒子效果就是创建很多精灵,放入性能容器里面)
# 创建一个高性能舞台(容器)
const prticleCntanier = new ParticleContainer();
// 将容器放入舞台(canvas) 中
renderer.stage.addChild(prticleCntanier);
额外的api
删除容器内精灵
//删除容器内所有的精灵
container.removeChildren();
//删除指定下标的精灵
contanier.removeChildAt(index);
//删除一个或者多个精灵
contanier.removeChild(...children)
获取容器中的精灵(child)
可以使用获取容器中的display object
属性,然后拿到children
,比如
container.children(index)
也可以使用更加优雅的方法, pixijs已经帮我封装好了一些方法
//根据下标拿到容器中的指定精灵,等同于上面的方法
container.getChildAt(index);
//deep:boolean,name:string 别名 。根据别名找到精灵,是否开启递归搜索,默认false; 比较耗费性能
container.getChildByName(name,deep);
//根据精灵返回下标
container.getChildIndex(child);
将容器添加精灵到指定的下标
//暂时我没用过,贴上官网的注释
//Adds a child to the container at a specified index. If the index is out of bounds an error will be thrown
container.addChildAt(child,index)
5、创建一个精灵
// 创建精灵,并给精灵设置图片
let sprite = PIXI.Sprite.from('assets/image.png');
//将精灵放入container(容器)或者application(舞台)当中
//容器
container.addchild(sprite);
//高性能容器
//prticleCntanier.addchild(sprite);
//舞台
renderer.stage.addchild(sprite)
使用texture来给精灵增加纹理
//创建一段纹理
const texture = PIXI.Texture.from('assets/image.png');
//将纹理放入精灵中
const sprite = PIXI.Sprite(texture);
通过 loader
来高性能加载资源
当资源很多很且复杂的时候,可以通过loader 来加载图片、视频等资源。而当sprite使用loader的资源时候,就不能使用from。
注:通过loader
可以写一个资源加载器的游戏开始页面。在刚进入游戏的时候可以写一个In progress 加载页面
//使用 loader 加载器
const loader = Loader.shared;
loader.add('name', assetsUrl).load(loaded)
const loaded = (loader, resources) => {
// resources 是资源的加载对象。key是name,value 是 资源
// They have a couple default properties:
// - `url`: The URL that the resource was loaded from
// - `error`: The error that happened when trying to load (if any)
// - `data`: The raw data that was loaded
// also may contain other properties based on the middleware that runs.
sprites.bunny = new PIXI.TilingSprite(resources.bunny.texture);
sprites.spaceship = new PIXI.TilingSprite(resources.spaceship.texture);
sprites.scoreFont = new PIXI.TilingSprite(resources.scoreFont.texture);
}
还能通过雪碧图,和一个确定雪碧图position
的json文件来更加优化性能
//异步加载图片,并使用他
PIXI.Loader.shared.add("assets/spritesheet.json").load(setup);
function setup() {
//雪碧图,确定图片位置
let sheet = Loader.shared.resources["assets/spritesheet.json"].spritesheet;
let sprite = new PIXI.Sprite(sheet.textures["image.png"]);
};
loader 加载资源的一些API
// loader加载远程资源的过程或者生命周期
loader.onProgress.add(() => {}); // called once per loaded/errored file
loader.onError.add(() => {}); // called once per errored file
loader.onLoad.add(() => {}); // called once per loaded file
loader.onComplete.add(() => {}); // called once when the queued resources all load.
特殊的精灵,平铺精灵(TilingSprite)
在一定范围内,你需要重复一个纹理,使他们创建无线滚动的背景效果。
let tilingSprite = new PIXI.extras.TilingSprite(texture, width, height);
name | defluat | desc |
---|---|---|
texture | 纹理 | |
width | 100 | 平铺精灵的宽度 |
height | 100 | 平铺精灵的高度 |
6、创建一段文字并给他样式
//创建文字
const mark = new Text('我创建一段文字',new TextStyle({
fontFamily: "Arial",
fontSize: 36,
fill: "white",
stroke: "#ff3300",
strokeThickness: 4,
dropShadow: true,
dropShadowColor: "#cccccc",
dropShadowBlur: 4,
dropShadowAngle: Math.PI / 6,
dropShadowDistance: 6,
});
//将文字放入容器(container)中
container.addChild(mark);
当一些精灵(可以是文字,纹理,绘制,哪怕是容器)存入一个容器中时,他会有一个相对位置和绝对位置,相对位置就是widget 在容器中的位置,绝对位置是相对的canvas 左上角的位置。
//使用Graphics创建一段墙壁,放入容器中,因为他drawRect 绘制要确定X,Y 这个X,Y是相对容器位置的
//需要使用position.set设置绝对位置才能到canvas的实际位置
const leftWall = new Graphics();
leftWall.beginFill(0xde3249);
leftWall.drawRect(0, 0, 10, this.height as number);
leftWall.position.set(0, 0);
leftWall.endFill();
container.addChild(leftWall);
附录,贴上两端祖传代码(百度也能查到):
一段,键盘事件的函数代码:
interface keyProps {
value: string
isDown:boolean
isUp:boolean
press?: any
release?: any
downHandler:(Event:KeyboardEvent)=>void
upHandler:(Event:KeyboardEvent)=>void
unsubscribe:()=>void
}
const keyboard = (value:string) => {
let key:keyProps = {
value: value,
isDown: false,
isUp: true,
press: undefined,
release: undefined,
downHandler: event => {
if (event.key === key.value) {
if (key.isUp && key.press)
key.press();
key.isDown = true;
key.isUp = false;
event.preventDefault();
}
},
upHandler: event => {
if (event.key === key.value) {
if (key.isDown && key.release) key.release();
key.isDown = false;
key.isUp = true;
event.preventDefault();
}
},
unsubscribe: () => {
window.removeEventListener("keydown", downListener);
window.removeEventListener("keyup", upListener);
}
};
const downListener = key.downHandler.bind(key);
const upListener = key.upHandler.bind(key);
window.addEventListener(
"keydown", downListener, false
);
window.addEventListener(
"keyup", upListener, false
);
return key;
}
export {
keyboard,
keyProps
};
一段碰撞检测:
hitTestRectangle(r1: any, r2: any) {
let hit, combinedHalfWidths, combinedHalfHeights, vx, vy;
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;
r1.halfWidth = r1.width / 2;
r1.halfHeight = r1.height / 2;
r2.halfWidth = r2.width / 2;
r2.halfHeight = r2.height / 2;
vx = r1.centerX - r2.centerX;
vy = r1.centerY - r2.centerY;
combinedHalfWidths = r1.halfWidth + r2.halfWidth;
combinedHalfHeights = r1.halfHeight + r2.halfHeight;
if (Math.abs(vx) < combinedHalfWidths) {
if (Math.abs(vy) < combinedHalfHeights) {
hit = true;
} else {
hit = false;
}
} else {
hit = false;
}
return hit;
}