Files
bansonic_beta_main/Assets/op/load_preload/preload_allScene.cs
T

725 lines
19 KiB
C#

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Video;
#if UNITY_EDITOR
using UnityEditor;
using UnityEditor.SceneManagement;
#endif
/// <summary>
/// Throttled preload: uses queues and concurrency limits to load Resources in small time slices
/// to avoid long frames on low-end devices.
/// The opening presentation now uses two videos: vj01 plays once, then vj02 loops.
/// </summary>
public class preload_allScene : MonoBehaviour
{
[Header("Preload settings")]
public string[] resourcesToPreload = new string[0];
[Header("UI references")]
public Text progressText;
[Tooltip("Optional Image whose fillAmount will be driven by the overall progress (0..1)")]
public Image progressFillImage;
[Tooltip("Optional Image to disable after 1 second")]
public Image startDisableImage;
[Tooltip("Documentation text normalized.")]
public Text loadingInfoText;
[Header("Opening Video")]
public RawImage openingVideoRawImage;
public VideoClip vj01Clip;
public VideoClip vj02Clip;
[Header("Behavior")]
public bool startOnAwake = true;
[Header("Throttle")]
public int maxConcurrentResourceLoads = 2;
[Tooltip("Max milliseconds spent per frame on driver work (approx)")]
public int maxMilliSecondsPerFrame = 4;
private readonly Queue<string> resourceQueue = new Queue<string>();
private readonly List<(string path, ResourceRequest rr)> activeResourceReqs = new List<(string, ResourceRequest)>();
private readonly List<Object> loadedResources = new List<Object>();
private Coroutine preloadCoroutine;
private VideoPlayer videoPlayer1;
private VideoPlayer videoPlayer2A;
private VideoPlayer videoPlayer2B;
private RenderTexture videoTexture1;
private RenderTexture videoTexture2A;
private RenderTexture videoTexture2B;
private VideoPlayer activeLoopPlayer;
private VideoPlayer standbyLoopPlayer;
private bool waitingForLoopStartup;
private Coroutine openingVideoStartupCoroutine;
private void Awake()
{
Debug.Log("[preload_allScene] Awake called. Forcing Time.timeScale = 1.");
Time.timeScale = 1f;
EnsureOpeningVideoSetup();
SetVideoDisplayVisible(openingVideoRawImage, false);
if (progressText == null)
{
progressText = GetComponentInChildren<Text>(true);
}
if (loadingInfoText == null)
{
var texts = GetComponentsInChildren<Text>(true);
foreach (var t in texts)
{
if (t != progressText)
{
loadingInfoText = t;
break;
}
}
}
}
private void Start()
{
Debug.Log("[preload_allScene] Start called. resourcesToPreload count: " + (resourcesToPreload?.Length ?? 0));
StartOpeningVideoSequence();
if (startDisableImage != null)
{
StartCoroutine(DelayedDisableImage());
}
StartPreload();
}
private void EnsureOpeningVideoSetup()
{
TryAutoAssignVideoClips();
if (videoPlayer1 == null && openingVideoRawImage != null)
{
videoPlayer1 = CreateVideoPlayer("OpeningVideoPlayer_VJ01", vj01Clip, false, ref videoTexture1);
if (videoPlayer1 != null)
{
videoPlayer1.loopPointReached += OnVj01Finished;
}
}
if (videoPlayer2A == null && openingVideoRawImage != null)
{
videoPlayer2A = CreateVideoPlayer("OpeningVideoPlayer_VJ02_A", vj02Clip, false, ref videoTexture2A);
if (videoPlayer2A != null)
{
videoPlayer2A.prepareCompleted += OnLoopPlayerPrepared;
videoPlayer2A.loopPointReached += OnLoopVideoReachedEnd;
}
}
if (videoPlayer2B == null && openingVideoRawImage != null)
{
videoPlayer2B = CreateVideoPlayer("OpeningVideoPlayer_VJ02_B", vj02Clip, false, ref videoTexture2B);
if (videoPlayer2B != null)
{
videoPlayer2B.prepareCompleted += OnLoopPlayerPrepared;
videoPlayer2B.loopPointReached += OnLoopVideoReachedEnd;
}
}
}
private void StartOpeningVideoSequence()
{
EnsureOpeningVideoSetup();
if (openingVideoStartupCoroutine != null)
{
StopCoroutine(openingVideoStartupCoroutine);
}
openingVideoStartupCoroutine = StartCoroutine(PrepareOpeningVideosAndStart());
}
private IEnumerator PrepareOpeningVideosAndStart()
{
SetVideoDisplayVisible(openingVideoRawImage, false);
if (videoPlayer1 != null && !videoPlayer1.isPrepared)
{
videoPlayer1.Prepare();
}
if (videoPlayer2A != null && !videoPlayer2A.isPrepared)
{
videoPlayer2A.Prepare();
}
if (videoPlayer2B != null && !videoPlayer2B.isPrepared)
{
videoPlayer2B.Prepare();
}
while (!AreOpeningVideosPrepared())
{
yield return null;
}
openingVideoStartupCoroutine = null;
if (videoPlayer1 != null)
{
if (openingVideoRawImage != null)
{
openingVideoRawImage.texture = videoTexture1;
}
SetVideoDisplayVisible(openingVideoRawImage, true);
ResetPlayerToStart(videoPlayer1);
videoPlayer1.Play();
yield break;
}
StartLoopVideo2();
}
private bool AreOpeningVideosPrepared()
{
if (videoPlayer1 != null && !videoPlayer1.isPrepared)
{
return false;
}
if (videoPlayer2A != null && !videoPlayer2A.isPrepared)
{
return false;
}
if (videoPlayer2B != null && !videoPlayer2B.isPrepared)
{
return false;
}
return true;
}
private IEnumerator DelayedDisableImage()
{
yield return new WaitForSeconds(1f);
if (startDisableImage != null)
{
startDisableImage.gameObject.SetActive(false);
Debug.Log("[preload_allScene] startDisableImage has been disabled after 1 second.");
}
}
private VideoPlayer CreateVideoPlayer(string objectName, VideoClip clip, bool loop, ref RenderTexture renderTexture)
{
if (clip == null || openingVideoRawImage == null)
{
return null;
}
Transform existing = transform.Find(objectName);
GameObject playerObject = existing != null ? existing.gameObject : new GameObject(objectName);
playerObject.transform.SetParent(transform, false);
var player = playerObject.GetComponent<VideoPlayer>();
if (player == null)
{
player = playerObject.AddComponent<VideoPlayer>();
}
bool mustStartFromFirstFrame = clip == vj01Clip;
player.playOnAwake = false;
player.isLooping = loop;
player.waitForFirstFrame = false;
player.skipOnDrop = !mustStartFromFirstFrame;
player.timeUpdateMode = VideoTimeUpdateMode.UnscaledGameTime;
player.playbackSpeed = 1f;
player.audioOutputMode = VideoAudioOutputMode.None;
player.source = VideoSource.VideoClip;
player.clip = clip;
player.renderMode = VideoRenderMode.RenderTexture;
int width = clip.width > 0 ? (int)clip.width : 1920;
int height = clip.height > 0 ? (int)clip.height : 1080;
EnsureRenderTexture(ref renderTexture, width, height, objectName + "_RT");
player.targetTexture = renderTexture;
return player;
}
private static void SetVideoDisplayVisible(RawImage image, bool visible)
{
if (image == null)
{
return;
}
image.gameObject.SetActive(visible);
}
private static void ResetPlayerToStart(VideoPlayer player)
{
if (player == null)
{
return;
}
try { player.playbackSpeed = 1f; } catch { }
try { player.Stop(); } catch { }
try { player.frame = 0; } catch { }
try { player.time = 0d; } catch { }
}
private void EnsureRenderTexture(ref RenderTexture renderTexture, int width, int height, string textureName)
{
if (renderTexture != null && renderTexture.width == width && renderTexture.height == height)
{
return;
}
ReleaseRenderTexture(ref renderTexture);
renderTexture = new RenderTexture(width, height, 0, RenderTextureFormat.ARGB32);
renderTexture.name = textureName;
renderTexture.Create();
}
private void ReleaseRenderTexture(ref RenderTexture renderTexture)
{
if (renderTexture == null)
{
return;
}
if (renderTexture.IsCreated())
{
renderTexture.Release();
}
Destroy(renderTexture);
renderTexture = null;
}
private void OnVj01Finished(VideoPlayer source)
{
if (videoPlayer1 != null)
{
videoPlayer1.Stop();
}
StartLoopVideo2();
}
private void StartLoopVideo2()
{
if (videoPlayer2A == null && videoPlayer2B == null)
{
return;
}
SetVideoDisplayVisible(openingVideoRawImage, true);
VideoPlayer firstPrepared = GetPreparedLoopPlayer();
if (firstPrepared != null)
{
StartPreparedLoopPlayer(firstPrepared);
return;
}
waitingForLoopStartup = true;
if (videoPlayer2A != null && !videoPlayer2A.isPrepared)
{
videoPlayer2A.Prepare();
}
if (videoPlayer2B != null && !videoPlayer2B.isPrepared)
{
videoPlayer2B.Prepare();
}
}
private VideoPlayer GetPreparedLoopPlayer()
{
if (videoPlayer2A != null && videoPlayer2A.isPrepared)
{
return videoPlayer2A;
}
if (videoPlayer2B != null && videoPlayer2B.isPrepared)
{
return videoPlayer2B;
}
return null;
}
private void StartPreparedLoopPlayer(VideoPlayer player)
{
if (player == null)
{
return;
}
activeLoopPlayer = player;
standbyLoopPlayer = player == videoPlayer2A ? videoPlayer2B : videoPlayer2A;
waitingForLoopStartup = false;
if (openingVideoRawImage != null)
{
openingVideoRawImage.texture = player.targetTexture;
}
SetVideoDisplayVisible(openingVideoRawImage, true);
if (!activeLoopPlayer.isPlaying)
{
activeLoopPlayer.Play();
}
if (standbyLoopPlayer != null && !standbyLoopPlayer.isPrepared)
{
standbyLoopPlayer.Prepare();
}
}
private void OnLoopPlayerPrepared(VideoPlayer source)
{
if (waitingForLoopStartup)
{
StartPreparedLoopPlayer(source);
}
}
private void OnApplicationPause(bool pauseStatus)
{
if (!pauseStatus)
{
ResumeOpeningVideosIfNeeded();
}
}
private void OnApplicationFocus(bool hasFocus)
{
if (hasFocus)
{
ResumeOpeningVideosIfNeeded();
}
}
private void ResumeOpeningVideosIfNeeded()
{
if (openingVideoRawImage == null || !openingVideoRawImage.gameObject.activeInHierarchy)
{
return;
}
if (videoPlayer1 != null && openingVideoRawImage.texture == videoTexture1 && !videoPlayer1.isPlaying)
{
videoPlayer1.Play();
return;
}
if (activeLoopPlayer != null && openingVideoRawImage.texture == activeLoopPlayer.targetTexture && !activeLoopPlayer.isPlaying)
{
activeLoopPlayer.Play();
return;
}
VideoPlayer prepared = GetPreparedLoopPlayer();
if (prepared != null && !prepared.isPlaying)
{
StartPreparedLoopPlayer(prepared);
}
}
private void OnLoopVideoReachedEnd(VideoPlayer source)
{
if (source != activeLoopPlayer)
{
return;
}
if (standbyLoopPlayer != null && standbyLoopPlayer.isPrepared)
{
VideoPlayer finishedPlayer = activeLoopPlayer;
VideoPlayer nextPlayer = standbyLoopPlayer;
if (openingVideoRawImage != null)
{
openingVideoRawImage.texture = nextPlayer.targetTexture;
}
if (!nextPlayer.isPlaying)
{
nextPlayer.Play();
}
activeLoopPlayer = nextPlayer;
standbyLoopPlayer = finishedPlayer;
if (standbyLoopPlayer.isPlaying)
{
standbyLoopPlayer.Stop();
}
standbyLoopPlayer.Prepare();
return;
}
source.Play();
}
public void StartPreload()
{
if (preloadCoroutine != null)
{
return;
}
resourceQueue.Clear();
if (resourcesToPreload != null)
{
foreach (var r in resourcesToPreload)
{
if (!string.IsNullOrWhiteSpace(r))
{
resourceQueue.Enqueue(r);
}
}
}
Debug.Log($"Preload starting. Resources queued: {resourceQueue.Count}");
UpdateProgressText(0f);
UpdateLoadingInfo(string.Empty);
if (resourceQueue.Count == 0)
{
UpdateProgressText(1f);
UpdateLoadingInfo("Loading Complete");
return;
}
preloadCoroutine = StartCoroutine(PreloadRoutine());
}
public void StopPreload()
{
if (preloadCoroutine != null)
{
StopCoroutine(preloadCoroutine);
preloadCoroutine = null;
}
}
private IEnumerator PreloadRoutine()
{
while (resourceQueue.Count > 0 || activeResourceReqs.Count > 0)
{
float frameStart = Time.realtimeSinceStartup;
while (activeResourceReqs.Count < maxConcurrentResourceLoads && resourceQueue.Count > 0)
{
string path = resourceQueue.Dequeue();
UpdateLoadingInfo($"Loading: {path}");
ResourceRequest rr = null;
try
{
rr = Resources.LoadAsync<Object>(path);
}
catch (System.Exception ex)
{
Debug.LogWarning($"Preload: failed to start loading resource '{path}': {ex}");
}
if (rr != null)
{
activeResourceReqs.Add((path, rr));
Debug.Log($"Preload: started resource load '{path}'");
}
if ((Time.realtimeSinceStartup - frameStart) * 1000f > maxMilliSecondsPerFrame)
{
break;
}
}
for (int i = activeResourceReqs.Count - 1; i >= 0; i--)
{
var tuple = activeResourceReqs[i];
var rr = tuple.rr;
if (rr.isDone)
{
if (rr.asset != null)
{
loadedResources.Add(rr.asset);
}
Debug.Log($"Preload: resource loaded '{tuple.path}'");
activeResourceReqs.RemoveAt(i);
}
if ((Time.realtimeSinceStartup - frameStart) * 1000f > maxMilliSecondsPerFrame)
{
break;
}
}
float overall = CalculateOverallProgress();
UpdateProgressText(overall);
bool anyActiveResources = activeResourceReqs.Count > 0 || resourceQueue.Count > 0;
if (!anyActiveResources)
{
UpdateProgressText(1f);
UpdateLoadingInfo("Loading Complete");
preloadCoroutine = null;
Debug.Log("Preload complete (throttled). Resources cached: " + loadedResources.Count);
yield break;
}
yield return null;
}
UpdateProgressText(1f);
UpdateLoadingInfo("Loading Complete");
preloadCoroutine = null;
}
private float CalculateOverallProgress()
{
float sum = 0f;
int items = 0;
items += resourceQueue.Count + activeResourceReqs.Count;
if (items == 0)
{
return 1f;
}
foreach (var (_, rr) in activeResourceReqs)
{
sum += rr.progress;
}
return Mathf.Clamp01(sum / items);
}
private void UpdateProgressText(float t)
{
int percent = Mathf.RoundToInt(t * 100f);
if (progressFillImage != null)
{
progressFillImage.fillAmount = Mathf.Clamp01(t);
}
if (progressText != null)
{
progressText.text = percent + "%";
return;
}
var textComponent = GetComponentInChildren<Text>(true);
if (textComponent != null)
{
progressText = textComponent;
progressText.text = percent + "%";
}
}
private void UpdateLoadingInfo(string message)
{
if (loadingInfoText != null)
{
loadingInfoText.text = message;
return;
}
var texts = GetComponentsInChildren<Text>(true);
foreach (var t in texts)
{
if (t != progressText)
{
loadingInfoText = t;
loadingInfoText.text = message;
return;
}
}
}
private void OnDestroy()
{
if (openingVideoStartupCoroutine != null)
{
StopCoroutine(openingVideoStartupCoroutine);
openingVideoStartupCoroutine = null;
}
if (videoPlayer1 != null)
{
videoPlayer1.loopPointReached -= OnVj01Finished;
}
if (videoPlayer2A != null)
{
videoPlayer2A.prepareCompleted -= OnLoopPlayerPrepared;
videoPlayer2A.loopPointReached -= OnLoopVideoReachedEnd;
}
if (videoPlayer2B != null)
{
videoPlayer2B.prepareCompleted -= OnLoopPlayerPrepared;
videoPlayer2B.loopPointReached -= OnLoopVideoReachedEnd;
}
ReleaseRenderTexture(ref videoTexture1);
ReleaseRenderTexture(ref videoTexture2A);
ReleaseRenderTexture(ref videoTexture2B);
}
#if UNITY_EDITOR
private void OnValidate()
{
TryAutoAssignVideoClips();
}
private void TryAutoAssignVideoClips()
{
bool changed = false;
if (vj01Clip == null)
{
vj01Clip = AssetDatabase.LoadAssetAtPath<VideoClip>("Assets/op/opVideo/vj01.mp4");
changed |= vj01Clip != null;
}
if (vj02Clip == null)
{
vj02Clip = AssetDatabase.LoadAssetAtPath<VideoClip>("Assets/op/opVideo/vj02.mp4");
changed |= vj02Clip != null;
}
if (changed)
{
EditorUtility.SetDirty(this);
if (gameObject.scene.IsValid())
{
EditorSceneManager.MarkSceneDirty(gameObject.scene);
}
}
}
#else
private void TryAutoAssignVideoClips()
{
}
#endif
}