using System.Collections.Generic; using UnityEngine; public class GlassRingController : MonoBehaviour { [Header("Auto Setup")] public bool autoCollectOnAwake = true; public bool includeInactive = true; public float pieceMaxHealth = 100f; [Header("Rotation")] public float rotationSpeedDegrees = 45f; [Header("Audio")] public AudioSource audioSource; public AudioClip damageClip; public AudioClip breakClip; [Range(0f, 1f)] public float damageVolume = 0.7f; [Range(0f, 1f)] public float breakVolume = 1f; private GlassPiece[] pieces; public GlassPiece[] Pieces => pieces; private void Awake() { if (audioSource == null) audioSource = GetComponent(); if (audioSource == null) audioSource = gameObject.AddComponent(); if (Application.isPlaying && autoCollectOnAwake) { CollectPiecesFromChildren(); } } private void Update() { if (!Application.isPlaying || Mathf.Abs(rotationSpeedDegrees) < 0.001f) return; transform.Rotate(Vector3.right, rotationSpeedDegrees * Time.deltaTime, Space.World); } [ContextMenu("Collect Pieces From Children")] public void CollectPiecesFromChildren() { List foundPieces = new List(); Transform[] allChildren = GetComponentsInChildren(includeInactive); foreach (Transform t in allChildren) { if (t == transform) continue; Renderer rend = t.GetComponent(); if (rend == null) rend = t.GetComponentInChildren(); if (rend == null) continue; Collider col = t.GetComponent(); bool invalidMeshCollider = false; MeshCollider existingMeshCollider = col as MeshCollider; if (existingMeshCollider != null && existingMeshCollider.sharedMesh == null) { invalidMeshCollider = true; } if (col == null || invalidMeshCollider) { if (invalidMeshCollider) Destroy(existingMeshCollider); BoxCollider box = t.GetComponent(); if (box == null) box = t.gameObject.AddComponent(); Bounds worldBounds = rend.bounds; Vector3 localCenter = t.InverseTransformPoint(worldBounds.center); box.center = localCenter; Vector3 lossy = t.lossyScale; box.size = new Vector3( worldBounds.size.x / Mathf.Max(lossy.x, 0.0001f), worldBounds.size.y / Mathf.Max(lossy.y, 0.0001f), worldBounds.size.z / Mathf.Max(lossy.z, 0.0001f) ); col = box; } GlassPiece piece = t.GetComponent(); if (piece == null) piece = t.gameObject.AddComponent(); Rigidbody rb = t.GetComponent(); if (rb == null) rb = t.gameObject.AddComponent(); rb.isKinematic = true; rb.useGravity = false; foundPieces.Add(piece); } foundPieces.Sort((a, b) => { float angleA = Mathf.Atan2(a.transform.localPosition.z, a.transform.localPosition.x); float angleB = Mathf.Atan2(b.transform.localPosition.z, b.transform.localPosition.x); return angleA.CompareTo(angleB); }); pieces = foundPieces.ToArray(); for (int i = 0; i < pieces.Length; i++) { pieces[i].Initialize(i, pieceMaxHealth); } Debug.Log($"GlassRingController: collected {pieces.Length} pieces."); } public void ApplyProjectileImpact(int hitIndex, ProjectileBehaviour projectile, float charge01, Vector3 hitPoint) { if (pieces == null || pieces.Length == 0) return; float sigma = Mathf.Max(0.01f, projectile.sigma); float chargeMultiplier = 0.55f + 1.45f * Mathf.Pow(charge01, 1.4f); bool anyHealthChanged = false; bool anyNewBreak = false; for (int i = 0; i < pieces.Length; i++) { GlassPiece piece = pieces[i]; if (piece == null || piece.IsBroken) continue; int d = CircularDistance(i, hitIndex, pieces.Length); float gaussian = Mathf.Exp(-(d * d) / (2f * sigma * sigma)); if (gaussian < 0.005f) continue; float damage = projectile.baseDamage * chargeMultiplier * gaussian; if (i == hitIndex) damage *= projectile.directHitMultiplier; Vector3 impulseDir = piece.transform.position - hitPoint; if (impulseDir.sqrMagnitude < 0.0001f) impulseDir = piece.transform.position - transform.position; float impulse = projectile.breakImpulse * chargeMultiplier * gaussian; if (i == hitIndex) impulse *= projectile.directHitMultiplier; bool brokeNow = piece.ApplyDamage(damage, hitPoint, impulseDir, impulse); anyHealthChanged = true; if (brokeNow) anyNewBreak = true; } if (anyNewBreak) { PlayBreakSound(); } else if (anyHealthChanged) { PlayDamageSound(); } } private void PlayDamageSound() { if (audioSource != null && damageClip != null) audioSource.PlayOneShot(damageClip, damageVolume); } private void PlayBreakSound() { if (audioSource != null && breakClip != null) audioSource.PlayOneShot(breakClip, breakVolume); } private int CircularDistance(int a, int b, int count) { int raw = Mathf.Abs(a - b); return Mathf.Min(raw, count - raw); } }