Phaser
yuhuo2023-10-30开发库渲染库
一. 初始化
1. 游戏配置
var game = new Phaser.Game({
parent: "container", // 父容器(默认添加到body)
type: Phaser.AUTO, // 渲染方式
width: 288, // 设计稿宽度
height: window.innerHeight / window.innerWidth * 288, // 适配当前屏幕的高度
backgroundColor: 0x802515, // 默认的背景色
// 缩放模式 FIT:实际宽或高自动跟随父元素缩放到最大,同时保持宽高比例(类似 css 的 contain)
scale: {
mode: Phaser.Scale.FIT,
},
// 缩放模式 NONE(默认):实际宽高根据 zoom 进行缩放
scale: {
mode: Phaser.Scale.NONE,
zoom: 1
},
physics: { // 物理引擎
default: 'arcade',
arcade: {
gravity: { y: 600 }, // x轴,y轴重力
debug: false, // 物理元素是否显示轮廓线,速度线
}
},
scene: [{ // 场景数组
key: 'load', // 场景名
active: true, // 是否启动(第一个场景固定启动状态,后面的场景根据此字段配置)
init: function() {}, // 初始化回调
preload: function() {}, // 加载回调(加载资源)
create: function() {}, // 创建回调(创建元素,绑定事件等)
update: function() {}// 变化回调
}],
});
// 窗口大小改变事件
window.onresize = function() {
// 获取配置宽度,计算出适配当前屏幕的高度
var width = game.config.width;
var height = window.innerHeight / window.innerWidth * width;
// 宽高比例修改时
// FIT 缩放模式处理
game.scale.setGameSize(width, height);
game.scale.refresh();
// NONE 缩放模式处理
game.scale.resize(width, heigth);
game.scale.setZoom(window.innerWidth / width);
}
2. 场景操作
// this代表场景对象
// Game对象
this.game.scene.pause('sceneKey'); // 暂停场景,update暂停
this.game.scene.resume('sceneKey'); // 恢复场景,update恢复
this.game.scene.start('sceneKey'); // 开启/重新开启场景,从头执行init,upload,create,update
this.game.scene.stop('sceneKey'); // 关闭场景,销毁所有元素
this.game.scene.scenes; // 获取场景数组
// Phaser.Scenes.Systems对象
this.sys.pause(); // 暂停场景
this.sys.resume(); // 恢复场景
this.sys.setVisible(true); // 场景是否见
this.sys.setActive(true); // 场景是否启用(作用类似pause/resume)
this.sys.sleep(); // 休眠场景(作用类似暂停+隐藏)
this.sys.wake(); // 唤醒场景
this.sys.shutdown(); // 关闭场景
// Phaser.Scenes.ScenePlugin对象
this.scene.pause("sceneKey");
this.scene.resume("sceneKey");
this.scene.setVisible(true, "sceneKey");
this.scene.setActive(true, "sceneKey");
this.scene.sleep("sceneKey");
this.scene.wake("sceneKey");
// ---------
this.scene.start("sceneKey"); // 开启指定场景,关闭当前场景!
this.scene.restart(); // 重启当前场景
this.scene.stop("sceneKey");
this.scene.get("sceneKey"); // 获取场景对象
3. 加载资源
// this 代表当前场景对象 scene
// 加载图片(资源名,路径)
this.load.image('img', 'assets/img/img.png');
// 加载音效
this.load.audio('audio', 'assets/audio/audio.wav');
// 加载精灵图
this.load.spritesheet('sprite', 'assets/img/sprite.png', {
frameWidth: 32, // 单个精灵的宽高
frameHeight: 48
});
二. 普通元素
1. 创建元素
// 创建图片元素(x坐标, y坐标, 资源名)
var ele = this.add.image(400, 300, 'img');
// 创建精灵元素,可播放序列帧动画(x坐标, y坐标, 资源名, 精灵序号[默认0])
var ele = this.add.sprite(400, 300, 'img');
var ele = this.add.sprite(400, 300, 'img', 1);
// 创建瓦片元素(x坐标, y坐标, 宽, 高, 资源名)
var ele = this.add.tileSprite(w/2, h/2, 204, 116, 'img');
// 瓦片元素的内容定位修改
ele.tilePositionX += 5;
ele.tilePositionY += 5;
// 创建文本元素
var text = this.make.text({
x: 16, // 位置
y: 16,
text: '加载中...', // 文本
style: { // 字体,字号,颜色
fontFamily: 'monospace',
fontSize: '20px',
color: '#ffffff'
}
});
var text = this.add.text(16, 16, '分数: 0', {
fontFamily: 'monospace',
fontSize: '32px',
color: '#fff'
});
// 文本元素修改样式
text.setText('哈哈哈');
text.setFontFamily('monospace');
text.setFontSize(36);
text.setColor('red');
// 播放音频
this.sound.play('score');
2. 元素通用的属性方法
物理元素也可用
// 属性
ele.x = 50; // x坐标
ele.y = 50; // y坐标
ele.active = false; // 是否有效(是否会在场景中更新,元素触发preUpdate事件,以及播放动画)
ele.visible = false; // 是否可见(不可见时依然占位置,可碰撞可移动可动画,不可点击)
ele.scale = 0.5; // 缩放
ele.depth = 1; // 层级
// 方法
ele.setActive(false); // 是否有效
ele.setVisible(false); // 是否可见
ele.setOrigin(0, 0.5); // 定位点(默认都是0.5)
ele.setScale(0.5); // 缩放
ele.setTint(0xff0000); // 前景色
ele.setRotation(1); // 旋转弧度
ele.destroy(); // 销毁元素
ele.setDepth(1); // 层级
ele.stop(); // 停止播放动画
// 场景
scene.children.list.forEach(function (child) { // 遍历场景所有元素
});
var len = scene.children.length; // 场景元素个数
三. 物理元素
1. 创建元素
// 创建空的静态/动态元素组(静态元素:没有重力,静止不动)
var staticGroup = this.physics.add.staticGroup();
var group = this.physics.add.group();
// 创建带元素的静态/动态元素组
var group = this.physics.add.group({
key: 'star',
repeat: 11, // 重复次数
setXY: { x: 12, y: 0, stepX: 70 } // 位置
});
// 元素组添加元素
var ele = group.create(600, 400, 'item');
// 添加元素到组里后,元素本身的物理属性会被重置
group.add(ele);
// 元素组遍历
group.children.iterate(function (child) {
return true;
});
for(let child of group.getChildren()) {
}
// 创建静态/动态元素
var ele = this.physics.add.sprite(100, 450, 'img');
var ele = this.physics.add.staticSprite(100, 450, 'img', 1);
var ele = this.physics.add.image(100, 450, 'img');
var ele = this.physics.add.staticImage(100, 450, 'img');
// 将普通元素转成静态/动态物理元素(普通元素, 是否静态)
var ele = this.physics.add.existing(ele, false);
2. 元素方法
// 重设元素位置
ele.body.reset(50, 50);
// 元素静止(速度,加速度为0)
ele.body.stop();
// 调整位置/定位点后,需要刷新
ele.refreshBody();
// 元素停止,参数1:是否失效(无动画),参数2:是否隐藏(不占位置)
ele.disableBody(true, true);
// 元素启动,参数1:是否重设位置,参数2,3:新位置(参数1为false有效),
// 参数4:是否有效,参数5:是否显示
ele.enableBody(false, 100, 500, false, true);
// 修改身体的大小和偏移、
// 身体的范围默认是和元素一致的,修改后不影响元素显示,但是影响碰撞重叠
ele.body.setSize(90, 90);
ele.body.setOffset(20, 40);
ele.body.width
ele.body.offset
// 是否禁用元素(不会产生碰撞重叠)
ele.body.enable = false;
// ** 仅动态元素生效 **
// 设定弹性, 默认0
ele.setBounce(0.5);
ele.setBounceX(0.5);
ele.setBounceY(0.5);
// 设置是否与边界碰撞,默认否
ele.setCollideWorldBounds(true);
// 设置速度
ele.setVelocity(100, 200);
ele.setVelocityX(100);
ele.setVelocityY(100);
// 是否有重力, 默认有
ele.body.allowGravity = false;
// ** 仅元素组生效 **
// 计算元素个数(true:有效,false 失效)
group.countActive(true);
::: tips
当我们要暂时性的销毁某个元素时,需要处理以下4种属性:
// 不再更新,不会触发元素preUpdate事件,停止播放动画
ele.setActive(false);
// 不可见
ele.setVisible(false);
// 停止,速度为0
ele.body.stop();
// 禁用物理属性,不会碰撞重叠
ele.body.enable = false;
可以直接用1个方法代替:
ele.disableBody(true, true);
反之让其出现:
ele.enableBody(true, x, y, true, true);
ele.setVelocityY(10);
:::
3. 元素碰撞重叠
// 添加元素碰撞(可以是元素组或静态元素)
this.physics.add.collider(group, ele);
this.physics.add.collider(group, ele, function() {
// 碰撞回调
}, null, this);
// 添加元素重叠
this.physics.add.overlap(group, ele, function() {
// 重叠回调
}, null, this);
4. 物理引擎操作
// 停止元素运动,不会影响动画播放和瓦片滚动
this.physics.pause();
// 恢复
this.physics.resume();
四. 事件
1. 绑定事件
// 加载中事件
this.load.on('progress', function (value) {
});
// 加载完成事件
this.load.on('complete', function () {
});
// 事件名
// pointerdown 在作用域按下
// pointerup 在作用域按起
// pointermove 在作用域按着移动,多次触发(从哪按下按起无所谓)
// pointerover 作用域进入按下状态(对场景无效)
// pointerout 作用域离开按下状态(对场景无效)
// 绑定场景事件
this.input.on('pointerdown', function() {
});
// 注:元素事件需要先设置元素可交互
ele.setInteractive();
// 绑定元素事件
ele.on('pointerdown', function () {
});
// 绑定一次事件
ele.once('pointerdown', function () {
});
// 移除事件
ele.off('pointerdown')
2. update事件
// 物理元素被触碰:down 底部, up 顶部, left 左边, right 右边
// 注:静态元素被触发后会一直保持true, 即使已没有触发
if(ele.body.touching.down) {
}
// 按键按下
// 注:需要在create处先创建按键对象
var cursors = this.input.keyboard.createCursorKeys();
// 按键:left, right, up, down, space, shift
// 状态:isDown, isUp
if(cursors.left.isDown) {
}
// 鼠标按下
if (this.input.activePointer.isDown) {
}
五. 动画
1. 序列帧动画
// 创建动画
var anim = this.anims.create({
key: 'turn',
frames: [ { key: 'dude', frame: 4 } ], // 帧数组
frames: this.anims.generateFrameNumbers('dude', { start: 0, end: 3 }), // 帧数组
frameRate: 20, // 每秒的帧数
duration: 2000, // 播放时长(毫秒)
repeat: 10, // 动画重复次数, -1表示无穷大(比如设2,实际共播放3遍动画)
showOnStart: false, // 播放前是否设 visible = true
hideOnComplete: false, // 播放完成是否设 visible = false
yoyo: false, // 是否反向播放(反向播放不占次数)
});
// 元素播放和停止动画(只有sprite类型元素或者元素组中的元素,才可播放动画)
ele.play(anim);
ele.paly("turn");
ele.play({key:"turn", repeat: 1});
ele.stop();
// 通过anims属性播放和停止动画
ele.anims.play("turn");
ele.anims.stop();
// 所有此动画暂停和恢复
anim.pause();
anim.resume();
2. 补间动画
// 播放动画
var tw = this.tweens.add({
targets: ele, // 元素或元素数组
duration: 5000, // 动画时长
loop: -1, // 重复次数,-1表示无数次(如为2时,实际播放3次)
yoyo: false, // 是否反向执行动画(默认false)
onStart: function () {}, // 开始动画回调
onLoop: function () {}, // 开始循环回调
onComplete: function () {}, // 完成动画回调
x: 0, // 目标x坐标
y: 0, // 目标y坐标
angle: 90, // 目标旋转角度(逆时针)
scale: 2, // 目标缩放倍数
});
// 暂停,停止和恢复动画
tw.pause();
tw.stop();
tw.resume();
// 暂停所有动画
this.tweens.pauseAll();
六. 其他对象
1. 数学对象
// 取随机浮点数
var num1 = Phaser.Math.FloatBetween(0.4, 0.8);
// 取随机整数
var num1 = Phaser.Math.Between(4, 8);
2. 相机对象
// 获取canvas宽高
var width = this.cameras.main.width;
var height = this.cameras.main.height;
// 设置场景背景色
this.cameras.main.setBackgroundColor(0x5ee270);