Phaser系列课程初步分成《基础篇上》、《基础篇下》、《动画篇》、《交互篇》、《物理引擎篇》、《图形绘制篇》、《场景篇》。

物理引擎为游戏模拟真实的物理场景,可以大大提升一个游戏的趣味性。官方完整的Phaser本身自带三个物理引擎(你也可以自己构建一个不带任何物理引擎的版本):Arcade、P2、Ninja,可以满足我们大部分的游戏需求,此外还有一个需要花钱购买的物理引擎Box2d插件。此篇,我们逐个介绍三个自带物理引擎,Box2d仅作为拓展知识。

首先,我们先来简单了解一下这三种物理引擎有什么区别?我们需要根据游戏需求选取适合的物理引擎。

Arcade:轻量级高性能AABB式物理碰撞系统,AABB即Axis-aligned Bounded Rectangles,译为轴对称盒子,只能以矩形框计算碰撞区域,精度低,运算速度快,可以实现简单的碰撞、重力等效果。

P2:可以实现多种物理模型和物理特性,如Arcade所不能实现的多边形碰撞区域、弹簧、摩擦力、碰撞材质、反弹系数等,功能强大但也必然会使运算复杂、耗费性能。

Ninja:可以实现平面、凹凸面、球面等的碰撞,物体在非平整面上碰撞时不会翻倒,跟忍者一样。

为专门讲解物理引擎,我们准备一个比较纯净的初始化界面,只保留前面课程中的背景箱子、艾斯(关键帧雪碧图, json文件),初始Demo,代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
var game = new Phaser.Game(750, 750 / window.innerWidth * window.innerHeight, Phaser.CANVAS, 'phaser-container', {
init: init,
preload: preload,
create: create,
update: update,
render: render
}, false, true);

function init() {
game.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL;
game.stage.backgroundColor = '#0c276e';
}

function preload() {
game.load.image('bg', 'img/bg.jpg');
game.load.image('box', 'img/box.png');
game.load.atlasJSONHash('sprite_animation', 'img/sprite_animation2.png', 'img/sprite_animation2.json');
}

var box;

function create() {
// 背景居于底部定位,箱子从最上方正中生成
game.add.sprite(0, game.height - 1334, 'bg');
box = game.add.sprite(game.world.centerX, 88, 'box');
box.anchor.set(0.5);

// 创建艾斯
hero = game.add.sprite(game.world.centerX+250, game.height - 400, 'sprite_animation', 'walk/01');
hero.anchor.set(0.5);
hero.animations.add('walk', Phaser.Animation.generateFrameNames('walk/', 1, 8, '', 2));
}

function update() {}
function render() {}

Arcade

不管是哪个物理引擎,都需要先给游戏引进物理引擎,引进Arcade:

1
2
3
4
// 开启Arcade物理引擎
game.physics.startSystem(Phaser.Physics.Arcade);
// 全局设置默认重力为300
game.physics.arcade.gravity.y = 300;

现在场景是看不出什么效果的,要使物理系统对某个物体起作用,需要开启该物体的物理特性,如下:

1
2
// 给hero和box添加物理特性
game.physics.enable([hero, box]);

这样,艾斯和箱子都会在重力的作用下做自由落体运动。对比设置前后,开启物理特性后的对象下的body新增了非常多的属性和方法,大多数和物理特性有关,你可以根据需要进行使用。当前的例子,物体会一直往下掉,通过设置body中一些属性,我们可以改变它们的表示,先熟悉一些常用的属性。

body常用属性

属性 类型 默认值 说明
acceleration object {x=0, y=0} 加速度
allowGravity boolean true 是否启用重力效果
bounce object {x=0, y=0} 设置反弹系数
collideWorldBounds boolean false 是否监测与游戏世界边沿的碰撞
friction object {x=1, y=0} 设置摩擦系数
gravity object {x=0, y=0} 设置物体重力,会和世界重力叠加
immovable boolean false 设置是否固定不动
mass number 1 设置重量
velocity object {x=0, y=0} 设置速度

body常用方法

方法 参数 说明
setSize (width, height, offsetX, offsetY) (宽度, 高度, 距离物体左边距, 距离物体上边距),重设物体的碰撞矩形区域

所以,我们要检测物体碰到游戏世界边沿就停止,并添加稍微的反弹效果,可以这么做:

1
2
3
4
hero.body.collideWorldBounds = true;
hero.body.bounce.set(0.3);
box.body.collideWorldBounds = true;
box.body.bounce.set(0.3);

为检测艾斯和箱子碰撞,我们添加方向盘事件,控制艾斯移动:

1
2
// create
cursors = game.input.keyboard.createCursorKeys();
1
2
3
4
5
6
7
8
9
10
11
12
13
// update
game.physics.arcade.collide(hero, box);
if (cursors.left.isDown) {
hero.body.velocity.x = -200;
hero.scale.x = 1;
hero.play('walk', 10);
} else if (cursors.right.isDown) {
hero.body.velocity.x = 200;
hero.scale.x = -1;
hero.play('walk', 10);
}else{
hero.body.velocity.x = 0;
}

这里要注意的是,在Arcade中,如果物体需要检测碰撞,改变物体位置要用velocity添加位移速度,而非动态改变x或者body.x,另外设置箱子静止,并开启调试模式查看物体碰撞区域:

1
2
// create
box.body.immovable = true;
1
2
3
// render
game.debug.body(hero);
game.debug.body(box);

改变box碰撞区域:

1
box.body.setSize(404, 176, -100, 0);

待续…..