Unity3D读取CSV时序数据控制游戏对象旋转过慢问题求助
问题:CSV驱动的对象旋转速度过慢解决方案
问题描述
我有一个CSV文件,第一列是时间向量(time,单位毫秒),第二列是俯仰角向量(pitch),数据共220000条,覆盖220秒时长。需求是按下R键后,游戏对象沿X轴按照CSV中pitch列的角度,对应time列的时序进行旋转,完整轨迹需在220秒内完成。但编写的C#脚本挂载后,旋转速度远慢于预期。
原脚本:
using System.Collections; using System.Collections.Generic; using System.IO; using UnityEngine; using System.Globalization; public class CsvReader : MonoBehaviour { public TextAsset csvFile; // Create arrays to store the values in "time" and "pitch" columns float[] timeArray = new float[220000]; float[] pitchArray = new float[220000]; int i = 0; void Update() { if (Input.GetKey(KeyCode.R)) { StartCoroutine(ReadCsv()); } } IEnumerator ReadCsv() { string[] records = csvFile.text.Split('\n'); for (int i = 1; i < records.Length; i++) { string[] fields = records[i].Split(','); // Store the values in "time" and "pitch" columns in separate arrays timeArray[i] = float.Parse(fields[0], CultureInfo.InvariantCulture); pitchArray[i] = float.Parse(fields[1], CultureInfo.InvariantCulture); Debug.Log(pitchArray[i]); transform.rotation = Quaternion.Euler(pitchArray[i], 0f, 0f); yield return new WaitForSeconds(0.001f); // this is the delay between each position change (1 ms) } } }
问题根源
- 协程等待逻辑错误:
WaitForSeconds(0.001f)的精度受Unity帧率限制,实际等待时间远大于1毫秒,加上22万条数据的遍历开销,总时长会远超220秒。 - 解析与旋转同步执行:每帧都在做字符串拆分、浮点数解析,额外增加性能开销,且
Input.GetKey(KeyCode.R)会每帧启动新协程,导致逻辑混乱。 - 忽略时间戳时序:固定间隔遍历数据,未利用CSV中time列的真实时序关系,无法精准匹配时间进度。
优化解决方案
1. 提前解析CSV数据
在游戏启动时完成CSV解析,避免运行时性能损耗:
using System.Collections; using System.Collections.Generic; using UnityEngine; using System.Globalization; public class CsvRotationController : MonoBehaviour { public TextAsset csvFile; private List<(float timeMs, float pitch)> rotationData = new List<(float, float)>(); private bool isPlaying = false; private float startTime; void Start() { ParseCsv(); } void ParseCsv() { string[] records = csvFile.text.Split('\n'); for (int i = 1; i < records.Length; i++) { string line = records[i].Trim(); if (string.IsNullOrEmpty(line)) continue; if (line.Split(',').Length < 2) continue; string[] fields = line.Split(','); if (float.TryParse(fields[0], NumberStyles.Float, CultureInfo.InvariantCulture, out float timeMs) && float.TryParse(fields[1], NumberStyles.Float, CultureInfo.InvariantCulture, out float pitch)) { rotationData.Add((timeMs, pitch)); } } // 确保数据按时间排序(若CSV未预先排序) rotationData.Sort((a, b) => a.timeMs.CompareTo(b.timeMs)); }
2. 基于真实时间的插值旋转
在Update中根据已流逝时间匹配对应角度,支持平滑插值:
void Update() { // 按下R键切换播放/暂停状态 if (Input.GetKeyDown(KeyCode.R)) { isPlaying = !isPlaying; if (isPlaying) startTime = Time.time; } if (isPlaying && rotationData.Count > 0) { float elapsedTimeMs = (Time.time - startTime) * 1000f; // 超过总时长自动停止 if (elapsedTimeMs > rotationData[^1].timeMs) { isPlaying = false; transform.rotation = Quaternion.Euler(rotationData[^1].pitch, 0f, 0f); return; } // 查找当前时间对应的前后数据点,计算插值角度 for (int i = 0; i < rotationData.Count - 1; i++) { var current = rotationData[i]; var next = rotationData[i + 1]; if (elapsedTimeMs >= current.timeMs && elapsedTimeMs <= next.timeMs) { float t = (elapsedTimeMs - current.timeMs) / (next.timeMs - current.timeMs); float currentPitch = Mathf.Lerp(current.pitch, next.pitch, t); transform.rotation = Quaternion.Euler(currentPitch, 0f, 0f); break; } } } } }
3. 额外优化建议
- 移除Debug.Log:原脚本中的
Debug.Log(pitchArray[i])会严重拖慢性能,必须删除。 - 固定间隔数据优化:如果CSV时间间隔固定为1毫秒,可直接通过索引计算角度,无需遍历查找:
float elapsedTimeMs = (Time.time - startTime) * 1000f; int index = Mathf.Clamp(Mathf.FloorToInt(elapsedTimeMs), 0, rotationData.Count - 1); transform.rotation = Quaternion.Euler(rotationData[index].pitch, 0f, 0f);
内容的提问来源于stack exchange,提问作者jket
相关产品推荐
相关产品推荐

