iT邦幫忙

第 12 屆 iThome 鐵人賽

0
自我挑戰組

30天找回寫程式手感計劃!!!系列 第 32

Day32 - 「登愣登愣,登愣登登登」~ 隱挑戰 Day8

  • 分享至 

  • xImage
  •  

覺得自己能做到和不能做到,其實只在一念之間。開始寫就距離完成不遠了。
https://ithelp.ithome.com.tw/upload/images/20201008/201298736AMHpu3nbL.png

今天的勵志小語送給自己,
其實在鐵人賽開始前,
小的也是一直擔心,
不曉得自己能不能辦到自己訂的目標,
但一旦開始了其實離終點就會越來越近,
其實就算那天只踏出一小步也算是踏出去了。

Day30 後的標題小的就不再做改變了,
畢竟鐵人賽完賽後剩下的就是小的自己的挑戰了。

正片開始

今日課題:選擇招式後,依照所選招式進行攻擊及法力消耗

昨天進度到顯示各角色的招式清單,
但是還沒把選擇做出來,
所以現在先把招式下面的箭頭做出來吧!
https://ithelp.ithome.com.tw/upload/images/20201008/20129873xiA4Rdf1Bh.png

招式下面的箭頭顯示

這邊我一樣先在 html 寫靜態資料,
再調整 CSS,再改到 JavaScript 做動態顯示。

html:

<div class="skill_menu">
    <div class="magiccost_show">
        5/<span class="total_magic">240</span>
    </div>
    <ul class="skill_list">
        <li value="0">
            <div class="skill_name active">風咒</div>
            <div class="flag">▲</div>
        </li>
        <li value="1">
            <div class="skill_name">雷咒</div>
            <div class="flag">▲</div>
        </li>
        <li value="2">
            <div class="skill_name">炎咒</div>
            <div class="flag">▲</div>
        </li>
        <li value="3">
            <div class="skill_name">風咒</div>
            <div class="flag">▲</div>
        </li>
        <li value="4">
            <div class="skill_name">雷咒</div>
            <div class="flag">▲</div>
        </li>
        <li value="5">
            <div class="skill_name">炎咒</div>
            <div class="flag">▲</div>
        </li>
    </ul>
</div>

CSS:

.skill_list{
    display: flex;
    flex-wrap: wrap;
    padding: 0 60px;
    padding-top: 40px;
    li{
        position: relative;
        display: flex;
        flex-direction: column;
        align-items: center;

        width: 31.33333%;
        margin: 20px 1%;

        .skill_name{
            color: #c2b6a5;
            font-size: 44px;
            font-family: "新細明體";
            font-weight: bold;
            text-shadow: 4px 4px #000000; // 文字陰影效果 (X偏移 Y偏移 顏色)
            text-align: center;

            &.active{
                animation-iteration-count: infinite; // 定義動畫重複的次數。你可以用 infinite 來讓動畫永遠重複播放。
                animation-direction: alternate; // 定義是否動畫播放完畢後將會反向播放。
                // alternate:正反轉輪流播放,奇數次為 0% 到 100%,偶數次為 100% 到 0%,若動畫播放次數只有一次就只會正常播放。
                animation-name: skillnameBlink; // 定義關鍵影格 @keyframes 的名字。
                animation-duration: 0.3s; // 定義動畫完成一次週期的時間。
            }
        }
        .flag{
            display: none;
            position: absolute;
            bottom: -10px;
            color: #ffffff;
            font-size: 24px;
        }
    }
}

@keyframes skillnameBlink{
    from {color: #c2b6a5;}
    to {color: #d3aa66;}
}

再來 JavaScript 的部份是,
在顯示招式(skillShow)時,在 li 埋 value(為該角色招式的第幾個),
方便之後比對 currentSkill 與 li value。
而現在選到的招式要做箭頭及閃爍顯示(setSkillActiveClass),
這邊比對 .skill_list li value 及 currentSkill,
一致的話表示為所選的招式,
就要在招式名字加上閃爍樣式(.active),
且箭頭(.flag) 要顯示出來;
反之則招式名字移除閃爍樣式(.active),
且箭頭(.flag) 要隱藏起來。

function skillShow(){
    console.log("顯示招式");
    currentSkill = 0; // 顯示招式預設第1個顯示為現在所選
    let skillHTMLStr = "";
    skillHTMLStr = `<div class="magiccost_show">
    ${roleStatus[currentActor].Moves[currentSkill].Cost}/<span class="total_magic">
    ${roleStatus[currentActor].MagicPoint[0]}</span>
    </div>`;
    skillHTMLStr += `<ul class="skill_list">`;
    for ( let i=0; i<roleStatus[currentActor].Moves.length; i++ ){
        skillHTMLStr += `<li value="${i}">
        <div class="skill_name">${roleStatus[currentActor].Moves[i].MoveName}</div>
        <div class="flag">▲</div>
        </li>`;
    }
    skillHTMLStr += `</ul>`;
    skillMenuFlagElement.innerHTML = skillHTMLStr;
    setSkillActiveClass(); // 現在選到的招式要做箭頭及閃爍顯示
    window.addEventListener("keydown", skillSelect);
}

function skillSelect(event){
    console.log("選擇招式");
    switch(event.keyCode){
        case 27: // ESC鍵
            skillMenuFlagElement.style = "display:none";
            window.removeEventListener("keydown", skillSelect); // 移除招式選擇的監聽
            window.addEventListener("keydown", optionSelect); // 回到原本動作選擇的監聽
            break;
        case 37: // 左鍵
            if ( currentSkill > 0 ){
                currentSkill -= 1;
            }
            setSkillActiveClass(); // 現在選到的招式要做箭頭及閃爍顯示
            break;
        case 39: // 右鍵
            if ( currentSkill < roleStatus[currentActor].Moves.length-1 ){
                currentSkill += 1;
            }
            setSkillActiveClass(); // 現在選到的招式要做箭頭及閃爍顯示
            break;
        default:
            console.log("default");
            break;
    }
}

function setSkillActiveClass(){
    const skillMagicCostElement = document.querySelector(".skill_menu .magiccost_show");
    const skillListOptionElement = document.querySelectorAll(".skill_list li");

    for ( let i=0; i<skillListOptionElement.length; i++){
        // skillListOptionElement[i].childNodes[1] 為招式名字
        // skillListOptionElement[i].childNodes[3].style 為箭頭
        if ( skillListOptionElement[i].value === currentSkill ){
            skillListOptionElement[i].childNodes[1].setAttribute("class","skill_name active");
            skillListOptionElement[i].childNodes[3].style = "display:block";
        } else{
            skillListOptionElement[i].childNodes[1].setAttribute("class","skill_name");
            skillListOptionElement[i].childNodes[3].style = "display:none";
        }

    }
 
    // 更新現在選擇招式所消耗法力
    let skillMagicCostHTMLStr = "";
    skillMagicCostHTMLStr = `
    ${roleStatus[currentActor].Moves[currentSkill].Cost}/<span class="total_magic">
    ${roleStatus[currentActor].MagicPoint[0]}</span>
    `;
    skillMagicCostElement.innerHTML = skillMagicCostHTMLStr;
}

讓我們看一下目前效果:
https://ithelp.ithome.com.tw/upload/images/20201008/20129873jkg3J9jdwf.png
https://ithelp.ithome.com.tw/upload/images/20201008/20129873a9E5AU9W8s.png
非常好,接下來就是要將選擇招式存下來,
然後依據這個進行攻擊。

將選擇招式儲存下來,依照所選招式進行攻擊及法力消耗

  1. 先在原本招式選擇增加按下 ENTER 鍵的動作,
    要將角色所選擇的招式儲存起來(roleStatus[currentActor].Action.SkillSelection),
    之後 currentActor+1,
    移除招式選擇的監聽,
    再回到角色動作選擇 roleActionSelect。
function skillSelect(event){
    console.log("選擇招式");
    switch(event.keyCode){
        ... 前略 ...
        case 13: // ENTER鍵
            roleStatus[currentActor].Action.SkillSelection = currentSkill;
            currentActor ++;

            skillMenuFlagElement.style = "display:none";
            window.removeEventListener("keydown", skillSelect); // 移除招式選擇的監聽
            roleActionSelect();
            break;
        default:
            console.log("default");
            break;
    }
}
  1. 再來在角色執行動作的地方增加角色選擇 skill 的動作,
    攻擊量 = 招式法力消耗x角色法攻X0.1 - 怪的防禦力。
    (PS. 這邊我在角色增加了法攻的屬性 MagicAttackPower,
    將普攻跟法攻的攻擊量分開來,
    不然李逍遙明明是普攻仔,
    法攻卻很強怪怪的XD)
function roleActionExecute(){
    console.log(`${new Date()}`);
    console.log(`開始執行動作,現在輪到 ${roleStatus[currentActor].Name}`);
    console.log(`${roleStatus[currentActor].Name} 選擇的動作是 ${roleStatus[currentActor].Action.OptionSelection}`);
    
    if ( currentActor < 2 ){
        let attackQty;
        switch(roleStatus[currentActor].Action.OptionSelection){
            case "attack": // 動作為攻擊
                attackQty = roleStatus[currentActor].AttackPower - roleStatus[2].DefensePower;
                break;
            case "skill": // 動作為招式
                let skillSelection = roleStatus[currentActor].Action.SkillSelection;
                attackQty = roleStatus[currentActor].Moves[skillSelection].Cost*roleStatus[currentActor].MagicAttackPower*0.5 - roleStatus[2].DefensePower;
                // 法力消耗
                roleStatus[currentActor].MagicPoint[0] -= roleStatus[currentActor].Moves[skillSelection].Cost;
                break;
            default:
                console.log("default");
                break;
        }
        attackQty = parseInt(attackQty);
        roleStatus[2].HealthPoint[0] -= attackQty;
        monsterBloodMinusElement.textContent = `-${attackQty}`;
        console.log(`現在 ${roleStatus[2].Name} 的血量為 ${roleStatus[2].HealthPoint[0]}`);
        
        // 重讀角色血量&法力
        roleStatusRefresh();
        
        ... 後略 ...
}

然後因為有多個地方用到重讀角色血量跟法力,
所以我將重讀角色血量跟法力弄成函數,
方便重複呼叫。

function roleStatusRefresh(){
    // 李逍遙 HP MP 顯示
    boyStatusElement.innerHTML = `<p class="HP">${roleStatus[0].HealthPoint[0]}/${roleStatus[0].HealthPoint[1]}</p>`;
    boyStatusElement.innerHTML += `<p class="MP">${roleStatus[0].MagicPoint[0]}/${roleStatus[0].MagicPoint[1]}</p>`;
    
    // 趙靈兒 HP MP 顯示
    girlStatusElement.innerHTML = `<p class="HP">${roleStatus[1].HealthPoint[0]}/${roleStatus[1].HealthPoint[1]}</p>`;
    girlStatusElement.innerHTML += `<p class="MP">${roleStatus[1].MagicPoint[0]}/${roleStatus[1].MagicPoint[1]}</p>`;
    
}

好,讓我們看看目前成果吧!

李逍遙選擇御劍術:
https://ithelp.ithome.com.tw/upload/images/20201008/20129873Ei2MnbSTBI.png
李逍遙御劍術攻擊,法力有消耗:
https://ithelp.ithome.com.tw/upload/images/20201008/20129873bHHl6zyIRV.png

趙靈兒選擇炎咒:
https://ithelp.ithome.com.tw/upload/images/20201008/20129873Lg2TOTEk4T.png
趙靈兒炎咒攻擊,法力有消耗:
https://ithelp.ithome.com.tw/upload/images/20201008/20129873Za7jsygTrV.png

耶!本日打完收工!

[後記]

其實今天動工中間遇到很多小 bug,
但覺得再把它記錄上來會很亂,
就自己默默解掉了XD

例如第二回合攻擊完,
怪的血條反而會恢復全滿,
這是因為我沒有考量到怪被砍完可能血量會小於 0,
width: -10% => 這樣寬度顯示就會有問題,
所以這邊要增加如果 monsterBloodWidth <=0 要設為 0 的判斷。

// 怪的血條寬度設定
let monsterBloodWidth = (roleStatus[2].HealthPoint[0]/roleStatus[2].HealthPoint[1])*100;
if ( monsterBloodWidth <=0 ) monsterBloodWidth = 0; // 避免monsterBloodWidth為負值時會有問題
monsterBloodElement.style = `width: ${monsterBloodWidth}%`;

Code 越寫越多,
感覺該來整理一下了orz
還有 SCSS,
很多地方該改用變數管理了。

明天進度就來弄戰鬥勝利的畫面跟音效好了XD


上一篇
Day31 - 「登愣登愣,登愣登登登」~ 隱挑戰 Day7
下一篇
Day33 - 「登愣登愣,登愣登登登」~ 隱挑戰 Day9
系列文
30天找回寫程式手感計劃!!!36
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言