引言
上篇主要講述了 pixi 平鋪精靈的創(chuàng)建與使用及視差滾動,這篇主要講述的是基于上篇 demo 的優(yōu)化與新增的功能笼痹。
目錄
-
新增功能
1.1 分數(shù)&&速度
1.2 上方障礙
1.3 碰撞檢測
1.4 二段跳
1.5 血量
1.6 配樂
setup代碼
總結(jié)
了解更多
1负懦、新增功能
1.1 分數(shù)&&速度
分數(shù)這里是偷了個小懶陪捷,這里是直接在 ticker
中做了個累加 1 的操作踩窖,想的是通過分數(shù)達到多少分,游戲整體速度會提升一個階段帜羊,當然也可以是避開障礙物獲得分數(shù)咒程,后續(xù)如果有金幣之類的可以做進一步的優(yōu)化。
app.ticker.add(() => {
// 累加得分
$('.score-ctn').text(`分數(shù):${score += 1}`);
// 提升速度
score > 10000 && score < 20000 && (speed = 11);
score > 20000 && score < 30000 && (speed = 12);
score > 30000 && score < 40000 && (speed = 13);
score > 40000 && (speed = 14);
// 無限滾動
bgSpr.tilePosition.x -= 1;
bgSpr.tilePosition.x %= PIXI.Loader.shared.resources['bg2_2'].texture.width;
// 碰撞
bump(role, monster) && (roleSmoothie.update = inverted.bind(this, 3));
});
1.2 上方障礙
上方障礙跟怪物是一樣的讼育,只不過位置不同帐姻,在做碰撞檢測的時候改變一下碰撞的反饋。setup() 新增代碼:
let column = new PIXI.Sprite(PIXI.Loader.shared.resources['column'].texture);
let columnSmoothie = null; // 上方障礙移動
column.position.set(7700, 0);
app.stage.addChild(column);
// 前景移動
columnSmoothie = new Smoothie({
engine: PIXI,
renderer: app.renderer,
root: app.stage,
update: translate.bind(this, column, speed)
});
columnSmoothie.start();
1.3 碰撞檢測
碰撞檢測這里與碰撞到陸地障礙物反饋不同窥淆,當碰撞到陸地障礙是直接扣除一點生命值卖宠,在碰撞到上方障礙物則不會直接扣除生命值,而是會隨著上方障礙物的移動把人物推出屏幕才會扣除生命值忧饭,這里與碰撞到陸地障礙不同的是上方障礙物不能直接穿過扛伍,后者可以穿過,但會直接扣除生命值词裤。
這里的碰撞檢測思路也比較簡單刺洒,陸地障礙物只要人物的 x,y 軸位置與障礙物的 x,y 軸相交則返回 true 相交的點需要注意的是現(xiàn)在精靈的中心點在正下方在做碰撞檢測點時候需要加上精靈的 width / 2
, 在做跳躍碰撞檢測的時候障礙物的 y 值則需要減去高度。 如果 x 軸減去障礙物 x 軸 大于等于 障礙物負的寬度的一半并且小于障礙物寬度的一半并且 y 值減去障礙物的 y 值減去障礙物的高度大于等于 20 則返回 true 吼砂。
由于精靈貼圖都是矩形的逆航,在跳躍的過程中可能會碰到貼圖多余的直角出現(xiàn)誤判,這里直接粗暴的減去了 20 按理來說大于等于 0 就算觸碰了渔肩。也可以做圓形碰撞檢測可以避免直角誤判因俐。
// 陸地障礙
function bottombump (spr1, spr2) {
let spr1X = spr1.x + spr1.width / 2;
let spr2Y = spr2.y - spr2.height;
return spr1X - spr2.x <= spr2.width / 2 && spr1X - spr2.x >= -spr2.width / 2 && spr1.y - spr2Y >= 20? true: false;
}
// 上方障礙
function topbump (spr1, spr2) {
return spr1.x - spr2.x < 30 && spr1.x - spr2.x > -30 && (spr1.y - spr1.height) - (spr2.y + spr2.height) <= 0? true: false;
}
效果預(yù)覽:
1.4 二段跳
二段跳是在一段跳的起跳完成且下落幀前再次按跳躍鍵觸發(fā),其余期間無法觸發(fā)周偎,跳躍動畫配置在最上方定義好了
一段跳思路是預(yù)先定義好一個動畫幀下標從 0 開始抹剩,起跳完成至最高點是 4 幀圖片。判斷下標小于 4 并且起跳的高度不超過一定值情況下下標累加蓉坎,人物 y 軸遞減 在下標大于 4 時做 y 值遞增下落澳眷,在最后一幀時將下標復(fù)原至 0 切換奔跑狀態(tài)。
二段跳需要兩個變量做判斷蛉艾,一個用于記錄點擊跳躍次數(shù)钳踊,一個用于判斷是否二段跳,連續(xù)兩次跳躍人物剛好在一段跳下落前勿侯,這里設(shè)定第二段跳的高度要比一段跳的高度低拓瞪,在完成二段跳動作之后人物處于至高點,然后接入一段跳的第 5 幀動畫繼續(xù)做一段跳的下落動作助琐。
let roleSprJumpIndex = 0吴藻; // 一段跳動畫下標
// 跳躍
$('.jump').on('touchstart', (e) => {
if (jumpNum < 2) { // 禁止多次跳躍
if (!isJump) {
roleSmoothie.update = jump.bind(this); // 一段跳
isJump = true;
} else {
roleSmoothie.update = jump2.bind(this); // 二段跳
isJump = false;
}
}
jumpNum++;
})
// 一段跳
function jump () {
role.texture = PIXI.Loader.shared.resources[config.jump[roleSprJumpIndex]].texture;
// 起跳
if (roleSprJumpIndex <= 4 && role.position.y > 285) {
roleSprJumpIndex++;
role.position.y -= 40;
isAction = false;
}
// 下落
if (roleSprJumpIndex > 4 ) {
if (role.position.y < 465) {
role.position.y += 35;
}
roleSprJumpIndex++;
isAction = true;
}
// 落地
if (roleSprJumpIndex > 8) {
roleSprJumpIndex = 0;
role.position.y = 465;
isAction = true;
jumpNum = 0; // 重置跳躍次數(shù)
isJump = false;
roleSmoothie.update = go.bind(this); // 奔跑狀態(tài)
}
}
// 二段跳
function jump2(){
role.texture = PIXI.Loader.shared.resources[config.jump2[roleSprJumpIndex2]].texture;
if (roleSprJumpIndex2 < 6) {
roleSprJumpIndex2++;
role.position.y -= 15;
} else {
roleSprJumpIndex = 5;
roleSprJumpIndex2 = 0;
roleSmoothie.update = jump.bind(this);
}
}
效果圖:
<font id="catalog1_5">1.5 血量</font>
血量在每次碰撞是扣除一點,ticker 碰撞反饋新增代碼:
這里在被上方障礙物推出屏幕之后會被扣除一點血弓柱,人物直接復(fù)原到原來的位置沟堡,如果在推出屏幕前下滑躲過上方障礙物則人物 x 軸慢慢遞增到原來位置侧但。
// 陸地障礙物反饋
if (isBottomBump || isBottomBump2) {
role.tint = 0xFFFF660;
if (isBlood && bloodNum >= 0) {
bloodArr[bloodNum].style.display = 'none';
bloodNum--;
isBlood = false;
}
} else {
role.tint = 0xFFFFFF;
isBlood = true;
}
// 上方障礙物反饋
if (topBupm) {
role.x = column.x - 30;
if (role.x < -100) {
bloodArr[bloodNum].style.display = 'none';
role.x = 400;
bloodNum--;
}
}
// 遞增到原來位置
if (role.x < 400) {
role.x = role.x + 1;
}
效果圖:
1.6 配樂
網(wǎng)上找了兩個音效,一個背景音樂航罗,一個按鍵音效禀横,加上音效讓能體驗不那么平淡。
這里是用了一個 audio 的插件 howler.js 使用非常簡單:
// 背景音樂
let bgBgm = new Howl({
src: './demo_bg.mp3',
loop: true // 是否循環(huán)
});
bgBgm.play(); // 播放
// 按鈕音效
let btnBgm = new Howl({
src: './demo2.mp3',
loop: false
});
在按鈕事件觸發(fā)播放即可粥血。
2柏锄、setup 代碼
function setup () {
// 背景
let bgSpr = new PIXI.extras.TilingSprite(PIXI.Loader.shared.resources['bg'].texture, app.renderer.width, app.renderer.height);
// 前景
let bridge = new PIXI.extras.TilingSprite(PIXI.Loader.shared.resources['bridge'].texture, 1600, 437);
// 人物
let role = new PIXI.Sprite(PIXI.Loader.shared.resources['role'].texture);
// 怪物
let monster = new PIXI.Sprite(PIXI.Loader.shared.resources['monster'].texture);
let monster2 = new PIXI.Sprite(PIXI.Loader.shared.resources['monster2'].texture);
// 上方障礙物
let column = new PIXI.Sprite(PIXI.Loader.shared.resources['column'].texture);
let roleSmoothie = null; // 人物動畫
let monsterSmoothie = null; // 怪物動畫
let monster2Smoothie = null;
let bridgeSmoothie = null; // 前景動畫
let columnSmoothie = null; // 上方障礙物動畫
let roleSprGoIndex = 0; // 走路動作圖片下標
let roleSprRunIndex = 0; // 跑動作圖片下標
let roleSprJumpIndex = 0; // 跳動作圖片下標
let roleSprJumpIndex2 = 0;
let roleSprInverIndex = 0; // 倒動作圖片下標
let speed = 10; // 滾動速度
let jumpNum = 0; // 跳躍次數(shù)
let isJump = false; // 跳躍狀態(tài)
let isBlood = true;
let bloodNum = 2; // 血量下標
let bloodArr = $('.blood-ctn').children(); // 血量數(shù)組
let score = 0; // 分數(shù)
let isAction = true; // 動作狀態(tài)
// 背景音樂
let bgBgm = new Howl({
src: './demo_bg.mp3',
loop: true // 是否循環(huán)
});
bgBgm.play(); // 播放
// 按鈕音效
let btnBgm = new Howl({
src: './demo2.mp3',
loop: false
});
// 中心點
role.anchor.set(0.5, 1);
monster.anchor.set(0.5, 1);
monster2.anchor.set(0.5, 1);
// 縮放比例
role.scale.set(1.5, 1.5);
monster2.scale.set(0.9, 0.9);
// 位置
role.position.set(400, 465);
monster.position.set(2000, 465);
monster2.position.set(3200, 465);
bridge.position.set(0, 440);
column.position.set(7700, 0);
// 添加到舞臺
app.stage.addChild(bgSpr, bridge, role, monster, monster2, column);
// 平移
function translate (spr, num) {
spr.tilePosition.x -= num;
spr.tilePosition.x %= PIXI.Loader.shared.resources['prospect'].texture.width;
};
// 怪物移動
function monsterTranslate (spr, num, x) {
spr.position.x -= num;
spr.position.x < -x && (spr.position.x = 1600);
};
// 跑
function go () {
role.texture = PIXI.Loader.shared.resources[config.go[roleSprGoIndex]].texture;
roleSprGoIndex < 6? roleSprGoIndex++ : roleSprGoIndex = 0;
};
// 跳
function jump () {
role.texture = PIXI.Loader.shared.resources[config.jump[roleSprJumpIndex]].texture;
if (roleSprJumpIndex <= 4 && role.position.y > 285) {
roleSprJumpIndex++;
role.position.y -= 40;
isAction = false;
}
if (roleSprJumpIndex > 4 ) {
if (role.position.y < 465) {
role.position.y += 35;
}
roleSprJumpIndex++;
isAction = true;
}
if (roleSprJumpIndex > 8) {
roleSprJumpIndex = 0;
role.position.y = 465;
isAction = true;
jumpNum = 0;
isJump = false;
roleSmoothie.update = go.bind(this);
}
}
// 二段跳
function jump2(){
role.texture = PIXI.Loader.shared.resources[config.jump2[roleSprJumpIndex2]].texture;
if (roleSprJumpIndex2 < 6) {
roleSprJumpIndex2++;
role.position.y -= 15;
} else {
roleSprJumpIndex = 5;
roleSprJumpIndex2 = 0;
roleSmoothie.update = jump.bind(this);
}
}
// 滑
function slip (num) {
role.texture = PIXI.Loader.shared.resources['sprite4_0'].texture;
}
// 人物移動
roleSmoothie = new Smoothie({
engine: PIXI,
renderer: app.renderer,
root: app.stage,
fps: 8,
update: go.bind(this)
});
roleSmoothie.start();
// 怪物移動
monsterSmoothie = new Smoothie({
engine: PIXI,
renderer: app.renderer,
root: app.stage,
update: monsterTranslate.bind(this, monster, 7, 100)
});
monsterSmoothie.start();
// 前景移動
prospectSmoothie = new Smoothie({
engine: PIXI,
renderer: app.renderer,
root: app.stage,
update: translate.bind(this, prospectSpr, 3)
});
prospectSmoothie.start();
// 柱子移動
columnSmoothie = new Smoothie({
engine: PIXI,
renderer: app.renderer,
root: app.stage,
update: monsterTranslate.bind(this, column, speed)
});
columnSmoothie.start();
// 跳躍事件
$('.jump').on('touchstart', (e) => {
if (jumpNum < 2) {
if (!isJump) {
roleSmoothie.update = jump.bind(this);
isJump = true;
} else {
roleSmoothie.update = jump2.bind(this);
isJump = false;
}
}
jumpNum++;
btnBgm.play();
});
// 下滑事件
$('.slip').on('touchstart', (e) => {
roleSmoothie.update = slip.bind(this);
roleSprJumpIndex = 0;
roleSprJumpIndex2 = 0;
jumpNum = 0;
isJump = false;
btnBgm.play();
});
// 下滑結(jié)束
$('.slip').on('touchend', (e) => {
roleSmoothie.update = go.bind(this);
});
app.ticker.add(() => {
$('.score-ctn').text(`分數(shù):${score += 1}`);
// 提升速度
score > 10000 && score < 20000 && (speed = 11);
score > 20000 && score < 30000 && (speed = 12);
score > 30000 && score < 40000 && (speed = 13);
score > 40000 && (speed = 14);
// 無限滾動
bgSpr.tilePosition.x -= 1;
bgSpr.tilePosition.x %= PIXI.Loader.shared.resources['bg2_2'].texture.width;
isBottomBump = bottombump(role, monster);
isBottomBump2 = bottombump(role, monster2);
topBupm = topbump(role, column);
// 怪物碰撞
if (isBottomBump || isBottomBump2) {
role.tint = 0xFFFF660;
if (isBlood && bloodNum >= 0) {
bloodArr[bloodNum].style.display = 'none';
bloodNum--;
isBlood = false;
}
} else {
role.tint = 0xFFFFFF;
isBlood = true;
}
// 柱子碰撞
if (topBupm) {
role.x = column.x - 30;
if (role.x < -100) {
bloodArr[bloodNum].style.display = 'none';
role.x = 400;
bloodNum--;
}
}
if (role.x < 400) {
role.x = role.x + 1;
}
})
});
// 怪物碰撞
function bottombump (spr1, spr2) {
let spr1X = spr1.x + spr1.width / 2;
let spr2Y = spr2.y - spr2.height;
return spr1X - spr2.x <= spr2.width / 2 && spr1X - spr2.x >= -spr2.width / 2 && spr1.y - spr2Y >= 20? true: false;
}
// 柱子碰撞
function topbump (spr1, spr2) {
return spr1.x - spr2.x < 30 && spr1.x - spr2.x > -30 && (spr1.y - spr1.height) - (spr2.y + spr2.height) <= 0? true: false;
}
項目鏈接:demo
3、總結(jié)
這個 demo 目前看來雖然沒什么比較難的點复亏,但目前還只是屬于能跑起來趾娃,比較粗糙,很多細節(jié)還是需要優(yōu)化的缔御,當然再繼續(xù)往下做肯定會有挑戰(zhàn)的抬闷,比如目前怪物出現(xiàn)都是定死的,需要改成隨機出現(xiàn)耕突,并且柱子與怪物不能再一個 Y 軸上,還有新增像金幣眷茁、飛行、斷橋等一些進階的功能上祈。
4、了解更多
原文鏈接:pixi 平鋪精靈 demo (二)