基于HTML5/Javascrip的游戏开发框架Phaser
Phaser是一个开源的HTML5游戏框架,也就是传说中100行代码之内搞定Flappy Bird的神器,通过这个框架我们可以很容易地开发桌面和移动的小游戏。目前Phaser的最新版本是2.0.7,它同时支持WebGL和Canvas。像其它游戏框架一样,Phaser封装了很多游戏开发的特性。在这篇文章中我们将会通过Phaser创建一个简单的游戏。为了专注于对框架学习,在下文中我们不会用到Phaser的任何对象,如精灵和组。
搭建Phaser
Phaser是一个通过Yeoman构建的开源的框架,我们可以在Github上下载到最新的Phaser,下载之后通过以下命令搭建:
- npm install -g generator-phaser-official
- yo phaser-official
Phaser框架的文件结构
一个Phaser工程将会有如下的文件结构:
|-- app.js
|-- main.js
|-- prefabs
`-- states
|-- boot.js
|-- level_intro.js
|-- level_master.js
|-- level_round.js
|-- main_intro.js
|-- main_menu.js
`-- preload.js
Main.js作为程序入口,开始游戏。app.js用来定义一个Phaser应用,prefabs文件夹存放游戏对象,states文件夹用来保存游戏状态。
如果你想要在这些文件中引入纯脚本,我推荐你用RequireJS或是Browserify。
Phaser.state
Phaser state将会封装游戏中不同的状态,比如游戏的加载、主菜单、级别、帮助、暂停等等……当一个state开始的时候,你可以创建与这个state相关的对象,同时你也可以在不同的state之间切换,Phaser将会为你清理过时的游戏对象,你可以在新的state中继续创建游戏对象并显示它们。
你可以通过实现预设方法来创建状态,下面我举一些重要的例子:
- init – 当state开始的时候执行的方法,它传递一个参数使state之间共享数据。
- preload – 当state开始的时候执行的方法,它的作用是在游戏开始前加载所有的资源文件。
- create – preload方法执行后开始执行的方法,用来创建游戏对象。
- shutdown – 当一个state结束的时候执行的方法,用来清理过时的游戏对象。
State执行流程
下图是state的执行流程。Boot和Preload状态用来配置和加载游戏资源,MainMenu状态用来显示主菜单。其他的level状态是为了解决游戏过程中不同的级别和关数,每一个级别可以分成很多关,通过后进入下一关。
Game State
Boot state在preload的hook方法中加载预加载资源,并构建游戏的配置,如窗口缩放和输入内容。
File: states/boot.js
- function Boot() {};
-
- Boot.prototype = {
- preload: function() {
- // load preloader assets
- },
- create: function() {
- // setup game environment
- // scale, input etc..
-
- this.game.state.start('preload');
- }
- };
Preload state加载所有的游戏资源,然后切换到main-intro状态。
File: states/preload.js
- Preload.prototype = {
- preload: function() {
- // load all game assets
- // images, spritesheets, atlases, audio etc..
- },
- create: function() {
- this.game.state.start('main-intro');
- }
- };
MainIntro state 用来显示游戏的介绍、logo、用户信息等等……它不需要preload方法,执行完之后就会转换到main-menu状态。
File: states/main_intro.js
- function MainIntroState() {};
-
- MainIntroState.prototype = {
- create: function() {
- // add main intro assets into the world
- this.tweenFadeState();
- },
-
- tweenFadeState: function() {
- this.game.add.tween({})
- .to({alpha: 1}, 2000)
- .onComplete.add(function() {
- this.game.state.start('main-menu');
- }, this);
- }
- };
MainMenu state显示游戏的主界面,用户可以通过主菜单和程序进行交互。为了简单的演示,我在以下代码中添加了一个键盘触发事件,事件触发后转换到level-master状态。
File: states/main_menu.js
- MainMenuState.prototype = {
- create: function() {
- this.enterKey = this.game.input.keyboard
- .addKey(Phaser.Keyboard.ENTER);
-
- this.enterKey.onDown.add(this.tweenPlayState, this);
- },
- tweenPlayState: function() {
- var tweenMenuShrink = this.game.add.tween({})
- .to({x: 0, y: 0}, 200);
-
- var tweenFadeIn = this.game.add.tween({})
- .to({alpha: 1}, 2000);
-
- tweenFadeIn.onComplete.add(function() {
- this.game.state.start('level-master');
- }, this);
-
- tweenMenuShrink.chain(tweenFadeIn);
- tweenMenuShrink.start();
- }
- };
这里为了演示简化了tween操作,在正常的开发中,你应该在create方法中创建游戏对象,具体的创建方法可以参见Phaser的Demo和文档。
LevelMaster state在游戏界面上不显示任何的内容,它要做的只是决定选择转换到level-round state还是leverl-intro state,同时,它也会在不同的state之间更新和传递游戏数据。
File: state/level_master.js
- LevelMasterState.prototype = {
- init: function(levelData) {
- if (!levelData) {
- levelData = {
- level: 0,
- round: 1,
- players: [
- { score: 0, skill: 1 },
- { score: 0, skill: 1 }
- ]
- };
- }
-
- this.levelData = levelData;
- this.winScore = 2;
- },
-
- create: function() {
- this.decideLevelState();
- }
- };
当一个新的level开始,level-intro state就开始了,level-intro state会显示对这个新level的介绍,比如当前是哪种级别,在level-intro之后,会转换到level-round,也就是游戏开始的地方。
当一个round结束之后,要么是新的level,要么就是新的round。这个逻辑在decideLevelState方法中实现,如果我们通过了一个level,游戏就会进入下一个level,当已经进入最高的level之后,会切换到下一个round。
this.levelData保存游戏的数据,像当前的level、round和游戏分数。我们在这些值发生逻辑变化和切换state的时候更新它们。
File: states/level_master.js
- LevelMasterState.prototype = {
- decideLevelState: function() {
- if (this.isFirstLevel() || this.getWinningPlayer() !== -1) {
- this.nextLevel();
- } else {
- this.nextRound();
- }
- },
- nextLevel: function() {
- this.levelData.level++;
-
- this.levelData.players.forEach(function(p) {
- p.score = 0;
- }, this);
-
- this.levelData.round = 1;
-
- this.game.state.start('level-intro', true, false, this.levelData);
- },
- nextRound: function() {
- this.levelData.round++;
- this.game.state.start('level-round', true, false, this.levelData);
- }
- };
LevelIntro state显示了level的介绍信息,如你正在哪个level中。我们可以通过传递levelDate参数来保存游戏数据。在create方法中,如果用户进入第一个level,我们可以通过一个技能菜单来获取levelData,技能菜单指的是用户选择的的难度级别,这个值由你来设定。结束的时候会切换到level-round state。
File: states/level_intro.js
- LevelIntroState.prototype = {
- init: function(levelData) {
- this.levelData = levelData;
- },
- create: function() {
- var tweenIntro = this.tweenIntro();
-
- if (this.levelData.level === 1) {
- var tweenSkillMenuPop = this.tweenSkillMenuPop();
-
- tweenIntro.chain(tweenSkillMenuPop);
- tweenSkillMenuPop.onComplete.add(this.levelStart, this);
- } else {
- tweenIntro.onComplete.add(this.levelStart, this);
- }
- },
- levelStart: function() {
- this.game.state.start('level-round', true, false, this.levelData);
- },
- tweenIntro: function() {
- var tween = this.game.add.tween({})
- .to({alpha: 0}, 1000, Phaser.Easing.Linear.None, true);
-
- return tween;
- },
- tweenSkillMenuPop: function() {
- var tween = this.game.add.tween({})
- .to({x: 1, y: 1}, 500, Phaser.Easing.Linear.None, true);
-
- return tween;
- }
- };
最后LevelRound state是游戏当前所发生的位置,你可以通过update方法进行更新。为了简化演示,我在state结束的时候添加了一个点击回车键的交互事件,它会返回到lever-master,在level-master的初始位置得到传递的levelData数据。
File: states/level_round.js
- LevelRoundState.prototype = {
- init: function(levelData) {
- this.levelData = levelData;
- },
- create: function() {
- this.enterKey = this.game.input.keyboard
- .addKey(Phaser.Keyboard.ENTER);
-
- this.enterKey.onDown.add(this.roundEnd, this);
- },
- roundEnd: function() {
- this.nextRound();
- },
- nextRound: function() {
- this.game.state.start('level-master', true, false, this.levelData);
- }
- };
我们已经完成了整个state的流程环,它们看起来像下面这样:
- Boot -> Preload ->
- main-intro -> main-menu ->
- level-master -> Level1 ->
- level-master -> L1 Round1 ->
- level-master -> L1 Round2 ->
- level-master -> Level2 ->
- level-master -> L2 Round1 ->
你可以通过在level-round state中设置一个事件转换到main-menu state来结束游戏的流程。
开始游戏
现在我们将会运行这个Phaser游戏,将这个div放到你的页面中,Phaser将会把它的canvas放在这里。
File: index.html
- <div id="game-area"></div>
我们需要创建一个Phaser.Game,把我们所有的state加入到StateManager中并开始Boot state。
File: app.js
- function Game() {}
-
- Game.prototype = {
- start: function() {
- var game = new Phaser.Game(640, 480, Phaser.AUTO, 'game-area');
-
- game.state.add('boot', BootState);
- game.state.add('preload', PreloadState);
- game.state.add('main-intro', MainIntroState);
- game.state.add('main-menu', MainMenuState);
- game.state.add('level-master', LevelMasterState);
- game.state.add('level-intro', MainIntroState);
- game.state.add('level-round', LevelRoundState);
- game.state.start('boot');
- }
- };
最后,都过以下代码触发这个游戏:
File: main.js
- var game = new Game();
- game.start();
总结
这只是利用Phaser框架开发了一个简单的游戏架子,你可以在文章上方点击下载这个Demo。同时,Phaser官方为你的开发游戏提供了很多资源,像精灵、动画、声音、物理、缩放等等,你可以访问Phaser的官网进行深入的学习。
英文原文地址:http://www.sitepoint.com/javascript-game-programming-using-phaser/