reckfellENGINE

C#: свойства в Inspector и префабах

Export, private-поля, Range, сохранение и проверка значений на полном примере.

На этой странице

Запустить пример

Добавьте пять файлов ниже в C#-проект Reckfell 1.0.0 и соберите. Создайте уровень с корнем Entity2D, прикрепите к корню ManagedDataDemo2D и запустите F6. Дочерние объекты создаются кодом. Ожидается боезапас 5; кнопка «Сделать выстрел» меняет его на 4.

ShotSettings.cs
C#
using Reckfell;

[GlobalClass]
public partial class ShotSettings : Resource
{
    [Export] public string DisplayName { get; set; } = "Pulse";
    [Export(PropertyHint.Range, "1,100,1")] public int Damage { get; set; } = 12;
    [Export] public Reckfell.Collections.Array<string> Tags { get; set; } = new() { "weapon" };

    public bool TryGetDamage(out int damage)
    {
        damage = 0;
        if (Damage < 1 || Damage > 100) return false;
        damage = Damage;
        return true;
    }
}
ExportedWeapon.cs
C#
using Reckfell;

public partial class ExportedWeapon : Entity
{
    [Signal] public delegate void FiredEventHandler(int damage);
    [Export] public ShotSettings? Settings { get; set; }
    [Export(PropertyHint.Range, "0,99,1")] private int _ammo = 5;
    public int Ammo => _ammo;
    public int TransientValue { get; set; } = 3;

    public bool TryFire()
    {
        if (_ammo <= 0 || !ReckfellObject.IsInstanceValid(Settings) ||
            !Settings!.TryGetDamage(out int damage)) return false;
        _ammo--;
        EmitSignal(SignalName.Fired, damage);
        return true;
    }
}
InventoryLedger.cs
C#
using System;
using System.Collections.Generic;

public sealed class InventoryLedger
{
    private readonly Dictionary<string, int> _counts = new(StringComparer.Ordinal);

    public int CountOf(string item) => _counts.TryGetValue(item, out int count) ? count : 0;

    public bool TryAdd(string item, int amount)
    {
        if (string.IsNullOrWhiteSpace(item) || amount <= 0) return false;
        int current = CountOf(item);
        if (current > int.MaxValue - amount) return false;
        _counts[item] = current + amount;
        return true;
    }

    public Reckfell.Collections.Dictionary<string, int> Snapshot() => new(_counts);
}
VariantCounter.cs
C#
using Reckfell;

public static class VariantCounter
{
    public static bool TryRead(Variant value, out int count)
    {
        count = 0;
        if (value.VariantType != Variant.Type.Int) return false;
        long raw = value.AsInt64();
        if (raw < 0 || raw > 999) return false;
        count = (int)raw;
        return true;
    }
}
ManagedDataDemo2D.cs
C#
using Reckfell;

public partial class ManagedDataDemo2D : Entity2D
{
    public ExportedWeapon Weapon { get; private set; } = null!;
    public InventoryLedger Inventory { get; } = new();
    public Label Readout { get; private set; } = null!;
    public Button FireButton { get; private set; } = null!;
    private bool _ru;

    public override void _Ready()
    {
        _ru = TranslationServer.GetLocale().StartsWith("ru");
        var title = new Label { Position = new Vector2(28, 24), Text = _ru ? "C#: настройки и данные" : "C#: settings and data" };
        title.AddThemeFontSizeOverride("font_size", 26);
        AddChild(title);
        AddChild(new Label { Position = new Vector2(28, 74), Text = _ru ? "Resource → свойство → игровая проверка" : "Resource → property → gameplay validation" });
        Readout = new Label { Position = new Vector2(28, 126) };
        AddChild(Readout);
        Weapon = new ExportedWeapon { Settings = new ShotSettings() };
        AddChild(Weapon);
        Inventory.TryAdd("pulse", 2);
        FireButton = new Button { Position = new Vector2(28, 260), Size = new Vector2(230, 48), Text = _ru ? "Сделать выстрел" : "Fire once" };
        FireButton.Modulate = new Color(0.35f, 0.55f, 1);
        FireButton.Pressed += Fire;
        AddChild(FireButton);
        AddChild(new Label { Position = new Vector2(28, 336), Text = _ru ? "Range — подсказка. TryFire проверяет данные." : "Range is a hint. TryFire validates the data." });
        Refresh();
    }

    private void Fire()
    {
        Weapon.TryFire();
        Refresh();
    }

    private void Refresh()
    {
        Variant sample = 25L;
        VariantCounter.TryRead(sample, out int count);
        Readout.Text = _ru
            ? $"Урон: {Weapon.Settings!.Damage}\nБоезапас: {Weapon.Ammo}\nВ инвентаре: {Inventory.CountOf("pulse")}\nVariant.Int: {count}"
            : $"Damage: {Weapon.Settings!.Damage}\nAmmo: {Weapon.Ammo}\nInventory: {Inventory.CountOf("pulse")}\nVariant.Int: {count}";
    }
}

Работающий C#-стенд после одного выстрела

Что сохраняет Export

ExportedWeapon.Settings — ссылка на Resource. Приватное поле _ammo тоже экспортировано: private ограничивает C#-доступ, а не исключает сохранение. При упаковке этого объекта значение _ammo = 9 восстанавливается как 9. Неэкспортированное TransientValue = 42 в новом экземпляре возвращается к инициализатору 3. Доступность свойства через Get/Set сама по себе не означает, что оно сохраняется.

Для сохранения уровня или префаба действуют также правила Owner и PackedEntity. Export не создаёт сохранение игры на диске и не сохраняет изменения запущенного экземпляра обратно в исходный файл автоматически.

Подсказка и проверка — разные операции

Range задаёт метаданные Inspector. Присваивание Damage = 1000 или Set для _ammo со значением −2 не ограничивается указанным диапазоном. TryGetDamage и TryFire явно отклоняют неверное состояние до расхода боезапаса и сигнала. Не полагайтесь на ползунок как на проверку файлов, сетевого ввода или собственного кода.

Экспортируйте совместимые с Variant поля либо читаемые/записываемые свойства экземпляра. List<int> не заменяет Reckfell.Collections.Array<int>. Static, readonly и неподдерживаемый тип дают RF0101, RF0103 и RF0102 соответственно. Resource не может экспортировать ссылку на Entity: RF0107; храните такую ссылку на Entity-компоненте.

Изменили поля — сначала соберите C#-проект. Если Inspector не обновился, проверьте ошибки сборки, прикреплённый скрипт и точное имя класса/файла. Нативное сохранение и метаданные проверены; мышиные действия в Inspector и сохранение из его окна в этой проверке не выполнялись.

Документация Reckfell EngineНаверх ↑
БЫСТРЫЙ ПЕРЕХОДНачало работыEntity и иерархияC# & .NET 10Сцены и префабы
Руководства и полный API Reckfell K
reckfellENGINE
RECKFELL ACCOUNT

Вход в аккаунт

Войдите в аккаунт Reckfell.

Один аккаунт для всей платформы Reckfell.