說明
小精靈冒險 是 Learn Pixi.js 一書中最後一個案例。點選螢幕讓小精靈飛起來,小精靈上升時,會拍打翅膀,並且會有小星星產生。如果她撞到柱子上,她會爆炸成一堆小星星。幫助她通過15個柱子的間隙到達終點,介面會顯示一個巨大 Finish
標誌。
下來讓我們看看怎麼製作這個遊戲的吧!
建立滾動背景
還記得 學習 PixiJS — 視覺效果 一文中提到的視差效果嗎?小精靈冒險的背景是使用視差效果做的,也就是背景比前景移動的速度慢一些,使得背景看上去好像距離更遠。
為了製作天空背景,我們使用無縫的512 x 512的雲影象。影象是紋理貼圖集中的一幀,名為 clouds.png,如下圖所示。
在程式的 setup
函式中,使用 clouds.png 幀建立一個名為 sky
的平鋪精靈 。
sky = new TilingSprite(
id["clouds.png"],
renderer.view.width,
renderer.view.height
);
stage.addChild(sky);
複製程式碼
在遊戲迴圈中,通過減小平鋪精靈( sky
) 的 tilePosition.x
值,使其向左移動。
sky.tilePosition.x -= 1;
複製程式碼
這樣就實現了背景的無限滾動了!
建立一個柱子
遊戲中有15根柱子,想要通關,小精靈必須通過這些柱子。每5根柱子,頂部和底部之間的間隙會變得更窄。前5個柱子有四塊的間隙,接下來的5根柱子有三塊的間隙,後5根柱子有兩塊的間隙。隨著小精靈飛得更遠,遊戲也變得越來越困難。每根柱子的間隙的確切位置是隨機的,並且每次玩遊戲時都是不同的。而每個柱子與柱子間隔384畫素,下圖顯示了它們彼此相鄰時的樣子。
每根柱子的頂部和底部之間的間隙逐漸變窄,你可以看到間隙從左側的四塊空間逐漸變窄到右側的兩塊空間。
構成柱子的所有塊都在一個名為 blocks
的容器中。
blocks = new Container();
stage.addChild(blocks);
複製程式碼
建立15根柱子需要兩層 for 迴圈,外層迴圈執行15次,一次建立一根柱子。內層迴圈執行8次,每次都會判斷是否要在柱子上加入1個綠色塊。不過只有在不是隨機間隙範圍內時,才會新增綠色塊。外層迴圈每執行5次,柱子與柱子的間隙的大小就會縮小1。
//柱子之間的初始間隙
let gapSize = 4;
//柱子的數量
let numberOfPillars = 15;
//迴圈15次,形成15根柱子
for (let i = 0; i < numberOfPillars; i++) {
//隨機確定單個柱子的間隙
let startGapNumber = randomInt(0, 8 - gapSize);
//每隔五根柱子就減少一個間隙
if (i > 0 && i % 5 === 0) gapSize -= 1;
//如果不在柱子的間隙內,就建立一個塊放入柱子
for (let j = 0; j < 8; j++) {
if (j < startGapNumber || j > startGapNumber + gapSize - 1) {
let block = u.sprite(id["greenBlock.png"]);
blocks.addChild(block);
//每根柱子之間間隔384畫素,第一根柱子的x位置為512
block.x = (i * 384) + 512;
block.y = j * 64;
}
}
//建立柱子之後,在新增finish影象
if (i === numberOfPillars - 1) {
finish = u.sprite(id["finish.png"]);
blocks.addChild(finish);
finish.x = (i * 384) + 896;
finish.y = 192;
}
}
複製程式碼
程式碼的最後一部分將 finish
精靈新增到 blocks
容器中,blocks
容器最後會新增到舞臺上,小精靈如果能堅持到最後,就能看見它。
如果 finish
精靈位於螢幕外時,每次遊戲迴圈在play
函式中都會將 blocks
容器向左移動2畫素。
if (finish.getGlobalPosition().x > 256) {
blocks.x -= 2;
}
複製程式碼
當 finish
精靈滾動到畫布的中心時, blocks
容器將停止移動。要注意,程式碼使用 finish
精靈的全域性座標的 x
位置來檢測它是否在畫布區域內。因為全域性座標是相對於畫布而不是父容器,所以它們對於在畫布上找到巢狀精靈的位置非常有用。
製作會飛的小精靈
在 學習 PixiJS — 動畫精靈 一文中提到了怎麼製作動畫精靈。
小精靈角色就是使用紋理貼圖集中的3個幀製作的動畫精靈。每個幀都是小精靈拍打翅膀動畫中的一個影象。
以下是 setup
函式中建立小精靈角色的程式碼。
let pixieFrames = [
id["0.png"],
id["1.png"],
id["2.png"]
];
pixie = u.sprite(pixieFrames);
stage.addChild(pixie);
pixie.fps = 24;
pixie.position.set(232, 32);
pixie.vy = 0;
pixie.oldVy = 0;
複製程式碼
你可以看到前面的程式碼使用了 SpriteUtilities
庫中的 sprite
方法。這個方法可以簡化建立精靈的步驟。
小精靈有了一個新的屬性,叫做 oldVy
, 它用來幫助我們計算小精靈的垂直速度(vy)。
在 play
函式中,小精靈的垂直速度(vy)在每幀上都會減去0.05
,使小精靈下落。
pixie.vy -= 0.05;
pixie.y -= pixie.vy;
複製程式碼
玩家可以通過點選畫布上的任何位置來讓小精靈飛行。這是通過為指標物件指定 tap
方法來完成的,指標物件在 學習 PixiJS — 互動工具 這篇文章中已經講的很清楚了。每次點選都會使小精靈的垂直速度(vy)增加1.5
,將她向上推。以下是 setup
函式中的程式碼,它生成指標物件並指定 tap
方法。
pointer = t.makePointer();
pointer.tap = () => {
pixie.vy += 1.5;
};
複製程式碼
產生五彩的小星星
產生的小星星就是在 學習 PixiJS — 粒子效果 一文只提到的粒子。
當小精靈拍打翅膀時,會產生一些五彩的小星星。小星星的產生會約束在2.4到3.6之間的角度,因此它們會被髮射到小精靈左側的錐形內,如下圖所示。
產生的小星星可能是紫色,粉紅色,綠色,或黃色,每個小星星都是單獨的一個幀。
正如 學習 PixiJS — 粒子效果 一文中使用的 粒子效果庫(Dust
),有一個 create
方法,如果一個精靈包含多個幀,它將在精靈上隨機顯示一個幀。使用這個方法,首先要定義要用於製作粒子的紋理圖集幀陣列。
dustFrames = [
id["pink.png"],
id["yellow.png"],
id["green.png"],
id["violet.png"]
];
複製程式碼
接下來,將這個陣列作為引數傳給 create
方法,然後再把 create
方法當做引數傳給粒子發射器方法(emitter
),以下是關鍵程式碼:
//建立一個Dust例項
d = new Dust(PIXI);
//建立粒子發射器
particleStream = d.emitter(
300, //時間間隔
() => d.create(
pixie.x + 8, //x 座標
pixie.y + pixie.height / 2, //y 座標
() => u.sprite(dustFrames), //粒子精靈
stage, //父容器
3, //粒子數
0, //重力
true, //隨機間隔
2.4, 3.6, //最小,最大角度
18, 24, //最小,最大尺寸
2, 3, //最小,最大速度
0.005, 0.01, //最小,最大比例速度
0.005, 0.01, //最小,最大alpha速度
0.05, 0.1 //最小,最大旋轉速度
)
);
複製程式碼
現在就有一個名為 particleStream
的粒子發射器。只需呼叫其 play
方法就可以開始發射粒子,產生小星星了。
particleStream.play();
複製程式碼
判斷小精靈是上升還是下降
當小精靈上升時,她會拍打翅膀,產生五彩的小星星。當她下落時,她停止拍打翅膀,並且停止產生小星星,但我們怎麼知道她是向上還是向下飛行呢?
我們必須找到當前幀和前一幀之間的速度差異。如果她當前的速度大於她以前的速度,她就會上升。如果小於,並且當前速度小於零,那麼她就會下落。程式碼將當前幀中的小精靈的 vy
值儲存在 oldVy
屬性中。在下一次遊戲迴圈時,通過比較這兩個屬性就可以知道是上升還是下落了。以下是關鍵程式碼:
//如果她上升,則拍打翅膀併產生五彩的小星星
if (pixie.vy > pixie.oldVy) {
if (!pixie.animating) {
pixie.playAnimation();
if (pixie.visible && !particleStream.playing) {
particleStream.play();
}
}
}
//如果她往下落,停止拍打翅膀,展示第一幀,並停止產生五彩的小星星
if (pixie.vy < 0 && pixie.oldVy > 0) {
if (pixie.animating) pixie.stopAnimation();
pixie.show(0);
if (particleStream.playing) particleStream.stop();
}
//儲存小精靈的當前vy值,以便我們可以在下一幀中使用它來確定小精靈是否改變了方向
pixie.oldVy = pixie.vy;
複製程式碼
小精靈與柱子發生碰撞
當小精靈撞到柱子時,她會爆炸成一堆小星星,如下圖所示。
實現這個效果需要使用 學習 PixiJS — 碰撞檢測 一文中提到的 Bump
庫中的 hitTestRectangle
方法。在程式碼中遍歷 blocks.children
陣列,檢測每個塊和小精靈之間的碰撞。如果 hitTestRectangle
方法返回 true
,則退出迴圈,表示小精靈碰撞到柱子了。
//小精靈碰撞到柱子時,pixieVsBlock 為 true
let pixieVsBlock = blocks.children.some(block => {
return b.hitTestRectangle(pixie, block, true);
});
複製程式碼
使用 some
迴圈,一旦找到一個等於 true
的值,迴圈就會退出,這樣可以避免多餘的檢測。
小精靈是 舞臺(stage
) 的子級,但每個 block
都是 blocks
容器的子級,這意味著它們不使用相同的區域性座標。所以 hitTestRectangle
方法的第三個引數必須為 true
,以便強制 hitTestRectangle
方法使用全域性座標進行碰撞檢測。
如果 pixieVsBlock
為 true
,並且當前小精靈可見,則執行小精靈爆炸成一堆小星星的程式碼。它使小精靈變的不可見,併產生粒子爆炸效果,而且在延遲3秒後呼叫遊戲的 reset
函式,重置遊戲。以下是在 play
函式中的程式碼:
if (pixieVsBlock && pixie.visible) {
//讓小精靈變得不可見
pixie.visible = false;
//製作爆炸小星星效果
d.create(
pixie.centerX, pixie.centerY, //x 和 y 座標
() => u.sprite(dustFrames), //粒子精靈
stage, //父容器
20, //粒子數
0, //重力
false, //隨機間隔
0, 6.28, //最小角度,最大角度
16, 32, //最小尺寸,最大尺寸
1, 3 //最小速度,最大速度
);
//停止粒子發射器
particleStream.stop();
//等待3秒,然後重置遊戲
wait(3000).then(() => reset());
}
複製程式碼
學習 PixiJS — 補間動畫 這篇文章中介紹了 wait
函式 。
重置遊戲
如果小精靈碰撞到柱子,則在3秒鐘延遲後重置遊戲。遊戲的 reset
函式通過將小精靈和塊重新定位到其初始位置,並使小精靈再次可見來實現此功能。
function reset() {
pixie.visible = true;
pixie.y = 32;
particleStream.play();
blocks.x = 0;
}
複製程式碼
總結
以上就是如何實現 小精靈冒險 這個遊戲的全部了,遊戲中需要的各種東西,在前面幾篇文章中都有提到。如果遇到什麼不明白的東西,可以看看前面的幾篇文章。 而這個小遊戲還有許多小細節可以去實現,比如增加玩家的分數,增加開始遊戲的按鈕,增加一些場景過渡,增加音效 等等,這些你都可以嘗試自己實現下。