unity3d 如何在Unity中使用JSON文件中的时间戳数据

vtwuwzda  于 2023-04-21  发布在  其他
关注(0)|答案(1)|浏览(92)

我目前正在处理一个JSON文件夹,这些文件是通过无人机跟踪实验收集的。数据包含无人机在跟踪系统中移动和悬浮时的位置,旋转和时间戳。我目前正在做的是尝试使用这些数据在Unity中模拟无人机的移动。到目前为止,我已经成功地解析了Unity中对象的位置和旋转,并将时间戳提取到Unity中的System.DateTime。但是,我不知道如何使用时间戳。我想使用时间戳来匹配对象的位置和旋转(即:在这个时间戳,无人机应该在这个位置(x,y,z)并且具有旋转(x,y,z,w)).有人能帮助我解决这个问题吗,真的很感谢你的帮助:D这是我现在的代码:

void Update()
 {
     if (loaded)
     {
         for(int i = 0; i <= pos_data.Count; i+= 10)
         {
             Cube.transform.position = pos_data[i];
             Cube.transform.rotation = rot_data[i];
         }
     }
     else
     {
         LoadJson();
         //startTime = datetime[0];
         loaded = true;
     }
 }
 public void LoadJson()
 {
     string HeadPath = @Application.dataPath + "/Data/" + "drone_data_1.json";
     string HeadJsonhold = File.ReadAllText(HeadPath);
     var data_ = JSON.Parse(HeadJsonhold);
     
     for (int rows = 0; rows <= data_.Count; rows += 10)
     {
         pos_data.Add(new Vector3(data_[rows]["location"]["x"].AsFloat, data_[rows]["location"]["y"].AsFloat, data_[rows]["location"]["z"].AsFloat));
         rot_data.Add(new Quaternion(data_[rows]["rotation"]["x"].AsFloat, data_[rows]["rotation"]["y"].AsFloat, data_[rows]["rotation"]["z"].AsFloat, data_[rows]["rotation"]["w"].AsFloat));
         Time = System.DateTime.ParseExact(data_[rows]["Timestamp"], "yyyyMMddHHmmss",null);
         //Debug.Log(Time);
     }    
 }
nfzehxib

nfzehxib1#

如果我没理解错的话,你得到的是真实世界无人机的样本,它以一定的速度存储了它运动的关键帧。
现在您已经成功加载了json数据,但不知道如何相应地为Unity对象设置动画。

时间戳本身你根本不能使用!^^

它很可能就在过去的某个地方;你不能只给Time赋值。
但是,您可以做的是获取第一个样本的时间戳(我假设您的样本都已经按时间排序),并计算与下一个样本的差异,依此类推。
然后,您可以使用该差异,以便始终使用给定的时间增量在当前和下一个采样变换之间进行插值。
目前你只是在一个帧中完成所有采样**,所以根本不会有任何动画。
也正如sidenote:

for(int i = 0; i <= pos_data.Count; i+= 10)

错了两次:

  • a)加载数据时已经跳过了10个样本-〉您确定现在要再次跳过其中的10个样本=〉每次总共跳过100个样本?
  • B)由于索引是基于0的,所以最后可访问的索引将是pos_data.Count - 1,因此通常当迭代列表/数组时,它应该是i < pos_data.Count;)
    **更新:**关于插值的更好解决方案,请参考this newer post-下面的数据类型可以进行调整,以更好地适应其中提到的AnimationCurve方法。

首先,我建议你使用一个更好的数据结构,使用一个单独的列表来保存属于一起的信息,而不是多个并行列表,而是像这样加载你的json。

[Serializable]
public class Sample 
{
    public readonly Vector3 Position;
    public readonly Quaternion Rotation;
    public readonly float TimeDelta;

    public Sample(Vector3 position, Quaternion rotation, float timeDelta)
    {
        Position = position;
        Rotation = rotation;
        TimeDelta = timeDelta;
    }
}

然后呢

// Just making this serialized so you can immediately see in the Inspector
// if your data loaded correctly
[SerializeField] private readonly List<Sample> _samples = new List<Sample>();

public void LoadJson()
{
     // start fresh
     _samples.Clear();

     // See https://learn.microsoft.com/dotnet/api/system.io.path.combine
     var path = Path.Combine(Application.dataPath, "Data", "drone_data_1.json");
     var json = File.ReadAllText(path);
     var data = JSON.Parse(json);
     
     DateTime lastTime = default;

     for (var i = 0; i <= data.Count; i += 10)
     {
         // First I would pre-cache these values 
         var sample = data[i];
         var sampleLocation = sample["location"];
         var sampleRotation = sample["rotation"];
         var sampleTime = sample["Timestamp"];

         // Get your values as you did already
         var position = new Vector3(sampleLocation["x"].AsFloat, sampleLocation["y"].AsFloat, sampleLocation["z"].AsFloat));
         var rotation = new Quaternion(sampleRotation["x"].AsFloat, sampleRotation["y"].AsFloat, sampleRotation["z"].AsFloat, sampleRotation["w"].AsFloat));
         
         var time = System.DateTime.ParseExact(sampleTime, "yyyyMMddHHmmss", null);
          
         // Now for the first sample there is no deltaTime
         // for all others calculate the difference in seconds between the
         // last and current sample
         // See https://learn.microsoft.com/dotnet/csharp/language-reference/operators/conditional-operator
         var deltaTime = i == 0 ? 0f : GetDeltaSeconds(lastTime, time);
         // and of course store it for the next iteration
         lastTime = time;

         // Now you can finally add the sample to the list of samples
         // instead of having multiple parallel lists 
         _samples.Add(new Sample(position, rotation, deltaTime));
     }    
 }

private float GetDeltaSeconds(DateTime first, DateTime second)
{
    // See https://learn.microsoft.com/dotnet/api/system.datetime.op_subtraction#System_DateTime_op_Subtraction_System_DateTime_System_DateTime_
    var deltaSpan = second - first;
    // See https://learn.microsoft.com/dotnet/api/system.timespan.totalseconds#System_TimeSpan_TotalSeconds
    return (float)deltaSpan.TotalSeconds;
}

那么现在如何处理这些信息?

现在,您有样本(仍然假设按时间排序),其中包含所有所需的信息,以便能够在它们之间进行插值。
我会使用Coroutines而不是Update,在我看来,它们更容易理解和维护

// Do your loading **once** in Start
private void Start()
{
    LoadJson();

    // Then start the animation routine
    // I just make it a method so you could also start it later e.g. via button etc
    StartAnimation();
}

// A flag just in case to avoid concurrent animations
private bool alreadyAnimating;

// As said just making this a method so you could also remove it from Start
// and call it in any other moment you like
public void StartAnimation()
{
    // Only start an animation if there isn't already one running
    // See https://docs.unity3d.com/ScriptReference/MonoBehaviour.StartCoroutine.html
    if(!alreadyAnimating) StartCoroutine(AnimationRoutine());
}

private IEnumerator AnimationRoutine()
{
    // Just in case abort if there is already another animation running
    if(alreadyAnimating) yield break;

    // Block concurrent routine
    alreadyAnimating = true;

    // Initially set your object to the first sample
    var lastSample = _samples[0];
    Cube.transform.position = lastSample.Position;
    Cube.transform.rotation = lastSample.Rotation;

    // This tells Unity to "pause" the routine here, render this frame
    // and continue from here in the next frame
    yield return null;

    // then iterate through the rest of samples
    for(var i = 1; i < _samples.Count; i++)
    {
        var lastPosition = lastSample.Position;
        var lastRottaion = lastSample.Rottaion;

        var currentSample = _samples[i];
        var targetPosition = sample.Position;
        var targetRotation = sample.Rotation; 

        // How long this interpolation/animation will take
        var duration = currentSample.TimeDelta;
       
        // You never know ;)
        // See https://docs.unity3d.com/ScriptReference/Mathf.Approximately.html
        if(Mathf.Approximately(duration, 0f))
        {
            Cube.transform.position = targetPosition;
            Cube.transform.rotation = targetRotation;
            lastSample = currentSample;
            continue;
        }
     
        // And this is where the animation magic happens
        var timePassed = 0f; 
        while(timePassed < duration)
        {
            // this factor will be growing linear between 0 and 1
            var factor = timePassed / duration;

            // Interpolate between the "current" transforms (the ones it had at beginning of this iteration)
            // towards the next sample target transforms using the factor between 0 and 1
            // See https://docs.unity3d.com/ScriptReference/Vector3.Lerp.html
            Cube.transform.position = Vector3.Lerp(lastPosition, targetPosition, factor);
            // See https://docs.unity3d.com/ScriptReference/Quaternion.Slerp.html
            Cube.transform.rotation = Quaternion.Slerp(lastRotation, targetRotation, factor);

            // This tells Unity to "pause" the routine here, render this frame
            // and continue from here in the next frame
            yield return null;

            // increase by the time passed since the last frame was rendered
            timePassed += Time.deltaTime;
        }

        // just to be sure to end with clean values
        Cube.transform.position = targetPosition;
        Cube.transform.rotation = targetRotation;
        lastSample = currentSample;
    }

    // Allow the next animation to start (or restart this one)
    alreadyAnimating = false;

    // Additional stuff to do once the animation is done
}

相关问题