using UnityEngine; using UnityEditor; using Minigames.BirdPooper; namespace Editor.CustomEditorsAndDrawers { /// /// Custom editor for Obstacle component. /// Provides utility button to auto-setup PolygonCollider2D from object bounds. /// [CustomEditor(typeof(Obstacle))] public class ObstacleEditor : UnityEditor.Editor { public override void OnInspectorGUI() { // Draw default inspector DrawDefaultInspector(); EditorGUILayout.Space(10); EditorGUILayout.LabelField("Collider Setup", EditorStyles.boldLabel); Obstacle obstacle = (Obstacle)target; // Count sprite renderers and colliders in hierarchy SpriteRenderer[] allSpriteRenderers = obstacle.GetComponentsInChildren(true); Collider2D[] allColliders = obstacle.GetComponentsInChildren(true); int enabledColliders = 0; int disabledColliders = 0; foreach (Collider2D col in allColliders) { if (col.enabled) enabledColliders++; else disabledColliders++; } int renderersWithoutColliders = CountRenderersWithoutColliders(obstacle.transform); EditorGUILayout.LabelField($"Sprite Renderers: {allSpriteRenderers.Length}", EditorStyles.miniLabel); EditorGUILayout.LabelField($"Colliders (enabled): {enabledColliders}", EditorStyles.miniLabel); if (disabledColliders > 0) { EditorGUILayout.LabelField($"Colliders (disabled): {disabledColliders} - will be left alone", EditorStyles.miniLabel); } if (renderersWithoutColliders > 0) { EditorGUILayout.LabelField($"Renderers missing colliders: {renderersWithoutColliders}", EditorStyles.miniLabel); } EditorGUILayout.Space(5); // Button to auto-fit existing colliders if (enabledColliders > 0) { if (GUILayout.Button("Auto Fit All Colliders to Bounds")) { AutoFitAllColliders(obstacle); } EditorGUILayout.HelpBox( "Adjusts all ENABLED colliders to match their renderer bounds. Disabled colliders are left alone.", MessageType.Info ); EditorGUILayout.Space(5); } // Button to create missing colliders and auto-fit all if (GUILayout.Button("Create & Adjust All Colliders")) { CreateAndAdjustAllColliders(obstacle); } EditorGUILayout.HelpBox( "Creates PolygonCollider2D on SpriteRenderers missing colliders, then auto-fits ENABLED colliders to bounds. Disabled colliders are left alone.", MessageType.Info ); } /// /// Count how many SpriteRenderers don't have ANY collider (enabled or disabled) on the same GameObject. /// private int CountRenderersWithoutColliders(Transform root) { int count = 0; SpriteRenderer spriteRenderer = root.GetComponent(); if (spriteRenderer != null && root.GetComponent() == null) { count++; } foreach (Transform child in root) { count += CountRenderersWithoutColliders(child); } return count; } /// /// Auto-fit all existing colliders recursively. /// private void AutoFitAllColliders(Obstacle obstacle) { int fittedCount = 0; AutoFitCollidersRecursive(obstacle.transform, ref fittedCount); Debug.Log($"[ObstacleEditor] Auto-fitted {fittedCount} collider(s) for '{obstacle.name}'"); EditorUtility.DisplayDialog( "Colliders Adjusted", $"Successfully adjusted {fittedCount} collider(s) to match their renderer bounds.", "OK" ); } /// /// Create missing colliders and auto-fit all colliders recursively. /// private void CreateAndAdjustAllColliders(Obstacle obstacle) { int createdCount = 0; int fittedCount = 0; CreateCollidersRecursive(obstacle.transform, ref createdCount); AutoFitCollidersRecursive(obstacle.transform, ref fittedCount); Debug.Log($"[ObstacleEditor] Created {createdCount} collider(s) and adjusted {fittedCount} collider(s) for '{obstacle.name}'"); EditorUtility.DisplayDialog( "Colliders Created & Adjusted", $"Created {createdCount} new collider(s) and adjusted {fittedCount} total collider(s).", "OK" ); } /// /// Recursively create PolygonCollider2D on GameObjects with Renderer but no Collider2D. /// Skips GameObjects that have disabled colliders (leaves them alone). /// private void CreateCollidersRecursive(Transform current, ref int createdCount) { // Check if this GameObject has a SpriteRenderer SpriteRenderer spriteRenderer = current.GetComponent(); if (spriteRenderer != null) { // Check if there's ANY collider (enabled or disabled) Collider2D existingCollider = current.GetComponent(); // Only create if there's NO collider at all if (existingCollider == null) { Undo.AddComponent(current.gameObject); EditorUtility.SetDirty(current.gameObject); createdCount++; } // If collider exists but is disabled, leave it alone (skip) } // Recurse to children foreach (Transform child in current) { CreateCollidersRecursive(child, ref createdCount); } } /// /// Recursively auto-fit all ENABLED PolygonCollider2D components to their renderer bounds. /// Skips disabled colliders (leaves them alone). /// private void AutoFitCollidersRecursive(Transform current, ref int fittedCount) { // Try to fit collider on this GameObject PolygonCollider2D polyCollider = current.GetComponent(); Renderer renderer = current.GetComponent(); // Only fit if collider exists, is ENABLED, and has a renderer if (polyCollider != null && polyCollider.enabled && renderer != null) { FitColliderToBounds(current, polyCollider, renderer); fittedCount++; } // If collider is disabled, skip it (leave it alone) // Recurse to children foreach (Transform child in current) { AutoFitCollidersRecursive(child, ref fittedCount); } } /// /// Fit a single PolygonCollider2D to its sprite bounds. /// Uses the sprite's local bounds directly to properly handle scale. /// private void FitColliderToBounds(Transform targetTransform, PolygonCollider2D polyCollider, Renderer renderer) { // Try to get SpriteRenderer for direct sprite bounds access SpriteRenderer spriteRenderer = renderer as SpriteRenderer; if (spriteRenderer != null && spriteRenderer.sprite != null) { // Use sprite's local bounds directly - this is already in local space and respects scale Bounds spriteBounds = spriteRenderer.sprite.bounds; Vector3 center = spriteBounds.center; Vector3 extents = spriteBounds.extents; // Create rectangle points (already in local space) Vector2[] points = new Vector2[4]; points[0] = new Vector2(center.x - extents.x, center.y - extents.y); // Bottom-left points[1] = new Vector2(center.x + extents.x, center.y - extents.y); // Bottom-right points[2] = new Vector2(center.x + extents.x, center.y + extents.y); // Top-right points[3] = new Vector2(center.x - extents.x, center.y + extents.y); // Top-left // Apply to collider Undo.RecordObject(polyCollider, "Auto Fit Collider"); polyCollider.SetPath(0, points); EditorUtility.SetDirty(polyCollider); } else { // Fallback to world space bounds if not a sprite renderer Bounds bounds = renderer.bounds; Vector3 center = targetTransform.InverseTransformPoint(bounds.center); Vector3 extents = bounds.extents; Vector2[] points = new Vector2[4]; points[0] = new Vector2(center.x - extents.x, center.y - extents.y); points[1] = new Vector2(center.x + extents.x, center.y - extents.y); points[2] = new Vector2(center.x + extents.x, center.y + extents.y); points[3] = new Vector2(center.x - extents.x, center.y + extents.y); Undo.RecordObject(polyCollider, "Auto Fit Collider"); polyCollider.SetPath(0, points); EditorUtility.SetDirty(polyCollider); } } } }