来自GitHub教程 GitHub - Zainking/LearningPixi: ⚡️Pixi教程中文版
PIXIJS教程【从入门到入土-上】地址【https://blog.csdn.net/qq_61672548/article/details/127253760】
5.2 加载纹理贴图集
可以使用Pixi的loader
来加载纹理贴图集。如果是用Texture Packer生成的JSON,loader
会自动读取数据,并对每一个帧创建纹理。下面就是怎么用loader
来加载treasureHunter.json
。当它成功加载,setup
方法将会执行
loader
.add("images/treasureHunter.json")
.load(setup);
现在每一个图像的帧都被加载进Pixi的纹理缓存之中了。你可以使用Texture Packer中定义的他们的名字来取用每一个纹理。
如果你是使用的 loader
来加载纹理贴图集, 使用loader的 resources
:
let sprite = new Sprite(
resources["images/treasureHunter.json"].textures["frameId.png"]
);
要创建一个精灵需要输入太多东西了! 所以我建议你给纹理贴图集的textures
对象创建一个叫做id
的别名,象是这样:
let id = PIXI.loader.resources["images/treasureHunter.json"].textures;
现在你就可以像这样实例化一个精灵了:
let sprite = new Sprite(id["frameId.png"]);
真不错啊~!
六、移动精灵
6.1 移动
现在你知道了如何展示精灵,但是让它们移动呢?很简单:使用Pixi的ticker
。这被称为 游戏循环 。任何在游戏循环里的代码都会1秒更新60次。你可以用下面的代码让 cat
精灵以每帧1像素的速率移动。
let icon
function setup() {
let id = resources["./images/treasureHunter.json"].textures
icon = new Sprite(id["icon.png"])
app.stage.addChild(icon)
//定义游戏循环
app.ticker.add(animation)
}
function animation() {
icon.x++
}
你也没必要非得用Pixi的ticker来创建游戏循环。如果你喜欢,也可以用requestAnimationFrame
像这样创建:
function gameLoop() {
//Call this `gameLoop` function on the next screen refresh
//(which happens 60 times per second)
requestAnimationFrame(gameLoop);
//Move the cat
cat.x += 1;
}
//Start the loop
gameLoop();
(注意 cat
变量需要在setup
和 gameLoop
函数之外定义,然后你可以在全局中任何地方都能获取到它们)
你可以让精灵的位置,角度或者大小动起来 - 什么都可以!你会在下面看到更多精灵动画的例子。
6.2 速度属性
为了给你更多的灵活性,这里有两个 速度属性 :vx
和 vy
去控制精灵的运动速度。 vx
被用来设置精灵在x轴(水平)的速度和方向。vy
被用来设置精灵在y轴(垂直)的速度和方向。 他们可以直接更新速度变量并且给精灵设定这些速度值。这是一个用来让你更方便的更新交互式动画的额外的模块。
第一步是给你的精灵创建vx
和vy
属性,然后给他们初始值。
cat.vx = 0;
cat.vy = 0;
给vx
和vy
设置为0表示精灵静止。
接下来,在游戏循环中,更新vx
和vy
为你想让精灵移动的速度值。然后把这些值赋给精灵的x
和y
属性。下面的代码讲明了你如何利用该技术让cat能够每帧向右下方移动一个像素:
function setup() {
//Create the `cat` sprite
cat = new Sprite(resources["images/cat.png"].texture);
cat.y = 96;
cat.vx = 0;
cat.vy = 0;
app.stage.addChild(cat);
//Start the game loop
app.ticker.add(gameLoop);
}
function gameLoop(){
//Update the cat's velocity
cat.vx = 1;
cat.vy = 1;
//Apply the velocity values to the cat's
//position to make it move
cat.x += cat.vx;
cat.y += cat.vy;
}
如果你想让猫往不同的方向移动怎么办?可以把它的 vx
赋值为 -1让猫向左移动。可以把它的 vy
赋值为 -1让猫向上移动。为了让猫移动的更快一点,把值设的更大一点,像3, 5, -2, 或者 -4。
你会在前面看到如何通过利用vx
和vy
的速度值来模块化精灵的速度,它对游戏的键盘和鼠标控制系统很有帮助,而且更容易实现物理模拟。
七、给精灵分组
7.1 Container分组
分组让你能够让你创建游戏场景,并且像一个单一单元那样管理相似的精灵图。Pixi有一个对象叫 Container
,它可以帮你做这些工作。让我们弄清楚它是怎么工作的。
想象一下你想展示三个精灵:一只猫,一只刺猬和一只老虎。创建它们,然后设置它们的位置 - 但是不要把它们添加到舞台上。
//The cat
let cat = new Sprite(id["cat.png"]);
cat.position.set(16, 16);
//The hedgehog
let hedgehog = new Sprite(id["hedgehog.png"]);
hedgehog.position.set(32, 32);
//The tiger
let tiger = new Sprite(id["tiger.png"]);
tiger.position.set(64, 64);
让后创建一个animals
容器像这样去把他们聚合在一起:
let animals = new PIXI.Container();
然后用 addChild
去把精灵图 添加到分组中 。
animals.addChild(cat);
animals.addChild(hedgehog);
animals.addChild(tiger);
最后把分组添加到舞台上。
app.stage.addChild(animals);
(你知道的,stage
对象也是一个Container
。它是所有Pixi精灵的根容器。)
这就是上面代码的效果:
你是看不到这个包含精灵图的animals
分组的。它仅仅是个容器而已。
不过你现在可以像对待一个单一单元一样对待animals
分组。你可以把Container
当作是一个特殊类型的不包含任何纹理的精灵。
如果你需要获取animals
包含的所有子精灵,你可以用它的children
数组获取。
console.log(animals.children)
//Displays: Array [Object, Object, Object]
这告诉你animals
有三个子精灵。
因为animals
分组跟其他精灵一样,你可以改变它的x
和y
的值,alpha
, scale
和其他精灵的属性。所有你改变了的父容器的属性值,都会改变它的子精灵的相应属性。所以如果你设置分组的x
和y
的位置,所有的子精灵都会相对于分组的左上角重新定位。如果你设置了 animals
的x
和y
的位置为64会发生什么呢?
animals.position.set(64, 64);
整个分组的精灵都会向右和向下移动64像素。
animals
分组也有它自己的尺寸,它是以包含的精灵所占的区域计算出来的。你可以像这样来获取width
和height
的值:
console.log(animals.width);
//Displays: 112
console.log(animals.height);
//Displays: 112
如果你改变了分组的宽和高会发生什么呢?
animals.width = 200;
animals.height = 200;
所有的孩子精灵都会缩放到刚才你设定的那个值。
如果你喜欢,你可以在一个 Container
里嵌套许多其他Container
,如果你需要,完全可以创建一个更深的层次。然而,一个 DisplayObject
(像 Sprite
或者其他 Container
)只能一次属于一个父级。如果你用 addChild
让一个精灵成为其他精灵的孩子。Pixi会自动移除它当前的父级,这是一个不用你操心的有用的管理方式。
7.2 局部位置和全局位置
当你往一个Container
添加一个精灵时,它的x
和y
的位置是 相对于分组的左上角 的。这是精灵的局部位置,举个例子,你认为这个猫在这张图的哪个位置?
让我们看看:
console.log(cat.x);
//Displays: 16
16?是的!这因为猫的只往分组的左上角偏移了16个像素。16是猫的局部位置。
精灵图还有 全局位置 。全局位置是舞台左上角到精灵锚点(通常是精灵的左上角)的距离。你可以通过toGlobal
方法的帮助找到精灵图的全局位置:
parentSprite.toGlobal(childSprite.position)
这意味着你能在animals
分组里找到猫的全局位置:
console.log(animals.toGlobal(cat.position));
//Displays: Object {x: 80, y: 80...};
上面给你返回了x
和y
的值为80。这正是猫相对于舞台左上角的相对位置,也就是全局位置。
如果你想知道一个精灵的全局位置,但是不知道精灵的父容器怎么办?每个精灵图有一个属性叫parent
能告诉你精灵的父级是什么。在上面的例子中,猫的父级是 animals
。这意味着你可以像如下代码一样得到猫的全局位置:
cat.parent.toGlobal(cat.position);
即使你不知道猫的当前父级是谁,上面的代码依然能够正确工作。
这还有一种方式能够计算出全局位置!而且,它实际上最好的方式,所以听好啦!如果你想知道精灵到canvas左上角的距离,但是不知道或者不关心精灵的父亲是谁,用getGlobalPosition
方法。这里展示如何用它来找到老虎的全局位置:
tiger.getGlobalPosition().x
tiger.getGlobalPosition().y
它会给你返回x
和y
的值为128。 特别的是,getGlobalPosition
是高精度的:当精灵的局部位置改变的同时,它会返回给你精确的全局位置。我曾要求Pixi开发团队添加这个特殊的特性,以便于开发精确的碰撞检测游戏。(谢谢Matt和团队真的把他加上去了!)
如果你想转换全局位置为局部位置怎么办?你可以用toLocal
方法。它的工作方式类似,但是通常是这种通用的格式:
sprite.toLocal(sprite.position, anyOtherSprite)
用 toLocal
找到一个精灵和其他任何一个精灵之间的距离。这段代码告诉你如何获取老虎的相对于猫头鹰的局部位置。
tiger.toLocal(tiger.position, hedgehog).x
tiger.toLocal(tiger.position, hedgehog).y
上面的代码会返回给你一个32的x
值和一个32的y
值。你可以在例子中看到老虎的左上角和猫头鹰的左上角距离32像素。
7.3 ParticleContainer 分组
Pixi有一个额外的,高性能的方式去分组精灵的方法称作:ParticleContainer
(PIXI.ParticleContainer
)。任何在ParticleContainer
里的精灵都会比在一个普通的Container
的渲染速度快2到5倍。这是用于提升游戏性能的一个很棒的方法。
可以像这样创建 ParticleContainer :
let superFastSprites = new PIXI.particles.ParticleContainer();
然后用 addChild
去往里添加精灵,就像往普通的 Container
添加一样。
如果你决定用ParticleContainer
你必须做出一些妥协。在 ParticleContainer
里的精灵图只有一小部分基本属性:x
, y
, width
, height
, scale
, pivot
, alpha
, visible
- 就这么多。而且,它包含的精灵不能再继续嵌套自己的孩子精灵。 ParticleContainer
也不能用Pixi的先进的视觉效果像过滤器和混合模式。每个 ParticleContainer
只能用一个纹理(所以如果你想让精灵有不同的表现方式你将不得不更换雪碧图)。但是为了得到巨大的性能提升,这些妥协通常是值得的。你可以在同一个项目中同时用 Container
和 ParticleContainer
,然后微调一下你自己的优化
八、文本
使用一个 Text
对象 (PIXI.Text
)在舞台上展示文本。简单来说,你可以这样使用它:
let message = new Text("Hello Pixi!");
app.stage.addChild(message);
这将会在画布上展示文本“Hello, Pixi”。Pixi的文本对象继承自Sprite
类,所以它包含了所有相同的属性,像x
, y
, width
, height
, alpha
, 和 rotation
。你可以像处理其他精灵一样在舞台上定位或调整文本。例如,你可以像下面这样使用position.set
来设置message
的x
和y
位置:
message.position.set(54, 96);
这样你会得到基础的未加修饰的文本。但是如果你想要更绚丽的文字,使用Pixi的TextStyle
函数来自定义文字效果。下面展示如何操作:
let 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,
});
这将创建一个新的包含所有你想用的样式的style
对象。所有样式属性,see here。
添加style
对象作为Text
函数的第二个参数来应用样式到文本上,就像这样:
let message = new Text("Hello Pixi!", style);
如果你想要在你创建文本对象之后改变它的内容,使用text
属性。
message.text = "Text changed!";
如果你想要重新定义样式属性,使用style
属性。
message.style = {fill: "black", font: "16px PetMe64"};
九、碰撞检测
碰撞检测函数
hitTestRectangle 函数都有些什么呢?它做了什么,还有它是如何工作的?
function hitTestRectangle(r1, r2) {
//Define the variables we'll need to calculate
let hit, combinedHalfWidths, combinedHalfHeights, vx, vy;
//hit will determine whether there's a collision
hit = false;
//Find the center points of each sprite
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;
//Find the half-widths and half-heights of each sprite
r1.halfWidth = r1.width / 2;
r1.halfHeight = r1.height / 2;
r2.halfWidth = r2.width / 2;
r2.halfHeight = r2.height / 2;
//Calculate the distance vector between the sprites
vx = r1.centerX - r2.centerX;
vy = r1.centerY - r2.centerY;
//Figure out the combined half-widths and half-heights
combinedHalfWidths = r1.halfWidth + r2.halfWidth;
combinedHalfHeights = r1.halfHeight + r2.halfHeight;
//Check for a collision on the x axis
if (Math.abs(vx) < combinedHalfWidths) {
//A collision might be occuring. Check for a collision on the y axis
if (Math.abs(vy) < combinedHalfHeights) {
//There's definitely a collision happening
hit = true;
} else {
//There's no collision on the y axis
hit = false;
}
} else {
//There's no collision on the x axis
hit = false;
}
//`hit` will be either `true` or `false`
return hit;
};
现在你知道了如何制造种类繁多的图形对象,但是你能用他们做什么?一个有趣的事情是利用它制作一个简单的 碰撞检测系统 。你可以用一个叫做:hitTestRectangle
的自定义的函数来检测两个矩形精灵是否接触。
hitTestRectangle(spriteOne, spriteTwo)
如果它们重叠, hitTestRectangle
会返回 true
。你可以用 hitTestRectangle
结合 if 条件语句去检测两个精灵是否碰撞:
if (hitTestRectangle(cat, box)) {
//There's a collision
} else {
//There's no collision
}
正如你所见, hitTestRectangle
是走入游戏设计这片宇宙的大门。
运行在 examples
文件夹的 collisionDetection.html
文件,看看怎么用 hitTestRectangle
工作。用方向按键去移动猫,如果猫碰到了盒子,盒子会变成红色,然后 “Hit!” 文字对象会显示出来。
你已经看到了创建这些所有元素的代码,让猫移动的键盘控制。唯一的新的东西就是 hitTestRectangle
函数被用在 play
函数里检测碰撞。
function play(delta) {
//use the cat's velocity to make it move
cat.x += cat.vx;
cat.y += cat.vy;
//check for a collision between the cat and the box
if (hitTestRectangle(cat, box)) {
//if there's a collision, change the message text
//and tint the box red
message.text = "hit!";
box.tint = 0xff3300;
} else {
//if there's no collision, reset the message
//text and the box's color
message.text = "No collision...";
box.tint = 0xccff99;
}
}
play
函数被每秒调用了60次,每一次这个 if 条件语句都会在猫和盒子之间进行碰撞检测。如果 hitTestRectangle
为 true
,那么文字 message
对象会用 setText
方法去显示 “Hit”:
message.text = "Hit!";
这个盒子的颜色改变的效果是把盒子的 tint
属性改成一个16进制的红色的值实现的。
box.tint = 0xff3300;
如果没有碰撞,消息和盒子会保持它们的原始状态。
message.text = "No collision...";
box.tint = 0xccff99;
代码很简单,但是你已经创造了一个看起来完全活着的互动的世界!它简直跟魔术一样!令人惊讶的是,你大概已经拥有了你需要用Pixi制作游戏的全部技能!
>>> 2022.10.12 19:24 教程已经全部更新完,很大部分都是来自github 原文地址【https://github.com/Zainking/learningPixi】本来我只想做一个文档方便我自己在开发的时候查阅 然后觉得PIXIJS在国内的教程很少 便把此文章放到了CSDN上,能有机会帮助需要的人,因为很多朋友还是很少去国外找教程。PIXIJS还有很多其他很好的教程 【https://pixijs.com/tutorials/】去这里可以查看。
教程原文地址 GitHub - Zainking/LearningPixi: ⚡️Pixi教程中文版
附加
<<< 2022-10-17 21:52 添加一些PixiJS 开发常用API的使用方法,这部分全靠自己看官方文档和实践摸索,难度还是比较大的。官方API文档都是英文的,虽然浏览器带翻译,但是效果并不好,我感觉还不如不翻译,因为如果不是写在代码块的代码也会被翻译,有些变量名或者方法名写在代码块外,然后给翻译成中文,这会让你相当的蒙蔽~ 一些翻译意思也不到位。所以只有中英结合着看~
精灵表
官方教程【小精灵 API 文档 (pixijs.download)】
在创建动画精灵中需要用到
实用程序类,用于维护对单个 Sprites 表中的纹理集合的引用。
要从代码访问子画面表,您可以将其JSON数据文件传递给Pixi的加载器:
PIXI.Loader.shared.add("images/spritesheet.json").load(setup);
function setup() {
let sheet = PIXI.Loader.shared.resources["images/spritesheet.json"].spritesheet;
...
}
动画精灵
官方教程【PixiJS API Documentation】
1. 创建精灵动画
1.1 创建精灵动画一般方法
注意和创建普通精灵不一样,注意差别 【Loader.shared】
PIXI.Loader.shared.add("./images/down.json").load(setup);
function setup() {
let sheet = PIXI.Loader.shared.resources["./images/spritesheet.json"].spritesheet;
let animatedSprite = new PIXI.AnimatedSprite(sheet.animations["down"]);
...
}
1.2 创建精灵帧的简单方法
- 从纹理帧创建AnimatedSprite的简单方法
PIXI.AnimatedSprite.fromFrames (frames)
Name | Type | Description |
---|---|---|
frames |
string[] | AnimatedSprite将使用的帧数组id作为它的纹理帧。 |
- 从图片创建AnimatedSprite的简单方法
PIXI.AnimatedSprite.fromImages (images)
Name | Type | Description |
---|---|---|
images |
string[] | AnimatedSprite将使用的图像url数组作为它的纹理帧 |
2.常用方法
2.1 play()
动画精灵开始运动(图片开始循环)
animatedSprte.play()
2.2 stop ()
动画精灵停止运动,停止在第一帧
animatedSprte.stop ()
2.3 update()
我不太清楚这个update是怎么用的,下面是官方的介绍,我没看懂。
Updates the object transform for rendering.
Name | Type | Description |
---|---|---|
deltaTime |
number | Time since last tick. |
控制动画精灵帧切换速度可以用 animatedSprte.animationSpeed = 0.1 数字越小越慢
2.4 gotoAndPlay()
gotoAndPlay(frameNumber) void
转到一个特定的帧并开始播放AnimatedSprite。
Name | Type | Description |
---|---|---|
frameNumber |
number | 起始帧索引. |
2.5 gotoAndStop ()
gotoAndStop (frameNumber) void
停止AnimatedSprite并进入特定帧。
Name | Type | Description |
---|---|---|
frameNumber |
number | 帧索引停止在哪帧 |
2.6 destroy ()
停止AnimatedSprite并销毁它。
2.7 小demo(部分代码)
// 简单方法创建动画精灵
let ss = new PIXI.AnimatedSprite.fromImages(['./images/down1.png', './images/down2.png', './images/down3.png', './images/down4.png'])
//添加到舞台
app.stage.addChild(ss)
//设置精灵位置
ss.y = 220
ss.animationSpeed = 0.1
// ss.play()
//从第一张图片开始
ss.gotoAndPlay(0)
//停在第二张图片
ss.gotoAndStop(1)
//销毁
ss.destroy()