using UnityEngine; namespace Minigames.Airplane.Interactive { /// /// Gravity well that pulls airplanes toward its Y position (vertical only). /// Plane below the well gets pulled UP, plane above gets pulled DOWN. /// Does NOT affect horizontal (X) velocity - plane maintains forward momentum. /// Creates challenging "danger zones" that players must avoid or escape from. /// [RequireComponent(typeof(Collider2D))] public class AirplaneGravityWell : MonoBehaviour, ISpawnInitializable { [Header("Gravity Configuration")] [SerializeField] private float pullStrength = 5f; [SerializeField] private bool useInverseSquare = false; [SerializeField] private float minPullDistance = 0.5f; [Header("Optional Modifiers")] [SerializeField] private float maxPullForce = 15f; [SerializeField] private AnimationCurve pullFalloff = AnimationCurve.Linear(0, 1, 1, 0); [Header("Visual Feedback (Optional)")] [SerializeField] private ParticleSystem gravityParticles; [SerializeField] private SpriteRenderer centerSprite; [SerializeField] private float rotationSpeed = 90f; [Header("Debug")] [SerializeField] private bool showDebugLogs; [SerializeField] private bool drawDebugLines = true; private Vector2 centerPosition; private void Awake() { // Ensure collider is trigger var collider = GetComponent(); if (collider != null) { collider.isTrigger = true; } } /// /// Called by spawn manager after object is positioned. /// Caches the final Y position for pull calculations. /// public void Initialize() { centerPosition = transform.position; } private void Update() { // Rotate center visual if present if (centerSprite != null) { centerSprite.transform.Rotate(0, 0, rotationSpeed * Time.deltaTime); } } private void OnTriggerStay2D(Collider2D other) { // Check if it's an airplane var airplane = other.GetComponent(); if (airplane == null || !airplane.IsFlying) return; var rb = other.GetComponent(); if (rb == null) return; // Calculate VERTICAL distance only (Y-axis only) Vector2 airplanePos = rb.position; float yDistance = centerPosition.y - airplanePos.y; float absYDistance = Mathf.Abs(yDistance); // Prevent division by zero if (absYDistance < minPullDistance) { absYDistance = minPullDistance; } // Calculate pull force based on vertical distance float forceMagnitude; if (useInverseSquare) { // Realistic gravity-like force (inverse square law) forceMagnitude = pullStrength / (absYDistance * absYDistance); } else { // Linear falloff based on vertical distance var collider = GetComponent(); float maxDistance = collider != null ? collider.bounds.extents.y * 2f : 5f; // Use height, not diagonal float normalizedDistance = Mathf.Clamp01(absYDistance / maxDistance); float falloff = pullFalloff.Evaluate(1f - normalizedDistance); forceMagnitude = pullStrength * falloff; } // Clamp force forceMagnitude = Mathf.Min(forceMagnitude, maxPullForce); // Apply force ONLY in Y direction, toward the well's Y position // If plane is below (yDistance > 0), pull up (+Y) // If plane is above (yDistance < 0), pull down (-Y) float yForceDirection = yDistance > 0 ? 1f : -1f; Vector2 pullForce = new Vector2(0f, yForceDirection * forceMagnitude); rb.AddForce(pullForce, ForceMode2D.Force); if (showDebugLogs && Time.frameCount % 30 == 0) // Log every 30 frames { Debug.Log($"[AirplaneGravityWell] Pulling {other.name}: force={forceMagnitude:F2} {(yForceDirection > 0 ? "UP" : "DOWN")}, Y-distance={absYDistance:F2}"); } } private void OnDrawGizmos() { // Visualize gravity well in editor Gizmos.color = new Color(1f, 0f, 1f, 0.3f); // Magenta transparent var collider = GetComponent(); if (collider != null) { // Draw zone bounds Gizmos.DrawWireSphere(collider.bounds.center, collider.bounds.extents.magnitude); // Draw center point Gizmos.color = Color.magenta; Gizmos.DrawWireSphere(transform.position, 0.5f); // Draw pull strength indicator Gizmos.DrawRay(transform.position, Vector3.up * pullStrength * 0.2f); } } private void OnDrawGizmosSelected() { if (!drawDebugLines) return; // Draw pull force visualization at multiple points var collider = GetComponent(); if (collider == null) return; float radius = collider.bounds.extents.magnitude; int samples = 8; for (int i = 0; i < samples; i++) { float angle = (i / (float)samples) * 360f * Mathf.Deg2Rad; Vector2 offset = new Vector2(Mathf.Cos(angle), Mathf.Sin(angle)) * radius; Vector3 samplePoint = transform.position + (Vector3)offset; Vector3 direction = (transform.position - samplePoint).normalized; Gizmos.color = Color.yellow; Gizmos.DrawLine(samplePoint, samplePoint + direction * 2f); } } } }