Files
GeoSusGame/Assets/GlassRingController.cs
2026-04-26 14:35:54 +02:00

194 lines
5.9 KiB
C#

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<AudioSource>();
if (audioSource == null)
audioSource = gameObject.AddComponent<AudioSource>();
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<GlassPiece> foundPieces = new List<GlassPiece>();
Transform[] allChildren = GetComponentsInChildren<Transform>(includeInactive);
foreach (Transform t in allChildren)
{
if (t == transform) continue;
Renderer rend = t.GetComponent<Renderer>();
if (rend == null)
rend = t.GetComponentInChildren<Renderer>();
if (rend == null)
continue;
Collider col = t.GetComponent<Collider>();
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<BoxCollider>();
if (box == null)
box = t.gameObject.AddComponent<BoxCollider>();
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<GlassPiece>();
if (piece == null)
piece = t.gameObject.AddComponent<GlassPiece>();
Rigidbody rb = t.GetComponent<Rigidbody>();
if (rb == null)
rb = t.gameObject.AddComponent<Rigidbody>();
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);
}
}