using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; using UnityEngine; namespace Game.HotFix { public enum AnimEnum { Anim_cloth, Anim_weapon, } // 帧数据结构 public struct FrameData { public int frameID; public int startX; public int startY; public int width; public int height; public int offsetX; public int offsetY; public int sourceWidth; public int sourceHeight; public override string ToString() { return $"Frame {frameID}: ({startX},{startY}) {width}x{height} offset:({offsetX},{offsetY})"; } } public class AnimParserComponent : MonoBehaviour { public static AnimParserComponent instance; // 存储所有动画数据: [部件ID][方向][帧] private Dictionary>> clothAnimData = new Dictionary>>(); private Dictionary>> weaponAnimData = new Dictionary>>(); private void Awake() { instance = this; System.Diagnostics.Stopwatch stopwatch = System.Diagnostics.Stopwatch.StartNew(); LoadAndParseCSV("cloth.biz.csv", clothAnimData); stopwatch.Stop(); Debug.Log($"加载装备CSV {clothAnimData.Count} 耗时 {stopwatch.Elapsed.Milliseconds} ms"); stopwatch.Start(); LoadAndParseCSV("weapon.biz.csv", weaponAnimData); stopwatch.Stop(); Debug.Log($"加载武器CSV {weaponAnimData.Count} 耗时 {stopwatch.Elapsed.Milliseconds} ms"); } // 加载并解析CSV文件 public void LoadAndParseCSV(string file, Dictionary>> AnimData) { string filePath = Path.Combine(Application.dataPath, "GameAssets/CSV", file); if (!File.Exists(filePath)) { Debug.LogError($"CSV file not found: {filePath}"); return; } string csvContent = File.ReadAllText(filePath); ParseCSVContent(csvContent, AnimData); } // 解析CSV文本内容 public void ParseCSVContent(string csvContent, Dictionary>> AnimData) { // 按行分割 var lines = csvContent.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); int lineCount = 0; foreach (var line in lines) { lineCount++; if (string.IsNullOrWhiteSpace(line) || line.StartsWith(",")) continue; try { // 分割行 var parts = line.Trim().Split(','); if (parts.Length < 7) { Debug.LogWarning($"Skipping line {lineCount}: insufficient parts ({parts.Length})"); continue; } string partID = parts[0]; int texWidth = ParseInt(parts[1]); int texHeight = ParseInt(parts[2]); int frameCount = ParseInt(parts[3]); //总共帧数 int totalDirections = ParseInt(parts[4]); //总共方向 int framesPerDirection = ParseInt(parts[5]); //每个方向帧数 // 创建方向帧列表 // 解析帧数据 for (int i = 6; i < 6 + frameCount; i++) { // 计算当前方向索引(基于已存在的方向数量) int directionIndex = (i - 6) / framesPerDirection; if (i >= parts.Length) { Debug.LogWarning($"Part {partID} direction {directionIndex}: expected {frameCount} frames, got {i - 6}"); break; } if (string.IsNullOrWhiteSpace(parts[i])) continue; var frameData = parts[i].Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); if (frameData.Length < 10) { Debug.LogWarning($"Skipping frame data: insufficient values ({frameData.Length})"); continue; } FrameData frame = new FrameData { frameID = ParseInt(frameData[0]), startX = ParseInt(frameData[1]), startY = ParseInt(frameData[2]), width = ParseInt(frameData[3]), height = ParseInt(frameData[4]), offsetX = ParseInt(frameData[5]), offsetY = ParseInt(frameData[6]), sourceWidth = ParseInt(frameData[7]), sourceHeight = ParseInt(frameData[8]) }; if (!AnimData.ContainsKey(partID)) { AnimData[partID] = new Dictionary>(); } if (!AnimData[partID].ContainsKey(directionIndex)) { AnimData[partID][frame.frameID / 10000] = new List(); } AnimData[partID][frame.frameID / 10000].Add(frame); } } catch (Exception e) { //Debug.LogError($"Error parsing line {lineCount}: {line}\n{e}"); } } } // 获取指定部件和方向的帧数据 public List GetFramesForPartAndDirection(Dictionary>> AnimData, string partID, int direction) { if (AnimData.TryGetValue(partID, out var directions) && directions.TryGetValue(direction, out var frames)) { return frames; } Debug.LogWarning($"No frames found for part {partID}, direction {direction}"); return null; } public List GetFramesForPartAndDirection(AnimEnum animEnum,string partId,int direction) { switch (animEnum) { case AnimEnum.Anim_cloth: return GetFramesForPartAndDirection(clothAnimData,partId,direction); case AnimEnum.Anim_weapon: return GetFramesForPartAndDirection(weaponAnimData, partId, direction); } return null; } // 获取部件可用的方向数量 public int GetDirectionCountForPart(string partID) { if (clothAnimData.TryGetValue(partID, out var directions)) { return directions.Count; } return 0; } // 获取所有部件ID public List GetAllPartIDs() { return clothAnimData.Keys.ToList(); } // 获取特定部件的所有方向数据 public Dictionary> GetPartData(string partID) { if (clothAnimData.TryGetValue(partID, out var directions)) { return directions; } return null; } // 清除所有数据 public void ClearAllData() { clothAnimData.Clear(); Debug.Log("Cleared all animation data"); } // 辅助方法:安全解析整数 private int ParseInt(string value) { if (int.TryParse(value, out int result)) { return result; } Debug.LogWarning($"Failed to parse integer: {value}"); return 0; } // 辅助方法:获取下一个方向索引 private int GetDirectionIndex(string partID) { if (clothAnimData.TryGetValue(partID, out var directions)) { return directions.Count; } return 0; } // 调试方法:打印所有动画数据 [ContextMenu("Print Animation Data")] public void PrintAnimationData() { Debug.Log("==== Animation Data Summary ===="); Debug.Log($"Total parts: {clothAnimData.Count}"); foreach (var part in clothAnimData) { Debug.Log($"Part: {part.Key}"); foreach (var direction in part.Value) { Debug.Log($" Direction {direction.Key}: {direction.Value.Count} frames"); // 打印前3帧数据 for (int i = 0; i < Mathf.Min(3, direction.Value.Count); i++) { Debug.Log($" {direction.Value[i]}"); } if (direction.Value.Count > 3) { Debug.Log($" ... and {direction.Value.Count - 3} more"); } } } Debug.Log("================================"); } } }