reckfellENGINE

Сохранить уровень в памяти при переключении

Хост двух комнат: отсоединение без удаления, сохранённые поля, повторный вход и освобождение кэша.

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

Выбрать стратегию

СтратегияЧто сохраняетсяЗа что отвечать
EntityTree.ChangeSceneToFileДанные вне заменяемого корняПовторно создать состояние уровня из файла/сохранения
Отсоединить и удерживать EntityТе же объекты и их поля в памятиЯвно освободить отсоединённые корни
Скрыть CanvasItemОбъект остаётся в активной иерархииОтдельно определить обработку, ввод, звук и физику

Обычная смена EntityTree отсоединяет старый корень сразу, а освобождает его при завершении отложенной смены. Она не является бесплатным кэшем предыдущих уровней. Hide меняет видимость, но не заменяет жизненный цикл. ProcessMode.Disabled тоже не универсальный метод остановить всё: учитывайте явные режимы детей и DisableMode физических объектов.

Запустить две сохраняемые комнаты

Добавьте оба C#-файла в корень проекта 1.0.0 и создайте Rooms/A.rscene, Rooms/B.rscene по указанным путям. Прикрепите RetainedLevelHost к Entity2D отдельного уровня, задайте viewport 640×420, соберите и запустите F6. Нажмите +1, перейдите B, вернитесь A: Value сохранится, Enter увеличится, Ready останется 1.

Комната A вернулась с сохранённым значением 42, вторым входом и одним Ready

RetainedRoom2D.cs
C#
using Reckfell;

public partial class RetainedRoom2D : Entity2D
{
    [Export] public int Value { get; set; }
    public int Entries { get; private set; }
    public int ReadyCalls { get; private set; }

    public override void _EnterTree() => Entries++;
    public override void _Ready() => ReadyCalls++;
    public void Increase() { Value++; QueueRedraw(); }
    public override void _Draw()
    {
        DrawRect(new Rect2(60, 150, 500, 170), new Color(.10f, .17f, .38f));
        bool ru = TranslationServer.GetLocale().StartsWith("ru");
        DrawString(ThemeDB.FallbackFont, new Vector2(84, 204), Name + (ru ? " · значение: " : " · value: ") + Value, fontSize: 23);
        DrawString(ThemeDB.FallbackFont, new Vector2(84, 254), "Enter: " + Entries + " · Ready: " + ReadyCalls, fontSize: 19);
    }
}
Rooms/A.rscene
Конфигурация
[reckfell_tree format=3]
[ext_resource type="Script" path="asset://RetainedRoom2D.cs" id="Room"]
[entity name="RoomA" type="Entity2D"]
script = ExtResource("Room")
Value = 10
Rooms/B.rscene
Конфигурация
[reckfell_tree format=3]
[ext_resource type="Script" path="asset://RetainedRoom2D.cs" id="Room"]
[entity name="RoomB" type="Entity2D"]
script = ExtResource("Room")
Value = 20
RetainedLevelHost.cs
C#
using System.Collections.Generic;
using System.Linq;
using Reckfell;

public partial class RetainedLevelHost : Entity2D
{
    public const string RoomA = "asset://Rooms/A.rscene";
    public const string RoomB = "asset://Rooms/B.rscene";
    public RetainedRoom2D? ActiveRoom { get; private set; }
    public int CachedCount => _rooms.Count;
    private readonly Dictionary<string, RetainedRoom2D> _rooms = new();
    private bool _switching;

    public Error SwitchTo(string path)
    {
        if (!IsInsideTree()) return Error.Unconfigured;
        if (_switching) return Error.Busy;
        if (path != RoomA && path != RoomB) return Error.InvalidParameter;
        _switching = true;
        try
        {
            if (!_rooms.TryGetValue(path, out var next))
            {
                var packed = ResourceLoader.Load<PackedEntity>(path);
                if (packed is null || !packed.CanInstantiate()) return Error.CantOpen;
                Entity instance = packed.Instantiate();
                if (instance is not RetainedRoom2D room) { instance.Free(); return Error.InvalidData; }
                next = room;
                _rooms.Add(path, next);
            }
            if (next == ActiveRoom) return Error.Ok;
            if (ActiveRoom is not null) RemoveChild(ActiveRoom);
            ActiveRoom = next;
            AddChild(next);
            next.QueueRedraw();
            return Error.Ok;
        }
        finally { _switching = false; }
    }

    public override void _Ready()
    {
        bool ru = TranslationServer.GetLocale().StartsWith("ru");
        AddChild(new Label { Position = new Vector2(28, 24), Text = ru ? "Комнаты: состояние сохраняется в памяти" : "Rooms: state stays in memory" });
        foreach (var item in new[] { ("A", RoomA), ("B", RoomB) })
        {
            string path = item.Item2;
            var button = new Button { Text = item.Item1, Position = new Vector2(item.Item1 == "A" ? 28 : 104, 82) };
            button.Pressed += () => { Error error = SwitchTo(path); if (error != Error.Ok) ReckfellApi.PrintErr(error); };
            AddChild(button);
        }
        var increase = new Button { Text = "+1", Position = new Vector2(180, 82) };
        increase.Pressed += () => ActiveRoom?.Increase();
        AddChild(increase);
        Error initial = SwitchTo(RoomA);
        if (initial != Error.Ok) ReckfellApi.PrintErr(initial);
    }

    public override void _ExitTree()
    {
        foreach (var pair in _rooms.ToArray())
        {
            if (pair.Value == ActiveRoom) continue;
            pair.Value.Free();
            _rooms.Remove(pair.Key);
        }
    }
}

Хост намеренно принимает только эти две комнаты и тип RetainedRoom2D. Новый экземпляр подготавливается до отключения прежнего. Неактивная комната остаётся в словаре без Parent и не получает обычную обработку дерева. Это те же native-объекты, а не пересоздание через Pack.

Владение и выход

RemoveChild не освобождает комнату. C#-ссылка тоже не является автоматическим управлением lifetime обычной Entity. Хост обязан вызвать Free для отсоединённых экземпляров; активного ребёнка освобождает обычное удаление родительского дерева. В примере _ExitTree освобождает неактивный кэш даже при временном удалении хоста. Активный ребёнок остаётся его ребёнком и при возврате хоста входит повторно.

Повторный вход вызывает _EnterTree/_ExitTree, но не обычный _Ready. Комнаты с внешними подписками должны восстанавливать и снимать их симметрично. Отсоединённый объект всё ещё может получить прямой метод или сигнал от внешней службы, если вы оставили такую связь.

Хост не назначает CurrentScene каждой комнате: текущим уровнем EntityTree остаётся содержащий его корень. ReloadCurrentScene относится к этому корню и может пересоздать хост вместе с кэшем. Для штатной полной замены используйте EntityTree.

Когда этот подход не подходит

Кэш занимает память: ссылки на меши, текстуры и прочие ресурсы могут удерживаться живыми комнатами. Здесь нет ограничения памяти, вытеснения, background streaming или сохранения состояния на диск. Не освобождайте комнату снаружи хоста и не используйте его как готовую систему бесшовного мира. Выбирайте стратегию по измерениям проекта, а не по обещанию, что ручная смена всегда быстрее.

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

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

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

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