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 = "";
}
}
}
}