You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 08:57:06