Phaser是一個簡單易用且功能強大的html5游戲框架,利用它可以很輕松的開發出一個html5游戲。在這篇文章中我就教大家如何用Phaser來制作一個前段時間很火爆的游戲:Flappy Bird,希望大家看后也能做出自己的html5游戲。大家可以先點擊這里來試玩一下我已經做好的這個游戲,感受一下Phaser的游戲效果,游戲的完整代碼我已經放到github上了。支持的瀏覽器:IE9+、Firefox、Chrome、Opera、Safari以及移動端的能支持html5的瀏覽器,推薦使用谷歌瀏覽器,因為它性能最好。
phaser.js的源碼可以到它在github上的托管里去下載,游戲要用到的圖片聲音等素材資源請點擊這里下載。Phaser的使用非常簡單,只需要引入它的主文件,然后在頁面中指定一個用來放置canvas的元素,然后實例化一個 Game 對象就可以了。
復制代碼
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>game</title>
<script src="http://pic1.codesec.net/app_attach/20141022/20141022_32_50760_0.js"></script>
</head>
<body>
<div id="game"></div>
<script>
var game = new Phaser.Game(288,505,Phaser.AUTO,'game'); //實例化一個Phaser的游戲實例
</script>
</body>
</html>
復制代碼
我們來看看Phaser.Game這個函數都有哪些參數:
Phaser.Game(width, height, renderer, parent, state, transparent, antialias, physicsConfig)
width: 游戲的寬度,也就是用來渲染游戲的canvas的寬度,單位為px
height: 游戲的高度,也就是用來渲染游戲的canvas的高度,單位為px
renderer: 使用哪種渲染方式,Phaser.CANVAS 為使用html5畫布,Phaser.WEBGL 為使用性能更加好的WebGL來渲染,Phaser.AUTO為自動偵測,如果瀏覽器支持WebGL則使用WebGL,否則使用Canvas
parent: 用來放置canvas元素的父元素,可以是一個元素id,也可以是dom元素本身,phaser會自動創建一個canvas並插入到這個元素中。
state: state可以理解為場景,在這里指定state表示讓游戲首先加載這個場景,但也可以不在這里指定state,而在之后的代碼中決定首先加載哪個state。關於state我后面還會有詳細的說明。
transparent: 是否使用透明的canvas背景
antialias: 是否啟用抗鋸齒
physicsConfig: 游戲物理系統配置參數
以上所有參數都是可選的,它們的默認值以及更詳細的信息可以看這里,一般我們只需指定前面那4到5個參數就行了。
實例化Game對象后,接下來要做的就是創建游戲會用到的各種場景了,也就是上面說的state,那么怎么才能創建一個state呢?state可以是一個js自定義對象,也可以是一個函數,只要它們存在preload、create、update這三個方法中的任意一個,就是一個合法的state。
復制代碼
//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.aaa = function(){ }; //其他方法
this.bbb = 'hello'; //其他屬性
}
復制代碼
其中的preload方法,是用來加載資源的,它會最先執行。create方法是用來初始化以及構建場景的,它要等到在preload里加載的資源全部加載完成后才執行。最后update方法是更新函數,它會在游戲的每一幀都執行,以此來創造一個動態的游戲。
在這個游戲中,我們會用到4個state,我們可以通過game.state.add()方法來給游戲添加state,然后用game.state.start()方法來調用state,詳細信息請看state的文檔
復制代碼
var game = new Phaser.Game(288,505,Phaser.AUTO,'game');
game.States = {}; //創建一個對象來存放要用到的state
game.State.boot = function(){ ... } //boot場景,用來做一些游戲啟動前的准備
game.State.prelaod = function(){ ... } //prelaod場景,用來顯示資源加載進度
game.State.menu = function(){ ... } //menu場景,游戲菜單
game.State.play = function(){ ... } //play場景,正式的游戲部分
//把定義好的場景添加到游戲中
game.state.add('boot',game.States.boot);
game.state.add('preload',game.States.preload);
game.state.add('menu',game.States.menu);
game.state.add('play',game.States.play);
//調用boot場景來啟動游戲
game.state.start('boot');
復制代碼
制作資源加載進度條
游戲要用到的一些圖片、聲音等資源都需要提前加載,有時候如果資源很多,就有必要做一個資源加載進度的頁面,提高用戶等待的耐心。這里我們用一個state來實現它,命名為preload。
因為資源加載進度條需要一個進度條的背景圖片,所以在制作這個state前,我們還需要另一個最基礎的state,用來加載那張進度條圖片,我們命名為boot。
復制代碼
game.States.boot = function(){
this.preload = function(){
game.load.image('loading','assets/preloader.gif'); //加載進度條圖片資源
};
this.create = function(){
game.state.start('preload'); //加載完成后,調用preload場景
};
}
復制代碼
Phaser中資源的加載都是通過 Phaser.Loader 這個對象的方法來完成的,游戲實例的load屬性就是指向當前游戲的Loader對象,在我們這里就是game.load。Loader對象有許多方法,不同的方法可以加載不同的資源,例如加載圖片我們用的是game.load.image()方法,具體的方法列表請自行參考文檔。
在preload這個場景中,我們需要把游戲后面會用到的所有資源都進行加載,然后還要展示一個加載進度條給用戶看。Loader對象提供了一個 setPreloadSprite 方法,只要把一個sprite對象指定給這個方法,那么這個sprite對象的寬度或高度就會根據當前加載的百分比自動調整,達到一個動態的進度條的效果。
復制代碼
game.States.preload = function(){
this.preload = function(){
var preloadSprite = game.add.sprite(50,game.height/2,'loading'); //創建顯示loading進度的sprite
game.load.setPreloadSprite(preloadSprite); //用setPreloadSprite方法來實現動態進度條的效果
//以下為要加載的資源
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'); //get ready圖片
game.load.image('play_tip','assets/instructions.png'); //玩法提示圖片
game.load.image('game_over','assets/gameover.png'); //gameover圖片
game.load.image('score_board','assets/scoreboard.png'); //得分板
}
this.create = function(){
game.state.start('menu'); //當以上所有資源都加載完成后就可以進入menu游戲菜單場景了
}
}
復制代碼
上面我們提到了Sprite對象,也就是游戲開發中俗稱的精靈,同樣在Phaser中sprite對象也是制作游戲過程中用得最多的也是最重要的一個對象之一。我們可以用一幅圖片來創建一個sprite,然后用Phaser提供給我們的眾多屬性和方法來對它進行操作。上面我們是利用game.add.sprite()來創建sprite的,並且創建后會自動把它添加到當前的游戲中,game.add代表的是Phaser.GameObjectFactory對象,該對象提供了了一系列快捷方法來方便我們創建游戲的各種組件。我們這里制作的資源加載進度頁面非常簡單,大概就是下面這個樣子:
1
制作游戲菜單頁面
資源加載完成后就該進入到游戲菜單頁面了,說是菜單頁,但我們這里只是提供一個開始游戲的按鈕而已,作為教程就別搞那么復雜啦。做好后的效果如下:
menu下面我們就來實現這個頁面。
首先是背景圖與地面,我們看到這兩個東西是會動的,地面移動動的速度快一些,背景圖慢一些,在Phaser中有專門的東西來處理這種效果,叫做TileSprite,什么是TileSprite呢?TileSprite本質上還是一個sprite對象,不過這個sprite的貼圖是可以移動的,並且會自動平鋪來彌補移動后的空缺,所以我們的素材圖片要是平鋪后看不出有縫隙,就可以拿來當做TileSprite的移動貼圖了。TileSprite的貼圖既可以水平移動也可以垂直移動,或者兩者同時移動,我們只需要調用TileSprite對象的autoScroll(x,y)方法就可以使它的貼圖動起來了,其中x是水平方向的速度,y是垂直方向的速度。
復制代碼
game.States.menu = function(){
this.create = function(){
var bg = game.add.tileSprite(0,0,game.width,game.height,'background'); //當作背景的tileSprite
var ground = game.add.tileSprite(0,game.height-112,game.width,112,'ground').autoScroll(-100,0); //當作地面的tileSprite
bg.autoScroll(-10,0); //讓背景動起來
ground.autoScroll(-100,0); //讓地面動起來
}
}
復制代碼
然后來制作游戲標題,游戲標題flappy bird這幾個字是一張圖片,然后那個鳥是一個sprite,並且我們在sprite上執行了動畫,使它的翅膀看起來是在動的。我要說的是怎么在sprite對象上實現動畫。首先在加載鳥的圖片時,我們加載的不當當就是一張鳥的圖片,我們加載的是一個這樣的圖片:bird
我們看到這張圖片有三只鳥,更確切的說是一只鳥的三個狀態,或者說是動畫中的三個幀。那我們怎樣讓他變成動畫呢?在Loader對象中有一個spritesheet的方法,就是專門用來加載這種多幀圖片的,我們看一下這個方法:
spritesheet(key, url, frameWidth, frameHeight, frameMax, margin, spacing)
key : 給這張圖片指定的名稱,以后在創建sprite等對象時會要用到的
url: 圖片的地址
frameWidth : 圖片中每幀的寬度
frameHeight : 圖片中每幀的高度
frameMax : 最多有幾幀
margin : 每幀的外邊距
spacing : 每幀之間的間隔
我們上面那張鳥的圖片,每一個鳥的寬高分別是34px和24px,所以frameWidth應該是34,frameHeight是24,然后我們這個動畫有三幀,frameMax為3,幀與幀之間沒有間隙,margin與spacing都為0。實際上spritesheet方法就是能讓我們加載一個圖片,並在這個圖片上划分出幀來,以后使用這個圖片的sprite就可以用這些幀來播放動畫啦。要在sprite上實現動畫,我們首先還得先定義一個動畫,就是定義這個動畫是由哪些幀組成的。sprite對象有個animations屬性,代表的是Phaser中專門管理動畫的對象:AnimationManager,該對象有一個add方法,用來添加動畫,還有一個play方法,用來播放動畫,它們具體的參數可以參閱文檔。
下面再說一個非常重要的對象:Phaser.Group,也就是組。組相當於一個父容器,我們可以把許多對象放進一個組里,然后就可以使用組提供的方法對這些對象進行一個批量或是整體的操作。比如要使組里的對象同意進行一個位移,只需要對組進行位移就可以了,又比如要對組里的所有對象都進行碰撞檢測,那么就只需要對這個組對象進行碰撞檢測就行了。下面我們要制作的這個游戲標題是由一張文字圖片和一支鳥組成的,我們就是把這兩個東西放在一個組中,然后來進行整體的操作。
復制代碼
game.States.menu = function(){
this.create = function(){
......
var titleGroup = game.add.group(); //創建存放標題的組
titleGroup.create(0,0,'title'); //通過組的create方法創建標題圖片並添加到組里
var bird = titleGroup.create(190, 10, 'bird'); //創建bird對象並添加到組里
bird.animations.add('fly'); //給鳥添加動畫
bird.animations.play('fly',12,true); //播放動畫
titleGroup.x = 35; //調整組的水平位置
titleGroup.y = 100; //調整組的垂直位置
game.add.tween(titleGroup).to({ y:2881064151 },1000,null,true,0,Number.MAX_VALUE,true); //對這個組添加一個tween動畫,讓它不停的上下移動
}
}
復制代碼
上面代碼中的Tween對象,是專門用來實現補間動畫的。通過game.add的tween方法得到一個Tween對象,這個方法的參數是需要進行補間動畫的物體。然后我們可以使用Tween對象的to方法來實現補間動畫。
to(properties, duration, ease, autoStart, delay, repeat, yoyo)
properties : 一個js對象,里面包含着需要進行動畫的屬性,如上面代碼中的 {y:120}
duration : 補間動畫持續的時間,單位為毫秒
ease : 緩動函數,默認為勻速動畫
autoStart : 是否自動開始
delay : 動畫開始前的延遲時間,單位為毫秒
repeat : 動畫重復的次數,如果需要動畫永遠循環,則把該值設為 Number.MAX_VALUE
yoyo : 如果該值為true,則動畫會自動反轉
最后是添加一個開始游戲的按鈕。Phaser提供了Button對象讓我們能很簡單的實現一個按鈕。
復制代碼
game.States.menu = function(){
this.create = function(){
......
var btn = game.add.button(game.width/2,game.height/2,'btn',function(){//添加一個按鈕
game.state.start('play'); //點擊按鈕時跳轉到play場景
});
btn.anchor.setTo(0.5,0.5); //設置按鈕的中心點
}
}
復制代碼
Phaser中很多對象都有一個anchor屬性,它表示這個物體的中心點,物體的位置平移、旋轉的軸,都是以這個中心點為參照的。所以上面代碼中我們要使按鈕水平垂直居中,除了要把按鈕的x,y屬性分別設為游戲的寬高的一半外,還要把按鈕的中心點設為按鈕的中心。
最后我們把所有代碼合起來,得到了menu這個state的最終代碼,該state只需要一個create方法就行了:
復制代碼
game.States.menu = 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(); //創建存放標題的組
titleGroup.create(0,0,'title'); //標題
var bird = titleGroup.create(190, 10, 'bird'); //添加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.start('play');
});
btn.anchor.setTo(0.5,0.5);
}