If I understand you correctly what you are getting are samples of a real-world drone that at some rate stores keyframes of its movement.
Now you already successfully load that json data but wonder how to animate the Unity object accordingly.
The timestamp itself you can't use at all! ^^
It most probably lies somewhere in the past ;) And you can't just assign something to Time
.
What you can do, however, is take the timestamp of the first sample (I will just assume that your samples are all already ordered by the time) and calculate the difference to the next sample and so on.
Then you can use that difference in order to always interpolate between the current and next sample transforms using the given time delta.
Currently you are just doing all samples in one single frame so there won't be any animation at all.
Also just as sidenote:
for(int i = 0; i <= pos_data.Count; i+= 10)
is wrong twice:
- a) you already skipped 10 samples when loading the data -> are you sure you now want to again skip 10 of these => In total every time skipping 100 samples?
- b) since indices are
0
based the last accessible index would be pos_data.Count - 1
so in general when iterating Lists/arrays it should be i < pos_data.Count
;)
UPDATE: For a better solution for interpolation refer to this newer post - below data types could be adjusted to better fit the AnimationCurve
approach mentioned there.
First of all I would suggest you use a better data structure and use one single list holding the information that belongs together instead of multiple parallel lists and rather load your json like e.g.
[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;
}
}
And then
// 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;
}
So now what to do with this information?
You now have samples (still assuming ordered by time) holding all required information to be able to interpolate between them.
I would use Coroutines instead of Update
, in my eyes they are easier to understand and maintain
// 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
}