163 lines
4.8 KiB
C#
163 lines
4.8 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
|
|
namespace GameFramework
|
|
{
|
|
/// <summary>
|
|
/// 单条玩家得分记录。
|
|
/// </summary>
|
|
[Serializable]
|
|
public class PlayerScore
|
|
{
|
|
public int score;
|
|
public string playerName;
|
|
|
|
public PlayerScore(int score, string playerName)
|
|
{
|
|
this.score = score;
|
|
this.playerName = playerName;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 可序列化的玩家得分列表(用于 JSON 存档)。
|
|
/// </summary>
|
|
[Serializable]
|
|
public class PlayerScoreData
|
|
{
|
|
public List<PlayerScore> list = new List<PlayerScore>();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 得分管理器(持久单例)。
|
|
/// 维护当前局得分,通过事件通知 UI,并管理 Top-10 排行榜存档。
|
|
/// </summary>
|
|
public class ScoreManager : PersistentSingleton<ScoreManager>
|
|
{
|
|
const int LEADERBOARD_SIZE = 10;
|
|
const string SAVE_FILE = "leaderboard.json";
|
|
|
|
// 当前局得分
|
|
int currentScore = 0;
|
|
|
|
// 当得分变化时触发,参数为最新得分(动画过程中的中间值也会触发)
|
|
public static event Action<int> onScoreChanged;
|
|
// 当得分完成最终增加时触发,参数为最终得分
|
|
public static event Action<int> onScoreSettled;
|
|
|
|
/// <summary>当前得分(只读)。</summary>
|
|
public int CurrentScore => currentScore;
|
|
|
|
/// <summary>默认玩家名,可在 Inspector 设置。</summary>
|
|
[SerializeField] string defaultPlayerName = "Player";
|
|
|
|
protected override void Awake()
|
|
{
|
|
base.Awake();
|
|
if (Instance != this) return;
|
|
currentScore = 0;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 增加得分(带数字滚动动画)。
|
|
/// </summary>
|
|
public void AddScore(int amount)
|
|
{
|
|
if (amount <= 0) return;
|
|
StartCoroutine(ScoreCountUpCoroutine(currentScore, currentScore + amount));
|
|
}
|
|
|
|
/// <summary>
|
|
/// 直接设置得分(无动画)。
|
|
/// </summary>
|
|
public void SetScore(int value)
|
|
{
|
|
currentScore = Mathf.Max(0, value);
|
|
onScoreChanged?.Invoke(currentScore);
|
|
onScoreSettled?.Invoke(currentScore);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 重置当前局得分。
|
|
/// </summary>
|
|
public void ResetScore()
|
|
{
|
|
currentScore = 0;
|
|
onScoreChanged?.Invoke(0);
|
|
onScoreSettled?.Invoke(0);
|
|
}
|
|
|
|
IEnumerator ScoreCountUpCoroutine(int from, int to)
|
|
{
|
|
currentScore = to;
|
|
float duration = 0.5f;
|
|
float elapsed = 0f;
|
|
|
|
while (elapsed < duration)
|
|
{
|
|
elapsed += Time.unscaledDeltaTime;
|
|
float t = Mathf.Clamp01(elapsed / duration);
|
|
int display = Mathf.RoundToInt(Mathf.Lerp(from, to, t));
|
|
onScoreChanged?.Invoke(display);
|
|
yield return null;
|
|
}
|
|
|
|
onScoreChanged?.Invoke(to);
|
|
onScoreSettled?.Invoke(to);
|
|
}
|
|
|
|
#region 排行榜
|
|
/// <summary>
|
|
/// 当前得分是否能进入排行榜 Top-10。
|
|
/// </summary>
|
|
public bool HasNewHighScore
|
|
{
|
|
get
|
|
{
|
|
var data = LoadPlayerScoreData();
|
|
if (data.list.Count < LEADERBOARD_SIZE)
|
|
return true;
|
|
return currentScore > data.list[LEADERBOARD_SIZE - 1].score;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 将当前得分以指定名字保存到排行榜。
|
|
/// 排行榜按分数降序排列,只保留 Top-10。
|
|
/// </summary>
|
|
public void SavePlayerScore(string playerName)
|
|
{
|
|
if (string.IsNullOrEmpty(playerName))
|
|
playerName = defaultPlayerName;
|
|
|
|
var data = LoadPlayerScoreData();
|
|
data.list.Add(new PlayerScore(currentScore, playerName));
|
|
data.list.Sort((a, b) => b.score.CompareTo(a.score));
|
|
if (data.list.Count > LEADERBOARD_SIZE)
|
|
data.list.RemoveRange(LEADERBOARD_SIZE, data.list.Count - LEADERBOARD_SIZE);
|
|
SaveSystem.Save(SAVE_FILE, data);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 读取排行榜数据。
|
|
/// </summary>
|
|
public PlayerScoreData LoadPlayerScoreData()
|
|
{
|
|
if (SaveSystem.SaveFileExists(SAVE_FILE))
|
|
return SaveSystem.Load<PlayerScoreData>(SAVE_FILE);
|
|
return new PlayerScoreData();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 清空排行榜存档。
|
|
/// </summary>
|
|
public void ClearLeaderboard()
|
|
{
|
|
SaveSystem.DeleteSaveFile(SAVE_FILE);
|
|
}
|
|
#endregion
|
|
}
|
|
}
|