Вы не прислали исходный код, поэтому ниже — проверенный шаблон рабочего скрипта и разбор типичных ошибок + варианты реализации (через C#-события и через UnityEvent для инспектора). Вставьте ваш код, если хотите, чтобы я поправил конкретно его.
1) Верный и простой вариант (C# events — удобно подписываться из кода):
```csharp
using System;
using UnityEngine;
public class Mana : MonoBehaviour
{
[SerializeField, Min(0f)]
private float maxMana = 100f;
[SerializeField, Min(0f)]
private float currentMana = 100f;
// публичные события для подписки из кода
public event Action<float> OnManaChanged;
public event Action OnDied;
// защищаем от многократного вызова смерти
private bool isDead;
public float CurrentMana => currentMana;
public float MaxMana => maxMana;
private void Awake()
{
// Корректируем начальные значения
currentMana = Mathf.Clamp(currentMana, 0f, maxMana);
isDead = currentMana <= 0f;
}
// Установить ману напрямую (вызывать событие при изменении и проверять смерть)
public void SetMana(float value)
{
float newVal = Mathf.Clamp(value, 0f, maxMana);
if (!Mathf.Approximately(newVal, currentMana))
{
currentMana = newVal;
OnManaChanged?.Invoke(currentMana);
}
if (currentMana <= 0f && !isDead)
{
isDead = true;
OnDied?.Invoke();
}
}
// Добавить ману (можно использовать отрицательное значение для уменьшения)
public void AddMana(float amount)
{
if (Mathf.Approximately(amount, 0f)) return;
SetMana(currentMana + amount);
}
// Потратить ману (удобное имя)
public void SpendMana(float amount)
{
if (amount <= 0f) return;
SetMana(currentMana - amount);
}
// Изменить максимум маны; опционально сохранить долю текущей маны
public void ChangeMaxMana(float newMax, bool keepRatio = true)
{
newMax = Mathf.Max(0f, newMax);
if (Mathf.Approximately(newMax, maxMana)) return;
if (keepRatio)
{
float ratio = (maxMana > 0f) ? currentMana / maxMana : 1f;
maxMana = newMax;
SetMana(maxMana * ratio);
}
else
{
maxMana = newMax;
SetMana(currentMana); // просто зафиксировать в новых рамках
}
}
}
```
2) Вариант с UnityEvent (если нужно назначать слушатели в инспекторе):
```csharp
using UnityEngine;
using UnityEngine.Events;
[Serializable] public class FloatEvent : UnityEvent<float> {}
public class Mana : MonoBehaviour
{
[SerializeField, Min(0f)] private float maxMana = 100f;
[SerializeField, Min(0f)] private float currentMana = 100f;
[SerializeField] private FloatEvent onManaChanged;
[SerializeField] private UnityEvent onDied;
private bool isDead;
public float CurrentMana => currentMana;
public float MaxMana => maxMana;
private void Awake()
{
currentMana = Mathf.Clamp(currentMana, 0f, maxMana);
isDead = currentMana <= 0f;
}
public void SetMana(float value)
{
float newVal = Mathf.Clamp(value, 0f, maxMana);
if (!Mathf.Approximately(newVal, currentMana))
{
currentMana = newVal;
onManaChanged?.Invoke(currentMana);
}
if (currentMana <= 0f && !isDead)
{
isDead = true;
onDied?.Invoke();
}
}
public void AddMana(float amount) => SetMana(currentMana + amount);
public void SpendMana(float amount) { if (amount > 0f) SetMana(currentMana - amount); }
}
```
3) Типичные ошибки и как их исправить
- Не клэмпить значение mаны (Mathf.Clamp) — может уйти в отрицательное или превысить максимум.
- Вызывать событие до обновления значения или не передавать актуальное значение.
- Не проверять null при вызове события — используйте OnChanged?.Invoke(...) или проверку на null.
- Вызывать событие смерти многократно — заведите флаг isDead, чтобы вызвать OnDied только один раз.
- Неправильно настроенные типы событий (например, UnityEvent без generic не принимает float).
- Использование целого типа (int) когда нужны дробные значения — решите заранее float или int.
- Забыл инициализировать currentMana в Awake/Start и не привести его к диапазону [0, max].
4) Как подписаться (пример)
- Через код (C# events):
mana.OnManaChanged += value => healthBar.SetValue(value);
mana.OnDied += () => Die();
- Через инспектор (UnityEvent version): добавить слушателей в поле onManaChanged / onDied.
Если пришлёте ваш исходный скрипт, я укажу конкретные строки с ошибками и предложу правки прямо в вашем коде.