using System.Collections.Generic; using asap.core; using UnityEngine; using Game; namespace PinballUncountable { public class PinballUncountableDome : MonoBehaviour { [SerializeField] private Rigidbody2D[] balls; [Range(0f, 1f)][SerializeField] private float xRange = 1f; [SerializeField] private float yRange = 15f; [Header("Baked Replay (optional, no physics)")] [Tooltip("若赋值则使用烘焙轨迹回放;否则小球保持静止,不进行实时物理")] [SerializeField] private PinballUncountableDomeBakedData bakedData; private const float FrameInterval = 0.02f; // 50fps,与 SimBall 一致 private PinballUncountableFrameData[][] _replayFrames; private float _replayStartTime; private bool _isReplaying; private Collider2D[] _ballColliders; private bool _bakePhysicsCollisionEnabled = true; private void Awake() { CacheBallColliders(); ApplyCollisionMode(); } public void Kick(bool block = false) { if (block) { return; } _bakePhysicsCollisionEnabled = false; ApplyCollisionMode(); GContext.Publish(new EventUISound("audio_ui_eventUncountablePinball_cowboy_pinball_anim")); if (bakedData != null && bakedData.variants != null && bakedData.variants.Length > 0) { StartBakedReplay(); return; } StopBalls(); } private void StartBakedReplay() { var variant = bakedData.variants[Random.Range(0, bakedData.variants.Length)]; if (variant?.trajectories == null || variant.trajectories.Length == 0) { StopBalls(); return; } int ballCount = Mathf.Min(balls.Length, variant.trajectories.Length); _replayFrames = new PinballUncountableFrameData[ballCount][]; for (int i = 0; i < ballCount; i++) { _replayFrames[i] = PinballUncountableDomeBakedData.ToFrameData(variant.trajectories[i]); if (_replayFrames[i] == null || _replayFrames[i].Length == 0) { StopBalls(); return; } } for (int i = 0; i < balls.Length; i++) { var rb = balls[i]; rb.simulated = false; rb.velocity = Vector2.zero; if (i < _replayFrames.Length && _replayFrames[i].Length > 0) rb.transform.position = _replayFrames[i][0].Position; } foreach (var ball in balls) { var trail = ball.GetComponentInChildren(); if (trail != null) trail.Clear(); } _replayStartTime = Time.time; _isReplaying = true; } public void StopBalls() { _isReplaying = false; _replayFrames = null; foreach (var ball in balls) { if (ball == null) continue; ball.velocity = Vector2.zero; ball.angularVelocity = 0f; ball.simulated = false; } } private void Update() { if (!_isReplaying || _replayFrames == null || balls == null) return; float elapsed = Time.time - _replayStartTime; float frameFloat = elapsed / FrameInterval; int frameIndex = (int)frameFloat; bool anyStillPlaying = false; for (int i = 0; i < _replayFrames.Length && i < balls.Length; i++) { var frames = _replayFrames[i]; var ball = balls[i]; if (frameIndex >= frames.Length - 1) { ball.transform.position = frames[frames.Length - 1].Position; continue; } anyStillPlaying = true; int nextFrame = frameIndex + 1; float t = frameFloat - frameIndex; ball.transform.position = Vector2.Lerp(frames[frameIndex].Position, frames[nextFrame].Position, t); } if (!anyStillPlaying) { StopBalls(); } } public void SetBakePhysicsCollisionEnabled(bool enabled) { _bakePhysicsCollisionEnabled = enabled; CacheBallColliders(); ApplyCollisionMode(); } private void CacheBallColliders() { if (balls == null || balls.Length == 0) { _ballColliders = System.Array.Empty(); return; } var colliders = new List(); foreach (var ball in balls) { if (ball == null) continue; colliders.AddRange(ball.GetComponentsInChildren(true)); } _ballColliders = colliders.ToArray(); } private void ApplyCollisionMode() { if (_ballColliders == null) return; foreach (var collider in _ballColliders) { if (collider != null) collider.enabled = _bakePhysicsCollisionEnabled; } } } } // namespace PinballUncountable