游戲要用到的一些圖片、聲音等資源都需要提前加載,有時候如果資源很多,就有必要做一個資源加載進度的頁面,提高用戶等待的耐心。這里我們用一個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:120 },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/2881064151,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);
}
