using System.Collections; using System.Collections.Generic; using UnityEngine; using System.Linq; using System.Threading.Tasks; using System; using System.Reactive; using UnityEngine.UI; namespace GhostSystem { public class GhostReplayManager : MonoBehaviour { [Header("Replay Settings")] public string SceneId; public string UserFilter = null; // optional: filtere nach User-ID public bool Loop = true; public DateTime selectedStartTime; public DateSelector dateSelector; [Header("Target Objects")] public List TrackedObjects; [Header("Playback")] public float PlaybackSpeed = 1f; public bool AutoStart = true; public Slider playbackSlider; private bool isScrubbing = false; private bool isPlaying = false; private float playbackTime = 0f; private float totalDuration = 0f; private Dictionary> objectTracks = new(); private bool supabaseReady = false; private bool sceneIdReady = false; private string loadedSceneId; [Serializable] public class TrackedObjectBinding { public string objectId; public GameObject targetObject; } private async void OnEnable() { SupabaseManager.OnSuperbaseReady += HandleSupabaseReady; RoomManager.OnSceneIdReady += HandleSceneIdReady; } private void OnDisable() { SupabaseManager.OnSuperbaseReady -= HandleSupabaseReady; RoomManager.OnSceneIdReady -= HandleSceneIdReady; } private void HandleSupabaseReady() { supabaseReady = true; TryInitializeReplay(); } private void HandleSceneIdReady(string id) { sceneIdReady = true; loadedSceneId = id; TryInitializeReplay(); } private async void TryInitializeReplay() { if (!supabaseReady || !sceneIdReady) return; SceneId = loadedSceneId; await LoadFrames(); StartReplay(); } public async Task LoadFrames() { Debug.Log("SceneId used in query: " + SceneId); var query = SupabaseManager.instance.supabase .From() .Filter("scene_id", Postgrest.Constants.Operator.Equals, SceneId) .Filter("timestamp", Postgrest.Constants.Operator.GreaterThanOrEqual, dateSelector.selectedDateTime().ToString("o")) .Order("timestamp", Postgrest.Constants.Ordering.Ascending); if (!string.IsNullOrEmpty(UserFilter)) query = query.Filter("user_id", Postgrest.Constants.Operator.Equals, UserFilter); var result = await query.Get(); var frames = result.Models; Debug.LogError("Frames: " + frames.Count); objectTracks.Clear(); foreach (var frame in frames) { if (!objectTracks.ContainsKey(frame.ObjectId)) objectTracks[frame.ObjectId] = new List(); objectTracks[frame.ObjectId].Add(frame); //Debug.Log($"Loaded frame: {frame.ObjectId} at {frame.Timestamp}"); Debug.Log($"Loaded frame: {frame.ObjectId} at {frame.TimestampParsed} Pos: {frame.Position}"); Debug.Log($"Parsed timestamp: {frame.Timestamp} -> {frame.TimestampParsed.ToString("o")}" + "Pos JSON: " + frame.PositionJson); } Debug.LogWarning("Got the Data!"); Debug.LogWarning(objectTracks.Count); // Gesamtzeit berechnen (in Sekunden) if (frames.Count > 1) { totalDuration = (float)(frames.Last().TimestampParsed - frames.First().TimestampParsed).TotalSeconds; } } public void StartReplay() // Für das Play UI-Element, um das Replay zu starten { isPlaying = true; playbackTime = 0f; Debug.LogError("Replay started: total duration = " + totalDuration); } public void PauseReplay() => isPlaying = false; // Für das Pause UI-Element, um das Replay zu pausieren public void StopReplay() // Für das Stop UI-Element, um das Replay zu beenden { isPlaying = false; playbackTime = 0f; } public void SetPlaybackTime(float time) // Für das Slider UI-Element zum Scrubben { playbackTime = Mathf.Clamp(time, 0f, totalDuration); } private void Update() { if (!isPlaying || objectTracks.Count == 0) return; playbackTime += Time.deltaTime * PlaybackSpeed; if (playbackTime > totalDuration) { if (Loop) playbackTime = 0f; else isPlaying = false; } DateTime currentTimestamp = objectTracks.Values .SelectMany(list => list) .First().TimestampParsed + TimeSpan.FromSeconds(playbackTime); foreach (var binding in TrackedObjects) { if (!objectTracks.ContainsKey(binding.objectId)) continue; var frames = objectTracks[binding.objectId]; if (frames.Count < 2) continue; FrameModel a = null, b = null; for (int i = 0; i < frames.Count - 1; i++) { var tA = frames[i].TimestampParsed; var tB = frames[i + 1].TimestampParsed; if (tA <= currentTimestamp && tB >= currentTimestamp) { a = frames[i]; b = frames[i + 1]; break; } } if (a != null && b != null) { double tA = a.TimestampParsed.Subtract(DateTime.UnixEpoch).TotalSeconds; double tB = b.TimestampParsed.Subtract(DateTime.UnixEpoch).TotalSeconds; double tCurrent = currentTimestamp.Subtract(DateTime.UnixEpoch).TotalSeconds; float t = Mathf.Clamp01((float)((tCurrent - tA) / (tB - tA))); Vector3 pos = Vector3.Lerp(a.Position, b.Position, t); Quaternion rot = Quaternion.Slerp(a.Rotation, b.Rotation, t); binding.targetObject.transform.SetPositionAndRotation(pos, rot); } } if (!isScrubbing && playbackSlider != null && totalDuration > 0f) { playbackSlider.value = playbackTime / totalDuration; } } /*public void OnSliderValueChanged(float value) { if (totalDuration <= 0f) return; isScrubbing = true; SetPlaybackTime(value * totalDuration); }*/ public void SetSliderScrubValue(float sliderValue) { if (isScrubbing) SetPlaybackTime(sliderValue * totalDuration); } public void OnSliderStartDrag() { isScrubbing = true; } public void OnSliderEndDrag() { isScrubbing = false; SetPlaybackTime(playbackSlider.value * totalDuration); } } }