包含此页的版本:
不含此页的版本:
Unity 运行时应用程序在称为 Player 循环的连续循环中运行。在 Player 循环的每次迭代中,Unity 都会调用各种系统来执行渲染、物理模拟和输入处理等任务。
在播放器循环期间更新的各种系统和子系统按定义的默认顺序调用。您可以使用PlayerLoop应用程序接口。您可以检索默认或当前的播放器循环,并插入您自己的自定义播放器循环系统,或者删除、重新排序或替换现有循环系统。
您可能想要自定义播放器循环的原因有以下几个:
Player 循环由一系列嵌套的 Player 循环系统组成,每个循环系统都是PlayerLoopSystem结构。根或最外层的 Player 循环系统表示完整的 Player 循环,其他 Player 循环系统嵌套在该循环下。
在这种情况下,Player 循环系统只是在 Player 循环期间需要更新的任何内容的抽象表示。默认系统是本机 Unity 系统(如音频和输入)的表示形式,但您也可以从 C# 代码创建自己的自定义系统。
要访问 Unity 的默认播放器循环,请使用PlayerLoop.GetDefaultPlayerLoop.要访问当前有效的播放器循环,请使用PlayerLoop.GetCurrentPlayerLoop.默认和当前的 Player 循环将是相同的,除非你应用一个新的自定义 Player 循环PlayerLoop.SetPlayerLoop.
播放器循环中的每个系统都有一个PlayerLoopSystem.type属性来标识它。您可以递归迭代 Player 循环系统结构并打印Name每个type属性,以在控制台中显示 Player 循环结构。您还可以使用type属性,如以下示例所示,以标识要添加、删除或替换的播放器循环子系统。默认系统的有效类型在UnityEngine.PlayerLoopNamespace。
API 参考PlayerLoop.GetDefaultPlayerLoop包括一个示例,说明如何检索默认的 Player 循环,迭代嵌套的 Player 循环系统,并打印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.SetPlayerLoopAPI 参考显示了如何在指定点将自定义播放器循环系统插入到默认播放器循环中。在这种情况下,的行为CustomUpdate方法在InsertSystem类。但是以下小的修改可以使CustomUpdate您的 MonoBehaviour脚本一段代码,允许您创建自己的组件、触发游戏事件、随时间修改组件属性以及以您喜欢的任何方式响应用户输入。更多信息
请参阅术语表可以订阅并提供自己的更新逻辑:
InsertSystem类:public static event Action AddCustomUpdate;
CustomUpdate调用此事件的方法:private static void CustomUpdate() => AddCustomUpdate?.Invoke();
然后,您可以从任何 MonoBehaviour 脚本订阅自定义更新,如下所示:
public class MyMonoBehaviour : MonoBehaviour
{
private void OnEnable()
{
SystemInsertion.AddCustomUpdate += MyCustomUpdate;
}
private void Update()
{
Debug.Log("Update");
}
private void LateUpdate()
{
Debug.Log("Late Update");
}
private void OnDisable()
{
SystemInsertion.AddCustomUpdate -= MyCustomUpdate;
}
private void MyCustomUpdate()
{
Debug.Log("Custom update running!");
}
}
注意:作为参数传递给的播放器循环系统PlayerLoop.SetPlayerLoop覆盖当前播放器循环。因此,请确保传递给此方法的任何循环都包含要保留的所有系统,包括不替换的系统。如果从头开始创建新的播放器循环系统,则必须将要保留的所有系统显式添加到新循环中。
以下示例将播放器循环中的默认系统替换为自定义更新。该示例将PreUpdate.AIUpdate系统,但你可以通过指定其 PlayerLoopSystem.type 标识符来替换 Player 循环中的任何系统。默认系统的有效类型可以通过迭代默认 Player 循环(如上一节所示)或从UnityEngine.PlayerLoopNamespace。
重要提示: 替换播放器循环中的系统可能会产生意想不到的后果,因为它会移除该系统的默认功能。请谨慎使用此方法,并且仅当确定要用自己的自定义逻辑替换默认功能时。
using UnityEngine.LowLevel;
using UnityEngine.PlayerLoop;
using UnityEngine;
// Replace an existing system in the Unity Player Loop with a custom update.
public class MyCustomUpdate { } // 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(MyCustomUpdate)
};
// 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 running!");
}
//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;
}
}
LowLevel.PlayerLoopAPI 参考
LowLevel.PlayerLoopUnity 讨论中的主题