pooper_minigame (#62)
Co-authored-by: Michal Pikulski <michal.a.pikulski@gmail.com> Reviewed-on: #62
This commit is contained in:
236
Assets/Editor/CustomEditorsAndDrawers/ObstacleEditor.cs
Normal file
236
Assets/Editor/CustomEditorsAndDrawers/ObstacleEditor.cs
Normal file
@@ -0,0 +1,236 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using Minigames.BirdPooper;
|
||||
|
||||
namespace Editor.CustomEditorsAndDrawers
|
||||
{
|
||||
/// <summary>
|
||||
/// Custom editor for Obstacle component.
|
||||
/// Provides utility button to auto-setup PolygonCollider2D from object bounds.
|
||||
/// </summary>
|
||||
[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<SpriteRenderer>(true);
|
||||
Collider2D[] allColliders = obstacle.GetComponentsInChildren<Collider2D>(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
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Count how many SpriteRenderers don't have ANY collider (enabled or disabled) on the same GameObject.
|
||||
/// </summary>
|
||||
private int CountRenderersWithoutColliders(Transform root)
|
||||
{
|
||||
int count = 0;
|
||||
SpriteRenderer spriteRenderer = root.GetComponent<SpriteRenderer>();
|
||||
if (spriteRenderer != null && root.GetComponent<Collider2D>() == null)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
|
||||
foreach (Transform child in root)
|
||||
{
|
||||
count += CountRenderersWithoutColliders(child);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Auto-fit all existing colliders recursively.
|
||||
/// </summary>
|
||||
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"
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create missing colliders and auto-fit all colliders recursively.
|
||||
/// </summary>
|
||||
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"
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recursively create PolygonCollider2D on GameObjects with Renderer but no Collider2D.
|
||||
/// Skips GameObjects that have disabled colliders (leaves them alone).
|
||||
/// </summary>
|
||||
private void CreateCollidersRecursive(Transform current, ref int createdCount)
|
||||
{
|
||||
// Check if this GameObject has a SpriteRenderer
|
||||
SpriteRenderer spriteRenderer = current.GetComponent<SpriteRenderer>();
|
||||
|
||||
if (spriteRenderer != null)
|
||||
{
|
||||
// Check if there's ANY collider (enabled or disabled)
|
||||
Collider2D existingCollider = current.GetComponent<Collider2D>();
|
||||
|
||||
// Only create if there's NO collider at all
|
||||
if (existingCollider == null)
|
||||
{
|
||||
Undo.AddComponent<PolygonCollider2D>(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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recursively auto-fit all ENABLED PolygonCollider2D components to their renderer bounds.
|
||||
/// Skips disabled colliders (leaves them alone).
|
||||
/// </summary>
|
||||
private void AutoFitCollidersRecursive(Transform current, ref int fittedCount)
|
||||
{
|
||||
// Try to fit collider on this GameObject
|
||||
PolygonCollider2D polyCollider = current.GetComponent<PolygonCollider2D>();
|
||||
Renderer renderer = current.GetComponent<Renderer>();
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fit a single PolygonCollider2D to its sprite bounds.
|
||||
/// Uses the sprite's local bounds directly to properly handle scale.
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user