게임의 로드 이벤트 실행 시점을 기록하는 것은 다음 두 가지 이유에서 중요합니다.
- 로드 중 프레임 시간 데이터 오염 방지
- 로드 시간을 분석하여 로드 시간이 허용치보다 길었던 시점과 위치 파악
로드 이벤트에 메타데이터를 연결할 수 있습니다.
public class LoadingTimeMetadata
{
public enum LoadingState
{
Unknown = 0,
/// <summary>
/// The first time the game is run.
/// </summary>
FirstRun = 1,
/// <summary>
/// App is not backgrounded.
/// </summary>
ColdStart = 2,
/// <summary>
/// App is backgrounded.
/// </summary>
WarmStart = 3,
/// <summary>
/// App is backgrounded, least work needed.
/// </summary>
HotStart = 4,
/// <summary>
/// Asset loading between levels.
/// </summary>
InterLevel = 5
}
public LoadingState state;
public enum LoadingSource
{
UnknownSource = 0,
/// <summary>
/// Uncompressing data.
/// </summary>
Memory = 1,
/// <summary>
/// Reading assets from APK bundle.
/// </summary>
Apk = 2,
/// <summary>
/// Reading assets from device storage.
/// </summary>
DeviceStorage = 3,
/// <summary>
/// Reading assets from external storage, e.g. SD card.
/// </summary>
ExternalStorage = 4,
/// <summary>
/// Loading assets from the network.
/// </summary>
Network = 5,
/// <summary>
/// Shader compilation.
/// </summary>
ShaderCompilation = 6,
/// <summary>
/// Time spent between process starting and onCreate.
/// </summary>
PreActivity = 7,
/// <summary>
/// Total time spent between process starting and first render frame.
/// </summary>
FirstTouchToFirstFrame = 8,
/// <summary>
/// Time from start to end of a group of events.
/// </summary>
TotalUserWaitForGroup = 9
}
public LoadingSource source;
/// <summary>
/// 0 = no compression, 100 = max compression
/// </summary>
public int compression_level;
public enum NetworkConnectivity
{
Unknown = 0,
Wifi = 1,
CellularNetwork = 2
}
public NetworkConnectivity network_connectivity;
/// <summary>
/// Bandwidth in bits per second.
/// </summary>
public ulong network_transfer_speed_bps;
/// <summary>
/// Latency in nanoseconds.
/// </summary>
public ulong network_latency_ns;
}
요구사항과 관련 없는 필드는 0일 수 있습니다.
로드 이벤트에는 주석도 연결될 수 있습니다. 이에 관한 주석은 Annotation
메시지에서 한 개 이상의 필드를 사용하여 프레임 시간 주석과 동일한 방법으로 정의할 수 있습니다.
Result<ulong> StartRecordingLoadingTime(LoadingTimeMetadata eventMetadata,
TAnnotation
annotation);
이 함수는 지정된 메타데이터 및 주석과 관련된 로드 시간 이벤트 기록을 시작하고 StopRecordingLoadingTime()
함수에 사용할 Result<ulong>.value
를 입력합니다.
ErrorCode StopRecordingLoadingTime(ulong handle);
이 함수는 이전에 StartRecordingLoadingTime()
에 의해 시작된 이벤트 기록을 중지합니다. 이벤트는 다음 세션 플러시에서 업로드됩니다.
로드 그룹 함수
게임에서 단일 로드 기간에 사용자에게 표시된 몇몇 로드 이벤트를 기록할 수 있습니다. 파일 로드, 장면 로드, 압축 해제, 셰이더 컴파일 등을 예로 들 수 있습니다.
Android Performance Tuner가 더 정확한 통계를 제공할 수 있도록 로드 이벤트가 이러한 그룹에 속한다는 사실을 Android Performance Tuner에 알려야 합니다. 그렇게 하려면 로드 이벤트를 다음과 같은 시작 함수와 중지 함수와 함께 괄호로 묶습니다.
Result<ulong> StartLoadingGroup(LoadingTimeMetadata eventMetadata, TAnnotation
annotation);
이 함수는 지정된 메타데이터 및 주석과 관련된 로드 그룹을 시작하고 StopLoadingGroup()
함수에 사용할 Result<ulong>.value
를 입력합니다. 메타데이터와 주석은 현재 Play 백엔드에서 사용되지 않으며 주석만 null
로 설정할 수 있습니다. 이후의 모든 로드 이벤트는 고유 그룹 ID별로 태그가 지정됩니다.
ErrorCode StopLoadingGroup(ulong handle);
이 함수는 이전에 StartLoadingGroup()
에 의해 시작된 로드 그룹을 중지합니다.
StartLoadingGroup()
이 다시 호출될 때까지 후속 로드 이벤트에는 그룹 ID가 없습니다.
그림 1. 로드 그룹의 예
예
다음은 게임에 로드 시간 함수를 추가하는 방법에 관한 예입니다.
파일 로드 이벤트
다음 코드 예는 게임에서 파일 로드 이벤트를 기록하는 방법을 보여줍니다.
public RawImage image;
IEnumerator LoadImageFromStreamingAssets(string imageName)
{
string imagePath = "file://" + Path.Combine(Application.streamingAssetsPath, imageName);
using (var r = UnityWebRequestTexture.GetTexture(imagePath))
{
LoadingTimeMetadata fileLoadingMetadata = new LoadingTimeMetadata()
{
state = LoadingTimeMetadata.LoadingState.InterLevel,
source = LoadingTimeMetadata.LoadingSource.DeviceStorage,
// Fields are zero by default but they could be set as follows
compression_level = 0,
network_connectivity = 0,
network_transfer_speed_bps = 0,
network_latency_ns = 0
};
Annotation annotation = new Annotation()
{
Scene = Scene.MagicalForest
};
// Start recording loading time.
Result<ulong> result = performanceTuner.StartRecordingLoadingTime(fileLoadingMetadata, annotation);
yield return r.SendWebRequest();
// Stop recording loading time.
performanceTuner.StopRecordingLoadingTime(result.value);
if (r.isNetworkError || r.isHttpError)
{
Debug.Log(r.error);
}
else
{
Texture2D tex = DownloadHandlerTexture.GetContent(r);
image.texture = tex;
}
}
}
장면 로드 이벤트
다음 코드 예는 게임에서 장면 로드 이벤트를 기록하는 방법을 보여줍니다.
IEnumerator LoadScene(int sceneIndex)
{
LoadingTimeMetadata metadata = new LoadingTimeMetadata()
{state = LoadingTimeMetadata.LoadingState.InterLevel};
Annotation annotation = new Annotation() {Scene = (Scene) (sceneIndex + 1)};
Result<ulong> result = performanceTuner.StartRecordingLoadingTime(metadata, annotation);
AsyncOperation asyncSceneLoad = SceneManager.LoadSceneAsync(sceneIndex, LoadSceneMode.Single);
while (!asyncSceneLoad.isDone)
{
yield return null;
}
performanceTuner.StopRecordingLoadingTime(result.value);
}
로드 그룹 함수
다음 코드 예는 로드 그룹 함수를 게임에 추가하는 방법을 보여줍니다.
IEnumerator LoadImages()
{
LoadingTimeMetadata groupMetadata = new LoadingTimeMetadata()
{
state = LoadingTimeMetadata.LoadingState.InterLevel,
source = LoadingTimeMetadata.LoadingSource.DeviceStorage,
};
Result<ulong> result = performanceTuner.StartLoadingGroup(groupMetadata, null);
yield return StartCoroutine(LoadImageFromStreamingAssets("image1.jpeg"));
yield return StartCoroutine(LoadImageFromStreamingAssets("image2.jpeg"));
yield return StartCoroutine(LoadImageFromStreamingAssets("image3.jpeg"));
yield return StartCoroutine(LoadImageFromStreamingAssets("image4.jpeg"));
var stopErrorCode = performanceTuner.StopLoadingGroup(0);
}