iT邦幫忙

2026 iThome 鐵人賽

DAY 16
0

製作分數系統

這一個遊戲已經大致上完成差不多了,今天就開始製作分數系統。
原本的遊戲主要是以存活時間為主,但如果只有一直躲避障礙物,玩家可能比較難感受到自己的遊玩成果,所以這次加入分數計算,讓玩家可以在遊戲過程中看到目前的分數,死亡後也能留下最高紀錄。
這次的分數主要會根據遊玩時間與目前遊戲速度來計算,讓遊戲後期速度越快時,分數增加也會越快。

讓分數隨時間增加

首先製作一個 ScoreManager,負責計算和顯示目前分數。
我設定基礎每秒增加 10 分,再透過前幾天製作好的RockSpawner取得目前遊戲速度,計算現在的速度倍率。

例如遊戲初始速度是 4:
速度 4.0 → 1 倍分數
速度 4.8 → 1.2 倍分數
速度 5.6 → 1.4 倍分數
速度 6.4 → 1.6 倍分數
速度 7.2 → 1.8 倍分數
速度 8.0 → 2 倍分數

這樣遊戲隨著時間逐漸加速時,分數也會跟著增加得更快。
這次的ScoreManager如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro; // 若使用 TextMeshPro 請保留,若用傳統 Text 改成 UnityEngine.UI

public class ScoreManager : MonoBehaviour
{
    public static ScoreManager Instance;

    [Header("UI 顯示元件")]
    [Tooltip("顯示當前分數的 TextMeshPro 元件")]
    public TMP_Text scoreText;
    
    [Tooltip("遊戲結束時顯示最佳紀錄的 TextMeshPro 元件")]
    public TMP_Text highScoreText;

    [Header("分數計算設定")]
    [Tooltip("基礎每秒增加的分數點數")]
    public float pointsPerSecond = 10f;
    
    [Tooltip("初始基礎速度(用來計算加速時的分數倍率)")]
    public float baseSpeedReference = 4f;

    private float currentScoreRaw = 0f; // 內部精確累加的浮點數分數
    private bool isScoring = false;

    private RockSpawner rockSpawner;

    void Awake()
    {
        if (Instance == null) Instance = this;
        else Destroy(gameObject);
    }

    void Start()
    {
        rockSpawner = FindObjectOfType<RockSpawner>();
        StartScoring();
    }

    void Update()
    {
        if (!isScoring) return;

        // 取得當前場景速度(開技能時會是最高速度 8f,平時隨時間提速)
        float currentSpeed = (rockSpawner != null) ? rockSpawner.GetCurrentSpeed() : baseSpeedReference;

        // 速度倍率:當前的速度是初始速度的幾倍(例如:8 / 4 = 2 倍速,分數也增長 2 倍)
        float speedMultiplier = currentSpeed / baseSpeedReference;

        // 累加分數:時間 * 基礎分 * 速度倍率
        currentScoreRaw += Time.deltaTime * pointsPerSecond * speedMultiplier;

        // 更新 UI 顯示
        UpdateScoreUI();
    }

    void UpdateScoreUI()
    {
        if (scoreText == null) return;

        // 【核心視覺魔法】
        // 將內部浮點數乘以 100 後取整數,後兩位就會因為每幀微小跳動而呈現「亂碼般快速飆升」的感覺!
        int displayScore = Mathf.FloorToInt(currentScoreRaw * 100f);
        
        // 格式化為 6 位數,前面補 0(例如:001428)
        scoreText.text = displayScore.ToString("D6"); 
    }

    public void StartScoring()
    {
        isScoring = true;
    }

    public void StopScoring()
    {
        isScoring = false;
        SaveHighScore();
    }

    // 取得最後結算分數(可供結算畫面讀取)
    public int GetFinalScore()
    {
        return Mathf.FloorToInt(currentScoreRaw * 100f);
    }

    // 保存與刷新高分紀錄
    private void SaveHighScore()
    {
        int finalScore = GetFinalScore();
        int savedHighScore = PlayerPrefs.GetInt("HighScore", 0);

        if (finalScore > savedHighScore)
        {
            savedHighScore = finalScore;
            PlayerPrefs.SetInt("HighScore", savedHighScore);
            PlayerPrefs.Save();
        }

        if (highScoreText != null)
        {
            highScoreText.text = "BEST: " + savedHighScore.ToString("D6");
        }
    }
}

這次主要利用currentScoreRaw累積分數,再透過speedMultiplier讓分數和遊戲速度產生關聯。
另外顯示分數時會乘上 100,再使用六位數格式顯示,例如:
000125
001428
012560
讓分數在遊戲中可以持續快速增加。

死亡後停止計分

分數不能在小鳥死亡後繼續增加,所以在原本的 Die() 裡加入停止計分的功能。

void Die()
{
    isDead = true;
    Debug.Log("小鳥撞到了!遊戲結束。");

    // 【新增】死亡時停止計分並結算保存高分紀錄
    if (ScoreManager.Instance != null)
    {
        ScoreManager.Instance.StopScoring();
    }
}

當小鳥撞到岩石或地面後,就會停止計分,並進行最後的分數結算。

保存最高分

遊戲結束後會將這次的分數與之前保存的最高分比較。如果這次分數比較高,就使用PlayerPrefs保存新的最高分,這樣即使重新開始遊戲,也可以保留之前的最佳紀錄。

目前的流程就是:
遊戲開始→開始計分→遊戲速度增加→分數增加速度也提高→小鳥死亡→停止計分→計算最終分數→與最高分比較→保存BEST

第十六天的成果與之後目標

今天大致完成這遊戲了,之後應該會從下一個小遊戲著手開始製作,畢竟開一個網頁只放一個遊戲感覺有一點單調,接下來這14天應該會再製作下一個小遊戲,目前就三個想法,小朋友下樓梯、打地鼠、打飛機三種遊戲做選擇。


上一篇
Day 15|新增玉米道具與隱身效果
下一篇
Day 17|製作遊戲開始與結算畫面
系列文
從基礎到實作:使用 Google AI 學習程式設計 共 17 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言