using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text.RegularExpressions; using System.Threading.Tasks; using UnityEngine; using UnityEngine.Events; using UnityEngine.Networking; /// /// 库存管理器 - 单例模式 /// public class InventoryManager : MonoSingleton { [Header("配置文件")] [SerializeField] private string fileName = "inventory.json"; [Header("事件")] public UnityEvent onInventoryLoaded; // 库存加载完成事件 public UnityEvent onInventoryUpdated; // 库存更新事件 // 库存数据 private InventoryData _inventoryData = new InventoryData(); private string _persistentFilePath; // 可读写路径 private string _streamingAssetsPath; // 只读路径 private bool _isInitialized = false; private readonly object _lockObject = new object(); // 线程安全锁 #region 属性 /// /// 获取所有库存项(只读) /// public IReadOnlyList AllItems { get { lock (_lockObject) { return _inventoryData.inventory.AsReadOnly(); } } } /// /// 库存是否已加载 /// public bool IsInitialized => _isInitialized; #endregion #region Unity 生命周期 protected override void Initialize() { // 初始化路径 _streamingAssetsPath = Path.Combine(Application.streamingAssetsPath, fileName); _persistentFilePath = Path.Combine(Application.persistentDataPath, fileName); } private IEnumerator Start() { yield return StartCoroutine(InitializeInventoryAsync()); } private void OnApplicationQuit() { // 退出时自动保存 SaveInventoryData(); } #endregion #region 初始化与加载 /// /// 异步初始化库存系统 /// public IEnumerator InitializeInventoryAsync(bool forceReload = false) { if (_isInitialized && !forceReload) yield break; Debug.Log($"初始化库存系统..."); //Debug.Log($"StreamingAssets路径: {_streamingAssetsPath}"); //Debug.Log($"可读写路径: {_persistentFilePath}"); bool loadSuccess = false; // 策略2: 如果可读写路径失败,从StreamingAssets加载 if (!loadSuccess) { yield return StartCoroutine(LoadFromStreamingAssets()); } _isInitialized = true; onInventoryLoaded?.Invoke(); } /// /// 从StreamingAssets加载(跨平台兼容) /// private IEnumerator LoadFromStreamingAssets() { // 使用UnityWebRequest加载 UnityWebRequest request = UnityWebRequest.Get(_streamingAssetsPath); yield return request.SendWebRequest(); if (request.result != UnityWebRequest.Result.Success) { Debug.LogError($"从StreamingAssets加载失败: {request.error}"); CreateDefaultInventory(); } else { // 获取文本并处理编码 string json = request.downloadHandler.text; // 尝试检测并修复编码问题 json = FixEncodingIfNeeded(json); //Debug.Log($"原始JSON内容: {json}"); try { lock (_lockObject) { _inventoryData = JsonUtility.FromJson(json); } Debug.Log($"从StreamingAssets加载成功,共 {_inventoryData.inventory.Count} 个种类"); SaveInventoryData(); // 保存到可读写路径 } catch (System.Exception e) { Debug.LogError($"JSON解析失败: {e.Message}"); Debug.Log($"原始JSON内容: {json}"); CreateDefaultInventory(); } } } /// /// 检测并修复编码问题 /// private string FixEncodingIfNeeded(string text) { // 检测是否是乱码(中文变问号等) if (string.IsNullOrEmpty(text)) return text; // 尝试检测UTF-8 BOM byte[] bytes = System.Text.Encoding.UTF8.GetBytes(text); // 检查是否有UTF-8 BOM (EF BB BF) if (bytes.Length >= 3 && bytes[0] == 0xEF && bytes[1] == 0xBB && bytes[2] == 0xBF) { // 去掉BOM return System.Text.Encoding.UTF8.GetString(bytes, 3, bytes.Length - 3); } return text; } /// /// 创建默认库存(当配置文件不存在时) /// private void CreateDefaultInventory() { lock (_lockObject) { _inventoryData.inventory.Clear(); _inventoryData.inventory.Add(new InventoryItem("红外测温仪", 99)); _inventoryData.inventory.Add(new InventoryItem("检测试纸-A", 23)); _inventoryData.inventory.Add(new InventoryItem("扳手", 3)); _inventoryData.lastModified = DateTime.Now; } Debug.Log("创建默认库存数据"); SaveInventoryData(); } #endregion #region 公开接口 /// /// 获取库存中物品种类数量 /// /// 库存中不同类型的物品数量 public int GetItemTypeCount() { lock (_lockObject) { return _inventoryData.inventory.Count; } } /// /// 获取所有物品类型的名称列表 /// /// 所有物品类型的名称数组 public string[] GetAllItemTypeNames() { lock (_lockObject) { return _inventoryData.inventory.Select(item => item.type).ToArray(); } } /// /// 获取所有物品类型的详细信息 /// 返回格式:类型名称 -> 数量 /// /// 字典,键为类型名称,值为数量 public Dictionary GetAllItemDetails() { lock (_lockObject) { return _inventoryData.inventory.ToDictionary( item => item.type, item => item.remaining ); } } /// /// 获取库存概要信息 /// 返回格式:数组,每个元素是元组(类型, 数量) /// public (string type, int quantity)[] GetInventorySummary() { lock (_lockObject) { return _inventoryData.inventory .Select(item => (item.type, item.remaining)) .ToArray(); } } /// /// 获取库存统计信息 /// 包含:种类数量、总数量、平均数量等 /// public InventoryStats GetInventoryStatistics() { lock (_lockObject) { if (_inventoryData.inventory.Count == 0) { return new InventoryStats { itemTypeCount = 0, totalQuantity = 0, averageQuantity = 0, maxQuantity = 0, minQuantity = 0 }; } var quantities = _inventoryData.inventory.Select(item => item.remaining).ToList(); return new InventoryStats { itemTypeCount = _inventoryData.inventory.Count, totalQuantity = quantities.Sum(), averageQuantity = (int)quantities.Average(), maxQuantity = quantities.Max(), minQuantity = quantities.Min(), lastModified = _inventoryData.lastModified }; } } /// /// 获取按数量排序的库存列表 /// /// 是否降序排列(默认true,数量多的在前) /// 排序后的库存列表 public IReadOnlyList GetSortedItems(bool descending = true) { lock (_lockObject) { if (descending) { return _inventoryData.inventory .OrderByDescending(item => item.remaining) .ToList() .AsReadOnly(); } else { return _inventoryData.inventory .OrderBy(item => item.remaining) .ToList() .AsReadOnly(); } } } /// /// 获取库存数量范围统计 /// /// 数量阈值 /// 低于阈值、等于阈值、高于阈值的物品数量 public (int belowThreshold, int atThreshold, int aboveThreshold) GetQuantityStats(int threshold = 10) { lock (_lockObject) { int below = 0, at = 0, above = 0; foreach (var item in _inventoryData.inventory) { if (item.remaining < threshold) below++; else if (item.remaining == threshold) at++; else above++; } return (below, at, above); } } /// /// 获取指定关键字匹配的物品列表 /// /// 搜索关键字 /// 是否区分大小写(默认false) /// 匹配的物品列表 public IReadOnlyList SearchItems(string keyword, bool caseSensitive = false) { if (string.IsNullOrEmpty(keyword)) return _inventoryData.inventory.AsReadOnly(); lock (_lockObject) { StringComparison comparison = caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase; return _inventoryData.inventory .Where(item => item.type.IndexOf(keyword, comparison) >= 0) .ToList() .AsReadOnly(); } } /// /// 获取指定类型的物品数量 /// /// 物品类型 /// 输出:数量,如果不存在则返回0 /// 是否找到该物品 public bool TryGetItemQuantity(string type, out int quantity) { lock (_lockObject) { var item = _inventoryData.inventory.Find(x => x.type == type); if (item != null) { quantity = item.remaining; return true; } quantity = 0; return false; } } /// /// 更新物品数量(接口1:直接设置) /// /// 物品类型 /// 新数量 /// 是否更新成功 public bool UpdateItemQuantity(string type, int newQuantity) { if (newQuantity < 0) { Debug.LogWarning($"数量不能为负数: {type}"); return false; } lock (_lockObject) { var item = _inventoryData.inventory.Find(x => x.type == type); if (item == null) { // 如果没有找到,创建一个新项 _inventoryData.inventory.Add(new InventoryItem(type, newQuantity)); Debug.Log($"添加新物品: {type}, 数量: {newQuantity}"); } else { item.remaining = newQuantity; } _inventoryData.lastModified = DateTime.Now; } // 保存到文件 SaveInventoryData(); onInventoryUpdated?.Invoke(); return true; } /// /// 增加物品数量(接口2:增量修改) /// /// 物品类型 /// 变化量(可正可负) /// 修改后的数量,如果失败返回-1 public int AddItemQuantity(string type, int delta) { lock (_lockObject) { var item = _inventoryData.inventory.Find(x => x.type == type); if (item == null && delta < 0) { Debug.LogWarning($"尝试减少不存在的物品: {type}"); return -1; } if (item == null) { // 创建新项 item = new InventoryItem(type, delta); _inventoryData.inventory.Add(item); } else { int newQuantity = item.remaining + delta; if (newQuantity < 0) { Debug.LogWarning($"数量不能为负数: {type}, 当前: {item.remaining}, 尝试减少: {delta}"); return -1; } item.remaining = newQuantity; } _inventoryData.lastModified = DateTime.Now; int result = item.remaining; // 异步保存 Task.Run(() => SaveInventoryDataAsync()); onInventoryUpdated?.Invoke(); return result; } } /// /// 添加新物品类型 /// /// 物品类型 /// 初始数量 /// 是否添加成功 public bool AddNewItemType(string type, int initialQuantity = 0) { if (initialQuantity < 0) { Debug.LogWarning($"初始数量不能为负数: {type}"); return false; } lock (_lockObject) { if (_inventoryData.inventory.Exists(x => x.type == type)) { Debug.LogWarning($"物品类型已存在: {type}"); return false; } _inventoryData.inventory.Add(new InventoryItem(type, initialQuantity)); _inventoryData.lastModified = DateTime.Now; } SaveInventoryData(); onInventoryUpdated?.Invoke(); return true; } /// /// 移除物品类型 /// public bool RemoveItemType(string type) { lock (_lockObject) { int removed = _inventoryData.inventory.RemoveAll(x => x.type == type); if (removed > 0) { _inventoryData.lastModified = DateTime.Now; SaveInventoryData(); onInventoryUpdated?.Invoke(); return true; } } return false; } /// /// 导出当前库存为JSON字符串 /// public string ExportToJson() { lock (_lockObject) { return JsonUtility.ToJson(_inventoryData, true); } } /// /// 重置为初始配置(从StreamingAssets重新加载) /// public IEnumerator ResetToDefault() { Debug.Log("重置库存为初始配置..."); // 删除可读写路径的文件 if (File.Exists(_persistentFilePath)) { File.Delete(_persistentFilePath); } // 重新初始化 _isInitialized = false; yield return StartCoroutine(InitializeInventoryAsync(true)); } #endregion #region 保存与持久化 /// /// 保存库存数据到可读写路径 /// public void SaveInventoryData() { try { string json; lock (_lockObject) { json = JsonUtility.ToJson(_inventoryData, true); } // 确保目录存在 string directory = Path.GetDirectoryName(_persistentFilePath); if (!Directory.Exists(directory)) { Directory.CreateDirectory(directory); } File.WriteAllText(_persistentFilePath, json); Debug.Log($"库存数据已保存: {_persistentFilePath}"); } catch (Exception e) { Debug.LogError($"保存库存数据失败: {e.Message}"); } } /// /// 异步保存库存数据 /// private async Task SaveInventoryDataAsync() { try { string json; lock (_lockObject) { json = JsonUtility.ToJson(_inventoryData, true); } string directory = Path.GetDirectoryName(_persistentFilePath); if (!Directory.Exists(directory)) { Directory.CreateDirectory(directory); } await File.WriteAllTextAsync(_persistentFilePath, json); } catch (Exception e) { Debug.LogError($"异步保存库存数据失败: {e.Message}"); } } #endregion /// /// 库存统计信息 /// public struct InventoryStats { public int itemTypeCount; // 物品种类数量 public int totalQuantity; // 总数量 public int averageQuantity; // 平均数量 public int maxQuantity; // 最大数量 public int minQuantity; // 最小数量 public DateTime lastModified; // 最后修改时间 public override string ToString() { return $"种类: {itemTypeCount}, 总数: {totalQuantity}, 平均: {averageQuantity}, 最大: {maxQuantity}, 最小: {minQuantity}, 最后更新: {lastModified:yyyy-MM-dd HH:mm:ss}"; } } } [System.Serializable] public class InventoryItem { public string type; // 物品类型 public int remaining; // 剩余数量 public InventoryItem(string type, int remaining) { this.type = type; this.remaining = remaining; } } [System.Serializable] public class InventoryData { public List inventory = new List(); public DateTime lastModified; // 最后修改时间 }