545 lines
18 KiB
C#
545 lines
18 KiB
C#
using System;
|
||
using System.Collections;
|
||
using System.Collections.Generic;
|
||
using UnityEngine;
|
||
using DG.Tweening;
|
||
using Yarn.Unity;
|
||
using System.Linq;
|
||
using UnityEngine.UI;
|
||
using AibisDream.FixSystem;
|
||
using Cinemachine;
|
||
using AibisDream.Framework;
|
||
using AibisDream.Kit;
|
||
using UnityEngine.ResourceManagement.AsyncOperations;
|
||
using Sequence = DG.Tweening.Sequence;
|
||
|
||
namespace AibisDream
|
||
{
|
||
public class EyeSystem : MonoBehaviour
|
||
{
|
||
private const string MemorySpritePath = "Sprite/Memory/{0}.png";
|
||
|
||
public BubbleSlotGroup bubbleSlotGroup;
|
||
[SerializeField] private List<EyeTarget> targets = new(); // 保存目标列表
|
||
public List<EyeTarget> Targets => targets; // 提供目标列表的公共访问
|
||
private EyeTarget currentTarget; // 当前选中的目标
|
||
public EyeTarget CurrentTarget => currentTarget; // 提供当前目标的公共访问
|
||
public RawImage eyeImage;
|
||
public Image memoryRender;
|
||
public GameObject eyeView;
|
||
public ViewCameraManager cameraManager;
|
||
private IEyeStateEffect eyeStateEffect;
|
||
private EyeData _eyeData = new();
|
||
private Tween hsvShiftTween;
|
||
private MouseFollowAndZoom mouseFollowAndZoom;
|
||
|
||
/// <summary>
|
||
/// 眼睛 RawImage 的故障/色相/饱和度等效果依赖 AllIn1SpriteShader(或兼容)材质上的属性。
|
||
/// 若未在 Inspector 指定 Material,Unity 会使用 Default UI Material(UI/Default),不含 _HsvShift 等属性。
|
||
/// </summary>
|
||
private bool TryGetEyeImageMaterialForEffects(out Material material)
|
||
{
|
||
material = null;
|
||
if (eyeImage == null)
|
||
{
|
||
Debug.LogError("[EyeSystem] eyeImage 未赋值。", this);
|
||
return false;
|
||
}
|
||
|
||
material = eyeImage.material;
|
||
if (material == null || !material.HasProperty("_HsvShift"))
|
||
{
|
||
Debug.LogError(
|
||
"[EyeSystem] eyeImage 需要指定 AllIn1SpriteShader(或兼容)材质;当前为 " +
|
||
(material != null && material.shader != null ? material.shader.name : "null") +
|
||
"。请在场景中选中该 RawImage,指定项目内 EyeMaterials / AllIn1 材质,勿留空(否则会使用 Default UI Material)。",
|
||
this);
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
private void Awake()
|
||
{
|
||
Init();
|
||
}
|
||
|
||
private bool EnsureMouseFollowAndZoom()
|
||
{
|
||
if (mouseFollowAndZoom != null)
|
||
{
|
||
return true;
|
||
}
|
||
|
||
mouseFollowAndZoom = FindObjectOfType<MouseFollowAndZoom>(true);
|
||
if (mouseFollowAndZoom == null)
|
||
{
|
||
Debug.LogError("[EyeSystem] MouseFollowAndZoom 未找到,无法进入视觉模块。", this);
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
public void Init()
|
||
{
|
||
FixSystemCenter.SystemDic.Register(this);
|
||
var virtualCam = transform.Find("Eye Camera").GetComponent<ICinemachineCamera>();
|
||
var virtualCamDeep = transform.Find("Eye DeepCamera").GetComponent<ICinemachineCamera>();
|
||
CameraKit.Instance.RegisterCamera(CameraEnum.Eye, virtualCam);
|
||
CameraKit.Instance.RegisterCamera(CameraEnum.EyeDeep, virtualCamDeep);
|
||
StorageSystem.Instance.RegisterData(_eyeData);
|
||
// 添加加载逻辑
|
||
_eyeData.LoadEvent += LoadData;
|
||
|
||
// 确保 ViewCamera 默认不激活
|
||
if (cameraManager != null)
|
||
{
|
||
cameraManager.gameObject.SetActive(false);
|
||
}
|
||
|
||
EnsureMouseFollowAndZoom();
|
||
}
|
||
|
||
private void LoadData(EyeData eyedata)
|
||
{
|
||
SetEyeColorState(eyedata.currentState);
|
||
}
|
||
|
||
public void OnEnter()
|
||
{
|
||
if (!EnsureMouseFollowAndZoom())
|
||
{
|
||
return;
|
||
}
|
||
|
||
// 先激活 ViewCamera
|
||
if (cameraManager != null)
|
||
{
|
||
cameraManager.gameObject.SetActive(true);
|
||
}
|
||
|
||
mouseFollowAndZoom.SetIsActive(true);
|
||
mouseFollowAndZoom.SetIsLock(false);
|
||
// 激活 ViewCamera 后再打开 EyeView
|
||
OpenEyeView();
|
||
// 进入视图时的逻辑
|
||
Debug.Log("EyeView entered.");
|
||
}
|
||
|
||
public void OpenEyeView()
|
||
{
|
||
eyeView.SetActive(true);
|
||
}
|
||
|
||
public void CloseEyeView()
|
||
{
|
||
eyeView.SetActive(false);
|
||
}
|
||
|
||
public void OnShow()
|
||
{
|
||
// 离开视图时的逻辑
|
||
Debug.Log("EyeView showed.");
|
||
}
|
||
|
||
public void OnExit()
|
||
{
|
||
if (currentTarget != null)
|
||
{
|
||
eyeStateEffect?.CleanupEffect(CurrentTarget);
|
||
}
|
||
|
||
if (EnsureMouseFollowAndZoom())
|
||
{
|
||
mouseFollowAndZoom.SetIsActive(false);
|
||
}
|
||
|
||
currentTarget = null;
|
||
// 先关闭 EyeView
|
||
CloseEyeView();
|
||
// 再关闭 ViewCamera
|
||
if (cameraManager != null)
|
||
{
|
||
cameraManager.gameObject.SetActive(false);
|
||
}
|
||
// 离开视图时的逻辑
|
||
Debug.Log("EyeView exited.");
|
||
}
|
||
|
||
private void Start()
|
||
{
|
||
SetEyeColorState(_eyeData.currentState);
|
||
|
||
EnsureMouseFollowAndZoom();
|
||
}
|
||
|
||
public EyeTarget FindEyeTargetByName(string name)
|
||
{
|
||
return Targets.FirstOrDefault(target => target.gameObject.name == name);
|
||
}
|
||
|
||
public void SetTarget(EyeTarget newTarget)
|
||
{
|
||
if (newTarget == null)
|
||
{
|
||
Debug.LogError("Target is null in SetTarget");
|
||
return;
|
||
}
|
||
|
||
StartCoroutine(SetTargetCoroutine(newTarget.gameObject.name));
|
||
}
|
||
|
||
[YarnCommand("set_Eyetarget")]
|
||
public IEnumerator SetTargetCoroutine(string targetName, bool startDialogue = true)
|
||
{
|
||
if (!EnsureMouseFollowAndZoom())
|
||
{
|
||
yield break;
|
||
}
|
||
|
||
mouseFollowAndZoom.SetIsLock(true);
|
||
EyeTarget target = FindEyeTargetByName(targetName);
|
||
if (target == null)
|
||
{
|
||
Debug.LogError("Invalid EyeTarget name: " + targetName);
|
||
yield break;
|
||
}
|
||
|
||
StorageSystem.Instance.SetValue("$currentEyeTarget", target.name);
|
||
StartCoroutine(EyeEffectFadeOut());
|
||
|
||
cameraManager.MoveCameraTo(target.targetTransform ? target.targetTransform : target.transform, 1f);
|
||
|
||
if (currentTarget != null)
|
||
{
|
||
currentTarget.BlurIn(eyeStateEffect.TransitionDuration, false);
|
||
}
|
||
|
||
if (currentTarget == target)
|
||
{
|
||
DialogController.Instance.StartDialogNode("重复看同一个目标");
|
||
currentTarget = target;
|
||
mouseFollowAndZoom.SetIndicatorTarget(currentTarget);
|
||
currentTarget.BlurOut(eyeStateEffect.TransitionDuration, false);
|
||
yield return new WaitForSeconds(eyeStateEffect.TransitionDuration);
|
||
yield break;
|
||
}
|
||
|
||
currentTarget = target;
|
||
mouseFollowAndZoom.SetIndicatorTarget(currentTarget);
|
||
currentTarget.BlurOut(eyeStateEffect.TransitionDuration, false);
|
||
|
||
yield return new WaitForSeconds(eyeStateEffect.TransitionDuration);
|
||
|
||
if (startDialogue)
|
||
{
|
||
DialogController.Instance.StartDialogNode("IntoEyeView");
|
||
}
|
||
}
|
||
|
||
[YarnCommand("Set_MouseMovementLockState")]
|
||
public void SetMouseMovementLockState(bool isActive)
|
||
{
|
||
if (!EnsureMouseFollowAndZoom())
|
||
{
|
||
return;
|
||
}
|
||
|
||
mouseFollowAndZoom.SetIsLock(isActive);
|
||
}
|
||
|
||
[YarnCommand("EyeEffect_FadeIn")]
|
||
public IEnumerator EyeEffectFadeIn()
|
||
{
|
||
if (currentTarget != null)
|
||
{
|
||
// AudioManager.RandomPlayInteraction("colorimagine");
|
||
AudioManager.Instance.PlaySfx("event:/ActionFB/color_imagine");
|
||
|
||
eyeStateEffect?.ApplyFocusedEffect(currentTarget);
|
||
}
|
||
|
||
yield return new WaitForSeconds(eyeStateEffect.TransitionDuration);
|
||
}
|
||
|
||
[YarnCommand("EyeEffect_FadeOut")]
|
||
public IEnumerator EyeEffectFadeOut()
|
||
{
|
||
if (currentTarget != null)
|
||
{
|
||
// AudioManager.RandomPlayInteraction("colorimagine");
|
||
AudioManager.Instance.PlaySfx("event:/ActionFB/color_imagine");
|
||
eyeStateEffect?.ApplyUnfocusedEffect(currentTarget);
|
||
}
|
||
|
||
yield return new WaitForSeconds(eyeStateEffect.TransitionDuration);
|
||
}
|
||
|
||
[YarnCommand("EyeEffect_Init")]
|
||
public void EyeEffectInit()
|
||
{
|
||
foreach (EyeTarget target in targets)
|
||
{
|
||
target.Init();
|
||
}
|
||
|
||
if (currentTarget != null)
|
||
{
|
||
eyeStateEffect?.InitializeEffect(currentTarget, cameraManager);
|
||
}
|
||
}
|
||
|
||
[YarnCommand("EyeEffect_Clean")]
|
||
public void EyeEffectClean()
|
||
{
|
||
if (currentTarget != null)
|
||
{
|
||
eyeStateEffect?.CleanupEffect(currentTarget);
|
||
}
|
||
}
|
||
|
||
[YarnCommand("set_EyeColorState")]
|
||
public void SetEyeColorStateForYarn(string state)
|
||
{
|
||
// Try to parse the string as an EyeColorState
|
||
if (!Enum.TryParse(state, true, out EyeData.EyeColorState eyeState))
|
||
{
|
||
throw new ArgumentException("Invalid eye state: " + state, nameof(state));
|
||
}
|
||
|
||
// Start the coroutine for setting the eye state
|
||
SetEyeColorState(eyeState);
|
||
}
|
||
|
||
private void SetEyeColorState(EyeData.EyeColorState state)
|
||
{
|
||
_eyeData.currentState = state;
|
||
switch (state)
|
||
{
|
||
case EyeData.EyeColorState.CanImagineColor:
|
||
eyeStateEffect = new CanImagineColorEffect();
|
||
break;
|
||
case EyeData.EyeColorState.CannotImagineColor:
|
||
eyeStateEffect = new CannotImagineColorEffect();
|
||
break;
|
||
case EyeData.EyeColorState.EyeDisorder:
|
||
eyeStateEffect = new EyeDisorderEffect();
|
||
break;
|
||
case EyeData.EyeColorState.CanSeeColor:
|
||
eyeStateEffect = new CanSeeColorEffect();
|
||
break;
|
||
case EyeData.EyeColorState.HaveChip:
|
||
eyeStateEffect = new HaveChip();
|
||
break;
|
||
default:
|
||
throw new ArgumentException("Unknown eye state: " + state.ToString(), nameof(state));
|
||
}
|
||
}
|
||
|
||
[YarnCommand("Set_Saturation")]
|
||
public IEnumerator Set_Saturation(float targetSaturation, float duration)
|
||
{
|
||
if (!TryGetEyeImageMaterialForEffects(out Material material))
|
||
{
|
||
yield break;
|
||
}
|
||
|
||
// 确保 targetSaturation 在 0 到 1 之间
|
||
targetSaturation = Mathf.Clamp01(targetSaturation);
|
||
|
||
Sequence saturationSequence = DOTween.Sequence();
|
||
|
||
// 同时启动材质的饱和度变化和 Volume 的weight变化
|
||
saturationSequence.Join(material.DOFloat(targetSaturation, "_HsvSaturation", duration));
|
||
|
||
// 获取 Tween 对象并添加到序列中
|
||
Tween saturationTween = ScreenEffectManager.Instance.TweenSaturation(targetSaturation, duration);
|
||
if (saturationTween != null)
|
||
{
|
||
saturationSequence.Join(saturationTween);
|
||
}
|
||
|
||
// 启动 Sequence 并等待完成
|
||
yield return saturationSequence.WaitForCompletion();
|
||
}
|
||
|
||
|
||
|
||
private Sequence glitchin;
|
||
|
||
[YarnCommand("EyeGlitch_in")]
|
||
public IEnumerator EyeGlitch_in()
|
||
{
|
||
if (!TryGetEyeImageMaterialForEffects(out Material material))
|
||
{
|
||
yield break;
|
||
}
|
||
|
||
glitchin = DOTween.Sequence();
|
||
// _HsvShift 从 0 ~ 360 来回变化
|
||
hsvShiftTween = DOTween
|
||
.To(() => material.GetFloat("_HsvShift"), x => material.SetFloat("_HsvShift", x), 360, 1f)
|
||
.SetLoops(-1, LoopType.Yoyo);
|
||
glitchin.Append(hsvShiftTween);
|
||
|
||
// _GlitchAmount 1 秒内从 0 ~ 8
|
||
glitchin.Insert(0,
|
||
DOTween.To(() => material.GetFloat("_GlitchAmount"), x => material.SetFloat("_GlitchAmount", x), 8,
|
||
1f));
|
||
|
||
// _WarpStrength 1 秒内从 0 ~ 0.015
|
||
glitchin.Insert(0,
|
||
DOTween.To(() => material.GetFloat("_WarpStrength"), x => material.SetFloat("_WarpStrength", x), 0.015f,
|
||
1f));
|
||
|
||
yield return new WaitForSeconds(1f);
|
||
}
|
||
|
||
[YarnCommand("EyeGlitch_out")]
|
||
public void EyeGlitch_out(float duration)
|
||
{
|
||
if (!TryGetEyeImageMaterialForEffects(out Material material))
|
||
{
|
||
return;
|
||
}
|
||
|
||
if (glitchin != null && glitchin.IsActive())
|
||
{
|
||
glitchin.Kill();
|
||
}
|
||
|
||
material.DOFloat(0, "_WarpStrength", duration);
|
||
material.DOFloat(0, "_GlitchAmount", duration);
|
||
material.DOFloat(0, "_HsvShift", duration);
|
||
// 确保之前的动画被停止
|
||
}
|
||
|
||
[YarnCommand("set_EyeColor")]
|
||
public IEnumerator SetEyeColor(string color, string memoryName = null)
|
||
{
|
||
if (!TryGetEyeImageMaterialForEffects(out Material material))
|
||
{
|
||
yield break;
|
||
}
|
||
|
||
if (memoryRender == null)
|
||
{
|
||
Debug.LogError("[EyeSystem] memoryRender 未赋值。", this);
|
||
yield break;
|
||
}
|
||
|
||
Material memoryMaterial = memoryRender.material;
|
||
memoryRender.sprite = null;
|
||
memoryMaterial.SetFloat("_FullDistortionFade", 0f);
|
||
memoryMaterial.SetFloat("_SqueezePower", 18f);
|
||
|
||
if (memoryName != null)
|
||
{
|
||
var memoryKey = string.Format(MemorySpritePath, memoryName);
|
||
var memoryHandle = ResourceSystem.LoadAsync<Sprite>(memoryKey);
|
||
yield return memoryHandle;
|
||
if (memoryHandle.Status != AsyncOperationStatus.Succeeded || memoryHandle.Result == null)
|
||
{
|
||
Debug.LogWarning("Memory not found: " + memoryKey);
|
||
yield break;
|
||
}
|
||
|
||
memoryRender.sprite = memoryHandle.Result;
|
||
// 使 memoryRender 可见
|
||
memoryRender.gameObject.SetActive(true);
|
||
|
||
// 创建一个新的 DOTween 序列
|
||
Sequence memorySequence = DOTween.Sequence();
|
||
|
||
memorySequence.Append(memoryMaterial.DOFloat(0.85f, "_FullDistortionFade", 1.5f));
|
||
memorySequence.AppendInterval(1.5f);
|
||
memorySequence.Append(memoryMaterial.DOFloat(0f, "_SqueezePower", 2f));
|
||
memorySequence.Insert(4.5f, memoryMaterial.DOFloat(0f, "_FullDistortionFade", 0.5f).OnComplete(() =>
|
||
{
|
||
memoryRender.gameObject.SetActive(false);
|
||
// AudioManager.RandomPlayInteraction("water_drip");
|
||
}));
|
||
|
||
// 等待序列完成
|
||
yield return memorySequence.WaitForCompletion();
|
||
}
|
||
|
||
Sequence sequence = DOTween.Sequence();
|
||
// 添加一个 tween 来改变 _RoundWaveStrength 的值
|
||
sequence.Append(material.DOFloat(1, "_RoundWaveStrength", 2)).OnComplete(() => { EyeGlitch_out(0.5f); });
|
||
// 根据颜色改变 _ColorChangeTolerance 的值
|
||
switch (color.ToLower())
|
||
{
|
||
case "blue":
|
||
sequence.Join(material.DOFloat(0, "_ColorChangeTolerance", 2));
|
||
sequence.Join(material.DOFloat(1, "_ColorChangeTolerance3", 2));
|
||
sequence.Join(material.DOFloat(1, "_ColorChangeTolerance2", 2));
|
||
break;
|
||
case "red":
|
||
sequence.Join(material.DOFloat(0, "_ColorChangeTolerance3", 2));
|
||
sequence.Join(material.DOFloat(1, "_ColorChangeTolerance", 2));
|
||
sequence.Join(material.DOFloat(1, "_ColorChangeTolerance2", 2));
|
||
break;
|
||
case "green":
|
||
sequence.Join(material.DOFloat(0, "_ColorChangeTolerance2", 2));
|
||
sequence.Join(material.DOFloat(1, "_ColorChangeTolerance", 2));
|
||
sequence.Join(material.DOFloat(1, "_ColorChangeTolerance3", 2));
|
||
break;
|
||
case "All":
|
||
sequence.Join(material.DOFloat(1, "_ColorChangeTolerance2", 2));
|
||
sequence.Join(material.DOFloat(1, "_ColorChangeTolerance", 2));
|
||
sequence.Join(material.DOFloat(1, "_ColorChangeTolerance3", 2));
|
||
break;
|
||
default:
|
||
Debug.LogWarning("Unknown color: " + color);
|
||
break;
|
||
}
|
||
|
||
// 添加一个 tween 来将 _RoundWaveStrength 的值回复为 0
|
||
sequence.Append(material.DOFloat(0, "_RoundWaveStrength", 0.5f));
|
||
|
||
// 开始这个序列
|
||
sequence.Play();
|
||
|
||
// 等待序列完成
|
||
yield return sequence.WaitForCompletion();
|
||
}
|
||
|
||
[YarnCommand("EyeGetColor")]
|
||
public void GetColor()
|
||
{
|
||
SetEyeColorState(EyeData.EyeColorState.CanImagineColor);
|
||
// 对目标列表中的每个目标执行 ColorBack 方法
|
||
foreach (EyeTarget target in targets)
|
||
{
|
||
target.ColorBack();
|
||
}
|
||
|
||
// 切换到 CanImagineColorEffect 状态
|
||
}
|
||
}
|
||
|
||
public class EyeData : IData
|
||
{
|
||
public enum EyeColorState
|
||
{
|
||
CanImagineColor,
|
||
CannotImagineColor,
|
||
EyeDisorder,
|
||
CanSeeColor,
|
||
HaveChip
|
||
}
|
||
|
||
public EyeColorState currentState = EyeColorState.CannotImagineColor;
|
||
public event Action<EyeData> LoadEvent;
|
||
|
||
public IEnumerator Load()
|
||
{
|
||
LoadEvent?.Invoke(this);
|
||
yield break;
|
||
}
|
||
}
|
||
}
|