using asap.core; using UnityEngine; namespace PinballUncountable { public class PinballUncountableSimBall : MonoBehaviour { // [SerializeField] private GameObject fxTail; [SerializeField] private TrailRenderer trailRenderer; // public GameObject FxTail => fxTail; private PinballUncountableFrameData[] _frames; private float _startTime; private bool _isPlaying; private const float FrameInterval = 0.02f; // 50fps private string _lastLoggedCollider = ""; // Track last logged collider to avoid spam /// /// 该小球从哪个 exit 出去 /// public int ExitIdx { get; private set; } /// /// 回放完成时的回调,参数为小球实例和出口编号 /// public System.Action OnReplayFinished; public void StartReplay(PinballUncountableFrameData[] frames, int exitIdx) { if (frames == null || frames.Length == 0) { Debug.LogError("Data error: Invalid data provided!"); return; } _frames = frames; ExitIdx = exitIdx; gameObject.SetActive(true); StartReplay(); } public async void StartReplay() { try { if (_frames == null || _frames.Length == 0) { Debug.LogError("BallReplay: No data to replay!"); return; } _startTime = Time.time; _isPlaying = true; await Awaiters.NextFrame; trailRenderer.Clear(); } catch (System.Exception e) { Debug.LogError(e); } } public void StopReplay() { _isPlaying = false; gameObject.SetActive(false); } private void Update() { if (!_isPlaying || _frames == null || _frames.Length == 0) return; float elapsed = Time.time - _startTime; float frameFloat = elapsed / FrameInterval; // Convert to 50fps frame index int frameIndex = (int)frameFloat; // Stop condition: freeze at last position if (frameIndex >= _frames.Length - 1) { transform.position = _frames[_frames.Length - 1].Position; StopReplay(); // 通知系统回收对象,传递出口编号 OnReplayFinished?.Invoke(this, ExitIdx); return; } int nextFrame = frameIndex + 1; // if (nextFrame == 20) // { // fxTail.SetActive(true); // } float t = frameFloat - frameIndex; // Interpolation factor (0-1) // Linear interpolation between current and next recorded frame transform.position = Vector2.Lerp(_frames[frameIndex].Position, _frames[nextFrame].Position, t); // Log collision if collider is present and different from last logged string currentCollider = _frames[frameIndex].Collider; if (!string.IsNullOrEmpty(currentCollider) && currentCollider != _lastLoggedCollider) { // Debug.Log($"[PinballUncountable] Collision with: {currentCollider}"); GContext.container.Resolve().EventAggregator.Publish(new EventHitDisc { DiscName = currentCollider }); _lastLoggedCollider = currentCollider; } else if (string.IsNullOrEmpty(currentCollider) && !string.IsNullOrEmpty(_lastLoggedCollider)) { // Reset when collision ends _lastLoggedCollider = ""; } } } }