持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第10天,点击查看活动详情
一、前言
接触gsap后,对pixijs也产生了兴趣,Pixi是一个非常快的2D sprite渲染引擎,Pixi可以自由的使用,同时能与其它框架无缝集成。话不多说,来试试看吧。
注意: 1、本文使用ts,部分代码存在不兼容情况; 2、本文所使用的pixi版本为6.5.5。
二、过程
1、下载
本文是在vite构建的vue3项目里,所以需要以下命令
npm i pixi.js pixi-tweener
局部引入
import * as PIXI from 'pixi.js'
2、初始化一个application对象
我们需要创建一个特殊Pixi容器对象,先按官网的来试一试
let app = new PIXI.Application({
width: 400, // default: 800 宽度
height: 400, // default: 600 高度
antialias: true, // default: false 反锯齿
transparent: false, // default: false 透明度
resolution: 1 // default: 1 分辨率
})
//Add the canvas that Pixi automatically created for you to the HTML document
document.body.appendChild(app.view)
生成的的stage先直接挂载到body上,效果如下
ok能达到预期效果,那下面我们正式开始制作我们的场景吧
3、正式开始
(1)准备好元素标签和样式
id为pixijsjs的是我们挂载canvas的元素,基础布局样式如下
<style scoped>
.pixilabel {
position: absolute;
top: 20%;
left: 50%;
transform: translate(-50%, -50%);
}
.pixibox {
width: 400px;
height: 400px;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
</style>
(2)生成Pixi容器对象,并往其中的stage(舞台)添加sprite(精灵)
生成的pixi容器对象这步骤跟上述一样,添加到页面标签后,调用加载器
app.loader
.add([
"tom.jpeg",
"jerry.jpeg"
])
.load(setup)
此处我们使用add函数加载了两张图片,图片位置是在public下
在加载完毕之后,会调用load里的setup函数
function setup() {
console.log("setup")
let cat = new PIXI.Sprite(app.loader.resources["tom.jpeg"].texture)
// 调整汤姆的大小和位置
//Change the sprite's position
cat.x = 300
cat.y = 200
// 用cat.scale.set(0.5, 0.5)也可以,缩放大小为原图的一半
//Change the sprite's size
cat.width = 80
cat.height = 120
app.stage.addChild(cat)
let mouse = new PIXI.Sprite(app.loader.resources["jerry.jpeg"].texture)
// 调整杰瑞的大小和位置
//Change the sprite's position
mouse.x = 40
mouse.y = 260
//Change the sprite's size
// 杰瑞应该比汤姆小只
mouse.width = 40
mouse.height = 60
app.stage.addChild(mouse)
}
到这里我们的tom和jerry就生成啦。
(3)游戏循环机制game loop和requestAnimationFrame
game loop每秒执行60次,requestAnimationFrame跟屏幕刷新率挂钩,多少HZ就一秒调用几次,一般都是60次。
本文使用ticker构建gameloop,添加完sprite精灵之后,补上以下语句
app.ticker.add(delta => gameLoop(delta))
gameloop函数
function gameLoop(delta: number){
console.log(delta)
//Move the cat 1 pixel
cat.x -= 1
}
下面是requestAnimationFrame的写法,可供参考
// app.ticker.add(delta => gameLoop(delta))
//换成下面的
gameLoop()
gameloop函数
function gameLoop(delta: number){
console.log(delta)
requestAnimationFrame(gameLoop)
//Move the cat 1 pixel
cat.x -= 1
}
效果如下:
(4)设置动画状态
官网是推荐的构建游戏循环步骤是,说是像这样构建戏循环,将会更容易去处理游戏场景和关卡的切换。
1、设置游戏的状态state
2、开启游戏循环
3、在游戏循环函数里更新状态
代码如下:
let state: Function // 创建状态变量
然后在加载完精灵图之后,
state = play // 挂载到状态函数
app.ticker.add(delta => gameLoop(delta)) // 开启游戏循环
gameloop和play函数如下
function gameLoop(delta: number){
// console.log(delta, cat)
//更新状态
state(delta)
}
function play(delta: number) {
console.log(delta)
//每次向左移动1px
cat.x -= 1
}
效果跟之前一样,虽然不知道这样有什么好处,但官网都这样推荐,应该是有它的道理的。
(5)碰撞检测
当我们的汤姆碰到杰瑞,那应该就结束这次动画
// 写了个检测函数hitTestRectangle,官网有直接copy,检测两个精灵是否有重合的部分
hitTestRectangle(spriteOne, spriteTwo)
需要改动的代码不多
let count = 0 // 初始化变量
修改play函数,同时增加hitTestRectangle函数
function play(delta: number) {
console.log(delta)
//Move the cat 1 pixel to the right each frame
if (hitTestRectangle(cat, mouse) && count === 0) {
//There's a collision
cat.x = 130
cat.tint = 0xff0054
count++
} else if (!hitTestRectangle(cat, mouse) && count === 0) {
//There's no collision
cat.x -= 1
cat.tint = 0xccff99
}
}
function hitTestRectangle(r1: PIXI.Sprite, r2: PIXI.Sprite) {
//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 occurring. 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
}
效果就是汤姆气的脸色发青去抓杰瑞,结果碰到之前后退100px,同时怒气使得汤姆变红(物理)。
三、小结
今天试了几个基础的函数,实现的效果也比较简单,大家伙看个乐呵哈,明天再带大家领略一下pixi的其他函数。
ps:我是地霊殿__三無,一起学习吧