using UnityEngine; public class BallBase : MonoBehaviour { public AudioClip spawnSound; public AudioClip bounceSound; public AudioClip hitSound; protected Animator animator; protected AudioSource audioSource; protected Vector2 speed; protected bool affectedByMagnet = true; protected bool magnetActiveLeftRight = false; protected bool magnetActiveTopBottom = false; protected struct MagnetBounceAxisState { public bool magnetWasActive; public bool bouncingToAnchor; public float anchor; public float target; public float bounceTimer; public int sideDirection; } void OnTriggerEnter2D(Collider2D other) { if(other.gameObject.CompareTag("Bar")) { if (!audioSource.isPlaying) { audioSource.PlayOneShot(hitSound); } // Destroy all bar segments when the ball hits a bar. var barSegments = GameObject.FindGameObjectsWithTag("Bar"); foreach (var barSegment in barSegments) { Destroy(barSegment); } // Notify the player that the bar has been hit. var player = GameObject.FindGameObjectWithTag("Player"); if (player != null) { player.GetComponent().OnBarHit(); } } } public void MagnetActive(bool flipped) { if (flipped) { magnetActiveTopBottom = true; } else { magnetActiveLeftRight = true; } } public void MagnetInactive() { magnetActiveTopBottom = false; magnetActiveLeftRight = false; } protected float ApplyMagnetBounceAxis( float currentValue, float minBound, float maxBound, bool magnetIsActive, ref MagnetBounceAxisState state, float colliderExtent, float bounceDuration, float centerLeeway) { if (!magnetIsActive) { state.magnetWasActive = false; state.sideDirection = 0; state.bounceTimer = 0.0f; return currentValue; } float center = (minBound + maxBound) * 0.5f; float halfSpan = (maxBound - minBound) * 0.5f; float maxLeeway = Mathf.Max(0.0f, halfSpan - colliderExtent); float safeLeeway = Mathf.Clamp(centerLeeway, 0.0f, maxLeeway); float leftAnchorMax = center - safeLeeway; float rightAnchorMin = center + safeLeeway; if (!state.magnetWasActive) { state.magnetWasActive = true; state.anchor = currentValue; state.sideDirection = state.anchor <= center ? -1 : 1; state.target = state.sideDirection < 0 ? minBound + colliderExtent : maxBound - colliderExtent; state.bouncingToAnchor = false; state.bounceTimer = 0.0f; } // Keep the selected side locked while magnet is active. if (state.sideDirection < 0) { state.anchor = Mathf.Min(state.anchor, leftAnchorMax); } else { state.anchor = Mathf.Max(state.anchor, rightAnchorMin); } state.bounceTimer += Time.fixedDeltaTime; float t = Mathf.Clamp01(state.bounceTimer / bounceDuration); if (state.bouncingToAnchor) { // Mirror NukeBall ascent: ease-out from target back to anchor. float easedT = 1.0f - Mathf.Pow(1.0f - t, 2.0f); currentValue = Mathf.Lerp(state.target, state.anchor, easedT); if (t >= 1.0f) { state.bouncingToAnchor = false; state.bounceTimer = 0.0f; currentValue = state.anchor; } } else { // Mirror NukeBall descent: ease-in from anchor down to side target. float easedT = t * t; currentValue = Mathf.Lerp(state.anchor, state.target, easedT); if (t >= 1.0f) { state.bouncingToAnchor = true; state.bounceTimer = 0.0f; currentValue = state.target; } } return currentValue; } protected bool TryCacheGameBoardBounds(out Bounds bounds) { var gameBoard = FindAnyObjectByType(); if (gameBoard != null) { return TryGetWorldBounds(gameBoard.gameObject, out bounds); } bounds = default; return false; } protected static bool TryGetWorldBounds(GameObject target, out Bounds bounds) { Collider2D collider2D = target.GetComponent(); if (collider2D != null) { bounds = collider2D.bounds; return true; } Renderer renderer = target.GetComponent(); if (renderer != null) { bounds = renderer.bounds; return true; } Collider collider3D = target.GetComponent(); if (collider3D != null) { bounds = collider3D.bounds; return true; } bounds = default; return false; } }