reckfellENGINE

Фоновая загрузка префаба

Запрос, опрос состояния и безопасный момент создания Entity.

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

Подготовка

Создайте префаб asset://Props/Crate.rprefab с корнем Entity3D и дочерним MeshInstance3D с BoxMesh. В главном уровне создайте Entity3D с именем Objects и обычную Entity со скриптом BackgroundPrefab.cs. В свойстве ParentForInstance назначьте Objects, соберите C#-проект и запустите уровень.

Скрипт сам вызывает Begin() из _Ready(). После загрузки в Objects появится экземпляр, Busy станет false, Progress — 1, LastError — Ok. Для видимого куба в уровне нужны камера и свет; загрузка ресурса их не создаёт.

BackgroundPrefab.cs
C#
using Reckfell;

public partial class BackgroundPrefab : Entity
{
    [Export(PropertyHint.File, "*.rprefab,*.rscene")]
    public string ResourcePath { get; set; } = "asset://Props/Crate.rprefab";
    [Export] public Entity ParentForInstance { get; set; } = null!;

    public bool Busy { get; private set; }
    public float Progress { get; private set; }
    public Error LastError { get; private set; } = Error.Ok;
    public Entity? LastInstance { get; private set; }
    private string _requestedPath = "";

    public override void _Ready() => Begin();

    public Error Begin()
    {
        if (Busy)
            return Error.Busy;
        if (!IsInsideTree() || IsQueuedForDeletion() || !ReckfellObject.IsInstanceValid(ParentForInstance) ||
            !ParentForInstance.IsInsideTree() || ParentForInstance.IsQueuedForDeletion())
            return Error.Unconfigured;
        if (!ResourcePath.StartsWith("asset://", System.StringComparison.Ordinal) ||
            !ResourceLoader.Exists(ResourcePath, "PackedEntity"))
            return Error.FileNotFound;
        _requestedPath = ResourcePath;
        Progress = 0;
        LastInstance = null;
        LastError = ResourceLoader.LoadThreadedRequest(_requestedPath, "PackedEntity");
        Busy = LastError == Error.Ok;
        return LastError;
    }

    public override void _Process(double delta)
    {
        if (!Busy)
            return;
        using var progress = new Reckfell.Collections.Array();
        var status = ResourceLoader.LoadThreadedGetStatus(_requestedPath, progress);
        if (progress.Count != 0)
            Progress = Mathf.Clamp((float)progress[0].AsDouble(), 0, 1);
        if (status == ResourceLoader.ThreadLoadStatus.InProgress)
            return;
        Busy = false;
        if (status == ResourceLoader.ThreadLoadStatus.InvalidResource)
        {
            LastError = Error.InvalidData;
            return;
        }
        Resource resource = ResourceLoader.LoadThreadedGet(_requestedPath);
        if (status != ResourceLoader.ThreadLoadStatus.Loaded || resource is not PackedEntity prefab)
        {
            LastError = Error.CantOpen;
            return;
        }
        if (!ReckfellObject.IsInstanceValid(ParentForInstance) || !ParentForInstance.IsInsideTree() ||
            ParentForInstance.IsQueuedForDeletion())
        {
            LastError = Error.Unconfigured;
            return;
        }
        LastInstance = prefab.Instantiate();
        ParentForInstance.AddChild(LastInstance);
        Progress = 1;
        LastError = Error.Ok;
    }

    public override void _ExitTree()
    {
        if (!Busy)
            return;
        Busy = false;
        // Collect the outstanding request; this may wait for its completion.
        _ = ResourceLoader.LoadThreadedGet(_requestedPath);
        LastError = Error.Unavailable;
        Progress = 0;
    }
}

Почему три вызова

LoadThreadedRequest() начинает загрузку ресурса. LoadThreadedGetStatus() опрашивается один раз за кадр, а не в цикле ожидания. Массив progress, если заполнен, содержит долю 0…1; это не прогноз оставшегося времени.

Только после Loaded код получает результат через LoadThreadedGet(), создаёт экземпляр и добавляет его в дерево в главном потоке. Ранний Get() может заблокировать поток до завершения. Статус Failed тоже требует забрать результат запроса; он вернёт ошибочный результат, который нельзя использовать как префаб. После получения токен запроса исчерпан: последующий статус может быть InvalidResource.

Время жизни и повторный запуск

Begin() во время загрузки возвращает Busy и не запускает второй запрос. Путь фиксируется в _requestedPath: изменение экспортированного свойства не меняет уже начатую операцию. Повторный успешный Begin() создаёт ещё один экземпляр; предыдущий объект остаётся в игре.

Если родитель исчез до завершения, результат забирается, но объект не создаётся. При выходе самого загрузчика из дерева _ExitTree() забирает незавершённый запрос, не создавая Entity. Этот аварийный путь может ждать загрузку и задержать выход; API отмены здесь нет. Для обычного экрана загрузки держите загрузчик в постоянном корне до завершения, затем меняйте уровень.

Это загрузка одного ресурса с зависимостями, не готовая система стриминга мира. Создание сложной иерархии, _Ready() и подготовка графики всё ещё могут занять главный поток. Передача useSubThreads: true также не гарантирует ускорения: измеряйте конкретный ресурс.

Ошибки

РезультатПричина
UnconfiguredЗагрузчик или назначенный родитель не находится в дереве
FileNotFoundНеверный путь или ресурс не существует как PackedEntity
Busy из Begin()Сначала дождитесь текущей операции
InvalidDataНет действующего запроса для сохранённого пути
CantOpenЗагрузка завершилась неудачно либо вернулся другой тип
UnavailableЗагрузчик вышел из дерева до обработки результата

Смена текущего уровня — отдельная операция: переходы между уровнями.

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

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

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

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