Phaser是一个简单易用且功能强大的html5游戏框架,利用它可以很轻松的开发出一个html5游戏。
项目上线一周,闲来无事,捣鼓一个新的框架吧!

1.Phaser的使用非常简单,只需要引入它的主文件,然后在页面中指定一个用来放置canvas的元素,然后实例化一个 Game 对象就可以了。当然如果不指定canvas元素的存放位置会默认到body里面。

html代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!DOCTYPE html>
<html>
<head>
<meta content="yes" name="apple-mobile-web-app-capable"/>
<meta content="yes" name="apple-touch-fullscreen"/>
<meta content="black" name="apple-mobile-web-app-status-bar-style">
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport" />
<meta charset="utf-8" />
<title>flappy bird</title>
<style type="text/css">
*{
padding: 0px;
margin: 0px;
}
</style>
</head>
<body>
<script src="js/phaser.min.js"></script>
<script src="js/game.js"></script>
</body>
</html>

2.然后实例化一个Game对象

部分代码如下:

1
2
3
4
window.onload = function(){
var renderMode = Phaser.Device.isAndroidStockBrowser() ? Phaser.CANVAS: Phaser.AUTO;
game = new Phaser.Game(320, 505, renderMode, null, bootState);
}

稍微解析下Phaser.Game这个函数

1
Phaser.Game(width, height, renderer, parent, state, transparent, antialias, physicsConfig)

width: 渲染游戏的canvas的宽度

height: 渲染游戏的canvas的高度

renderer: 渲染方式,Phaser.CANVAS为画布,Phaser.WEBGL为WebGL来渲染,Phaser.AUTO为自动侦测

parent: 用来放置canvas元素的父元素,可以是一个元素id,也可以是dom元素本身,phaser会自动创建一个canvas并插入到这个元素中,实测不带会追加到body

state: state可以理解为场景,在这里指定state表示让游戏首先加载这个场景,但也可以不在这里指定state,而在之后的代码中决定首先加载哪个state。

transparent: 是否使用透明的canvas背景

antialias: 是否启用抗锯齿

physicsConfig: 游戏物理系统配置参数

3.实例化Game对象后,接下来要做的就是创建游戏的各种场景(state)。state可以是一个js自定义对象,也可以是一个函数,只要它们存在preload、create、update这三个方法中的任意一个,就是一个合法的state。

ex:

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
//state可以是一个自定义对象
var state1 = {
preload : function(){},
create : function(){},
update : function(){}
}

//state也可以是一个构造函数
var state2 = function(){
this.preload = function(){};
this.create = function(){};
this.update = function(){};
}

//只要存在preload、create、update三个方法中的一个就可以了
var state3 = function(){
this.update = function(){};
}

//当然state里也可以存在其他属性或方法
var state4 = function(){
this.create = function(){};
this.funcA = function(){}; //其他方法
this.funcB = 'hello'; //其他属性
}

其中

  • preload是预加载(最先执行)
  • create是初始化以及构建场景
  • update是更新函数,会在游戏的每一帧都执行,以此来创造一个动态的游戏。

在文中代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var bootState = function(){
this.preload = function(){};
this.create = function(){};
this.update = function(){};
}
var preloadState = function(){
this.preload = function(){};
this.create = function(){};
this.update = function(){};
}
var menuState = function(){
this.preload = function(){};
this.create = function(){};
this.update = function(){};
}
var playState = function(){
this.preload = function(){};
this.create = function(){};
this.update = function(){};
}

4.制作资源加载进度条

游戏要用到的一些图片、声音等资源都需要提前加载,有时候如果资源很多,就有必要做一个资源加载进度的页面,提高用户体验,这时在预加载场景用到的进度条需要在一个基础的场景来加载,命名这个场景为bootState。
具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
bootState = function(){
this.preload = function(){
game.load.image('loading','assets/preloader.gif');
};
this.create = function(){
game.canvas.style.backgroundColor = "#000000";
game.canvas.oncontextmenu = function(e) {
e.preventDefault()
};
game.stage.disableVisibilityChange = true;
game.device.desktop ? this.scale.scaleMode = Phaser.ScaleManager.EXACT_FIT : this.scale.scaleMode = Phaser.ScaleManager.EXACT_FIT;
game.scale.pageAlignHorizontally = true;
game.scale.pageAlignVertically = true;
game.scale.refresh();
this.scale.forcePortrait = true;
this.input.maxPointers = 1;

game.state.add('preloadState',preloadState);
game.state.start('preloadState');
};
}

5.加载资源

Phaser中资源的加载都是通过 Phaser.Loader 这个对象的方法来完成的,游戏实例的load属性就是指向当前游戏的Loader对象,在我们这里就是game.load。
代码如下:

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
preloadState = function(){
this.preload = function(){
var preloadSprite = game.add.sprite(35,game.height/2,'loading');
game.load.setPreloadSprite(preloadSprite);

game.load.image('background','assets/background.png');
game.load.image('ground','assets/ground.png');
game.load.image('title','assets/title.png');
game.load.spritesheet('bird','assets/bird.png',34,24,3);
game.load.image('btn','assets/start-button.png');
game.load.spritesheet('pipe','assets/pipes.png',54,320,2);
game.load.bitmapFont('flappy_font', 'assets/fonts/flappyfont/flappyfont.png', 'assets/fonts/flappyfont/flappyfont.fnt');
game.load.audio('fly_sound', 'assets/flap.wav');
game.load.audio('score_sound', 'assets/score.wav');
game.load.audio('hit_pipe_sound', 'assets/pipe-hit.wav');
game.load.audio('hit_ground_sound', 'assets/ouch.wav');

game.load.image('ready_text','assets/get-ready.png');
game.load.image('play_tip','assets/instructions.png');
game.load.image('game_over','assets/gameover.png');
game.load.image('score_board','assets/scoreboard.png');
}
this.create = function(){
game.state.add('menuState',menuState);
game.state.start('menuState');
}
}

6.制作游戏菜单页面

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
menuState = function(){
this.create = function(){
game.add.tileSprite(0,0,game.width,game.height,'background').autoScroll(-10,0);
game.add.tileSprite(0,game.height-112,game.width,112,'ground').autoScroll(-100,0);
var titleGroup = game.add.group();//Phaser.Group,也就是组。组相当于一个父容器
titleGroup.create(0,0,'title');
var bird = titleGroup.create(190, 10, 'bird');
bird.animations.add('fly');
bird.animations.play('fly',12,true);
titleGroup.x = 35;
titleGroup.y = 100;
game.add.tween(titleGroup).to({ y:120 },1000,null,true,0,Number.MAX_VALUE,true);
var btn = game.add.button(game.width/2,game.height/2,'btn',function(){
game.state.add('playState',playState);
game.state.start('playState');
});
btn.anchor.setTo(0.5,0.5);
}
}

TileSprite本质上还是一个sprite对象,TileSprite的贴图既可以水平移动也可以垂直移动,或者两者同时移动,我们只需要调用TileSprite对象的autoScroll(x,y)方法就可以使它的贴图动起来

7.游戏主场景

部分代码:

启用物理系统,默认是关闭的
1
game.physics.enable(object, system, debug)

object : 要开启物理系统的对象,可以是单个对象,也可以是一个包含多个对象的数组
system : 要启用的物理系统,默认为 Phaser.Physics.ARCADE,Phaser目前支持三种物理引擎,分别是Arcade ,P2 以及 Ninja。
debug : 是否开启调试

鼠标点击事件
1
2
3
var input = game.input; //当前游戏的input对象
var signal = input.onDown; //鼠标按下时的 Signal对象
signal.add(function(){}); //给Signal 绑定事件处理函数
计时器

Phaser提供Timer对象来实现

1
2
loop(delay, callback, callbackContext, arguments); //以指定的时间间隔无限重复执行某一个函数,直到调用了Timer对象的stop()方法才停止
repeat(delay, repeatCount, callback, callbackContext, arguments); //让某个函数重复执行,可以指定重复的次数

ex:

1
2
game.time.events.loop(time, function(){}, this); //利用时钟对象来重复产生管道
game.time.events.stop(false); //先让他停止,因为即使没调用start方法,它也会自动启动,这应该是一个bug

重力和速度

Phaser.Physics.Arcade.Body 对象,也就是当你是用arcade物理引擎时 sprite.body 所指向的对象,拥有很多跟物理相关的属性和方法。其中的 gravity 对象代表重力,它有x和y两个属性,分别代表水平方向和垂直方向的重力。我们可以使用它的 setTo(x,y)方法来同事设置两个方向的重力。设置了重力的物体,它的运动会受到重力的影响,与真实生活中的物理现象是一致的。然后这个body它还有一个 velocity 对象,表示物体的速度,跟重力一样,都分水平和垂直两个方向,也可以用setTo(x,y)方法来设置。一旦给物体设置了合适的速度,它便能动了

碰撞检测

在Arcade物理引擎中,碰撞检测主要用到两个函数,一个是collide,还有一个是overlap。

声音的播放

ex:

1
2
3
game.load.audio('score_sound', 'assets/score.wav');//得分的音效
this.soundScore = game.add.sound('score_sound');
this.soundScore.play(); //播放声音