fix(eye): 修复视口遮罩不可见并避免遮挡系统 UI
- 将 overlay Canvas 设到 UI 层以匹配 UI Camera culling mask - sortingOrder 设为 0,位于 UI Canvas(1) 与 Dialog(3) 之下 - 修正 EnterModule 与 Start 时序,避免模块激活后被误关 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,403 +1,421 @@
|
||||
using System.Collections;
|
||||
using DG.Tweening;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace AibisDream
|
||||
{
|
||||
/// <summary>
|
||||
/// Mobile-friendly fullscreen eye viewport: elliptical sight + vertical ellipse blink.
|
||||
/// </summary>
|
||||
[DisallowMultipleComponent]
|
||||
[ExecuteAlways]
|
||||
public class EyeViewportOverlay : MonoBehaviour
|
||||
{
|
||||
private const string OverlaySortingLayer = "StartUI";
|
||||
private const float OverlayPlaneDistance = 10f;
|
||||
|
||||
private static readonly int EyeWidthId = Shader.PropertyToID("_EyeWidth");
|
||||
private static readonly int EyeHeightId = Shader.PropertyToID("_EyeHeight");
|
||||
private static readonly int EdgeSoftnessId = Shader.PropertyToID("_EdgeSoftness");
|
||||
private static readonly int BlinkOpenId = Shader.PropertyToID("_BlinkOpen");
|
||||
|
||||
/// <summary>须低于 Persistence 中 UI Canvas(1) 与 Dialog Canvas(3)。</summary>
|
||||
[SerializeField] private int sortingOrder;
|
||||
|
||||
private Canvas overlayCanvas;
|
||||
private Image overlayImage;
|
||||
private Material instanceMaterial;
|
||||
private EyeVisualSettings activeSettings;
|
||||
private Coroutine autoBlinkRoutine;
|
||||
private Tween blinkTween;
|
||||
private float blinkOpen = 1f;
|
||||
private bool moduleActive;
|
||||
private bool editorPreviewActive;
|
||||
private bool hierarchyReady;
|
||||
private bool lifecycleReady;
|
||||
private Coroutine deferPrepareRoutine;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
lifecycleReady = true;
|
||||
if (!Application.isPlaying)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PrepareHierarchyIfNeeded();
|
||||
SetOverlayActive(false);
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
StopEffects();
|
||||
if (instanceMaterial != null)
|
||||
{
|
||||
Destroy(instanceMaterial);
|
||||
}
|
||||
}
|
||||
|
||||
public void EnterModule(EyeVisualSettings settings)
|
||||
{
|
||||
moduleActive = true;
|
||||
editorPreviewActive = false;
|
||||
ShowOverlay(settings);
|
||||
}
|
||||
|
||||
public void ExitModule()
|
||||
{
|
||||
moduleActive = false;
|
||||
if (editorPreviewActive)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
StopEffects();
|
||||
SetOverlayActive(false);
|
||||
activeSettings = null;
|
||||
}
|
||||
|
||||
public void EnterEditorPreview(EyeVisualSettings settings)
|
||||
{
|
||||
editorPreviewActive = true;
|
||||
ShowOverlay(settings);
|
||||
}
|
||||
|
||||
public void ExitEditorPreview()
|
||||
{
|
||||
editorPreviewActive = false;
|
||||
if (moduleActive)
|
||||
{
|
||||
ShowOverlay(activeSettings);
|
||||
return;
|
||||
}
|
||||
|
||||
StopEffects();
|
||||
SetOverlayActive(false);
|
||||
activeSettings = null;
|
||||
}
|
||||
|
||||
public void ApplySettings(EyeVisualSettings settings)
|
||||
{
|
||||
if (settings == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!settings.enableViewportOverlay)
|
||||
{
|
||||
if (editorPreviewActive)
|
||||
{
|
||||
ExitEditorPreview();
|
||||
}
|
||||
else if (moduleActive)
|
||||
{
|
||||
ExitModule();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
PrepareHierarchyIfNeeded();
|
||||
if (instanceMaterial == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
activeSettings = settings;
|
||||
PushViewportParams(settings);
|
||||
SetBlinkOpen(blinkOpen);
|
||||
|
||||
if (!overlayCanvas.gameObject.activeInHierarchy)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (settings.autoBlink && Application.isPlaying && (moduleActive || editorPreviewActive))
|
||||
{
|
||||
StartAutoBlink(settings);
|
||||
}
|
||||
else
|
||||
{
|
||||
StopAutoBlink();
|
||||
}
|
||||
}
|
||||
|
||||
public Tween PlayBlink(float? totalDuration = null)
|
||||
{
|
||||
if (instanceMaterial == null || activeSettings == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
blinkTween?.Kill();
|
||||
|
||||
float closeDuration = activeSettings.blinkCloseDuration;
|
||||
float openDuration = activeSettings.blinkOpenDuration;
|
||||
if (totalDuration.HasValue)
|
||||
{
|
||||
float total = Mathf.Max(0.05f, totalDuration.Value);
|
||||
closeDuration = total * 0.42f;
|
||||
openDuration = total * 0.58f;
|
||||
}
|
||||
|
||||
blinkTween = DOTween.Sequence()
|
||||
.Append(CreateBlinkTween(blinkOpen, 0f, closeDuration, Ease.InQuad))
|
||||
.Append(CreateBlinkTween(0f, 1f, openDuration, Ease.OutQuad))
|
||||
.OnComplete(() => SetBlinkOpen(1f));
|
||||
|
||||
return blinkTween;
|
||||
}
|
||||
|
||||
private void ShowOverlay(EyeVisualSettings settings)
|
||||
{
|
||||
if (settings == null || !settings.enableViewportOverlay)
|
||||
{
|
||||
ExitModule();
|
||||
return;
|
||||
}
|
||||
|
||||
PrepareHierarchyIfNeeded();
|
||||
activeSettings = settings;
|
||||
ApplySettings(settings);
|
||||
if (!editorPreviewActive)
|
||||
{
|
||||
SetBlinkOpen(1f);
|
||||
}
|
||||
|
||||
SetOverlayActive(true);
|
||||
|
||||
if (settings.autoBlink && Application.isPlaying)
|
||||
{
|
||||
StartAutoBlink(settings);
|
||||
}
|
||||
}
|
||||
|
||||
private void PushViewportParams(EyeVisualSettings settings)
|
||||
{
|
||||
instanceMaterial.SetFloat(EyeWidthId, settings.eyeWidth);
|
||||
instanceMaterial.SetFloat(EyeHeightId, settings.eyeHeight);
|
||||
instanceMaterial.SetFloat(EdgeSoftnessId, settings.edgeSoftness);
|
||||
}
|
||||
|
||||
private Tween CreateBlinkTween(float from, float to, float duration, Ease ease)
|
||||
{
|
||||
blinkOpen = from;
|
||||
SetBlinkOpen(from);
|
||||
return DOTween.To(() => blinkOpen, value =>
|
||||
{
|
||||
blinkOpen = value;
|
||||
SetBlinkOpen(value);
|
||||
}, to, duration).SetEase(ease);
|
||||
}
|
||||
|
||||
private void StartAutoBlink(EyeVisualSettings settings)
|
||||
{
|
||||
StopAutoBlink();
|
||||
if (!settings.autoBlink)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
autoBlinkRoutine = StartCoroutine(AutoBlinkRoutine(settings));
|
||||
}
|
||||
|
||||
private IEnumerator AutoBlinkRoutine(EyeVisualSettings settings)
|
||||
{
|
||||
while (enabled && overlayCanvas != null && overlayCanvas.gameObject.activeInHierarchy)
|
||||
{
|
||||
float min = Mathf.Max(1f, settings.autoBlinkInterval.x);
|
||||
float max = Mathf.Max(min, settings.autoBlinkInterval.y);
|
||||
yield return new WaitForSeconds(Random.Range(min, max));
|
||||
|
||||
Tween blink = PlayBlink();
|
||||
if (blink != null)
|
||||
{
|
||||
yield return blink.WaitForCompletion();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void StopEffects()
|
||||
{
|
||||
blinkTween?.Kill();
|
||||
blinkTween = null;
|
||||
StopAutoBlink();
|
||||
}
|
||||
|
||||
private void StopAutoBlink()
|
||||
{
|
||||
if (autoBlinkRoutine == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
StopCoroutine(autoBlinkRoutine);
|
||||
autoBlinkRoutine = null;
|
||||
}
|
||||
|
||||
public void SetBlinkOpenPreview(float value)
|
||||
{
|
||||
SetBlinkOpen(value);
|
||||
}
|
||||
|
||||
public float GetBlinkOpenPreview() => blinkOpen;
|
||||
|
||||
private void SetBlinkOpen(float value)
|
||||
{
|
||||
blinkOpen = Mathf.Clamp01(value);
|
||||
instanceMaterial?.SetFloat(BlinkOpenId, blinkOpen);
|
||||
}
|
||||
|
||||
private void SetOverlayActive(bool active)
|
||||
{
|
||||
if (overlayCanvas != null)
|
||||
{
|
||||
overlayCanvas.gameObject.SetActive(active);
|
||||
}
|
||||
}
|
||||
|
||||
private void PrepareHierarchyIfNeeded()
|
||||
{
|
||||
if (hierarchyReady && overlayCanvas != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (Application.isPlaying && !lifecycleReady)
|
||||
{
|
||||
ScheduleDeferredPrepare();
|
||||
return;
|
||||
}
|
||||
|
||||
EnsureOverlayHierarchy();
|
||||
hierarchyReady = overlayCanvas != null;
|
||||
}
|
||||
|
||||
private void ScheduleDeferredPrepare()
|
||||
{
|
||||
if (deferPrepareRoutine != null || !isActiveAndEnabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
deferPrepareRoutine = StartCoroutine(DeferredPrepareHierarchy());
|
||||
}
|
||||
|
||||
private IEnumerator DeferredPrepareHierarchy()
|
||||
{
|
||||
yield return null;
|
||||
deferPrepareRoutine = null;
|
||||
EnsureOverlayHierarchy();
|
||||
hierarchyReady = overlayCanvas != null;
|
||||
|
||||
if (activeSettings != null && instanceMaterial != null)
|
||||
{
|
||||
PushViewportParams(activeSettings);
|
||||
SetBlinkOpen(blinkOpen);
|
||||
}
|
||||
}
|
||||
|
||||
private void EnsureOverlayHierarchy()
|
||||
{
|
||||
if (overlayCanvas == null)
|
||||
{
|
||||
var canvasGo = new GameObject("EyeViewportOverlayCanvas");
|
||||
canvasGo.transform.SetParent(transform, false);
|
||||
|
||||
overlayCanvas = canvasGo.AddComponent<Canvas>();
|
||||
overlayCanvas.pixelPerfect = false;
|
||||
|
||||
var scaler = canvasGo.AddComponent<CanvasScaler>();
|
||||
scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
|
||||
scaler.referenceResolution = new Vector2(1920f, 1080f);
|
||||
scaler.matchWidthOrHeight = 0.5f;
|
||||
|
||||
canvasGo.AddComponent<GraphicRaycaster>();
|
||||
|
||||
var imageGo = new GameObject("Overlay", typeof(RectTransform));
|
||||
imageGo.transform.SetParent(canvasGo.transform, false);
|
||||
|
||||
var rect = (RectTransform)imageGo.transform;
|
||||
rect.anchorMin = Vector2.zero;
|
||||
rect.anchorMax = Vector2.one;
|
||||
rect.offsetMin = Vector2.zero;
|
||||
rect.offsetMax = Vector2.zero;
|
||||
|
||||
overlayImage = imageGo.AddComponent<Image>();
|
||||
overlayImage.raycastTarget = false;
|
||||
overlayImage.color = Color.black;
|
||||
SetupMaterial();
|
||||
}
|
||||
|
||||
ApplyCanvasRenderSettings();
|
||||
}
|
||||
|
||||
private void SetupMaterial()
|
||||
{
|
||||
Shader shader = Shader.Find("UI/EyeViewportOverlay");
|
||||
if (shader == null)
|
||||
{
|
||||
Debug.LogError("[EyeViewportOverlay] 找不到 UI/EyeViewportOverlay shader。", this);
|
||||
return;
|
||||
}
|
||||
|
||||
instanceMaterial = new Material(shader)
|
||||
{
|
||||
name = "EyeViewportOverlay (Instance)",
|
||||
hideFlags = HideFlags.DontSave
|
||||
};
|
||||
overlayImage.material = instanceMaterial;
|
||||
}
|
||||
|
||||
private void ApplyCanvasRenderSettings()
|
||||
{
|
||||
if (overlayCanvas == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
overlayCanvas.renderMode = RenderMode.ScreenSpaceCamera;
|
||||
overlayCanvas.worldCamera = ResolveUICamera();
|
||||
overlayCanvas.planeDistance = OverlayPlaneDistance;
|
||||
overlayCanvas.sortingLayerName = OverlaySortingLayer;
|
||||
overlayCanvas.sortingOrder = sortingOrder;
|
||||
}
|
||||
|
||||
private static Camera ResolveUICamera()
|
||||
{
|
||||
if (UIManager.Instance != null && UIManager.Instance.UICamera != null)
|
||||
{
|
||||
return UIManager.Instance.UICamera;
|
||||
}
|
||||
|
||||
var uiManager = FindObjectOfType<UIManager>();
|
||||
if (uiManager != null && uiManager.UICamera != null)
|
||||
{
|
||||
return uiManager.UICamera;
|
||||
}
|
||||
|
||||
return Camera.main;
|
||||
}
|
||||
}
|
||||
}
|
||||
using System.Collections;␍
|
||||
using DG.Tweening;␍
|
||||
using UnityEngine;␍
|
||||
using UnityEngine.UI;␍
|
||||
␍
|
||||
namespace AibisDream␍
|
||||
{␍
|
||||
/// <summary>␍
|
||||
/// Mobile-friendly fullscreen eye viewport: elliptical sight + vertical ellipse blink.␍
|
||||
/// </summary>␍
|
||||
[DisallowMultipleComponent]␍
|
||||
[ExecuteAlways]␍
|
||||
public class EyeViewportOverlay : MonoBehaviour␍
|
||||
{␍
|
||||
private const string OverlaySortingLayer = "StartUI";␍
|
||||
private const float OverlayPlaneDistance = 10f;␍
|
||||
␍
|
||||
private static readonly int EyeWidthId = Shader.PropertyToID("_EyeWidth");␍
|
||||
private static readonly int EyeHeightId = Shader.PropertyToID("_EyeHeight");␍
|
||||
private static readonly int EdgeSoftnessId = Shader.PropertyToID("_EdgeSoftness");␍
|
||||
private static readonly int BlinkOpenId = Shader.PropertyToID("_BlinkOpen");␍
|
||||
␍
|
||||
/// <summary>Persistence:本遮罩=0,UI Canvas=1,Dialog Canvas=3。</summary>␍
|
||||
[SerializeField] private int sortingOrder;␍
|
||||
␍
|
||||
private Canvas overlayCanvas;␍
|
||||
private Image overlayImage;␍
|
||||
private Material instanceMaterial;␍
|
||||
private EyeVisualSettings activeSettings;␍
|
||||
private Coroutine autoBlinkRoutine;␍
|
||||
private Tween blinkTween;␍
|
||||
private float blinkOpen = 1f;␍
|
||||
private bool moduleActive;␍
|
||||
private bool editorPreviewActive;␍
|
||||
private bool hierarchyReady;␍
|
||||
private bool lifecycleReady;␍
|
||||
private Coroutine deferPrepareRoutine;␍
|
||||
␍
|
||||
private void Start()␍
|
||||
{␍
|
||||
lifecycleReady = true;␍
|
||||
if (!Application.isPlaying)␍
|
||||
{␍
|
||||
return;␍
|
||||
}␍
|
||||
␍
|
||||
PrepareHierarchyIfNeeded();␍
|
||||
if (!moduleActive && !editorPreviewActive)␍
|
||||
{␍
|
||||
SetOverlayActive(false);␍
|
||||
}␍
|
||||
}␍
|
||||
␍
|
||||
private void OnDestroy()␍
|
||||
{␍
|
||||
StopEffects();␍
|
||||
if (instanceMaterial != null)␍
|
||||
{␍
|
||||
Destroy(instanceMaterial);␍
|
||||
}␍
|
||||
}␍
|
||||
␍
|
||||
public void EnterModule(EyeVisualSettings settings)␍
|
||||
{␍
|
||||
moduleActive = true;␍
|
||||
editorPreviewActive = false;␍
|
||||
ShowOverlay(settings);␍
|
||||
}␍
|
||||
␍
|
||||
public void ExitModule()␍
|
||||
{␍
|
||||
moduleActive = false;␍
|
||||
if (editorPreviewActive)␍
|
||||
{␍
|
||||
return;␍
|
||||
}␍
|
||||
␍
|
||||
StopEffects();␍
|
||||
SetOverlayActive(false);␍
|
||||
activeSettings = null;␍
|
||||
}␍
|
||||
␍
|
||||
public void EnterEditorPreview(EyeVisualSettings settings)␍
|
||||
{␍
|
||||
editorPreviewActive = true;␍
|
||||
ShowOverlay(settings);␍
|
||||
}␍
|
||||
␍
|
||||
public void ExitEditorPreview()␍
|
||||
{␍
|
||||
editorPreviewActive = false;␍
|
||||
if (moduleActive)␍
|
||||
{␍
|
||||
ShowOverlay(activeSettings);␍
|
||||
return;␍
|
||||
}␍
|
||||
␍
|
||||
StopEffects();␍
|
||||
SetOverlayActive(false);␍
|
||||
activeSettings = null;␍
|
||||
}␍
|
||||
␍
|
||||
public void ApplySettings(EyeVisualSettings settings)␍
|
||||
{␍
|
||||
if (settings == null)␍
|
||||
{␍
|
||||
return;␍
|
||||
}␍
|
||||
␍
|
||||
if (!settings.enableViewportOverlay)␍
|
||||
{␍
|
||||
if (editorPreviewActive)␍
|
||||
{␍
|
||||
ExitEditorPreview();␍
|
||||
}␍
|
||||
else if (moduleActive)␍
|
||||
{␍
|
||||
ExitModule();␍
|
||||
}␍
|
||||
␍
|
||||
return;␍
|
||||
}␍
|
||||
␍
|
||||
PrepareHierarchyIfNeeded();␍
|
||||
if (instanceMaterial == null)␍
|
||||
{␍
|
||||
return;␍
|
||||
}␍
|
||||
␍
|
||||
activeSettings = settings;␍
|
||||
PushViewportParams(settings);␍
|
||||
SetBlinkOpen(blinkOpen);␍
|
||||
␍
|
||||
if (!overlayCanvas.gameObject.activeInHierarchy)␍
|
||||
{␍
|
||||
return;␍
|
||||
}␍
|
||||
␍
|
||||
if (settings.autoBlink && Application.isPlaying && (moduleActive || editorPreviewActive))␍
|
||||
{␍
|
||||
StartAutoBlink(settings);␍
|
||||
}␍
|
||||
else␍
|
||||
{␍
|
||||
StopAutoBlink();␍
|
||||
}␍
|
||||
}␍
|
||||
␍
|
||||
public Tween PlayBlink(float? totalDuration = null)␍
|
||||
{␍
|
||||
if (instanceMaterial == null || activeSettings == null)␍
|
||||
{␍
|
||||
return null;␍
|
||||
}␍
|
||||
␍
|
||||
blinkTween?.Kill();␍
|
||||
␍
|
||||
float closeDuration = activeSettings.blinkCloseDuration;␍
|
||||
float openDuration = activeSettings.blinkOpenDuration;␍
|
||||
if (totalDuration.HasValue)␍
|
||||
{␍
|
||||
float total = Mathf.Max(0.05f, totalDuration.Value);␍
|
||||
closeDuration = total * 0.42f;␍
|
||||
openDuration = total * 0.58f;␍
|
||||
}␍
|
||||
␍
|
||||
blinkTween = DOTween.Sequence()␍
|
||||
.Append(CreateBlinkTween(blinkOpen, 0f, closeDuration, Ease.InQuad))␍
|
||||
.Append(CreateBlinkTween(0f, 1f, openDuration, Ease.OutQuad))␍
|
||||
.OnComplete(() => SetBlinkOpen(1f));␍
|
||||
␍
|
||||
return blinkTween;␍
|
||||
}␍
|
||||
␍
|
||||
private void ShowOverlay(EyeVisualSettings settings)␍
|
||||
{␍
|
||||
if (settings == null || !settings.enableViewportOverlay)␍
|
||||
{␍
|
||||
ExitModule();␍
|
||||
return;␍
|
||||
}␍
|
||||
␍
|
||||
PrepareHierarchyIfNeeded();␍
|
||||
ApplyCanvasRenderSettings();␍
|
||||
activeSettings = settings;␍
|
||||
ApplySettings(settings);␍
|
||||
if (!editorPreviewActive)␍
|
||||
{␍
|
||||
SetBlinkOpen(1f);␍
|
||||
}␍
|
||||
␍
|
||||
SetOverlayActive(true);␍
|
||||
␍
|
||||
if (settings.autoBlink && Application.isPlaying)␍
|
||||
{␍
|
||||
StartAutoBlink(settings);␍
|
||||
}␍
|
||||
}␍
|
||||
␍
|
||||
private void PushViewportParams(EyeVisualSettings settings)␍
|
||||
{␍
|
||||
instanceMaterial.SetFloat(EyeWidthId, settings.eyeWidth);␍
|
||||
instanceMaterial.SetFloat(EyeHeightId, settings.eyeHeight);␍
|
||||
instanceMaterial.SetFloat(EdgeSoftnessId, settings.edgeSoftness);␍
|
||||
}␍
|
||||
␍
|
||||
private Tween CreateBlinkTween(float from, float to, float duration, Ease ease)␍
|
||||
{␍
|
||||
blinkOpen = from;␍
|
||||
SetBlinkOpen(from);␍
|
||||
return DOTween.To(() => blinkOpen, value =>␍
|
||||
{␍
|
||||
blinkOpen = value;␍
|
||||
SetBlinkOpen(value);␍
|
||||
}, to, duration).SetEase(ease);␍
|
||||
}␍
|
||||
␍
|
||||
private void StartAutoBlink(EyeVisualSettings settings)␍
|
||||
{␍
|
||||
StopAutoBlink();␍
|
||||
if (!settings.autoBlink)␍
|
||||
{␍
|
||||
return;␍
|
||||
}␍
|
||||
␍
|
||||
autoBlinkRoutine = StartCoroutine(AutoBlinkRoutine(settings));␍
|
||||
}␍
|
||||
␍
|
||||
private IEnumerator AutoBlinkRoutine(EyeVisualSettings settings)␍
|
||||
{␍
|
||||
while (enabled && overlayCanvas != null && overlayCanvas.gameObject.activeInHierarchy)␍
|
||||
{␍
|
||||
float min = Mathf.Max(1f, settings.autoBlinkInterval.x);␍
|
||||
float max = Mathf.Max(min, settings.autoBlinkInterval.y);␍
|
||||
yield return new WaitForSeconds(Random.Range(min, max));␍
|
||||
␍
|
||||
Tween blink = PlayBlink();␍
|
||||
if (blink != null)␍
|
||||
{␍
|
||||
yield return blink.WaitForCompletion();␍
|
||||
}␍
|
||||
}␍
|
||||
}␍
|
||||
␍
|
||||
private void StopEffects()␍
|
||||
{␍
|
||||
blinkTween?.Kill();␍
|
||||
blinkTween = null;␍
|
||||
StopAutoBlink();␍
|
||||
}␍
|
||||
␍
|
||||
private void StopAutoBlink()␍
|
||||
{␍
|
||||
if (autoBlinkRoutine == null)␍
|
||||
{␍
|
||||
return;␍
|
||||
}␍
|
||||
␍
|
||||
StopCoroutine(autoBlinkRoutine);␍
|
||||
autoBlinkRoutine = null;␍
|
||||
}␍
|
||||
␍
|
||||
public void SetBlinkOpenPreview(float value)␍
|
||||
{␍
|
||||
SetBlinkOpen(value);␍
|
||||
}␍
|
||||
␍
|
||||
public float GetBlinkOpenPreview() => blinkOpen;␍
|
||||
␍
|
||||
private void SetBlinkOpen(float value)␍
|
||||
{␍
|
||||
blinkOpen = Mathf.Clamp01(value);␍
|
||||
instanceMaterial?.SetFloat(BlinkOpenId, blinkOpen);␍
|
||||
}␍
|
||||
␍
|
||||
private void SetOverlayActive(bool active)␍
|
||||
{␍
|
||||
if (overlayCanvas != null)␍
|
||||
{␍
|
||||
overlayCanvas.gameObject.SetActive(active);␍
|
||||
}␍
|
||||
}␍
|
||||
␍
|
||||
private void PrepareHierarchyIfNeeded()␍
|
||||
{␍
|
||||
if (Application.isPlaying && !lifecycleReady)␍
|
||||
{␍
|
||||
ScheduleDeferredPrepare();␍
|
||||
return;␍
|
||||
}␍
|
||||
␍
|
||||
EnsureOverlayHierarchy();␍
|
||||
hierarchyReady = overlayCanvas != null;␍
|
||||
ApplyCanvasRenderSettings();␍
|
||||
}␍
|
||||
␍
|
||||
private void ScheduleDeferredPrepare()␍
|
||||
{␍
|
||||
if (deferPrepareRoutine != null || !isActiveAndEnabled)␍
|
||||
{␍
|
||||
return;␍
|
||||
}␍
|
||||
␍
|
||||
deferPrepareRoutine = StartCoroutine(DeferredPrepareHierarchy());␍
|
||||
}␍
|
||||
␍
|
||||
private IEnumerator DeferredPrepareHierarchy()␍
|
||||
{␍
|
||||
yield return null;␍
|
||||
deferPrepareRoutine = null;␍
|
||||
EnsureOverlayHierarchy();␍
|
||||
hierarchyReady = overlayCanvas != null;␍
|
||||
␍
|
||||
if (activeSettings != null && instanceMaterial != null)␍
|
||||
{␍
|
||||
PushViewportParams(activeSettings);␍
|
||||
SetBlinkOpen(blinkOpen);␍
|
||||
}␍
|
||||
␍
|
||||
ApplyCanvasRenderSettings();␍
|
||||
}␍
|
||||
␍
|
||||
private void EnsureOverlayHierarchy()␍
|
||||
{␍
|
||||
if (overlayCanvas == null)␍
|
||||
{␍
|
||||
var canvasGo = new GameObject("EyeViewportOverlayCanvas");␍
|
||||
canvasGo.transform.SetParent(transform, false);␍
|
||||
␍
|
||||
overlayCanvas = canvasGo.AddComponent<Canvas>();␍
|
||||
overlayCanvas.pixelPerfect = false;␍
|
||||
␍
|
||||
var scaler = canvasGo.AddComponent<CanvasScaler>();␍
|
||||
scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;␍
|
||||
scaler.referenceResolution = new Vector2(1920f, 1080f);␍
|
||||
scaler.matchWidthOrHeight = 0.5f;␍
|
||||
␍
|
||||
canvasGo.AddComponent<GraphicRaycaster>();␍
|
||||
␍
|
||||
var imageGo = new GameObject("Overlay", typeof(RectTransform));␍
|
||||
imageGo.transform.SetParent(canvasGo.transform, false);␍
|
||||
␍
|
||||
var rect = (RectTransform)imageGo.transform;␍
|
||||
rect.anchorMin = Vector2.zero;␍
|
||||
rect.anchorMax = Vector2.one;␍
|
||||
rect.offsetMin = Vector2.zero;␍
|
||||
rect.offsetMax = Vector2.zero;␍
|
||||
␍
|
||||
overlayImage = imageGo.AddComponent<Image>();␍
|
||||
overlayImage.raycastTarget = false;␍
|
||||
overlayImage.color = Color.black;␍
|
||||
SetupMaterial();␍
|
||||
}␍
|
||||
␍
|
||||
ApplyCanvasRenderSettings();␍
|
||||
}␍
|
||||
␍
|
||||
private void SetupMaterial()␍
|
||||
{␍
|
||||
Shader shader = Shader.Find("UI/EyeViewportOverlay");␍
|
||||
if (shader == null)␍
|
||||
{␍
|
||||
Debug.LogError("[EyeViewportOverlay] 找不到 UI/EyeViewportOverlay shader。", this);␍
|
||||
return;␍
|
||||
}␍
|
||||
␍
|
||||
instanceMaterial = new Material(shader)␍
|
||||
{␍
|
||||
name = "EyeViewportOverlay (Instance)",␍
|
||||
hideFlags = HideFlags.DontSave␍
|
||||
};␍
|
||||
overlayImage.material = instanceMaterial;␍
|
||||
}␍
|
||||
␍
|
||||
private void ApplyCanvasRenderSettings()␍
|
||||
{␍
|
||||
if (overlayCanvas == null)␍
|
||||
{␍
|
||||
return;␍
|
||||
}␍
|
||||
␍
|
||||
overlayCanvas.renderMode = RenderMode.ScreenSpaceCamera;␍
|
||||
overlayCanvas.worldCamera = ResolveUICamera();␍
|
||||
overlayCanvas.planeDistance = OverlayPlaneDistance;␍
|
||||
overlayCanvas.sortingLayerName = OverlaySortingLayer;␍
|
||||
overlayCanvas.sortingOrder = sortingOrder;␍
|
||||
SetOverlayUILayer(overlayCanvas.gameObject);␍
|
||||
}␍
|
||||
␍
|
||||
private static void SetOverlayUILayer(GameObject root)␍
|
||||
{␍
|
||||
int uiLayer = LayerMask.NameToLayer("UI");␍
|
||||
if (uiLayer < 0)␍
|
||||
{␍
|
||||
uiLayer = 5;␍
|
||||
}␍
|
||||
␍
|
||||
foreach (Transform node in root.GetComponentsInChildren<Transform>(true))␍
|
||||
{␍
|
||||
node.gameObject.layer = uiLayer;␍
|
||||
}␍
|
||||
}␍
|
||||
␍
|
||||
private static Camera ResolveUICamera()␍
|
||||
{␍
|
||||
if (UIManager.Instance != null && UIManager.Instance.UICamera != null)␍
|
||||
{␍
|
||||
return UIManager.Instance.UICamera;␍
|
||||
}␍
|
||||
␍
|
||||
var uiManager = Object.FindObjectOfType<UIManager>();␍
|
||||
if (uiManager != null && uiManager.UICamera != null)␍
|
||||
{␍
|
||||
return uiManager.UICamera;␍
|
||||
}␍
|
||||
␍
|
||||
return Camera.main;␍
|
||||
}␍
|
||||
}␍
|
||||
}␍
|
||||
␍
|
||||
Reference in New Issue
Block a user