iT邦幫忙

2026 iThome 鐵人賽

DAY 14
0

調整技能效果

昨天已經完成技能可以無視障礙物的效果,今天則想再增加一個技能效果,讓技能不只是單純穿過岩石,而是可以進入短時間的高速衝刺。
這次的想法是讓遊戲本身會隨著時間慢慢加速,同時技能發動時直接進入最高速度。

這次的設定為:

  • 遊戲初始速度為 4。
  • 每 30 秒自然增加 0.8。
  • 總共增加 5 次後達到最高速度 8。
  • 使用技能時直接提升到最高速度。
  • 技能持續 3 秒。
  • 技能結束後恢復到當下原本的遊戲速度。

更改速度限制

原先我忘記給遊戲設置最高速度,所以加速系統難以去實現,今天做到這步驟時腦袋就有點小卡住,「加速要多快才是加速?」、「假如玩家遊玩超過那個速度,那會不會變成減速?」,想到這幾個問題,我就先給遊戲設置一個最高速度,
我就使用speedTimer計算時間,每經過30秒就增加一次baseSpeed,加速5次也就是遊戲逕行兩分半時就會達到最高速。

例如:
開始:4.0
30秒:4.8
60秒:5.6
90秒:6.4
120秒:7.2
150秒:8.0
當速度達到 8 之後,就不會再繼續增加。
這次修改的RockSpawner如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class RockSpawner : MonoBehaviour
{
    [Header("岩石 Prefab 設定")]
    public GameObject topRockPrefab;
    public GameObject bottomRockPrefab;

    [Header("上方岩石 Y 軸生成範圍")]
    public float topYMin = 2.45f;
    public float topYMax = 4.7f;

    [Header("下方岩石 Y 軸生成範圍")]
    public float bottomYMax = -2.2f;
    public float bottomYMin = -4.6f;

    [Header("生成頻率與速度設定")]
    public float spawnRate = 2f;
    public float baseSpeed = 4f;              // 當前基礎速度(未開技能時的速度)
    public float speedIncreaseAmount = 0.8f;  // 每次提速幅度
    public float speedUpInterval = 30f;       // 自然提速間隔(秒)

    [Header("最高速度與技能設定")]
    [Tooltip("加速 5 次後的上限速度(預設 4 + 0.8 * 5 = 8.0)")]
    public float maxSpeed = 8f; 
    
    [HideInInspector]
    public bool isSkillBoosting = false;     // 是否正在處於技能加速 3 秒狀態

    [HideInInspector]
    public bool lastSpawnedIsTop = false;

    private float spawnTimer = 0f;
    private float speedTimer = 0f;

    // 【新增】用來精確追蹤與管理正在執行的技能 Coroutine 引用
    private Coroutine skillBoostCoroutine;

    void Start()
    {
        // 自動計算最高速度上限(基礎速度 + 加速 5 次)
        maxSpeed = baseSpeed + (speedIncreaseAmount * 5f);
        
        // 初始同步背景速度
        SyncAllSpeeds(baseSpeed);
        SpawnRock();
    }

    void Update()
    {
        // 1. 生成岩石計時器
        spawnTimer += Time.deltaTime;
        if (spawnTimer >= spawnRate)
        {
            SpawnRock();
            spawnTimer = 0f;
        }

        // 2. 每 30 秒難度自然提升計時器
        speedTimer += Time.deltaTime;
        if (speedTimer >= speedUpInterval)
        {
            // 如果還沒達到最高速度上限,就繼續提速
            if (baseSpeed < maxSpeed)
            {
                baseSpeed = Mathf.Min(baseSpeed + speedIncreaseAmount, maxSpeed);

                if (spawnRate > 0.8f)
                {
                    spawnRate -= 0.1f;
                }

                Debug.Log($"【難度提升】30秒已到!當前基礎速度:{baseSpeed},生成間隔:{spawnRate}s");

                // 若非技能加速中,同步更新畫面上所有物件速度
                if (!isSkillBoosting)
                {
                    SyncAllSpeeds(baseSpeed);
                }
            }
            else
            {
                Debug.Log("【已達最高速度上限】維持極限速度進行!");
            }

            speedTimer = 0f;
        }
    }

    void SpawnRock()
    {
        lastSpawnedIsTop = Random.value > 0.5f;

        GameObject prefabToSpawn;
        float targetY;

        if (lastSpawnedIsTop)
        {
            prefabToSpawn = topRockPrefab;
            targetY = Random.Range(topYMin, topYMax);
        }
        else
        {
            prefabToSpawn = bottomRockPrefab;
            targetY = Random.Range(bottomYMin, bottomYMax);
        }

        Vector3 spawnPosition = new Vector3(transform.position.x, targetY, 0);
        Instantiate(prefabToSpawn, spawnPosition, Quaternion.identity);
    }

    /// <summary>
    /// 取得目前遊戲中岩石與背景應該運行的實際速度
    /// </summary>
    public float GetCurrentSpeed()
    {
        return isSkillBoosting ? maxSpeed : baseSpeed;
    }

    /// <summary>
    /// 供外部 (BirdController) 呼叫:啟動 3 秒技能加速
    /// </summary>
    public void TriggerSkillBoost(float duration)
    {
        // 【修正】如果有正在執行中的技能 Coroutine,將其正確停止
        if (skillBoostCoroutine != null)
        {
            StopCoroutine(skillBoostCoroutine);
        }

        // 開啟新的 Coroutine 並把 Reference 存起來
        skillBoostCoroutine = StartCoroutine(SkillBoostRoutine(duration));
    }

    private IEnumerator SkillBoostRoutine(float duration)
    {
        isSkillBoosting = true;
        
        // 立即切換為最高速度 (maxSpeed)
        SyncAllSpeeds(maxSpeed);
        Debug.Log($"【技能啟動】加速 3 秒!當前衝刺速度:{maxSpeed}");

        yield return new WaitForSeconds(duration);

        isSkillBoosting = false;
        
        // 恢復技能前原本的速度
        SyncAllSpeeds(baseSpeed);
        Debug.Log($"【技能結束】還原基礎速度:{baseSpeed}");

        // 【新增】結束後清空引用
        skillBoostCoroutine = null;
    }

    /// <summary>
    /// 同步更新場景中所有背景與岩石的速度
    /// </summary>
    private void SyncAllSpeeds(float targetSpeed)
    {
        BackgroundRepeat[] scrollables = FindObjectsOfType<BackgroundRepeat>();
        foreach (var bg in scrollables)
        {
            bg.UpdateSpeed(targetSpeed);
        }
    }
}

這次主要增加的是 baseSpeed、maxSpeed 和 speedTimer,讓遊戲可以隨時間逐漸增加難度。

技能直接進入最高速度

接著修改技能的部分。
技能發動時,會找到場景中的 RockSpawner,並呼叫:
spawner.TriggerSkillBoost(skillDuration);
讓 RockSpawner 開始執行技能加速。
技能啟動後,會暫時把遊戲速度設定成最高的 8:
SyncAllSpeeds(maxSpeed);
同時小鳥本身也會進入之前製作好的無敵狀態,因此技能發動期間可以直接穿過岩石。

這樣技能就會變成:無視障礙物+最高速度衝刺。

技能部分則是在原本的 UseSkill() 裡增加:

void UseSkill()
{
    Debug.Log("釋放技能:無敵穿透 + 全場加速衝刺!");

    ResetFeathersUI();
    SetSkillReady(false);

    // 1. 啟動小鳥本體的無敵穿透(無視死亡碰撞)
    StartCoroutine(InvincibleRoutine());

    // 2.【新增】找到場景中的 RockSpawner 並觸發 3 秒全場最高速度衝刺!
    RockSpawner spawner = FindObjectOfType<RockSpawner>();
    if (spawner != null)
    {
        spawner.TriggerSkillBoost(skillDuration); // skillDuration 預設 3 秒
    }
}
技能結束後恢復原本速度,技能持續時間跟無視障礙物一樣設定為3秒。

3 秒結束後,會關閉技能加速狀態,並將速度恢復成當時的 baseSpeed。
例如玩家已經玩了 90 秒,遊戲速度原本是 6.4,這時候使用技能:

原本速度:6.4
↓
技能發動
↓
直接提升到:8.0
↓
持續 3 秒
↓
恢復:6.4

因此技能的高速效果不會永久改變遊戲原本的速度。

第十四天的成果

今天主要是做技能的加速效果,原本只有無視障礙物,跟之後要做的另一個道具效果有相似之處,所以就給技能一個加速效果。
目前技能應該已經完善了,之後就要新增下一個道具,並給它一個能隱身的效果。


上一篇
Day 13|製作技能效果(碰到障礙物跳過死亡判定)
下一篇
Day 15|新增玉米道具與隱身效果
系列文
從基礎到實作:使用 Google AI 學習程式設計 共 17 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言