666 lines
21 KiB
C#
666 lines
21 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using UnityEngine.EventSystems;
|
|
using UnityEngine.SceneManagement;
|
|
using UnityEngine.UI;
|
|
using TMPro;
|
|
using DG.Tweening;
|
|
|
|
public class UI_FunctionHoverGuide : MonoBehaviour
|
|
{
|
|
[Header("Scene")]
|
|
public bool onlyInUiUi = true;
|
|
public string uiUiSceneName = "UI_UI";
|
|
|
|
[Header("Config")]
|
|
public UI_FunctionHoverConfig config;
|
|
public string configResourcesName = "UI_FunctionHoverConfig";
|
|
public bool debugLogs = false;
|
|
public bool useManualHover = true;
|
|
|
|
[Header("Function References")]
|
|
public string functionRootName = "Function";
|
|
public string functionRootPath = "UI_Panel_Main_main/Content/Function";
|
|
public string targetBasePath = "UI_Panel_Main_main";
|
|
public string iconNameContains = "ICON";
|
|
public string textNameContains = "TEXT";
|
|
public RectTransform functionRoot;
|
|
public Image functionIcon;
|
|
public RawImage functionIconRaw;
|
|
public TMP_Text functionTextTmp;
|
|
public Text functionTextLegacy;
|
|
|
|
[Header("Animation")]
|
|
public float iconStartScale = 0.6f;
|
|
public float iconPopDuration = 0.35f;
|
|
public int iconFlashCount = 2;
|
|
public float iconFlashInterval = 0.05f;
|
|
public float textTypeInterval = 0.02f;
|
|
public bool useUnscaledTime = true;
|
|
|
|
[Header("Gyro")]
|
|
public bool enableGyro = false;
|
|
public float gyroMaxRotation = 4f;
|
|
public float gyroSmooth = 10f;
|
|
|
|
private Sprite defaultIcon;
|
|
private Texture defaultIconTexture;
|
|
private string defaultText;
|
|
private Vector3 iconBaseScale = Vector3.one;
|
|
private bool iconBaseScaleCached;
|
|
private bool initialized;
|
|
private Coroutine typeRoutine;
|
|
private Sequence iconSeq;
|
|
private UI_FunctionHoverEntry currentEntry;
|
|
private Coroutine retryRoutine;
|
|
private readonly List<HoverTarget> hoverTargets = new();
|
|
|
|
private class HoverTarget
|
|
{
|
|
public UI_FunctionHoverEntry entry;
|
|
public RectTransform rect;
|
|
public float area;
|
|
}
|
|
|
|
private void OnEnable()
|
|
{
|
|
SceneManager.sceneLoaded += OnSceneLoaded;
|
|
TryInit();
|
|
}
|
|
|
|
private void OnDisable()
|
|
{
|
|
SceneManager.sceneLoaded -= OnSceneLoaded;
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
if (!initialized || !useManualHover) return;
|
|
if (hoverTargets.Count == 0) return;
|
|
if (!IsUiUiScene()) return;
|
|
|
|
UI_FunctionHoverEntry entry = GetHoveredEntry();
|
|
if (entry == currentEntry) return;
|
|
if (entry != null)
|
|
ApplyEntry(entry);
|
|
else if (currentEntry != null)
|
|
ClearEntry(currentEntry);
|
|
}
|
|
|
|
private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
|
|
{
|
|
initialized = false;
|
|
TryInit();
|
|
}
|
|
|
|
private void TryInit()
|
|
{
|
|
if (initialized) return;
|
|
if (onlyInUiUi && !IsUiUiScene()) return;
|
|
|
|
if (config == null && !string.IsNullOrEmpty(configResourcesName))
|
|
config = Resources.Load<UI_FunctionHoverConfig>(configResourcesName);
|
|
if (config == null)
|
|
{
|
|
Debug.LogWarning("UI_FunctionHoverGuide: config is missing. Create a UI_FunctionHoverConfig asset and assign it.");
|
|
return;
|
|
}
|
|
|
|
ResolveFunctionRefs();
|
|
if (functionRoot == null || functionIcon == null || (functionTextTmp == null && functionTextLegacy == null))
|
|
{
|
|
StartRetry();
|
|
return;
|
|
}
|
|
|
|
CacheDefault();
|
|
EnsureWrapping();
|
|
EnsureGyro();
|
|
int wired = WireTargets();
|
|
|
|
if (debugLogs)
|
|
Debug.Log($"UI_FunctionHoverGuide: wired targets = {wired}, functionRoot = {functionRoot?.name}");
|
|
|
|
if (wired == 0)
|
|
{
|
|
StartRetry();
|
|
return;
|
|
}
|
|
|
|
initialized = true;
|
|
}
|
|
|
|
private void StartRetry()
|
|
{
|
|
if (retryRoutine != null) return;
|
|
retryRoutine = StartCoroutine(RetryInit());
|
|
}
|
|
|
|
private IEnumerator RetryInit()
|
|
{
|
|
int tries = 0;
|
|
while (!initialized)
|
|
{
|
|
ResolveFunctionRefs();
|
|
if (functionRoot != null && functionIcon != null && (functionTextTmp != null || functionTextLegacy != null))
|
|
{
|
|
CacheDefault();
|
|
EnsureWrapping();
|
|
EnsureGyro();
|
|
int wired = WireTargets();
|
|
if (debugLogs)
|
|
Debug.Log($"UI_FunctionHoverGuide: retry wired targets = {wired}");
|
|
if (wired > 0)
|
|
{
|
|
initialized = true;
|
|
break;
|
|
}
|
|
}
|
|
tries++;
|
|
yield return new WaitForSecondsRealtime(0.25f);
|
|
}
|
|
retryRoutine = null;
|
|
}
|
|
|
|
private bool IsUiUiScene()
|
|
{
|
|
if (string.IsNullOrEmpty(uiUiSceneName)) return true;
|
|
var active = SceneManager.GetActiveScene().name;
|
|
return active.IndexOf(uiUiSceneName, StringComparison.OrdinalIgnoreCase) >= 0;
|
|
}
|
|
|
|
private void ResolveFunctionRefs()
|
|
{
|
|
if (functionRoot == null)
|
|
{
|
|
GameObject go = null;
|
|
if (!string.IsNullOrWhiteSpace(functionRootPath))
|
|
go = GameObject.Find(functionRootPath);
|
|
if (go == null && !string.IsNullOrWhiteSpace(targetBasePath))
|
|
go = GameObject.Find(CombinePath(targetBasePath, functionRootName));
|
|
if (go == null)
|
|
go = FindByNameContains(functionRootName);
|
|
if (go != null)
|
|
functionRoot = go.GetComponent<RectTransform>();
|
|
}
|
|
|
|
if (functionRoot == null) return;
|
|
|
|
if (functionIcon == null)
|
|
{
|
|
var icon = FindChildGraphic(functionRoot, iconNameContains);
|
|
if (icon != null) functionIcon = icon;
|
|
}
|
|
if (functionIconRaw == null && functionIcon == null)
|
|
{
|
|
var raw = FindChildRawImage(functionRoot, iconNameContains);
|
|
if (raw != null) functionIconRaw = raw;
|
|
}
|
|
|
|
if (functionTextTmp == null && functionTextLegacy == null)
|
|
{
|
|
var tmp = FindChildTmp(functionRoot, textNameContains);
|
|
if (tmp != null)
|
|
functionTextTmp = tmp;
|
|
else
|
|
functionTextLegacy = FindChildLegacyText(functionRoot, textNameContains);
|
|
}
|
|
}
|
|
|
|
private void CacheDefault()
|
|
{
|
|
if (functionIcon != null)
|
|
defaultIcon = functionIcon.sprite;
|
|
if (functionIconRaw != null)
|
|
defaultIconTexture = functionIconRaw.texture;
|
|
|
|
var iconGraphic = functionIcon != null ? (Graphic)functionIcon : functionIconRaw;
|
|
if (iconGraphic != null && iconGraphic.rectTransform != null)
|
|
{
|
|
var scale = iconGraphic.rectTransform.localScale;
|
|
if (IsFinite(scale) && scale != Vector3.zero)
|
|
{
|
|
iconBaseScale = scale;
|
|
iconBaseScaleCached = true;
|
|
}
|
|
}
|
|
if (functionTextTmp != null)
|
|
defaultText = functionTextTmp.text;
|
|
else if (functionTextLegacy != null)
|
|
defaultText = functionTextLegacy.text;
|
|
}
|
|
|
|
private void EnsureWrapping()
|
|
{
|
|
if (functionTextTmp != null)
|
|
{
|
|
functionTextTmp.textWrappingMode = TextWrappingModes.Normal;
|
|
functionTextTmp.overflowMode = TextOverflowModes.Overflow;
|
|
}
|
|
if (functionTextLegacy != null)
|
|
{
|
|
functionTextLegacy.horizontalOverflow = HorizontalWrapMode.Wrap;
|
|
functionTextLegacy.verticalOverflow = VerticalWrapMode.Overflow;
|
|
}
|
|
}
|
|
|
|
private void EnsureGyro()
|
|
{
|
|
if (functionRoot == null) return;
|
|
var gyro = functionRoot.GetComponent<BgmGyroRotator>();
|
|
if (gyro != null)
|
|
{
|
|
if (Application.isPlaying)
|
|
Destroy(gyro);
|
|
else
|
|
DestroyImmediate(gyro);
|
|
}
|
|
}
|
|
|
|
private int WireTargets()
|
|
{
|
|
if (config == null || config.entries == null) return 0;
|
|
hoverTargets.Clear();
|
|
int wired = 0;
|
|
for (int i = 0; i < config.entries.Count; i++)
|
|
{
|
|
var entry = config.entries[i];
|
|
if (entry == null || string.IsNullOrWhiteSpace(entry.path)) continue;
|
|
var target = FindByPath(entry.path);
|
|
if (target == null)
|
|
{
|
|
if (debugLogs)
|
|
Debug.LogWarning("UI_FunctionHoverGuide: target not found for path: " + entry.path);
|
|
continue;
|
|
}
|
|
|
|
if (AttachHoverTargets(target.transform, entry))
|
|
wired++;
|
|
}
|
|
return wired;
|
|
}
|
|
|
|
public void ApplyEntry(UI_FunctionHoverEntry entry)
|
|
{
|
|
if (entry == null) return;
|
|
if (currentEntry == entry) return;
|
|
currentEntry = entry;
|
|
|
|
if (functionIcon != null && entry.icon != null)
|
|
functionIcon.sprite = entry.icon;
|
|
if (functionIconRaw != null)
|
|
functionIconRaw.texture = entry.icon != null ? entry.icon.texture : defaultIconTexture;
|
|
|
|
PlayIconAnim();
|
|
PlayTypewriter(entry.text);
|
|
}
|
|
|
|
public void ClearEntry(UI_FunctionHoverEntry entry)
|
|
{
|
|
if (currentEntry != entry) return;
|
|
currentEntry = null;
|
|
|
|
if (functionIcon != null && defaultIcon != null)
|
|
functionIcon.sprite = defaultIcon;
|
|
if (functionIconRaw != null && defaultIconTexture != null)
|
|
functionIconRaw.texture = defaultIconTexture;
|
|
|
|
PlayIconAnim();
|
|
PlayTypewriter(defaultText);
|
|
}
|
|
|
|
private void PlayIconAnim()
|
|
{
|
|
Graphic iconGraphic = functionIcon != null ? (Graphic)functionIcon : functionIconRaw;
|
|
if (iconGraphic == null) return;
|
|
var rt = iconGraphic.rectTransform;
|
|
var baseScale = iconBaseScaleCached ? iconBaseScale : Vector3.one;
|
|
if (!iconBaseScaleCached && rt != null)
|
|
baseScale = rt.localScale;
|
|
if (!IsFinite(baseScale))
|
|
baseScale = Vector3.one;
|
|
|
|
if (iconSeq != null && iconSeq.IsActive())
|
|
iconSeq.Kill();
|
|
|
|
var color = iconGraphic.color;
|
|
iconGraphic.color = new Color(color.r, color.g, color.b, 0f);
|
|
if (rt != null)
|
|
rt.localScale = baseScale * iconStartScale;
|
|
|
|
iconSeq = DOTween.Sequence();
|
|
for (int i = 0; i < Mathf.Max(1, iconFlashCount); i++)
|
|
{
|
|
iconSeq.Append(iconGraphic.DOFade(1f, iconFlashInterval));
|
|
iconSeq.Append(iconGraphic.DOFade(0.25f, iconFlashInterval));
|
|
}
|
|
iconSeq.Append(iconGraphic.DOFade(1f, iconFlashInterval));
|
|
if (rt != null)
|
|
iconSeq.Join(rt.DOScale(baseScale, iconPopDuration).SetEase(Ease.OutBack));
|
|
}
|
|
|
|
private static bool IsFinite(Vector3 v)
|
|
{
|
|
return !(float.IsNaN(v.x) || float.IsNaN(v.y) || float.IsNaN(v.z)
|
|
|| float.IsInfinity(v.x) || float.IsInfinity(v.y) || float.IsInfinity(v.z));
|
|
}
|
|
|
|
private void PlayTypewriter(string text)
|
|
{
|
|
if (string.IsNullOrEmpty(text)) text = string.Empty;
|
|
if (typeRoutine != null)
|
|
StopCoroutine(typeRoutine);
|
|
|
|
if (functionTextTmp != null)
|
|
typeRoutine = StartCoroutine(TypeTmp(functionTextTmp, text));
|
|
else if (functionTextLegacy != null)
|
|
typeRoutine = StartCoroutine(TypeLegacy(functionTextLegacy, text));
|
|
}
|
|
|
|
private IEnumerator TypeTmp(TMP_Text tmp, string text)
|
|
{
|
|
tmp.text = text;
|
|
tmp.maxVisibleCharacters = 0;
|
|
tmp.ForceMeshUpdate();
|
|
int total = tmp.textInfo.characterCount;
|
|
for (int i = 0; i <= total; i++)
|
|
{
|
|
tmp.maxVisibleCharacters = i;
|
|
yield return Wait(textTypeInterval);
|
|
}
|
|
}
|
|
|
|
private IEnumerator TypeLegacy(Text txt, string text)
|
|
{
|
|
txt.text = string.Empty;
|
|
for (int i = 0; i <= text.Length; i++)
|
|
{
|
|
txt.text = text.Substring(0, i);
|
|
yield return Wait(textTypeInterval);
|
|
}
|
|
}
|
|
|
|
private IEnumerator Wait(float t)
|
|
{
|
|
if (useUnscaledTime)
|
|
yield return new WaitForSecondsRealtime(t);
|
|
else
|
|
yield return new WaitForSeconds(t);
|
|
}
|
|
|
|
private GameObject FindByPath(string path)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(path)) return null;
|
|
var go = GameObject.Find(path);
|
|
if (go != null) return go;
|
|
if (!string.IsNullOrWhiteSpace(targetBasePath))
|
|
{
|
|
var combined = CombinePath(targetBasePath, path);
|
|
go = GameObject.Find(combined);
|
|
if (go != null) return go;
|
|
}
|
|
|
|
string normalized = NormalizePath(path);
|
|
if (string.IsNullOrEmpty(normalized)) return null;
|
|
|
|
var all = Resources.FindObjectsOfTypeAll<Transform>();
|
|
for (int i = 0; i < all.Length; i++)
|
|
{
|
|
var t = all[i];
|
|
if (t == null) continue;
|
|
if (!t.gameObject.scene.IsValid() || !t.gameObject.scene.isLoaded) continue;
|
|
string full = NormalizePath(GetFullPath(t));
|
|
if (string.Equals(full, normalized, StringComparison.OrdinalIgnoreCase) ||
|
|
full.EndsWith("/" + normalized, StringComparison.OrdinalIgnoreCase))
|
|
return t.gameObject;
|
|
}
|
|
|
|
string[] parts = path.Split('/');
|
|
string last = parts[parts.Length - 1];
|
|
return FindByNameContains(last);
|
|
}
|
|
|
|
private GameObject FindByNameContains(string name)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(name)) return null;
|
|
var all = Resources.FindObjectsOfTypeAll<Transform>();
|
|
string target = name.Replace(" ", "");
|
|
for (int i = 0; i < all.Length; i++)
|
|
{
|
|
var t = all[i];
|
|
if (t == null) continue;
|
|
if (!t.gameObject.scene.IsValid() || !t.gameObject.scene.isLoaded) continue;
|
|
string n = t.name.Replace(" ", "");
|
|
if (n.IndexOf(target, StringComparison.OrdinalIgnoreCase) >= 0)
|
|
return t.gameObject;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private static string CombinePath(string basePath, string path)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(basePath)) return path;
|
|
if (string.IsNullOrWhiteSpace(path)) return basePath;
|
|
string trimmedBase = basePath.Trim().TrimEnd('/', '\\');
|
|
string trimmedPath = path.Trim().TrimStart('/', '\\');
|
|
if (trimmedPath.StartsWith(trimmedBase, StringComparison.OrdinalIgnoreCase))
|
|
return trimmedPath;
|
|
return trimmedBase + "/" + trimmedPath;
|
|
}
|
|
|
|
private static string NormalizePath(string path)
|
|
{
|
|
if (string.IsNullOrEmpty(path)) return string.Empty;
|
|
return path.Replace('\\', '/').Replace(" ", "");
|
|
}
|
|
|
|
private static string GetFullPath(Transform t)
|
|
{
|
|
if (t == null) return string.Empty;
|
|
var parts = new List<string>();
|
|
var cur = t;
|
|
while (cur != null)
|
|
{
|
|
parts.Add(cur.name);
|
|
cur = cur.parent;
|
|
}
|
|
parts.Reverse();
|
|
return string.Join("/", parts);
|
|
}
|
|
|
|
private static Image FindChildGraphic(Transform root, string nameContains)
|
|
{
|
|
var imgs = root.GetComponentsInChildren<Image>(true);
|
|
if (imgs == null || imgs.Length == 0) return null;
|
|
if (!string.IsNullOrEmpty(nameContains))
|
|
{
|
|
string target = nameContains.Replace(" ", "");
|
|
for (int i = 0; i < imgs.Length; i++)
|
|
{
|
|
var img = imgs[i];
|
|
if (img == null) continue;
|
|
string n = img.name.Replace(" ", "");
|
|
if (n.IndexOf(target, StringComparison.OrdinalIgnoreCase) >= 0)
|
|
return img;
|
|
}
|
|
}
|
|
return imgs[0];
|
|
}
|
|
|
|
private static RawImage FindChildRawImage(Transform root, string nameContains)
|
|
{
|
|
var raws = root.GetComponentsInChildren<RawImage>(true);
|
|
if (raws == null || raws.Length == 0) return null;
|
|
if (!string.IsNullOrEmpty(nameContains))
|
|
{
|
|
string target = nameContains.Replace(" ", "");
|
|
for (int i = 0; i < raws.Length; i++)
|
|
{
|
|
var img = raws[i];
|
|
if (img == null) continue;
|
|
string n = img.name.Replace(" ", "");
|
|
if (n.IndexOf(target, StringComparison.OrdinalIgnoreCase) >= 0)
|
|
return img;
|
|
}
|
|
}
|
|
return raws[0];
|
|
}
|
|
|
|
private static TMP_Text FindChildTmp(Transform root, string nameContains)
|
|
{
|
|
var tmps = root.GetComponentsInChildren<TMP_Text>(true);
|
|
if (tmps == null || tmps.Length == 0) return null;
|
|
if (!string.IsNullOrEmpty(nameContains))
|
|
{
|
|
string target = nameContains.Replace(" ", "");
|
|
for (int i = 0; i < tmps.Length; i++)
|
|
{
|
|
var t = tmps[i];
|
|
if (t == null) continue;
|
|
string n = t.name.Replace(" ", "");
|
|
if (n.IndexOf(target, StringComparison.OrdinalIgnoreCase) >= 0)
|
|
return t;
|
|
}
|
|
}
|
|
return tmps[0];
|
|
}
|
|
|
|
private static Text FindChildLegacyText(Transform root, string nameContains)
|
|
{
|
|
var texts = root.GetComponentsInChildren<Text>(true);
|
|
if (texts == null || texts.Length == 0) return null;
|
|
if (!string.IsNullOrEmpty(nameContains))
|
|
{
|
|
string target = nameContains.Replace(" ", "");
|
|
for (int i = 0; i < texts.Length; i++)
|
|
{
|
|
var t = texts[i];
|
|
if (t == null) continue;
|
|
string n = t.name.Replace(" ", "");
|
|
if (n.IndexOf(target, StringComparison.OrdinalIgnoreCase) >= 0)
|
|
return t;
|
|
}
|
|
}
|
|
return texts[0];
|
|
}
|
|
|
|
private static void EnsureRaycastTarget(GameObject go)
|
|
{
|
|
var graphics = go.GetComponentsInChildren<Graphic>(true);
|
|
if (graphics != null && graphics.Length > 0)
|
|
{
|
|
for (int i = 0; i < graphics.Length; i++)
|
|
{
|
|
if (graphics[i] != null)
|
|
graphics[i].raycastTarget = true;
|
|
}
|
|
return;
|
|
}
|
|
|
|
var img = go.AddComponent<Image>();
|
|
img.color = new Color(1f, 1f, 1f, 0f);
|
|
img.raycastTarget = true;
|
|
}
|
|
|
|
private bool AttachHoverTargets(Transform root, UI_FunctionHoverEntry entry)
|
|
{
|
|
if (root == null || entry == null) return false;
|
|
|
|
Transform attachRoot = root;
|
|
Button button = root.GetComponent<Button>();
|
|
if (button == null)
|
|
button = root.GetComponentInParent<Button>();
|
|
if (button != null)
|
|
attachRoot = button.transform;
|
|
|
|
var hover = attachRoot.GetComponent<UI_FunctionHoverTarget>();
|
|
if (hover == null)
|
|
hover = attachRoot.gameObject.AddComponent<UI_FunctionHoverTarget>();
|
|
hover.guide = this;
|
|
hover.entry = entry;
|
|
|
|
if (button != null)
|
|
{
|
|
if (button.targetGraphic != null)
|
|
button.targetGraphic.raycastTarget = true;
|
|
else
|
|
EnsureRaycastTarget(attachRoot.gameObject);
|
|
}
|
|
else
|
|
{
|
|
var graphic = attachRoot.GetComponent<Graphic>();
|
|
if (graphic != null)
|
|
graphic.raycastTarget = true;
|
|
else
|
|
EnsureRaycastTarget(attachRoot.gameObject);
|
|
}
|
|
|
|
var rect = attachRoot as RectTransform;
|
|
if (rect != null)
|
|
{
|
|
hoverTargets.Add(new HoverTarget
|
|
{
|
|
entry = entry,
|
|
rect = rect,
|
|
area = Mathf.Abs(rect.rect.width * rect.rect.height)
|
|
});
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private UI_FunctionHoverEntry GetHoveredEntry()
|
|
{
|
|
if (hoverTargets.Count == 0) return null;
|
|
Vector2 mouse = Input.mousePosition;
|
|
UI_FunctionHoverEntry best = null;
|
|
float bestArea = float.MaxValue;
|
|
for (int i = 0; i < hoverTargets.Count; i++)
|
|
{
|
|
var target = hoverTargets[i];
|
|
if (target == null || target.rect == null) continue;
|
|
if (!target.rect.gameObject.activeInHierarchy) continue;
|
|
var canvas = target.rect.GetComponentInParent<Canvas>();
|
|
Camera cam = canvas != null && canvas.renderMode != RenderMode.ScreenSpaceOverlay
|
|
? canvas.worldCamera
|
|
: null;
|
|
if (RectTransformUtility.RectangleContainsScreenPoint(target.rect, mouse, cam))
|
|
{
|
|
float area = target.area > 0f ? target.area : Mathf.Abs(target.rect.rect.width * target.rect.rect.height);
|
|
if (area < bestArea)
|
|
{
|
|
bestArea = area;
|
|
best = target.entry;
|
|
}
|
|
}
|
|
}
|
|
return best;
|
|
}
|
|
|
|
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
|
|
private static void Bootstrap()
|
|
{
|
|
SceneManager.sceneLoaded += BootstrapOnSceneLoaded;
|
|
int count = SceneManager.sceneCount;
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
var scene = SceneManager.GetSceneAt(i);
|
|
if (scene.isLoaded)
|
|
BootstrapOnSceneLoaded(scene, LoadSceneMode.Single);
|
|
}
|
|
}
|
|
|
|
private static void BootstrapOnSceneLoaded(Scene scene, LoadSceneMode mode)
|
|
{
|
|
if (!scene.IsValid()) return;
|
|
if (scene.name.IndexOf("UI_UI", StringComparison.OrdinalIgnoreCase) < 0) return;
|
|
if (UnityEngine.Object.FindAnyObjectByType<UI_FunctionHoverGuide>() != null) return;
|
|
var go = new GameObject("UI_FunctionHoverGuide");
|
|
go.AddComponent<UI_FunctionHoverGuide>();
|
|
}
|
|
}
|
|
|
|
|