Сигналы C#: параметры и время жизни
Объявите собственный сигнал, передайте значение и управляйте временем жизни подписки.
Собственный сигнал
Сохраните в Health.cs и прикрепите к обычной Entity:
using Reckfell;
public partial class Health : Entity
{
[Signal]
public delegate void HealthChangedEventHandler(int remaining);
public int Remaining { get; private set; } = 100;
public void Damage(int amount)
{
if (amount <= 0 || Remaining == 0)
return;
Remaining = System.Math.Max(0, Remaining - amount);
EmitSignal(SignalName.HealthChanged, Remaining);
}
}[Signal] ставится на открытый делегат. Его имя заканчивается на EventHandler; генератор создаёт событие HealthChanged и SignalName.HealthChanged. Параметры должны поддерживаться Variant. Соберите проект, чтобы сгенерированные члены появились в IDE и редакторе.
Damage(25) уменьшает здоровье со 100 до 75 и отправляет одно событие. Нулевой и отрицательный урон ничего не меняют. После смерти повторный урон не отправляет событие с тем же нулём. EmitSignal() запускает сигнал движка; вызывать событие как HealthChanged?.Invoke(...) здесь не нужно.
Слушатель с явным отключением
Сохраните HealthListener.cs, прикрепите к другой Entity и назначьте Source ссылкой на объект со скриптом Health. Назначение источника делается до запуска или до AddChild() из кода:
using Reckfell;
public partial class HealthListener : Entity
{
[Export] public Health? Source { get; set; }
private Health? _subscribed;
public int LastHealth { get; private set; }
public override void _EnterTree()
{
if (!ReckfellObject.IsInstanceValid(Source))
return;
_subscribed = Source;
_subscribed!.HealthChanged += OnHealthChanged;
OnHealthChanged(_subscribed.Remaining);
}
public override void _ExitTree()
{
if (ReckfellObject.IsInstanceValid(_subscribed))
_subscribed!.HealthChanged -= OnHealthChanged;
_subscribed = null;
}
private void OnHealthChanged(int remaining)
{
LastHealth = remaining;
ReckfellApi.Print("Health: ", remaining);
}
}Слушатель сразу считывает текущее значение: предыдущие события не воспроизводятся. При выходе из дерева подписка снимается. При повторном входе создаётся одна новая подписка, а не ещё одна поверх старой. Пример не переключает источник на лету: если изменять Source во время работы, реализуйте явное отключение старого и подключение нового.
Не рассчитывайте на автоматическое отключение всех C#-делегатов. Пользовательские сигналы и лямбды с захваченными переменными требуют внимания к времени жизни. Для отписки от лямбды сохраните тот же экземпляр делегата; новая лямбда с таким же текстом не снимает прежнюю подписку.
Ожидание одного сигнала
Следующий метод поместите внутрь скрипта Entity; нужны using Reckfell; и класс Health выше:
public async System.Threading.Tasks.Task WaitForNextDamage(Health health)
{
Variant[] values = await ToSignal(health, Health.SignalName.HealthChanged);
ReckfellApi.Print(values[0].AsInt32());
}Ожидание завершается следующим событием после начала ожидания. Сам ToSignal() не задаёт тайм-аут или отмену. Если отправитель удалён раньше события, нельзя считать продолжение гарантированным; для долгих игровых операций проектируйте отмену отдельно и проверяйте действительность объектов после await.
Если нужен OneShot или Deferred, используйте Connect() с Callable и ReckfellObject.ConnectFlags, проверьте возвращённый Error. Подписка через += не передаёт эти флаги. Справочник ReckfellObject, основы сигналов.