Зависимости объектов: ссылки, интерфейсы и сигналы
Передавайте внешнюю цель явно; не привязывайте переиспользуемый объект к чужой иерархии.
Команда и уведомление
| Связь | Когда применять |
|---|---|
| Прямой вызов по переданной ссылке | Нужен результат команды сейчас |
| C#-интерфейс | Разные типы должны поддерживать одну команду |
| Сигнал | Объект сообщает об изменении, не выбирая получателей |
| Внутренний путь к ребёнку | Префаб сам владеет этой неизменной частью структуры |
HitController получает IDamageable через SetTarget. Ему не нужны GetParent, имя уровня или глобальный сервис. HealthModel реализует этот контракт без Entity, DamageReceiver — как объект движка.
HealthModel.cs
using System;
public interface IDamageable
{
bool TryApplyDamage(int amount);
}
public sealed class HealthModel : IDamageable
{
public int Maximum { get; }
public int Current { get; private set; }
public HealthModel(int maximum)
{
if (maximum <= 0) throw new ArgumentOutOfRangeException(nameof(maximum));
Maximum = maximum;
Current = maximum;
}
public bool TryApplyDamage(int amount)
{
if (amount <= 0 || Current == 0) return false;
Current = amount >= Current ? 0 : Current - amount;
return true;
}
}HitController.cs
using Reckfell;
public partial class HitController : Entity
{
private IDamageable? _target;
public void SetTarget(IDamageable? target) => _target = target;
public bool TryHit(int amount)
{
if (_target is ReckfellObject native && !ReckfellObject.IsInstanceValid(native))
{
_target = null;
return false;
}
if (_target is Entity entity && entity.IsQueuedForDeletion()) return false;
return _target?.TryApplyDamage(amount) ?? false;
}
public override void _ExitTree() => _target = null;
}Время жизни ссылки
C#-ссылка может остаться после Free нативного объекта. Контроллер проверяет IsInstanceValid до обращения к Entity и отдельно отклоняет цель с QueueFree, пока удаление ещё отложено. При _ExitTree ссылка очищается; после повторного добавления назначьте цель заново.
RemoveChild не удаляет контроллер. Если сохраняете отсоединённый объект, явно определите, кто позже вызовет Free или вернёт его в иерархию.
IDamageable — C#-контракт, а не экспортируемый тип данных. Этот интерфейс не включает автоматическое сохранение ссылок, RPC или возможность передать произвольную модель через Variant.
Начальное состояние и сигнал
DamageReceiver отправляет HealthChanged после успешного изменения. Подписка, сделанная после _Ready, не получает уже прошедшее начальное уведомление: сначала прочитайте CurrentHealth и MaximumHealth, затем поддерживайте отображение сигналами. Для внешнего долгоживущего отправителя отписывайтесь при завершении подписки.