using UnityEngine; namespace Interaction { public class InteractOutlineTarget : MonoBehaviour { [SerializeField] private Renderer[] renderers; [SerializeField] private bool autoFindRenderers = true; [SerializeField] private float outlineWidth = 0.02f; [SerializeField] private float emission = 1f; [Header("Debug")] [SerializeField] private bool debugLog; private bool highlighted; private Material outlineMaterial; private MaterialPropertyBlock mpb; private Material[][] originalSharedMaterials; private static readonly int OutlineColorId = Shader.PropertyToID("_OutlineColor"); private static readonly int OutlineWidthId = Shader.PropertyToID("_OutlineWidth"); private static readonly int EmissionId = Shader.PropertyToID("_Emission"); public void SetOutlineMaterial(Material material) { outlineMaterial = material; } public void SetHighlighted(bool value, Color color) { if (outlineMaterial == null) { if (debugLog) Debug.Log($"[InteractOutlineTarget] Missing outlineMaterial on '{name}'", this); return; } if (renderers == null || renderers.Length == 0) { if (autoFindRenderers) { renderers = GetComponentsInChildren(true); } } if (renderers == null || renderers.Length == 0) return; if (mpb == null) mpb = new MaterialPropertyBlock(); if (value) { mpb.SetColor(OutlineColorId, color); mpb.SetFloat(OutlineWidthId, outlineWidth); mpb.SetFloat(EmissionId, emission); } if (value && !highlighted) { CacheOriginalMaterialsIfNeeded(); AppendOutlineMaterial(); highlighted = true; } else if (!value && highlighted) { RestoreOriginalMaterials(); highlighted = false; } for (int i = 0; i < renderers.Length; i++) { var r = renderers[i]; if (r == null) continue; r.SetPropertyBlock(value ? mpb : null); } } private void CacheOriginalMaterialsIfNeeded() { if (originalSharedMaterials != null && originalSharedMaterials.Length == (renderers == null ? 0 : renderers.Length)) { return; } if (renderers == null) { originalSharedMaterials = null; return; } originalSharedMaterials = new Material[renderers.Length][]; for (int i = 0; i < renderers.Length; i++) { var r = renderers[i]; originalSharedMaterials[i] = r != null ? r.sharedMaterials : null; } } private void AppendOutlineMaterial() { if (renderers == null) return; for (int i = 0; i < renderers.Length; i++) { var r = renderers[i]; if (r == null) continue; var mats = r.sharedMaterials; if (mats == null) { r.sharedMaterials = new[] { outlineMaterial }; continue; } for (int j = 0; j < mats.Length; j++) { if (mats[j] == outlineMaterial) goto NextRenderer; } var newMats = new Material[mats.Length + 1]; for (int j = 0; j < mats.Length; j++) newMats[j] = mats[j]; newMats[mats.Length] = outlineMaterial; r.sharedMaterials = newMats; NextRenderer: ; } } private void RestoreOriginalMaterials() { if (renderers == null || originalSharedMaterials == null) return; int count = Mathf.Min(renderers.Length, originalSharedMaterials.Length); for (int i = 0; i < count; i++) { var r = renderers[i]; if (r == null) continue; r.sharedMaterials = originalSharedMaterials[i]; } } } }