Класс, Entity, Resource и префаб: в чём разница
Как связаны C#-тип, экземпляр движка, данные настроек и сохранённая иерархия.
Четыре разных понятия
| Понятие | Что оно означает | Пример |
|---|---|---|
| C#-класс | Тип с полями и методами; наследование задаётся в C# | HealthModel, DamageReceiver |
| Entity | Объект иерархии с родителем, детьми и жизненным циклом | Экземпляр DamageReceiver |
| Resource | Движковые данные, которые можно сохранять и разделять между объектами | ActorSettings |
| Префаб .rprefab | Сохранённая композиция Entity, свойств и ссылок | Заготовка противника |
C#-скрипт действительно объявляет C#-класс. Файл скрипта также представлен движку как Script-ресурс; это другой уровень представления. Обычный HealthModel не наследуется от ReckfellObject, не добавляется через AddChild и не получает _Ready.
Наследование и композиция
DamageReceiver наследуется от Entity и реализует IDamageable. Его HealthModel — отдельный объект с правилами здоровья. ActorSettings задаёт начальные параметры. Два получателя могут ссылаться на один ActorSettings, но иметь разные HealthModel.
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;
}
}ActorSettings.cs
using Reckfell;
[GlobalClass]
public partial class ActorSettings : Resource
{
[Export(PropertyHint.Range, "1,10000,1")]
public int MaximumHealth { get; set; } = 100;
}DamageReceiver.cs
using Reckfell;
public partial class DamageReceiver : Entity, IDamageable
{
[Export] public ActorSettings? Settings { get; set; }
[Signal] public delegate void HealthChangedEventHandler(int current, int maximum);
private HealthModel? _health;
public int CurrentHealth => _health?.Current ?? 0;
public int MaximumHealth => _health?.Maximum ?? 0;
public override void _Ready() => ResetFromSettings();
public bool ResetFromSettings()
{
if (Settings is null || !ReckfellObject.IsInstanceValid(Settings) ||
Settings.MaximumHealth <= 0) return false;
_health = new HealthModel(Settings.MaximumHealth);
EmitSignal(SignalName.HealthChanged, _health.Current, _health.Maximum);
return true;
}
public bool TryApplyDamage(int amount)
{
if (_health is null || !_health.TryApplyDamage(amount)) return false;
EmitSignal(SignalName.HealthChanged, _health.Current, _health.Maximum);
return true;
}
}Что меняет префаб
PackedEntity.Instantiate создаёт иерархию из сохранённых данных. Он не объявляет новый CLR-тип: тип корневого скрипта остаётся DamageReceiver. .rscene обозначает уровень, .rprefab — переиспользуемый объект; оба используют PackedEntity. Это роли файлов, а не два подкласса Entity.
Родитель определяет положение в иерархии и удаление детей. Owner определяет принадлежность сохраняемой композиции; это не ссылка на владельца игрового счёта, команды или сетевого объекта.