Префаб или создание из кода
Выбор способа сборки объекта и полный пример двух независимых экземпляров.
Что выбирать
| Задача | Подход |
|---|---|
| Настроить визуальную и физическую иерархию, ссылки и значения в редакторе | Префаб .rprefab |
| Создать простой объект или собрать структуру по данным во время игры | new и AddChild |
| Описать правила и команды | C#-класс или скрипт Entity |
| Хранить общие параметры | Resource .rdata |
Префаб и скрипт дополняют друг друга: префаб хранит композицию, скрипт задаёт поведение. Выбирайте по удобству настройки и изменения, а не по обещанию, что один способ всегда быстрее.
Запустить пример
Нужен C#-проект с Reckfell.NET.Sdk 1.0.0 и net10.0. Если проекта ещё нет, начните с настройки C#.
- Добавьте все пять файлов ниже в проект, сохранив имена файлов и классов.
- Выполните dotnet build в папке с .csproj.
- Создайте уровень с корневым Entity, прикрепите ArchitectureExample.cs и сохраните уровень как architecture.rscene.
- Запустите этот уровень и откройте вывод приложения.
Ожидаемая строка: First=75; Second=100. Оба объекта получили один ресурс настроек; урон изменил здоровье только первого. Пример затем удаляет созданные объекты. Это короткая проверка устройства системы, а не готовая игровая сцена.
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;
}
}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;
}ArchitectureExample.cs
using Reckfell;
public partial class ArchitectureExample : Entity
{
public string Summary { get; private set; } = "";
public override void _Ready()
{
using var settings = new ActorSettings { MaximumHealth = 100 };
var first = new DamageReceiver { Name = "First", Settings = settings };
var second = new DamageReceiver { Name = "Second", Settings = settings };
var hits = new HitController();
AddChild(first);
AddChild(second);
AddChild(hits);
hits.SetTarget(first);
hits.TryHit(25);
Summary = $"First={first.CurrentHealth}; Second={second.CurrentHealth}";
ReckfellApi.Print(Summary);
hits.SetTarget(null);
hits.Free();
first.Free();
second.Free();
}
}Перенести объект в префаб
Для авторского префаба создайте корневой Entity со скриптом DamageReceiver, назначьте ActorSettings в Settings и добавьте нужные дочерние объекты. Сохраните композицию как .rprefab. Код создания экземпляра использует PackedEntity.Instantiate; new DamageReceiver создаёт только класс, без сохранённых дочерних объектов и настроек префаба.
При программном Pack дочерний объект должен принадлежать сохраняемой композиции через Owner. Один AddChild этого не задаёт. В примере проверки сохранение и повторная загрузка .rprefab восстанавливают Settings и принадлежащего корню ребёнка.
Текущее здоровье хранится в обычном HealthModel и не экспортируется: новый экземпляр получает начальное здоровье из Settings при _Ready. Сохранение прогресса игрока — отдельная операция, а не побочный эффект сохранения префаба.
Если вывод не появился: проверьте успешную сборку, прикреплённый ArchitectureExample.cs и запущенный уровень. Если здоровье равно нулю в своём получателе, проверьте Settings и результат ResetFromSettings.