phaser.js是一款开源的HTML5游戏框架,GitHub主页上star超过了2万。phaser.js支持使用JavaScript或TypeScript编写游戏,支持WebGL和Canvas渲染并可借助第三方工具编译成iOS,Android原生程序。phaser.js有两个版本:phaser 3和phaser CE(Community Edition)。phaser CE基于phaser 3并由社区主导,phaser 3是下一代phaser。

1、安装phaser 3

使用npm安装

1
npm install phaser

使用时引入

1
import 'phaser';

官方针对Webpack发布了Webpack模板(点击)。 ‘

2、创建游戏对象

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
36
37
38
import 'phaser';  

// 游戏对象
let game;

// 全局游戏设置
const gameOptions = {
// 角速度,每帧多少度
rotationSpeed: 3,
// 投掷飞刀的时间,单位毫秒
throwSpeed: 150,
// 两刀间最小间隔角度
minAngle: 15
}

// 载入时初始化
window.onload = function () {
// 配置信息
const config = {
// 渲染类型
type: Phaser.CANVAS,
// 界面宽度,单位像素
width: 750,
// 界面高度
height: 1334,
// 背景色
backgroundColor: 0x444444,
// 场景
scene: [playGame]
};

// 声明游戏对象
game = new Phaser.Game(config);
// 获取焦点,调节界面大小
window.focus();
resize();
window.addEventListener("resize", resize, false);
}

首先定义全局游戏设置,主要包括各种游戏参数。之后在窗口载入时声明配置信息并将配置信息传入Phaser.Game构造函数,获取焦点、调整窗口。
窗口调整方法如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 调节界面大小  
function resize() {
const canvas = document.querySelector("canvas");
const windowWidth = window.innerWidth;
const windowHeight = window.innerHeight;
const windowRatio = windowWidth / windowHeight;
const gameRatio = game.config.width / game.config.height;
if(windowRatio < gameRatio){
canvas.style.width = windowWidth + "px";
canvas.style.height = (windowWidth / gameRatio) + "px";
}
else{
canvas.style.width = (windowHeight * gameRatio) + "px";
canvas.style.height = windowHeight + "px";
}
}

高度始终为窗口高度,宽度根据高度等比例调整。

3、创建游戏主场景

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
// 游戏主场景  
class playGame extends Phaser.Scene {
// 构造器
constructor() {
super("PlayGame");
}

// 游戏载入前加载资源
preload() {
// 加载图片
this.load.image("target", "target.png");
this.load.image("knife", "knife.png");
}

// 场景创建时执行
create(){
// 是否可以投掷飞刀
this.canThrow = true;
// 已投飞刀数组
this.knifeGroup = this.add.group();
// 增加一把刀
this.knife = this.add.sprite(game.config.width / 2, game.config.height / 5 * 4, "knife");
// 增加圆木
this.target = this.add.sprite(game.config.width / 2, 400, "target");
// 圆木在前,刀在后
this.target.depth = 1;
// 等待投掷飞刀
this.input.on("pointerdown", this.throwKnife, this);
}

// 投掷飞刀方法
throwKnife() {
// 判断是否可以投掷
if (this.canThrow) {
// 投掷后一段时间不可再次投掷
this.canThrow = false;
// 飞刀动画
this.tweens.add({
// 将到加入targets
targets: [this.knife],
// y方向目标
y: this.target.y + this.target.width / 2,
// 动画时间
duration: gameOptions.throwSpeed,
// 回调范围
callbackScope: this,
// 动画完成后的回调函数
onComplete: function (tween) {
// 判断飞刀是否可以插入圆木
let legalHit = true;
// 获取在圆木上旋转的飞刀数组
const children = this.knifeGroup.getChildren();
// 遍历飞刀数组
for (let i = 0; i < children.length; i++) {
// 判断刀间夹角是否满足条件
if(Math.abs(Phaser.Math.Angle.ShortestBetween(
this.target.angle,
children[i].impactAngle)) < gameOptions.minAngle) {
// 不满足条件
legalHit = false;
break;
}
}
// 判断是否满足条件
if (legalHit) {
// 可以继续投掷
this.canThrow = true;
// 飞刀数组中增加本次飞刀
const knife = this.add.sprite(this.knife.x, this.knife.y, "knife");
// 存储目标角度
knife.impactAngle = this.target.angle;
// 添加到数组
this.knifeGroup.add(knife);
// 新生成一把刀
this.knife.y = game.config.height / 5 * 4;
}
else{
// 掉下来的动画
this.tweens.add({
// 加到targets数组
targets: [this.knife],
// y方向目标
y: game.config.height + this.knife.height,
// 旋转度数,弧度制
rotation: 5,
// 动画时间
duration: gameOptions.throwSpeed * 4,
// 回调范围
callbackScope: this,
// 回调函数
onComplete: function(tween) {
// 开始新一局
this.scene.start("PlayGame")
}
});
}
}
});
}
}

// 每帧更新
update() {
// 旋转圆木
this.target.angle += gameOptions.rotationSpeed;
// 获取圆木上的飞刀数组
const children = this.knifeGroup.getChildren();
// 遍历飞刀数组
for (let i = 0; i < children.length; i++){
// 旋转每个飞刀
children[i].angle += gameOptions.rotationSpeed;
// 度转弧度
const radians = Phaser.Math.DegToRad(children[i].angle + 90);
// 计算x,y使其围绕中心旋转
children[i].x = this.target.x + (this.target.width / 2) * Math.cos(radians);
children[i].y = this.target.y + (this.target.width / 2) * Math.sin(radians);
}
}
}

场景类中除构造函数外主要有4种方法:preload、create、throwKnife和update。preload主要用于在场景开始前预加载资源,如图片、音乐等。create用于场景创建,在场景第一次被创建时调用一次,主要用于创建游戏元素和初始化一些参数。throwKnife是create方法中pointerdown事件的回调函数,当检测到点击事件时,判断是否可以投掷飞刀并调用tweens动画管理器执行动画。update每帧都会执行,主要用于添加动画。
游戏截图如下。
game
完整程序见我的GitHub。git clone后先执行npm install安装phaser,之后执行webpack打包,最后启动http-server访问index.html。