Component that tracks structural health of a GameObject and applies damage from sustained overload or impulses, plays a break sound, spawns debris prefab, and destroys the object when health reaches zero. It listens to trigger enter/exit to sum masses on top and applies periodic stress damage in fixed updates.
using Sandbox;
using System;
using System.Collections.Generic;
using System.Linq;
public sealed class StructuralStress : Component, Component.ITriggerListener
{
[Property] public float MaxStructuralHealth { get; set; } = 100f;
[Property] public float CurrentHealth { get; private set; } = 100f;
[Property] public float MassCapacity { get; set; } = 200f; // Максимальная масса сверху до начала разрушения
[Property] public bool IsFoundation { get; set; } = false; // Является ли несущей опорой
[Property] public SoundEvent BreakSound { get; set; }
[Property] public GameObject DebrisPrefab { get; set; }
private Rigidbody _rb;
private float _currentOverloadWeight = 0f;
protected override void OnStart()
{
CurrentHealth = MaxStructuralHealth;
_rb = Components.Get<Rigidbody>();
}
protected override void OnFixedUpdate()
{
// Проверяем перегрузку весом сверху
if ( _currentOverloadWeight > MassCapacity )
{
float excess = _currentOverloadWeight - MassCapacity;
ApplyStressDamage( excess * Time.Delta * 0.5f );
}
}
public void ApplyStressDamage( float damage )
{
CurrentHealth -= damage;
// Визуальная реакция — легкое встряхивание или треск при низком HP
if ( CurrentHealth <= 0f )
{
Collapse();
}
}
public void ApplyImpulseDamage( Vector3 force )
{
float impactStrength = force.Length / 100f;
if ( impactStrength > 5f )
{
ApplyStressDamage( impactStrength );
}
}
private void Collapse()
{
// Воспроизводим звук разрушения
if ( BreakSound != null )
{
Sound.Play( BreakSound, Transform.Position );
}
// Спавним осколки / Debris
if ( DebrisPrefab != null )
{
var debris = DebrisPrefab.Clone( Transform.World );
debris.Transform.Scale = Transform.Scale;
}
Log.Info( $"[StructuralStress] Объект {GameObject.Name} разрушился под напряжением!" );
// Уничтожаем основной объект
GameObject.Destroy();
}
void ITriggerListener.OnTriggerEnter( Collider other )
{
if ( other.Components.Get<Rigidbody>() is Rigidbody otherRb )
{
_currentOverloadWeight += otherRb.PhysicsBody.Mass;
}
}
void ITriggerListener.OnTriggerExit( Collider other )
{
if ( other.Components.Get<Rigidbody>() is Rigidbody otherRb )
{
_currentOverloadWeight = MathF.Max( 0f, _currentOverloadWeight - otherRb.PhysicsBody.Mass );
}
}
}