Roblox GuidebookБаза знаний
Русский ⌄

Разработка / ROBLOX

Экран загрузки Roblox: какие ресурсы подготовить и что означает готовность

Подготовьте небольшой список важных ресурсов первого экрана и отделите завершение попыток от успешной загрузки. Разберём учебное меню с тремя ресурсами, понятным состоянием ошибки и продолжением без обещаний, что вся игра уже готова.

Обновлено:

Определите, что игрок должен увидеть первым #

Начните с конкретного первого действия: игрок читает название учебного маршрута, видит кнопку начала и понимает, куда она ведёт. Запишите, какие изображения и звуки нужны именно для этого момента. Большая коллекция питомцев в дальнем магазине пока не относится к первому экрану. Такой список помогает обсуждать необходимость каждого ресурса, а не объявлять всю игру обязательной частью заставки.

Наш пример — вымышленное меню с иллюстрацией, значком кнопки и коротким звуком. Названия illustration, start-icon и first-sound служат учебными идентификаторами; это не опубликованные Roblox asset ID. Мы не подключаем чужие материалы и не выдаём пример за проверку пяти игр автора. Цель упражнения — построить честный учёт выбранных ресурсов, прежде чем связывать его с настоящей загрузкой и интерфейсом.

Отделите загрузку контента от готовности игры #

ContentProvider предоставляет PreloadAsync для предварительной загрузки контента. Справочник отмечает, что метод приостанавливает вызывающий поток до завершения своей работы; его callback сообщает идентификатор ресурса и AssetFetchStatus. Эти сведения относятся к контенту. Они сами по себе не подтверждают чтение сохранений, создание персонажа, доступность сервера или готовность всех объектов мира. Экрану нужны разные признаки для разных частей запуска.

Запишите отдельно, что меню ждёт от выбранных изображений, и отдельно, что должно произойти перед началом игрового маршрута. Если значок готов, но персонаж ещё не появился, не показывайте сообщение «вся игра загружена». Если закончилась только обработка списка, используйте формулировку про список. Это редакционное правило нашего упражнения: надпись должна описывать тот результат, который система действительно умеет проверить.

Не превращайте очередь запросов в процент #

RequestQueueSize выглядит удобным числом для индикатора, но официальное руководство по производительности предупреждает о колебаниях очереди. Она не является постоянным знаменателем вашей задачи. Поступление новых запросов способно изменить число во время наблюдения. Поэтому уменьшение очереди нельзя автоматически представлять как точный процент готовности меню или использовать как надёжное доказательство завершения всех необходимых ресурсов.

Для упражнения выберите собственный неизменный список из трёх разных идентификаторов. Его размер равен трём до и после любых сообщений о результатах. Важное ограничение: такая модель работает только при заранее понятном соответствии между записью списка и учитываемым результатом. Нельзя взять число произвольных объектов, посчитать каждый callback как один объект и без проверки утверждать, что получен процент всей сцены.

Составьте небольшой список необходимого #

Руководство рекомендует избирательную предварительную загрузку: например, изображения заставки, важные картинки меню и ресурсы стартовой области. Загрузка всего Workspace названа неудачной практикой из-за увеличения времени ожидания. Составьте таблицу с ролью каждого выбранного ресурса: зачем он нужен сейчас, что увидит игрок без него и какое допустимое поведение есть при недоступности. Это полезнее списка всего, что когда-либо встретится в игре.

В учебном меню иллюстрация может иметь текстовую замену, кнопка должна сохранять читаемую подпись, а отсутствие звука не должно делать её смысл непонятным. Это решения конкретного прототипа, которые ещё нужно проверить на игроках. Они не означают, что любой ресурс любой игры можно игнорировать. Если без модели невозможно безопасно начать уровень, её готовность относится к отдельному условию запуска, которое нельзя заменить декоративной заставкой.

От списка к результатуОткрыть изображение крупнее ↗
Оригинальная схема выбранной задачи загрузки.

Разделите счётчики попыток и результата #

Оригинальный пример на чистом Luau хранит total, resolved, succeeded и failed. Первый счётчик задаёт размер списка, второй — число полученных результатов, оставшиеся два разделяют успехи и неудачи. Функция record принимает учебный идентификатор и логическое значение. Она не вызывает Roblox API, не скачивает файл и не принимает Enum.AssetFetchStatus напрямую. Настоящий адаптер должен отдельно переводить проверенный результат загрузки в нужное состояние.

В статье этот пример служит проверяемой моделью учёта. После одной неудачи и двух успехов получится resolved=3, succeeded=2, failed=1. Состояние settled означает, что по всем записям есть результат; allSucceeded в этом случае остаётся false. Не заменяйте обе проверки одним словом «готово». Получить сведения о трёх попытках и успешно получить три ресурса — разные результаты даже при одинаковом количестве обработанных записей.

-- Pure Luau bookkeeping, not a ContentProvider adapter.
-- Each manifest entry represents exactly one distinct content identifier.
local function newTracker(ids)
    assert(type(ids) == "table", "Manifest must be a table")
    local expected = {}
    local total = 0
    for _, id in ipairs(ids) do
        assert(type(id) == "string" and id ~= "", "Invalid content identifier")
        assert(not expected[id], "Duplicate content identifier")
        expected[id] = true
        total += 1
    end
    local entries = 0
    for index in pairs(ids) do
        assert(type(index) == "number" and index >= 1 and index % 1 == 0,
            "Manifest must use consecutive array indices")
        entries += 1
    end
    assert(entries == total, "Manifest cannot contain array gaps")
    local outcomes = {}
    local resolved = 0
    local succeeded = 0
    local failed = 0
    local tracker = {}

    function tracker.record(id, success)
        assert(type(success) == "boolean", "Outcome must be boolean")
        if not expected[id] or outcomes[id] ~= nil then
            return false
        end
        outcomes[id] = success
        resolved += 1
        if success then
            succeeded += 1
        else
            failed += 1
        end
        return true
    end

    function tracker.snapshot()
        return {
            total = total,
            resolved = resolved,
            succeeded = succeeded,
            failed = failed,
            settled = resolved == total,
            allSucceeded = resolved == total and failed == 0,
        }
    end

    return tracker
end

return newTracker

Не позволяйте повтору менять результат #

Если обработчик получает повторный результат для start-icon, счётчики не должны увеличиваться второй раз. Особенно важно не перезаписывать уже зарегистрированную неудачу случайным повторным успехом. Наш трекер принимает только первый результат для известного идентификатора, а посторонние сообщения игнорирует. Это правило конкретной учебной попытки. Для намеренного повторного скачивания потребуется новая попытка и явно выбранная политика обновления состояния.

Список также проверяется до начала работы: идентификаторы должны быть непустыми строками без повторов, а таблица — последовательным массивом без пропусков. Иначе обход ipairs может остановиться раньше ожидаемого и дать неправильный знаменатель. Возвращаемый snapshot является новой таблицей, поэтому изменение её полей не исправляет внутреннюю ошибку и не меняет трекер. Эти ограничения проверяются автономно; они не доказывают поведение сети или Roblox.

Три результатаОткрыть изображение крупнее ↗
Результат чистого Luau-теста; не реальная загрузка ресурсов.

Объясняйте неудачу понятным сообщением #

Разделите сообщение о ходе работы и сообщение о результате. Например, «обработано 2 из 3 выбранных ресурсов» описывает учёт, а «один ресурс не получен» — результат попытки. Не пишите «всё загружено» при failed больше нуля. Не придумывайте точную оставшуюся длительность без измерений. Пользователю важнее понятный следующий шаг, чем гладкая полоска, которая скрывает неизвестную часть состояния.

Для вымышленного меню предложим читаемую подпись вместо недоступной картинки и отдельное сообщение о звуке. Проверка прототипа должна выяснить, сохраняются ли направление и доступность начала. Для важных игровых объектов нужен иной сценарий: объяснить ограничение и показать разрешённый выход или повтор. Не обещайте, что повтор обязательно поможет. Ошибка ресурса может требовать проверки идентификатора и доступа, а не бесконечных одинаковых запросов.

ПолеЗначение
total3 выбранных ресурса
resolved3 результата
succeeded2 успеха
failed1 неудача

Продолжение не должно изображать отмену загрузки #

Официальное руководство предлагает возможность пропустить ожидание, если требуется загрузить много ресурсов. Решите, что именно означает кнопка в вашем прототипе: закрыть декоративный экран и продолжить с доступной заменой или перейти в ограниченное меню. Надпись должна соответствовать действию. Из этого совета нельзя делать вывод, что нажатие автоматически отменяет уже выполняющийся PreloadAsync или делает ресурсы успешно полученными.

Если результат придёт после закрытия панели, обработчик должен обновить своё состояние корректно и не возвращать пользователя на заставку. Связь конкретной GUI-панели с жизненным циклом загрузки требует отдельной проверки. Наш чистый трекер не создаёт GUI и не реализует эту кнопку. Он показывает, какие данные нужны для решения; это не готовая система отмены, повторных попыток или смены экрана в любой игре.

Проверяйте учёт и интеграцию по отдельности #

Сначала проверьте модель искусственными событиями: неизвестный идентификатор, повтор, неудача, два успеха в другом порядке и незавершённый список. Для трекера важны точные счётчики и отсутствие ложного allSucceeded. Набор автономных проверок можно запускать без сети. Он подтверждает только логику выбранного примера. Название теста и вывод должны сохранять эту границу, чтобы проверка таблиц не выглядела проверкой настоящего загрузчика.

Затем в отдельном учебном проекте предстоит подключить собственные разрешённые ресурсы, увидеть реальные статусы и проверить меню на нужных устройствах. Запишите идентификаторы, окружение, итог каждого запроса и доступность обычного действия. Отдельно рассмотрите поведение при недоступности и после закрытия панели. Такая интеграционная проверка пока не выполнена в этом материале; время загрузки, изменение памяти и эффект на удержание не измерялись.

ПроверкаГраница
Повтор результатаНе увеличивает счётчик
Чужой идентификаторНе учитывается
Пустая очередьНе показатель всей готовности
Реальный Roblox APIЕщё не проверен

Сохраните воспроизводимый итог #

Полезный итог содержит небольшой список ресурсов, объяснение их необходимости, различие между resolved и succeeded, поведение при failed и границу готовности экрана. Для случая «два успеха и одна неудача» запись должна прямо сохранять неудачу. Для пустого списка трекер не делит число на ноль: он сообщает завершённое состояние без ресурсов. Интерфейс такого случая лучше сформулировать словами, а не рисовать неопределённый процент.

Перед переносом идеи в настоящую игру передайте другому разработчику код, автономные проверки и список ещё не проверенных условий. Не добавляйте весь мир в предварительную загрузку только ради красивых 100%. Цель первого экрана — позволить понять и начать нужное действие с честным описанием состояния. Статья и учебная модель оригинальные; актуальные API и ограничения следует сверять с указанными официальными источниками, а результаты конкретной игры — измерять отдельно.

Первоисточники

Roblox Creator Hub — ContentProvider
Roblox Creator Hub — Improve performance