功能批量实现 优化 服务端 新浮动小游戏框架 新界面UI
This commit is contained in:
@@ -0,0 +1,218 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
|
||||
public sealed class PlayerExperienceLedger : MonoBehaviour
|
||||
{
|
||||
[Serializable]
|
||||
private class PlayerExperiencePayload
|
||||
{
|
||||
public int version = 1;
|
||||
public long lastUpdatedUtcTicks;
|
||||
public int playerExp;
|
||||
}
|
||||
|
||||
public static PlayerExperienceLedger Instance { get; private set; }
|
||||
|
||||
public event Action<int> OnExperienceChanged;
|
||||
|
||||
private PlayerExperiencePayload payload;
|
||||
private bool initialized;
|
||||
private bool loadedFromSave;
|
||||
private Player_SO boundPlayerData;
|
||||
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
|
||||
private static void Bootstrap()
|
||||
{
|
||||
EnsureInstance();
|
||||
}
|
||||
|
||||
public static PlayerExperienceLedger EnsureInstance()
|
||||
{
|
||||
if (Instance != null)
|
||||
{
|
||||
return Instance;
|
||||
}
|
||||
|
||||
var host = new GameObject("__runtime_player_exp_bridge");
|
||||
host.hideFlags = HideFlags.HideInHierarchy | HideFlags.HideInInspector;
|
||||
DontDestroyOnLoad(host);
|
||||
Instance = host.AddComponent<PlayerExperienceLedger>();
|
||||
return Instance;
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (Instance != null && Instance != this)
|
||||
{
|
||||
Destroy(gameObject);
|
||||
return;
|
||||
}
|
||||
|
||||
Instance = this;
|
||||
DontDestroyOnLoad(gameObject);
|
||||
InitializeIfNeeded();
|
||||
TryAttachDefaultPlayerData();
|
||||
}
|
||||
|
||||
private void OnApplicationPause(bool pauseStatus)
|
||||
{
|
||||
if (pauseStatus)
|
||||
{
|
||||
SaveNow();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnApplicationQuit()
|
||||
{
|
||||
SaveNow();
|
||||
}
|
||||
|
||||
public void InitializeIfNeeded()
|
||||
{
|
||||
if (initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
loadedFromSave = SecureSaveVault.TryLoadJson("player_experience", "runtime", out payload, GetLegacySavePath());
|
||||
if (payload == null)
|
||||
{
|
||||
payload = CreateDefaultPayload();
|
||||
}
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
public void AttachPlayerData(Player_SO playerData)
|
||||
{
|
||||
if (playerData == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
InitializeIfNeeded();
|
||||
boundPlayerData = playerData;
|
||||
|
||||
if (!loadedFromSave)
|
||||
{
|
||||
payload.playerExp = Mathf.Max(0, playerData.player_currentEXP);
|
||||
SaveNow();
|
||||
loadedFromSave = true;
|
||||
return;
|
||||
}
|
||||
|
||||
SyncToPlayerData();
|
||||
NotifyExperienceChanged();
|
||||
}
|
||||
|
||||
public int GetExperience()
|
||||
{
|
||||
InitializeIfNeeded();
|
||||
return payload.playerExp;
|
||||
}
|
||||
|
||||
public void AddExperience(int amount)
|
||||
{
|
||||
if (amount == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
InitializeIfNeeded();
|
||||
long next = (long)payload.playerExp + amount;
|
||||
if (next < 0)
|
||||
{
|
||||
next = 0;
|
||||
}
|
||||
else if (next > int.MaxValue)
|
||||
{
|
||||
next = int.MaxValue;
|
||||
}
|
||||
|
||||
payload.playerExp = (int)next;
|
||||
SaveNow();
|
||||
}
|
||||
|
||||
public bool TryConsumeExperience(int amount)
|
||||
{
|
||||
if (amount <= 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
InitializeIfNeeded();
|
||||
if (payload.playerExp < amount)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
payload.playerExp -= amount;
|
||||
SaveNow();
|
||||
return true;
|
||||
}
|
||||
|
||||
public void SaveNow()
|
||||
{
|
||||
if (!initialized)
|
||||
{
|
||||
InitializeIfNeeded();
|
||||
}
|
||||
|
||||
if (payload == null)
|
||||
{
|
||||
payload = CreateDefaultPayload();
|
||||
}
|
||||
|
||||
payload.lastUpdatedUtcTicks = DateTime.UtcNow.Ticks;
|
||||
SecureSaveVault.SaveJson("player_experience", "runtime", payload, GetLegacySavePath());
|
||||
SyncToPlayerData();
|
||||
NotifyExperienceChanged();
|
||||
}
|
||||
|
||||
private void SyncToPlayerData()
|
||||
{
|
||||
if (boundPlayerData == null || payload == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
boundPlayerData.player_currentEXP = Mathf.Max(0, payload.playerExp);
|
||||
#if UNITY_EDITOR
|
||||
if (!Application.isPlaying)
|
||||
{
|
||||
UnityEditor.EditorUtility.SetDirty(boundPlayerData);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
private void NotifyExperienceChanged()
|
||||
{
|
||||
OnExperienceChanged?.Invoke(GetExperience());
|
||||
}
|
||||
|
||||
private void TryAttachDefaultPlayerData()
|
||||
{
|
||||
Player_SO player = RuntimeResourcesCache.LoadDefaultPlayerSo();
|
||||
|
||||
if (player != null)
|
||||
{
|
||||
AttachPlayerData(player);
|
||||
}
|
||||
}
|
||||
|
||||
private static PlayerExperiencePayload CreateDefaultPayload()
|
||||
{
|
||||
return new PlayerExperiencePayload
|
||||
{
|
||||
version = 1,
|
||||
lastUpdatedUtcTicks = DateTime.UtcNow.Ticks,
|
||||
playerExp = 0,
|
||||
};
|
||||
}
|
||||
|
||||
private static string GetLegacySavePath()
|
||||
{
|
||||
return Path.Combine(Application.persistentDataPath, "player_experience.json");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user