// MIT License - Copyright (c) 2025 wallstop
// Full license text: https://github.com/wallstop/unity-helpers/blob/main/LICENSE
namespace WallstopStudios.UnityHelpers.Utils
{
using System;
using System.Collections.Generic;
using Core.Attributes;
using Core.Helper;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
///
/// Polygon collider optimizer. Removes points from the collider polygon with
/// the given reduction Tolerance
///
[AddComponentMenu("2D Collider Optimization/ Polygon Collider Optimizer")]
[RequireComponent(typeof(PolygonCollider2D))]
public sealed class PolygonCollider2DOptimizer : MonoBehaviour
{
[Serializable]
private sealed class Path
{
public List points = new();
public Path() { }
public Path(IEnumerable points)
{
this.points.AddRange(points);
}
}
public double tolerance;
[SiblingComponent]
#pragma warning disable CS0649 // Field is never assigned to, and will always have its default value
private PolygonCollider2D _collider;
#pragma warning restore CS0649 // Field is never assigned to, and will always have its default value
[SerializeField]
private List _originalPaths = new();
public void Refresh()
{
OnValidate();
}
private void OnValidate()
{
if (_collider == null)
{
this.AssignRelationalComponents();
}
/*
When first getting a reference to the collider save the paths
so that the optimization is re-doable (by performing it on the original path
every time)
*/
if (_originalPaths.Count == 0)
{
for (int i = 0; i < _collider.pathCount; ++i)
{
Vector2[] current = _collider.GetPath(i);
List points = new(current);
// Preserve closed-loop paths as originally authored by ensuring the last point
// matches the first when applicable (Unity may omit the duplicate end point).
if (points.Count > 0)
{
Vector2 first = points[0];
Vector2 last = points[^1];
if (first != last)
{
points.Add(first);
}
}
Path path = new(points);
_originalPaths.Add(path);
}
}
//Reset the original paths
if (tolerance <= 0)
{
for (int i = 0; i < _originalPaths.Count; ++i)
{
_collider.SetPath(i, _originalPaths[i].points);
}
return;
}
for (int i = 0; i < _originalPaths.Count; ++i)
{
List path = _originalPaths[i].points;
List updatedPath = LineHelper.SimplifyPrecise(path, tolerance);
_collider.SetPath(i, updatedPath);
}
#if UNITY_EDITOR
if (!Application.isPlaying)
{
EditorUtility.SetDirty(this);
}
#endif
}
}
}