抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

体验Demo

开始游戏

PC端请注意打开开发者工具使用移动端游玩

1. 环境

Phaser.js v2

Phaser.js是一个快速、免费、有趣的开源HTML5游戏框架,提供跨桌面和移动web浏览器的WebGL和画布渲染。游戏可以通过使用第三方工具编译成iOS、Android和本机应用程序。您可以使用JavaScript或TypeScript进行开发。

2. 基础模板

2.1 导入phaser.min.js

也可以 直接从本站下载 phaser.js的压缩版本。

2.2 新建 main.js

通过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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
// 创建对象,把舞台绑定到id为game的容器上
// 前两个参数为舞台的宽度和高度
const game = new Phaser.Game(360, 600, Phaser.AUTO, 'game');
game.states = {};

// 开始页面场景
game.states.preload = function () {
// 预加载
this.preload = function () {

};

// 舞台被创建时执行
this.create = function () {
};
}

// 游戏讲解镜头
game.states.help = function () {
// 舞台被创建时执行
this.create = function () {

};
};

// 主游戏场景
game.states.main = function () {
// 舞台被创建时执行
this.create = function () {

}

// 更新回调,这个部分会一直不断执行
this.update = function () {

}
};

// 游戏结束场景
game.states.end = function () {
// 舞台被创建时执行
this.create = function () {

};
};

// 场景注册
game.state.add("preload", game.states.preload);
game.state.add("help", game.states.help);
game.state.add("main", game.states.main);
game.state.add("end", game.states.end);

// 默认先启动preload场景
game.state.start("preload");

这里出现了许多preloadcreateupdate方法,这分别代表场景被创建前的预加载方法,场景被创建时执行以及不断执行的update方法

2.3 新建 index.html

点击展开查看源码
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
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>飞机大战</title>
<style>
html, body {
width: 100%;
height: 100%;
margin: 0;
}

#game {
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<!-- 整个舞台会绑定在这个容器上 -->
<div id="game"></div>
</body>
<script src="js/phaser.min.js"></script>
<script src="js/main.js"></script>
</html>

3. 游戏全屏显示

首先我们定义宽度和高度的放大倍数,用于后续的坐标计算

1
2
3
4
5
6
7
// 创建对象,把舞台绑定到id为game的容器上
// 前两个参数为舞台的宽度和高度
const game = new Phaser.Game(360, 600, Phaser.AUTO, 'game');
game.states = {};

// 舞台放大倍数
let multipleX = 1, multipleY = 1;

然后在游戏的初始场景的create方法中计算放大的倍数和设置舞台全屏

Phaser.js v3 版本中并无 this.scale.scaleMode = Phaser.ScaleManager.EXACT_FIT; 如此方便的调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 开始页面场景
game.states.preload = function () {
// 预加载
this.preload = function () {

};

// 舞台被创建时执行
this.create = function () {
// 全屏
this.scale.scaleMode = Phaser.ScaleManager.EXACT_FIT;

// 记录倍数
multipleX = document.body.clientWidth / game.width;
multipleY = document.body.clientHeight / game.height;
};
}

此时会发现屏幕一片黑,这是正常情况,因为舞台默认就是黑色的

4. 素材库

我们在全局定义一个素材库,方便我们的使用,这里value部分是phaser加载资源文件后内部存储的key值,后面调用资源都是使用这个key,此处可以看下面的代码

点击展开查看源码
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
// 素材库
const sourcesList = {
background: "background",
startButton: "startButton",
startBGM: "startBackgroundMusic",
playBGM: "playBackgroundMusic",
gameOverMusic: "gameOverMusic",
beAttackedMusic: "beAttackedMusic",
playerExplodeMusic: "playerBeAttackedMusic",
getAwardMusic: "getAwardMusic",
myShot: "myShot",

myPlane: "myPlane",
myBullet: "myBullet",
myExplode: "myExplode",
enemyBullet: "enemyBullet",
enemy1: "enemy1",
enemy2: "enemy2",
enemy3: "enemy3",
enemyExplode1: "enemyExplode1",
enemyExplode2: "enemyExplode2",
enemyExplode3: "enemyExplode3",
boss: "boss",
bossExplode: "bossExplode",

startMusicButton: "startBackgroundMusic",
stopMusicButton: "stopBackgroundMusic",

killAward: "killAward",
lifeAward: "lifeAward",
levelAward: "levelAward",

menuButtonAudio: "menuButtonAudio",
startButtonAudio: "startButtonAudio",
replayButton: "replayButton",
shareButton: "shareButton"
};

5. 游戏初始界面

5.1 背景

这里解释一下,在preload方法中我们对所有的资源文件进行预加载

1
2
3
4
5
6
// 这里的sourcesList.background的值就是上面素材库对应的值
game.load.image(sourcesList.background, "assets/image/bg.png"); // 背景图

// 开始精灵图的尺寸为200*40
// 这里初始化一个精灵图,后面的100, 40, 2表示自左向右以一张100*40的大小切割两份
game.load.spritesheet(sourcesList.startButton, "assets/image/startButton.png", 100, 40, 2); // 开始按钮

什么是精灵图呢?就像这样->

5.1.1 不断滚动的背景

1
2
3
4
5
6
7
// 舞台被创建时执行
this.create = function () {
// 平铺背景图
const bg = game.add.tileSprite(0, 0, game.width, game.height, sourcesList.background);
// 滚动背景
bg.autoScroll(0, 60);
};

5.1.2 开始按钮

1
2
// 开始按钮 X坐标,Y坐标,日常状态,按下时,松开时
this.startButton = game.add.button(125, 330, sourcesList.startButton, this.StartClick, this, 1, 1, 0);

5.1.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
// 开始页面场景
game.states.preload = function () {
// 预加载
this.preload = function () {
/******************************* 图片素材部分 ***************************/
game.load.image(sourcesList.background, "assets/image/bg.png"); // 背景图
game.load.spritesheet(sourcesList.startButton, "assets/image/startButton.png", 100, 40, 2); // 开始按钮
};

// 舞台被创建时执行
this.create = function () {
// 全屏
this.scale.scaleMode = Phaser.ScaleManager.EXACT_FIT;

// 记录倍数
multipleX = document.body.clientWidth / game.width;
multipleY = document.body.clientHeight / game.height;

// 滚动背景
const bg = game.add.tileSprite(0, 0, game.width, game.height, sourcesList.background);
bg.autoScroll(0, 60);

// 开始按钮 日常状态,按下时,松开时
this.startButton = game.add.button(125, 330, sourcesList.startButton, this.StartClick, this, 1, 1, 0);
};

// 开始按钮事件,跳转到讲解页面
this.StartClick = function () {
game.state.start("help");
};
}

5.1.4 效果图

点击展开查看
效果图
效果图

5.2 玩家飞机

与开始按钮同理,不做阐述

1
this.myplane.animations.play("fly", 14, true); // 14帧可循环动画
点击展开查看源码
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
// 开始页面场景
game.states.preload = function () {
// 预加载
this.preload = function () {
/******************************* 图片素材部分 ***************************/
game.load.image(sourcesList.background, "assets/image/bg.png"); // 背景图
game.load.spritesheet(sourcesList.startButton, "assets/image/startButton.png", 100, 40, 2); // 开始按钮
game.load.spritesheet(sourcesList.myPlane, "assets/image/myPlane.png", 32, 40, 4); // 玩家飞机
};

// 舞台被创建时执行
this.create = function () {
// 全屏
this.scale.scaleMode = Phaser.ScaleManager.EXACT_FIT;

// 记录倍数
multipleX = document.body.clientWidth / game.width;
multipleY = document.body.clientHeight / game.height;

// 平铺背景图
const bg = game.add.tileSprite(0, 0, game.width, game.height, sourcesList.background);
// 滚动背景
bg.autoScroll(0, 60);

// 开始按钮 日常状态,按下时,松开时
this.startButton = game.add.button(125, 330, sourcesList.startButton, this.StartClick, this, 1, 1, 0);

// 飞机
this.myplane = game.add.sprite(157, 150, sourcesList.myPlane);
this.myplane.animations.add("fly");
this.myplane.animations.play("fly", 14, true); // 14帧可循环动画
};

// 开始按钮事件,跳转到讲解页面
this.StartClick = function () {
game.state.start("help");
};
}

5.3 背景音乐

同样需要预加载

1
game.load.audio(sourcesList.startBGM, "assets/music/bgm.mp3"); // 开始背景音乐

播放音乐(这里已经加上播放音乐和停止播放音乐的功能,但不做阐述)

1
2
3
// 背景音乐 音乐资源,响度,是否循环
this.backgroundMusicPlayer = game.add.audio(sourcesList.startBGM, 0.2, true);
if (isBGMPlay) this.backgroundMusicPlayer.play();
点击展开查看此时的完整源码
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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
// 创建对象,把舞台绑定到id为game的容器上
// 前两个参数为舞台的宽度和高度
const game = new Phaser.Game(360, 600, Phaser.AUTO, 'game');
game.states = {};

// 舞台放大倍数
let multipleX = 1, multipleY = 1;

// 全局音乐播放标识
let isBGMPlay = true;

// 素材库
const sourcesList = {
background: "background",
startButton: "startButton",
startBGM: "startBackgroundMusic",
playBGM: "playBackgroundMusic",
gameOverMusic: "gameOverMusic",
beAttackedMusic: "beAttackedMusic",
playerExplodeMusic: "playerBeAttackedMusic",
getAwardMusic: "getAwardMusic",
myShot: "myShot",

myPlane: "myPlane",
myBullet: "myBullet",
myExplode: "myExplode",
enemyBullet: "enemyBullet",
enemy1: "enemy1",
enemy2: "enemy2",
enemy3: "enemy3",
enemyExplode1: "enemyExplode1",
enemyExplode2: "enemyExplode2",
enemyExplode3: "enemyExplode3",
boss: "boss",
bossExplode: "bossExplode",

startMusicButton: "startBackgroundMusic",
stopMusicButton: "stopBackgroundMusic",

killAward: "killAward",
lifeAward: "lifeAward",
levelAward: "levelAward",

menuButtonAudio: "menuButtonAudio",
startButtonAudio: "startButtonAudio",
replayButton: "replayButton",
shareButton: "shareButton"
};

// 开始页面场景
game.states.preload = function () {
// 预加载
this.preload = function () {
/******************************* 图片素材部分 ***************************/
game.load.image(sourcesList.background, "assets/image/bg.png"); // 背景图
game.load.spritesheet(sourcesList.startButton, "assets/image/startButton.png", 100, 40, 2); // 开始按钮
game.load.spritesheet(sourcesList.myPlane, "assets/image/myPlane.png", 32, 40, 4); // 玩家飞机
game.load.image(sourcesList.startMusicButton, "assets/image/startMusic.png"); // 播放音乐按钮
game.load.image(sourcesList.stopMusicButton, "assets/image/stopMusic.png"); // 暂停播放音乐按钮

/******************************* 音效部分 ***************************/
game.load.audio(sourcesList.startBGM, "assets/music/bgm.mp3"); // 开始背景音乐
};

// 舞台被创建时执行
this.create = function () {
// 全屏
this.scale.scaleMode = Phaser.ScaleManager.EXACT_FIT;

// 记录倍数
multipleX = document.body.clientWidth / game.width;
multipleY = document.body.clientHeight / game.height;

// 平铺背景图
const bg = game.add.tileSprite(0, 0, game.width, game.height, sourcesList.background);
// 滚动背景
bg.autoScroll(0, 60);

// 开始按钮 日常状态,按下时,松开时
this.startButton = game.add.button(125, 330, sourcesList.startButton, this.StartClick, this, 1, 1, 0);

// 飞机
this.myplane = game.add.sprite(157, 150, sourcesList.myPlane);
this.myplane.animations.add("fly");
this.myplane.animations.play("fly", 14, true); // 14帧可循环动画

// 背景音乐
this.backgroundMusicPlayer = game.add.audio(sourcesList.startBGM, 0.2, true);
if (isBGMPlay) this.backgroundMusicPlayer.play();

// 暂停按钮
this.stopMusicButton = game.add.button(335, 10, sourcesList.stopMusicButton, function () {
if (this.backgroundMusicPlayer.isPlaying) {
isBGMPlay = false;
this.backgroundMusicPlayer.stop();
}
}, this, 0, 0, 0);

// 播放按钮
this.startMusicButton = game.add.button(310, 10, sourcesList.startMusicButton, function () {
if (!this.backgroundMusicPlayer.isPlaying) {
isBGMPlay = true;
this.backgroundMusicPlayer.play();
}
}, this, 0, 0, 0);
};

// 开始按钮事件,跳转到讲解页面
this.StartClick = function () {
game.state.start("help");
};
}

// 游戏讲解镜头
game.states.help = function () {
// 舞台被创建时执行
this.create = function () {

};
};

// 主游戏场景
game.states.main = function () {
// 舞台被创建时执行
this.create = function () {

}

// 更新回调,这个部分会一直不断执行
this.update = function () {

}
};

// 游戏结束场景
game.states.end = function () {
// 创建舞台
this.create = function () {

};
};

// 场景注册
game.state.add("preload", game.states.preload);
game.state.add("help", game.states.help);
game.state.add("main", game.states.main);
game.state.add("end", game.states.end);

// 默认先启动preload场景
game.state.start("preload");

6. 游戏操作讲解页面

此处和初始页面原理基本一致,直接贴源码

点击展开查看源码
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
// 游戏讲解镜头
game.states.help = function () {
// 舞台被创建时执行
this.create = function () {
// 滚动背景
const bg = game.add.tileSprite(0, 0, game.width, game.height, sourcesList.background);
bg.autoScroll(0, 60);

// 玩家飞机
this.myplane = game.add.sprite(80, 135, sourcesList.myPlane);
this.myplane.animations.add("fly");
this.myplane.animations.play("fly", 14, true); // 14帧可循环动画

const style = {font: "12px 宋体", fill: "#ff0000"};
game.add.text(125, 150, "玩家飞机,初始生命为10,等级为5", style);

// 奖励
game.add.sprite(80, 175, sourcesList.killAward);
game.add.text(125, 185, "获得后清除所有可见敌人", style);

game.add.sprite(85, 210, sourcesList.lifeAward);
game.add.text(125, 220, "获得后随机增加生命或护盾", style);

game.add.sprite(85, 250, sourcesList.levelAward);
game.add.text(125, 260, "获得后等级+1", style);

game.add.text(50, 320, "★玩家与敌人碰撞或每10个敌人逃脱均会减少生命\n进入Boss关卡时判定区域为飞机中间的红点", style);

// 开始按钮
this.startButton = game.add.button(125, 430, sourcesList.startButton, this.StartClick, this, 1, 1, 0);
};

this.StartClick = function () {
game.add.audio(sourcesList.startButtonAudio, 1, false).play();
game.state.start("main");
};
};

到此为止的完整源码

点击展开查看此时的完整源码
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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
// 创建对象,把舞台绑定到id为game的容器上
// 前两个参数为舞台的宽度和高度
const game = new Phaser.Game(360, 600, Phaser.AUTO, 'game');
game.states = {};

// 舞台放大倍数
let multipleX = 1, multipleY = 1;

// 全局音乐播放标识
let isBGMPlay = true;

// 素材库
const sourcesList = {
background: "background",
startButton: "startButton",
startBGM: "startBackgroundMusic",
playBGM: "playBackgroundMusic",
gameOverMusic: "gameOverMusic",
beAttackedMusic: "beAttackedMusic",
playerExplodeMusic: "playerBeAttackedMusic",
getAwardMusic: "getAwardMusic",
myShot: "myShot",

myPlane: "myPlane",
myBullet: "myBullet",
myExplode: "myExplode",
enemyBullet: "enemyBullet",
enemy1: "enemy1",
enemy2: "enemy2",
enemy3: "enemy3",
enemyExplode1: "enemyExplode1",
enemyExplode2: "enemyExplode2",
enemyExplode3: "enemyExplode3",
boss: "boss",
bossExplode: "bossExplode",

startMusicButton: "startBackgroundMusic",
stopMusicButton: "stopBackgroundMusic",

killAward: "killAward",
lifeAward: "lifeAward",
levelAward: "levelAward",

menuButtonAudio: "menuButtonAudio",
startButtonAudio: "startButtonAudio",
replayButton: "replayButton",
shareButton: "shareButton"
};

// 开始页面场景
game.states.preload = function () {
// 预加载
this.preload = function () {
/******************************* 图片素材部分 ***************************/
game.load.image(sourcesList.background, "assets/image/bg.png"); // 背景图
game.load.spritesheet(sourcesList.startButton, "assets/image/startButton.png", 100, 40, 2); // 开始按钮
game.load.spritesheet(sourcesList.myPlane, "assets/image/myPlane.png", 32, 40, 4); // 玩家飞机
game.load.image(sourcesList.startMusicButton, "assets/image/startMusic.png"); // 播放音乐按钮
game.load.image(sourcesList.stopMusicButton, "assets/image/stopMusic.png"); // 暂停播放音乐按钮

game.load.image(sourcesList.killAward, "assets/image/award_kill.png"); // 全屏秒杀
game.load.image(sourcesList.lifeAward, "assets/image/award_life.png"); // 加生命
game.load.image(sourcesList.levelAward, "assets/image/award_level.png"); // 加等级

/******************************* 音效部分 ***************************/
game.load.audio(sourcesList.startBGM, "assets/music/bgm.mp3"); // 开始背景音乐
game.load.audio(sourcesList.startButtonAudio, 'assets/music/button.mp3'); // 开始按钮点击音效
game.load.audio(sourcesList.menuButtonAudio, 'assets/music/button1.mp3'); // 菜单点击按钮音效
};

// 舞台被创建时执行
this.create = function () {
// 全屏
this.scale.scaleMode = Phaser.ScaleManager.EXACT_FIT;

// 记录倍数
multipleX = document.body.clientWidth / game.width;
multipleY = document.body.clientHeight / game.height;

// 平铺背景图
const bg = game.add.tileSprite(0, 0, game.width, game.height, sourcesList.background);
// 滚动背景
bg.autoScroll(0, 60);

// 开始按钮 日常状态,按下时,松开时
this.startButton = game.add.button(125, 330, sourcesList.startButton, this.StartClick, this, 1, 1, 0);

// 飞机
this.myplane = game.add.sprite(157, 150, sourcesList.myPlane);
this.myplane.animations.add("fly");
this.myplane.animations.play("fly", 14, true); // 14帧可循环动画

// 背景音乐 音乐资源,响度,是否循环
this.backgroundMusicPlayer = game.add.audio(sourcesList.startBGM, 0.2, true);
if (isBGMPlay) this.backgroundMusicPlayer.play();

// 暂停按钮
this.stopMusicButton = game.add.button(335, 10, sourcesList.stopMusicButton, function () {
if (this.backgroundMusicPlayer.isPlaying) {
isBGMPlay = false;
this.backgroundMusicPlayer.stop();
}
}, this, 0, 0, 0);

// 播放按钮
this.startMusicButton = game.add.button(310, 10, sourcesList.startMusicButton, function () {
if (!this.backgroundMusicPlayer.isPlaying) {
isBGMPlay = true;
this.backgroundMusicPlayer.play();
}
}, this, 0, 0, 0);
};

// 开始按钮事件,跳转到讲解页面
this.StartClick = function () {
game.add.audio(sourcesList.menuButtonAudio, 1, false).play();
this.backgroundMusicPlayer.stop();
game.state.start("help");
};
}

// 游戏讲解镜头
game.states.help = function () {
// 舞台被创建时执行
this.create = function () {
// 滚动背景
const bg = game.add.tileSprite(0, 0, game.width, game.height, sourcesList.background);
bg.autoScroll(0, 60);

// 玩家飞机
this.myplane = game.add.sprite(80, 135, sourcesList.myPlane);
this.myplane.animations.add("fly");
this.myplane.animations.play("fly", 14, true); // 14帧可循环动画

const style = {font: "12px 宋体", fill: "#ff0000"};
game.add.text(125, 150, "玩家飞机,初始生命为10,等级为5", style);

// 奖励
game.add.sprite(80, 175, sourcesList.killAward);
game.add.text(125, 185, "获得后清除所有可见敌人", style);

game.add.sprite(85, 210, sourcesList.lifeAward);
game.add.text(125, 220, "获得后随机增加生命或护盾", style);

game.add.sprite(85, 250, sourcesList.levelAward);
game.add.text(125, 260, "获得后等级+1", style);

game.add.text(50, 320, "★玩家与敌人碰撞或每10个敌人逃脱均会减少生命\n进入Boss关卡时判定区域为飞机中间的红点", style);

// 开始按钮
this.startButton = game.add.button(125, 430, sourcesList.startButton, this.StartClick, this, 1, 1, 0);
};

this.StartClick = function () {
game.add.audio(sourcesList.startButtonAudio, 1, false).play();
game.state.start("main");
};
};

// 主游戏场景
game.states.main = function () {
// 舞台被创建时执行
this.create = function () {

}

// 更新回调,这个部分会一直不断执行
this.update = function () {

}
};

// 游戏结束场景
game.states.end = function () {
// 创建舞台
this.create = function () {

};
};

// 场景注册
game.state.add("preload", game.states.preload);
game.state.add("help", game.states.help);
game.state.add("main", game.states.main);
game.state.add("end", game.states.end);

// 默认先启动preload场景
game.state.start("preload");

下一部分预告

篇幅问题,下篇讲游戏主题页面与结算页面。牙膏要一点一点挤

评论