Unity 6.3
0 онлайн 13 гостей 3 в системе
Вход

Настройка цикла Player

Unity Приложения времени выполнения работают в непрерывном цикле, называемом циклом Player. Во время каждой итерации цикла Player, Unity вызывает различные системы для выполнения таких задач, как рендирование, моделирование физики и обработка ввода.

Различные системы и подсистемы, обновляемые в цикле Player, вызываются в заданном порядке по умолчанию. Посмотреть и изменить этот порядок можно статическими методами из PlayerLoop API. Вы можете получить стандартный или текущий цикл проигрывателя и вставить свои собственные системы циклов проигрывателя или удалить, изменить порядок или заменить существующие.

Есть несколько причин, по которым вы можете пожелать настроить цикл проигрывателя:

  • Создание собственных управляемых обновлений в рамках настраиваемого обновления менеджер обновлений может быть более эффективной альтернативой UnityДополнительные сведения см. в 10000 Обновление вызовов по вопросу Unity блог.
  • Вы можете оптимизировать цикл Player для конкретной платформы, например, удалив обновления Physics, AI и XR для мобильной игры.
  • Возможно, потребуется создать визуализации цикла обновления в режиме редактирования или воспроизведения для облегчения отладки.

Доступ к циклу Player

Цикл проигрывателя состоит из ряда вложенных систем циклов проигрывателя, каждая из которых представляет собой структуру PlayerLoopSystem. Корневая или самая внешняя система циклов проигрывателя представляет полный цикл проигрывателя, под которым вложены другие системы циклов проигрывателя.

В этом контексте система цикла проигрывателя является просто абстрактным представлением всего, что должно обновляться во время цикла проигрывателя. Системы по умолчанию являются представлениями собственных систем Unity, таких как Audio и Input, но вы также можете создать свои собственные системы из вашего кода C#.

Для доступа к циклу проигрывателя по умолчанию Unity используйте PlayerLoop.GetDefaultPlayerLoop. Для доступа к действующему в данный момент циклу проигрывателя используйте PlayerLoop.GetCurrentPlayerLoop. Цикл проигрывателя по умолчанию и текущий будут одинаковыми, если только вы не примените новый пользовательский цикл проигрывателя с PlayerLoop.SetPlayerLoop.

Каждая система в цикле проигрывателя имеет свойство PlayerLoopSystem.type, которое идентифицирует ее. Можно рекурсивно итерировать по структуре системы цикла проигрывателя и вывести свойства Name каждого свойства type для отображения структуры цикла проигрывателя в консоли. Также можно использовать свойство type, как показано в следующих примерах, для определения подсистем цикла проигрывателя для добавления, удаления или замены. Допустимые типы для систем по умолчанию доступны в пространстве имен UnityEngine.PlayerLoop.

Ссылка API PlayerLoop.GetDefaultPlayerLoop содержит пример того, как получить цикл проигрывателя по умолчанию, итерировать через вложенные системы циклов проигрывателя и вывести Name их свойств type. Код производит следующий вывод консоли:

ROOT NODE
    TimeUpdate
        WaitForLastPresentationAndUpdateTime
    Initialization
        ProfilerStartFrame
        UpdateCameraMotionVectors
        DirectorSampleTime
        AsyncUploadTimeSlicedUpdate
        SynchronizeInputs
        SynchronizeState
        XREarlyUpdate
    EarlyUpdate
        PollPlayerConnection
        GpuTimestamp
        AnalyticsCoreStatsUpdate
        UnityWebRequestUpdate
        ExecuteMainThreadJobs
        ProcessMouseInWindow
        ClearIntermediateRenderers
        ClearLines
        PresentBeforeUpdate
        ResetFrameStatsAfterPresent
        UpdateAsyncInstantiate
        UpdateAsyncReadbackManager
        UpdateStreamingManager
        UpdateTextureStreamingManager
        UpdatePreloading
        UpdateContentLoading
        RendererNotifyInvisible
        PlayerCleanupCachedData
        UpdateMainGameViewRect
        UpdateCanvasRectTransform
        XRUpdate
        UpdateInputManager
        ProcessRemoteInput
        ScriptRunDelayedStartupFrame
        UpdateKinect
        DeliverIosPlatformEvents
        ARCoreUpdate
        DispatchEventQueueEvents
        Physics2DEarlyUpdate
        PhysicsResetInterpolatedTransformPosition
        SpriteAtlasManagerUpdate
        PerformanceAnalyticsUpdate
    FixedUpdate
        ClearLines
        NewInputFixedUpdate
        DirectorFixedSampleTime
        AudioFixedUpdate
        ScriptRunBehaviourFixedUpdate
        DirectorFixedUpdate
        LegacyFixedAnimationUpdate
        XRFixedUpdate
        PhysicsFixedUpdate
        Physics2DFixedUpdate
        PhysicsClothFixedUpdate
        DirectorFixedUpdatePostPhysics
        ScriptRunDelayedFixedFrameRate
    PreUpdate
        PhysicsUpdate
        Physics2DUpdate
        PhysicsClothUpdate
        CheckTexFieldInput
        IMGUISendQueuedEvents
        NewInputUpdate
        InputForUIUpdate
        SendMouseEvents
        AIUpdate
        WindUpdate
        UpdateVideo
    Update
        ScriptRunBehaviourUpdate
        ScriptRunDelayedDynamicFrameRate
        ScriptRunDelayedTasks
        DirectorUpdate
    PreLateUpdate
        AIUpdatePostScript
        DirectorUpdateAnimationBegin
        LegacyAnimationUpdate
        DirectorUpdateAnimationEnd
        DirectorDeferredEvaluate
        AccessibilityUpdate
        UIElementsUpdatePanels
        EndGraphicsJobsAfterScriptUpdate
        ConstraintManagerUpdate
        ParticleSystemBeginUpdateAll
        Physics2DLateUpdate
        PhysicsLateUpdate
        ScriptRunBehaviourLateUpdate
    PostLateUpdate
        PlayerSendFrameStarted
        DirectorLateUpdate
        ScriptRunDelayedDynamicFrameRate
        PhysicsSkinnedClothBeginUpdate
        UpdateRectTransform
        PlayerUpdateCanvases
        UIElementsRepaintPanels
        UpdateAudio
        VFXUpdate
        ParticleSystemEndUpdateAll
        EndGraphicsJobsAfterScriptLateUpdate
        UpdateCustomRenderTextures
        XRPostLateUpdate
        UpdateAllRenderers
        UpdateLightProbeProxyVolumes
        EnlightenRuntimeUpdate
        UpdateAllSkinnedMeshes
        ProcessWebSendMessages
        SortingGroupsUpdate
        UpdateVideoTextures
        UpdateVideo
        DirectorRenderImage
        PlayerEmitCanvasGeometry
        UIElementsRenderBatchModeOffscreen
        PhysicsSkinnedClothFinishUpdate
        FinishFrameRendering
        BatchModeUpdate
        PlayerSendFrameComplete
        UpdateCaptureScreenshot
        PresentAfterDraw
        ClearImmediateRenderers
        PlayerSendFramePostPresent
        UpdateResolution
        InputEndFrame
        TriggerEndOfFrameCallbacks
        GUIClearEvents
        ShaderHandleErrors
        ResetInputAxis
        ThreadedLoadingDebug
        ProfilerSynchronizeStats
        MemoryFrameMaintenance
        ExecuteGameCenterCallbacks
        XRPreEndFrame
        ProfilerEndFrame
        GraphicsWarmupPreloadedShaders
        ObjectDispatcherPostLateUpdate

Вставить пользовательское обновление в цикл проигрывателя по умолчанию

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

Пример кода в PlayerLoop.SetPlayerLoop Ссылка API показывает, как вставить пользовательскую систему циклов Player в цикл Player по умолчанию в указанной точке. В этом случае поведение CustomUpdate метод определяется в InsertSystem класс. Но следующие небольшие изменения позволяют сделать CustomUpdate событие, на которое могут подписываться ваши скрипты MonoBehaviour и для которого они могут предоставлять собственную логику обновления:

  • Объявите событие как открытое статическое поле InsertSystem class:
public static event Action AddCustomUpdate;
  • Измените определение метода CustomUpdate, чтобы вызвать это событие:
private static void CustomUpdate() => AddCustomUpdate?.Invoke();

Затем можно подписаться на пользовательское обновление из любого скрипта MonoBehaviour следующим образом:

using UnityEngine;

public class CustomUpdateFromEvent : MonoBehaviour
{
    private void OnEnable()
    {
        InsertSystem.AddCustomUpdate += MyCustomUpdate;
    }

    private void Update()
    {
        Debug.Log("Update");
    }

    private void LateUpdate()
    {
        Debug.Log("Late Update");
    }

    private void OnDisable()
    {
        InsertSystem.AddCustomUpdate -= MyCustomUpdate;
    }

    private void MyCustomUpdate()
    {
        Debug.Log("Custom update from an event that your MonoBehaviour scripts can subscribe to.");
    }
}

Примечание: Система циклов Player, переданная в качестве параметра PlayerLoop.SetPlayerLoop, перезаписывает текущую петлю Player. Поэтому убедитесь, что любая петля, переданная этому методу, содержит все системы, которые вы хотите сохранить, включая те, которые вы не заменяете. Если вы создаете новую систему циклов Player с нуля, вы должны явно добавить все системы, которые вы хотите сохранить, в новую петлю.

Замена системы по умолчанию в цикле Player

В следующем примере заменяется система по умолчанию в цикле проигрывателя настраиваемым обновлением. Пример заменяет систему PreUpdate.AIUpdate, но можно заменить любую систему в цикле проигрывателя, указав ее идентификатор PlayerLoopSystem.type. Допустимые типы для систем по умолчанию можно получить путем итерации через цикл проигрывателя по умолчанию, как показано в предыдущем разделе, или из пространства имен UnityEngine.PlayerLoop.

Важно: Замена системы в цикле Player может иметь непреднамеренные последствия, так как это удаляет функциональность по умолчанию этой системы. Используйте этот метод с осторожностью и только в том случае, если вы уверены, что хотите заменить функциональность по умолчанию собственной логикой.

using UnityEngine.LowLevel;
using UnityEngine.PlayerLoop;
using UnityEngine;

// Replace an existing system in the Unity Player Loop with a custom update.

public class MyReplacementCustomUpdate { } // Empty class to use as a type identifier for the custom update

public class SystemReplacement
{
    //Run this method on runtime initialization
    [RuntimeInitializeOnLoadMethod]
    private static void AppStart()
    {
        // Retrieve the default Player loop system. Get the current loop instead if the default was already modified previously.
        var defaultSystems = PlayerLoop.GetDefaultPlayerLoop();
        // Create a custom update system to replace an existing one
        var customUpdate = new PlayerLoopSystem()
        {
            updateDelegate = CustomUpdate,
            type = typeof(MyReplacementCustomUpdate)
        };
        // Specify the system to replace as the type parameter, in this case PreUpdate.AIUpdate
        ReplaceSystem<PreUpdate.AIUpdate>(ref defaultSystems, customUpdate);
        PlayerLoop.SetPlayerLoop(defaultSystems);
    }

    // Custom update method that will be called in the Player Loop
    private static void CustomUpdate()
    {
        Debug.Log("Custom update from a replacement system.");
    }

    //Recursively replace a system of type T with a replacement system in the Player Loop
    private static bool ReplaceSystem<T>(ref PlayerLoopSystem system, PlayerLoopSystem replacement)
    {
        if (system.type == typeof(T))
        {
            system = replacement;
            return true;
        }
        if (system.subSystemList != null)
        {
            for (var i = 0; i < system.subSystemList.Length; i++)
            {
                if (ReplaceSystem<T>(ref system.subSystemList[i], replacement))
                {
                    return true;
                }
            }
        }
        return false;
    }
}

Дополнительные ресурсы и примеры