Merge branch 'develop' into feature/UI更新

This commit is contained in:
2026-07-01 19:09:45 +08:00
166 changed files with 10005 additions and 1725 deletions
+10 -10
View File
@@ -26,14 +26,14 @@ namespace AibisDream
}
[YarnCommand("load_scene")]
public static IEnumerator LoadScene(string sceneName, float duration = 1)
public static IEnumerator LoadScene(string sceneName)
{
HideDialog();
yield return SceneLoader.Instance.LoadSceneAsync(sceneName);
}
[YarnCommand("unload_scene")]
public static IEnumerator UnloadScene(string sceneName)
public static IEnumerator UnloadScene()
{
yield return SceneLoader.Instance.UnloadSceneAsync();
}
@@ -73,17 +73,17 @@ namespace AibisDream
}
[YarnCommand("show_obj")]
public static IEnumerator ShowObj(string picName)
public static IEnumerator ShowObj(string picName, float duration = 1)
{
var panel = UIManager.Instance.GetPanel<PlayToolPanel>();
return panel.ShowObj(picName);
return panel.ShowObj(picName, duration);
}
[YarnCommand("hide_obj")]
public static IEnumerator HideObj()
public static IEnumerator HideObj(float duration = 1)
{
var panel = UIManager.Instance.GetPanel<PlayToolPanel>();
return panel.HideObj();
return panel.HideObj(duration);
}
[YarnCommand("show_sprite")]
@@ -141,17 +141,17 @@ namespace AibisDream
}
[YarnCommand("show_full_screen")]
public static IEnumerator ShowFullScreen(string picName)
public static IEnumerator ShowFullScreen(string picName, float duration = 1)
{
var panel = UIManager.Instance.GetPanel<PlayToolPanel>();
return panel.ShowFullScreen(picName);
return panel.ShowFullScreen(picName, duration);
}
[YarnCommand("hide_full_screen")]
public static IEnumerator HideFullScreen()
public static IEnumerator HideFullScreen(float duration = 1)
{
var panel = UIManager.Instance.GetPanel<PlayToolPanel>();
return panel.HideFullScreen();
return panel.HideFullScreen(duration);
}
[YarnCommand("open_progress_window")]
@@ -1,39 +1,61 @@
using UnityEngine;
using TMPro;
using AibisDream.Framework;
using DG.Tweening;
using TMPro;
using UnityEngine;
using UnityEngine.Serialization;
namespace AibisDream
{
public class MouseFollowAndZoom : MonoBehaviour, IInteraction
{
public GameObject targetIndicator; // UI 目标框
public TMP_Text targetNameText; // 显示目标名称的文本
public Vector2 sceneBoundsMin; // 场景边界的左下角
public Vector2 sceneBoundsMax; // 场景边界的右上角
private EyeSystem eyemanager;
private EyeTarget currentTarget; // 当前目标
private bool isActive = false;
private bool isLocked = false;
// 交互状态管理
[Header("交互状态")]
[SerializeField] private bool _isInteractionActive = true; // 是否激活交互
[SerializeField] private bool _isInteractionAvailable = true; // 是否可用交互
[SerializeField] private float _dialogEndCooldown = 0.1f; // 对话结束后的冷却时间
// 内部状态
private float _lastDialogEndTime = -1f; // 上次对话结束的时间
private bool _isDialogCooldownActive = false; // 是否在对话冷却期间
public GameObject targetIndicator;
public TMP_Text targetNameText;
public Vector2 sceneBoundsMin;
public Vector2 sceneBoundsMax;
[Header("Cinemachine Pan")]
[Tooltip("平移目标;留空则平移本物体。Eye 模块应指向 Eye DeepCamera 虚拟相机 Transform。")]
[SerializeField] private Transform panTransform;
[Tooltip("虚拟相机可移动的世界区域(与 ViewCamera / EyeSystem 聚焦边界一致)。")]
[SerializeField] private Vector2 worldRegionMin = new(30.2f, -5f);
[SerializeField] private Vector2 worldRegionMax = new(50.2f, 5f);
[Header("Input")]
[SerializeField] private float mouseScreenOffsetX;
[FormerlySerializedAs("viewCamera")]
[SerializeField] private Camera legacyInputCamera;
public Camera viewCamera;
public float parallaxEffectMultiplier = 0.1f;
public Vector2 maxOffset = new Vector2(2f, 2f);
[Header("Interaction")]
[SerializeField] private bool _isInteractionActive = true;
[SerializeField] private bool _isInteractionAvailable = true;
[SerializeField] private float _dialogEndCooldown = 0.1f;
private EyeSystem eyemanager;
private EyeTarget currentTarget;
private bool isActive;
private bool isLocked;
private float _lastDialogEndTime = -1f;
private bool _isDialogCooldownActive;
private Vector3 initialPosition;
private Vector3 currentOffset;
private Tween focusTween;
private float viewOffset = 0;
private Transform PanTransform => panTransform != null ? panTransform : transform;
private bool UsesCinemachinePan => panTransform != null;
public Vector3 ViewPosition
{
get
{
Camera cameraForInput = GetInputCamera();
return cameraForInput != null ? cameraForInput.transform.position : PanTransform.position;
}
}
// 读写访问
public void SetIsActive(bool value)
{
isActive = value;
@@ -43,152 +65,212 @@ namespace AibisDream
{
isLocked = value;
}
// 交互状态控制方法
public void SetInteractionActive(bool value)
{
_isInteractionActive = value;
}
public void SetInteractionAvailable(bool value)
{
_isInteractionAvailable = value;
}
void Start()
public void ResetPanOrigin()
{
initialPosition = transform.position;
targetIndicator.SetActive(false); // 初始时隐藏目标框
focusTween?.Kill();
currentOffset = Vector3.zero;
initialPosition = PanTransform.position;
}
public void MoveFocusTo(Transform target, float duration, Vector2 minBounds, Vector2 maxBounds, Vector2 viewportSize)
{
if (target == null)
{
return;
}
Vector3 focusPosition = ClampFocusPosition(target.position, minBounds, maxBounds, viewportSize);
Vector3 targetPanPosition;
if (UsesCinemachinePan)
{
targetPanPosition = ClampFocusPosition(
new Vector3(focusPosition.x, focusPosition.y, PanTransform.position.z),
minBounds,
maxBounds,
viewportSize);
}
else
{
Vector3 viewPosition = ViewPosition;
viewPosition.z = focusPosition.z;
targetPanPosition = PanTransform.position + viewPosition - focusPosition;
targetPanPosition.z = PanTransform.position.z;
}
initialPosition = targetPanPosition;
focusTween?.Kill();
if (duration <= 0f)
{
PanTransform.position = initialPosition + currentOffset;
return;
}
focusTween = PanTransform.DOMove(initialPosition + currentOffset, duration).SetEase(Ease.InOutSine);
}
private void Start()
{
initialPosition = PanTransform.position;
targetIndicator.SetActive(false);
eyemanager = FindObjectOfType<EyeSystem>();
if (eyemanager == null)
{
Debug.LogError("EyeManager instance not found in the scene.");
Debug.LogError("[MouseFollowAndZoom] EyeSystem 未找到。", this);
}
// 注册对话事件以处理对话结束后的冷却
RegisterDialogEvents();
}
void Update()
private void Update()
{
if (!isActive)
{
targetIndicator.SetActive(false);
return;
}
// 检查对话结束冷却
UpdateDialogCooldown();
HandleTargetIndicator(); // 显示指示器
if (isLocked) return;
UpdateDialogCooldown();
HandleTargetIndicator();
if (isLocked)
{
return;
}
HandleMouseMovement();
UpdateCurrentTarget(); // 更新当前目标
UpdateCurrentTarget();
}
private Vector3 currentOffset; // 当前的偏移量
void HandleMouseMovement()
private void HandleMouseMovement()
{
// 获取鼠标位置
Vector3 mousePosition = Input.mousePosition;
mousePosition.x -= 40f; // 为x坐标加上40.3
Vector3 worldMousePosition = viewCamera.ScreenToWorldPoint(mousePosition);
worldMousePosition.z = 0; // 确保 z 为 0
Camera cameraForInput = GetInputCamera();
if (cameraForInput == null)
{
return;
}
// 计算目标偏移量并反向
Vector3 targetOffset = (viewCamera.transform.position - worldMousePosition) * parallaxEffectMultiplier;
Vector3 mousePosition = GetMouseScreenPosition();
Vector3 worldMousePosition = cameraForInput.ScreenToWorldPoint(mousePosition);
worldMousePosition.z = 0;
// 视差锚点用 initialPosition,避免相机跟随平移后 delta 被抵消
Vector3 parallaxAnchor = UsesCinemachinePan ? initialPosition : ViewPosition;
parallaxAnchor.z = 0;
Vector3 delta = UsesCinemachinePan
? worldMousePosition - parallaxAnchor
: parallaxAnchor - worldMousePosition;
Vector3 targetOffset = delta * parallaxEffectMultiplier;
targetOffset.x = Mathf.Clamp(targetOffset.x, -maxOffset.x, maxOffset.x);
targetOffset.y = Mathf.Clamp(targetOffset.y, -maxOffset.y, maxOffset.y);
// 平滑处理偏移量
float smoothFactor = 0.01f; // 平滑系数,值越小平滑效果越强,响应越慢
currentOffset = Vector3.Lerp(currentOffset, targetOffset, smoothFactor);
currentOffset = Vector3.Lerp(currentOffset, targetOffset, 0.01f);
// 计算目标位置
Vector3 targetPosition = initialPosition + currentOffset;
if (UsesCinemachinePan)
{
Vector2 parallaxSlack = maxOffset * parallaxEffectMultiplier;
targetPosition = ClampFocusPositionWithSlack(
targetPosition,
worldRegionMin,
worldRegionMax,
GetViewportSize(),
parallaxSlack);
}
else
{
targetPosition.x = Mathf.Clamp(targetPosition.x, sceneBoundsMin.x, sceneBoundsMax.x);
targetPosition.y = Mathf.Clamp(targetPosition.y, sceneBoundsMin.y, sceneBoundsMax.y);
}
// 限制目标位置在场景边界范围内
targetPosition.x = Mathf.Clamp(targetPosition.x, sceneBoundsMin.x, sceneBoundsMax.x);
targetPosition.y = Mathf.Clamp(targetPosition.y, sceneBoundsMin.y, sceneBoundsMax.y);
targetPosition.z = PanTransform.position.z;
// 使用插值(Lerp)平滑更新位置
float followSpeed = 0.1f; // 跟随速度,值越小延迟越大
Vector3 smoothedPosition = Vector3.Lerp(transform.position, targetPosition, followSpeed);
// 应用平滑后的位置
transform.position = smoothedPosition;
PanTransform.position = Vector3.Lerp(PanTransform.position, targetPosition, 0.1f);
}
void UpdateCurrentTarget()
private void UpdateCurrentTarget()
{
// 获取鼠标位置
Vector3 mousePosition = Input.mousePosition;
mousePosition.x -= 40f; // 为x坐标加上40.3
Vector3 worldPosition = Camera.main.ScreenToWorldPoint(mousePosition);
RaycastHit2D hit =
Physics2D.Raycast(worldPosition + viewCamera.transform.position - Camera.main.transform.position,
Vector2.zero);
Camera cameraForInput = GetInputCamera();
if (cameraForInput == null)
{
currentTarget = null;
return;
}
// 检查是否有目标
Vector3 worldPosition = cameraForInput.ScreenToWorldPoint(GetMouseScreenPosition());
worldPosition.z = 0;
RaycastHit2D hit = Physics2D.Raycast(worldPosition, Vector2.zero);
if (hit.collider != null && hit.collider.CompareTag("EyeTarget"))
{
currentTarget = hit.collider.GetComponent<EyeTarget>(); // 更新当前目标
// 处理鼠标点击事件 - 只有在交互可用且不在对话冷却期时才处理
currentTarget = hit.collider.GetComponent<EyeTarget>();
if (Input.GetMouseButtonDown(0) && _isInteractionActive && _isInteractionAvailable && !_isDialogCooldownActive)
{
Debug.Log("点击目标触发了");
if (currentTarget != null)
{
eyemanager.SetTarget(currentTarget); // 设置目标
eyemanager.SetTarget(currentTarget);
}
else
{
Debug.LogWarning("The collider does not have an EyeTarget component.");
Debug.LogWarning("[MouseFollowAndZoom] Collider 缺少 EyeTarget 组件。", hit.collider);
}
}
}
else
{
currentTarget = null; // 如果没有目标,设置为 null
currentTarget = null;
}
}
void HandleTargetIndicator()
private Vector3 GetMouseScreenPosition()
{
Vector3 mousePosition = Input.mousePosition;
mousePosition.x -= mouseScreenOffsetX;
mousePosition.z = GetInputCamera() != null
? GetInputCamera().nearClipPlane
: mousePosition.z;
return mousePosition;
}
private void HandleTargetIndicator()
{
if (currentTarget == null || !_isInteractionActive)
{
targetIndicator.SetActive(false); // 隐藏指示器
targetIndicator.SetActive(false);
return;
}
// 获取目标的 BoxCollider2D
BoxCollider2D boxCollider = currentTarget.GetComponent<BoxCollider2D>();
if (boxCollider != null)
if (boxCollider == null)
{
Vector3 boxCenter = boxCollider.bounds.center;
Vector3 boxSize = boxCollider.bounds.size;
// 转换为世界空间坐标
Vector3 worldCenter = boxCenter;
Vector3 worldSize = boxSize;
// 设置目标框的位置
RectTransform indicatorRectTransform = targetIndicator.GetComponent<RectTransform>();
indicatorRectTransform.position = worldCenter;
indicatorRectTransform.sizeDelta = new Vector2(worldSize.x, worldSize.y);
// 设置目标名称
targetNameText.text = currentTarget.name + " " +
"R" + currentTarget._color.r.ToString("F1") +
" G" + currentTarget._color.g.ToString("F1") +
" B" + currentTarget._color.b.ToString("F1");
// 显示指示器
targetIndicator.SetActive(true);
return;
}
Vector3 boxCenter = boxCollider.bounds.center;
Vector3 boxSize = boxCollider.bounds.size;
RectTransform indicatorRectTransform = targetIndicator.GetComponent<RectTransform>();
indicatorRectTransform.position = boxCenter;
indicatorRectTransform.sizeDelta = new Vector2(boxSize.x, boxSize.y);
targetNameText.text = currentTarget.name + " " +
"R" + currentTarget._color.r.ToString("F1") +
" G" + currentTarget._color.g.ToString("F1") +
" B" + currentTarget._color.b.ToString("F1");
targetIndicator.SetActive(true);
}
public void SetIndicatorTarget(EyeTarget target)
@@ -196,85 +278,123 @@ namespace AibisDream
currentTarget = target;
}
void OnDrawGizmos()
private Camera GetInputCamera()
{
if (UsesCinemachinePan && CameraKit.Instance != null)
{
return CameraKit.Instance.GetCurrentCamera();
}
if (legacyInputCamera != null)
{
return legacyInputCamera;
}
return Camera.main;
}
private Vector2 GetViewportSize()
{
Camera cam = GetInputCamera();
if (cam == null || !cam.orthographic)
{
return Vector2.zero;
}
float height = cam.orthographicSize * 2f;
return new Vector2(height * cam.aspect, height);
}
private static Vector3 ClampFocusPositionWithSlack(
Vector3 targetPosition,
Vector2 minBounds,
Vector2 maxBounds,
Vector2 viewportSize,
Vector2 slack)
{
if (maxBounds.x > minBounds.x)
{
float halfWidth = Mathf.Max(0f, viewportSize.x * 0.5f);
targetPosition.x = Mathf.Clamp(
targetPosition.x,
minBounds.x + halfWidth - slack.x,
maxBounds.x - halfWidth + slack.x);
}
if (maxBounds.y > minBounds.y)
{
float halfHeight = Mathf.Max(0f, viewportSize.y * 0.5f);
targetPosition.y = Mathf.Clamp(
targetPosition.y,
minBounds.y + halfHeight - slack.y,
maxBounds.y - halfHeight + slack.y);
}
return targetPosition;
}
private static Vector3 ClampFocusPosition(Vector3 targetPosition, Vector2 minBounds, Vector2 maxBounds, Vector2 viewportSize)
{
if (maxBounds.x > minBounds.x)
{
float halfWidth = Mathf.Max(0f, viewportSize.x * 0.5f);
targetPosition.x = Mathf.Clamp(targetPosition.x, minBounds.x + halfWidth, maxBounds.x - halfWidth);
}
if (maxBounds.y > minBounds.y)
{
float halfHeight = Mathf.Max(0f, viewportSize.y * 0.5f);
targetPosition.y = Mathf.Clamp(targetPosition.y, minBounds.y + halfHeight, maxBounds.y - halfHeight);
}
return targetPosition;
}
private void OnDrawGizmos()
{
// 设置 Gizmos 颜色
Gizmos.color = Color.red;
Vector2 min = UsesCinemachinePan ? worldRegionMin : sceneBoundsMin;
Vector2 max = UsesCinemachinePan ? worldRegionMax : sceneBoundsMax;
Vector3 bottomLeft = new Vector3(min.x, min.y, 0);
Vector3 topRight = new Vector3(max.x, max.y, 0);
// 绘制场景边界
Vector3 bottomLeft = new Vector3(sceneBoundsMin.x, sceneBoundsMin.y, 0);
Vector3 topRight = new Vector3(sceneBoundsMax.x, sceneBoundsMax.y, 0);
// 绘制边界框
Gizmos.DrawLine(bottomLeft, new Vector3(bottomLeft.x, topRight.y, 0));
Gizmos.DrawLine(bottomLeft, new Vector3(topRight.x, bottomLeft.y, 0));
Gizmos.DrawLine(topRight, new Vector3(bottomLeft.x, topRight.y, 0));
Gizmos.DrawLine(topRight, new Vector3(topRight.x, bottomLeft.y, 0));
}
#region
/// <summary>
/// 注册对话事件
/// </summary>
private void RegisterDialogEvents()
{
// 监听对话结束事件
EnumEventSystem.Global.Register(InteractionEventEnum.DialogEnd, OnDialogueComplete);
}
/// <summary>
/// 对话结束时的处理
/// </summary>
private void OnDialogueComplete()
{
_lastDialogEndTime = Time.time;
_isDialogCooldownActive = true;
Debug.Log($"[交互冷却] 对话结束,开始 {_dialogEndCooldown} 秒冷却期");
}
/// <summary>
/// 更新对话冷却状态
/// </summary>
private void UpdateDialogCooldown()
{
if (_isDialogCooldownActive && Time.time - _lastDialogEndTime >= _dialogEndCooldown)
{
_isDialogCooldownActive = false;
Debug.Log("[交互冷却] 冷却期结束,交互恢复正常");
}
}
/// <summary>
/// 销毁时取消事件注册
/// </summary>
private void OnDestroy()
{
focusTween?.Kill();
EnumEventSystem.Global.UnRegister(InteractionEventEnum.DialogEnd, OnDialogueComplete);
}
#endregion
#region IInteraction
/// <summary>
/// 判断是否激活,激活就正常显示鼠标,否则就显示禁用标志
/// </summary>
public bool IsActive => _isInteractionActive && !_isDialogCooldownActive;
/// <summary>
/// 判断是否可用,可用就正常显示鼠标,否则就保持原有标志不变
/// </summary>
public bool IsAvailable => _isInteractionAvailable && !_isDialogCooldownActive;
/// <summary>
/// 返回GameObject
/// </summary>
public GameObject GetGameObject()
{
return gameObject;
}
#endregion
}
}
}
@@ -1,140 +0,0 @@
fileFormatVersion: 2
guid: 714af643a5975bf4aacfd94e297e7ee6
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 12
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 1
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: WebGL
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Server
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID: 5e97eb03825dee720800000000000000
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:
+337 -30
View File
@@ -5,7 +5,9 @@ using Yarn.Unity;
using AibisDream.Framework;
using AibisDream.Kit;
using UnityEngine.Rendering;
using UnityEngine.UI;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.Serialization;
namespace AibisDream.FixSystem
{
@@ -16,11 +18,37 @@ namespace AibisDream.FixSystem
private const float DefaultFadeDuration = 1f;
private const float SearchStartDelay = 1f;
private const float ProjectorMoveDuration = 2f;
private const float MaxClarityDistortion = 0.58f;
private const float MaxFrequencyBlur = 0.6f;
private const float MinPixelDensity = 18f;
private const float MaxPixelDensity = 96f;
private const string DirectionalDistortionFadeProperty = "_DirectionalDistortionFade";
private static readonly int EnablePixelateId = Shader.PropertyToID("_EnablePixelate");
private static readonly int FrequencyTuningId = Shader.PropertyToID("_FrequencyTuning");
private static readonly int ClarityTuningId = Shader.PropertyToID("_ClarityTuning");
private static readonly int DirectionalDistortionFadeId = Shader.PropertyToID(DirectionalDistortionFadeProperty);
private static readonly int PixelateFadeId = Shader.PropertyToID("_PixelateFade");
private static readonly int PixelatePixelDensityId = Shader.PropertyToID("_PixelatePixelDensity");
private static readonly int EnableGaussianBlurId = Shader.PropertyToID("_EnableGaussianBlur");
private static readonly int GaussianBlurFadeId = Shader.PropertyToID("_GaussianBlurFade");
private static readonly int GaussianBlurOffsetId = Shader.PropertyToID("_GaussianBlurOffset");
private static readonly int EnableUvDistortId = Shader.PropertyToID("_EnableUVDistort");
private static readonly int UvDistortFadeId = Shader.PropertyToID("_UVDistortFade");
private static readonly int MemoryGrayscaleId = Shader.PropertyToID("_MemoryGrayscale");
private static readonly int MemoryContrastId = Shader.PropertyToID("_MemoryContrast");
[SerializeField] private GameObject memoryView;
[SerializeField] private GameObject projector;
[SerializeField] private SpriteRenderer memoryFinishedSpriteRenderer; // 用于显示记忆处理结束的表现
[SerializeField] private SpriteRenderer memoryFinishedImageNoColor; // 用于显示记忆处理结束的表现
[SerializeField] private Image memoryFinishedImage; // UI彩色记忆层
[SerializeField] private Image memoryFinishedNoColorImage; // UI去色记忆层
[FormerlySerializedAs("memoryFinishedspRender")]
[FormerlySerializedAs("memoryFinishedSpriteRenderer")]
[HideInInspector]
[SerializeField] private SpriteRenderer legacyMemoryFinishedSpriteRenderer;
[FormerlySerializedAs("memoryFinishedImageNoColor")]
[HideInInspector]
[SerializeField] private SpriteRenderer legacyMemoryFinishedImageNoColor;
[SerializeField] private Volume mainEffect;
[SerializeField] private Volume memoryClarityVolume;
@@ -28,8 +56,15 @@ namespace AibisDream.FixSystem
[SerializeField] private Volume memoryProcessVolume;
[SerializeField] private MemorySlider frequencySlider; // 频率调节滑动条
[SerializeField] private MemorySlider claritySlider; // 清晰度调节滑动条
[SerializeField] private bool useSpriteShaderTuning = true;
[SerializeField] private CanvasGroup playBackground;
[SerializeField] private Graphic standbyOverlay;
[SerializeField, Range(0f, 1f)] private float standbyOverlayMinAlpha = 0.04f;
[SerializeField, Range(0f, 1f)] private float standbyOverlayMaxAlpha = 0.12f;
[SerializeField, Range(0f, 1f)] private float searchOverlayAlpha = 0.85f;
[SerializeField] private float standbyOverlayPulseDuration = 0.8f;
[SerializeField] private float searchTargetMinDistanceFromStart = 2.5f;
[SerializeField] private GameObject memoryGroup;
@@ -41,12 +76,14 @@ namespace AibisDream.FixSystem
[SerializeField] private MemoryPunchTapeSlot punchTapeSlot;
[SerializeField] private Material fadeMaterial;
private Material runtimeMemoryMaterial;
private Material runtimeNoColorMemoryMaterial;
private Tween overlayTween;
public void OpenMemoryView()
{
memoryGroup.SetActive(true);
memoryView.gameObject.SetActive(true);
}
public IEnumerator DropMemoryProjector()
@@ -64,6 +101,8 @@ namespace AibisDream.FixSystem
public void CloseMemoryView()
{
StopStandbyOverlay();
SetOverlayAlpha(0f);
memoryView.gameObject.SetActive(false);
memoryGroup.SetActive(false);
}
@@ -86,6 +125,7 @@ namespace AibisDream.FixSystem
PunchTapeManager.Instance.OpenFavoritesPanel();
playBackground.gameObject.SetActive(false);
playBackground.alpha = 0;
StartStandbyOverlay();
}
public void OnExit()
@@ -134,6 +174,7 @@ namespace AibisDream.FixSystem
private void InitSystem()
{
FixSystemCenter.SystemDic.Register(this);
DisableLegacyRenderTextureCameras();
if (memoryGroup != null)
memoryGroup.SetActive(false);
}
@@ -141,28 +182,57 @@ namespace AibisDream.FixSystem
private void Start()
{
PrepareRuntimeMemoryMaterial();
frequencySlider.OnFound += CheckAllFound;
claritySlider.OnFound += CheckAllFound;
Init();
}
private void OnDestroy()
{
overlayTween?.Kill();
if (runtimeMemoryMaterial != null)
Destroy(runtimeMemoryMaterial);
if (runtimeNoColorMemoryMaterial != null)
Destroy(runtimeNoColorMemoryMaterial);
}
private void DisableLegacyRenderTextureCameras()
{
if (memoryGroup == null)
return;
var cameras = memoryGroup.GetComponentsInChildren<Camera>(true);
foreach (var renderCamera in cameras)
{
if (renderCamera != null && renderCamera.targetTexture != null)
renderCamera.enabled = false;
}
}
private void CheckAllFound()
{
if (frequencySlider.IsFound && claritySlider.IsFound)
OnSearchComplete();
}
private float GetRandomNumber()
private float GetRandomSearchTarget(MemorySlider slider)
{
// 随机决定选择哪个区间
if (UnityEngine.Random.value < 0.5f) // 50% 概率选择第一个区间
{
return UnityEngine.Random.Range(0.5f, 2f);
}
else // 50% 概率选择第二个区间
{
return UnityEngine.Random.Range(4f, 5.5f);
}
float start = slider != null ? slider.StartValue : 0f;
float max = slider != null ? slider.MaxValue : 6f;
float minDistance = Mathf.Max(0f, searchTargetMinDistanceFromStart);
float nearBandMin = start + minDistance;
float nearBandMax = start + (max - start) * 0.55f;
float farBandMin = max - 1.5f;
float farBandMax = max - 0.5f;
if (nearBandMin >= nearBandMax)
return UnityEngine.Random.Range(farBandMin, farBandMax);
if (UnityEngine.Random.value < 0.5f)
return UnityEngine.Random.Range(nearBandMin, nearBandMax);
return UnityEngine.Random.Range(farBandMin, farBandMax);
}
private void OnSearchComplete()
@@ -184,6 +254,9 @@ namespace AibisDream.FixSystem
public void TweenWeight(Volume postProcessingVolume, float targetWeight, float duration)
{
if (postProcessingVolume == null)
return;
// 动态调整权重
DOTween.To(() => postProcessingVolume.weight, x => postProcessingVolume.weight = x, targetWeight, duration);
}
@@ -200,9 +273,19 @@ namespace AibisDream.FixSystem
}
if (frequencySlider != null)
{
frequencySlider.OnTuningChanged -= ApplyFrequencyTuning;
frequencySlider.Init(memoryFrequencyVolume);
frequencySlider.SetVolumeOutputEnabled(!useSpriteShaderTuning);
frequencySlider.OnTuningChanged += ApplyFrequencyTuning;
}
if (claritySlider != null)
{
claritySlider.OnTuningChanged -= ApplyClarityTuning;
claritySlider.Init(memoryClarityVolume);
claritySlider.SetVolumeOutputEnabled(!useSpriteShaderTuning);
claritySlider.OnTuningChanged += ApplyClarityTuning;
}
ResetPresentationState();
}
@@ -232,10 +315,12 @@ namespace AibisDream.FixSystem
private void ResetPresentationState()
{
if (fadeMaterial != null)
fadeMaterial.SetFloat("_DirectionalDistortionFade", FadeMaterialStartValue);
memoryFinishedSpriteRenderer.color = Color.black;
memoryProcessVolume.weight = 0;
SetFloatIfExists(DirectionalDistortionFadeId, FadeMaterialStartValue);
ResetSpriteTuning();
SetMemoryColor(Color.black);
SetVolumeWeight(memoryProcessVolume, 0f);
_activePunchTape = null;
StartStandbyOverlay();
playBackground.gameObject.SetActive(false);
playBackground.alpha = 0;
@@ -264,27 +349,30 @@ namespace AibisDream.FixSystem
{
if (fadeMaterial == null) yield break;
fadeMaterial.SetFloat("_DirectionalDistortionFade", FadeMaterialStartValue);
Tween tween = fadeMaterial.DOFloat(0, "_DirectionalDistortionFade", 3);
SetFloatIfExists(DirectionalDistortionFadeId, FadeMaterialStartValue);
DG.Tweening.Sequence sequence = DOTween.Sequence();
AppendMaterialFloatTween(sequence, fadeMaterial, 0f, DirectionalDistortionFadeProperty, 3f);
if (runtimeNoColorMemoryMaterial != null && runtimeNoColorMemoryMaterial != fadeMaterial)
AppendMaterialFloatTween(sequence, runtimeNoColorMemoryMaterial, 0f, DirectionalDistortionFadeProperty, 3f);
Tween tween = sequence;
yield return tween.WaitForCompletion();
}
[YarnCommand("PlayMemory")]
public IEnumerator PlayMemory(bool useYarn = false, string memName = "")
{
memoryFinishedSpriteRenderer.color = Color.white;
SetMemoryColor(Color.white);
PunchTapeManager.Instance.CloseFavoritesPanel();
if (useYarn)
{
if (fadeMaterial != null)
fadeMaterial.SetFloat("_DirectionalDistortionFade", FadeMaterialStartValue);
SetFloatIfExists(DirectionalDistortionFadeId, FadeMaterialStartValue);
var memoryKey = string.Format(MemorySpritePath, memName);
var memoryHandle = ResourceSystem.LoadAsync<Sprite>(memoryKey);
yield return memoryHandle;
Sprite memSprite = memoryHandle.Status == AsyncOperationStatus.Succeeded ? memoryHandle.Result : null;
memoryFinishedSpriteRenderer.sprite = memSprite;
memoryFinishedImageNoColor.sprite = memSprite;
SetMemorySprite(memSprite);
if (memSprite == null)
{
Debug.LogError("Memory not found: " + memoryKey);
@@ -320,13 +408,14 @@ namespace AibisDream.FixSystem
isPlayingFaderSound = false;
}
frequencySlider.TargetValue = GetRandomNumber();
claritySlider.TargetValue = GetRandomNumber();
frequencySlider.TargetValue = GetRandomSearchTarget(frequencySlider);
claritySlider.TargetValue = GetRandomSearchTarget(claritySlider);
PunchTapeManager.Instance.CloseFavoritesPanel();
if (punchTape == null)
{
Debug.Log("no punchTape");
StartStandbyOverlay();
yield break;
}
@@ -338,30 +427,31 @@ namespace AibisDream.FixSystem
yield break;
}
StopStandbyOverlay();
ShowSearchOverlay();
var searchMemoryKey = string.Format(MemorySpritePath, _activePunchTape.MemoryIndex);
var searchMemoryHandle = ResourceSystem.LoadAsync<Sprite>(searchMemoryKey);
yield return searchMemoryHandle;
Sprite memorySprite = searchMemoryHandle.Status == AsyncOperationStatus.Succeeded
? searchMemoryHandle.Result
: null;
memoryFinishedSpriteRenderer.sprite = memorySprite;
memoryFinishedImageNoColor.sprite = memorySprite;
SetMemorySprite(memorySprite);
if (memorySprite == null)
{
Debug.LogError("Memory not found: " + searchMemoryKey);
}
memoryProcessVolume.weight = 1;
AudioManager.Instance.PlaySfx("event:/Scriptal/mem_load");
yield return new WaitForSeconds(SearchStartDelay);
AudioManager.Instance.SetSfxParam("event:/Amb/amb_mem_tuning", "is_mem_tuning", 1);
memoryFinishedSpriteRenderer.color = Color.white;
memoryProcessVolume.weight = 0;
SetMemoryColor(Color.white);
isProcessing = true;
frequencySlider.PrepareForSearch();
claritySlider.PrepareForSearch();
ClearSearchOverlay();
bool freqUnlocked = false;
bool clarityUnlocked = false;
frequencySlider.UnlockSlider(() => freqUnlocked = true);
@@ -371,10 +461,227 @@ namespace AibisDream.FixSystem
claritySlider.SetInteractable(true);
}
private void ApplyFrequencyTuning(float strength)
{
if (!useSpriteShaderTuning || fadeMaterial == null)
return;
bool active = strength > 0.001f;
SetFloatIfExists(FrequencyTuningId, strength);
SetFloatIfExists(EnablePixelateId, active ? 1f : 0f);
SetFloatIfExists(PixelateFadeId, strength);
SetFloatIfExists(PixelatePixelDensityId, Mathf.Lerp(MaxPixelDensity, MinPixelDensity, strength));
SetFloatIfExists(EnableGaussianBlurId, active ? 1f : 0f);
SetFloatIfExists(GaussianBlurFadeId, strength * MaxFrequencyBlur);
SetFloatIfExists(GaussianBlurOffsetId, Mathf.Lerp(0.05f, 0.35f, strength));
}
private void ApplyClarityTuning(float strength)
{
if (!useSpriteShaderTuning || fadeMaterial == null)
return;
bool active = strength > 0.001f;
SetFloatIfExists(ClarityTuningId, strength);
SetFloatIfExists(EnableUvDistortId, active ? 1f : 0f);
SetFloatIfExists(UvDistortFadeId, strength * MaxClarityDistortion);
}
private void ResetSpriteTuning()
{
if (!useSpriteShaderTuning || fadeMaterial == null)
return;
ApplyFrequencyTuning(0f);
ApplyClarityTuning(0f);
}
private void SetFloatIfExists(int propertyId, float value)
{
SetFloatIfExists(fadeMaterial, propertyId, value);
if (runtimeNoColorMemoryMaterial != null && runtimeNoColorMemoryMaterial != fadeMaterial)
SetFloatIfExists(runtimeNoColorMemoryMaterial, propertyId, value);
}
private static void SetVolumeWeight(Volume volume, float weight)
{
if (volume != null)
volume.weight = weight;
}
private static void SetFloatIfExists(Material material, int propertyId, float value)
{
if (material != null && material.HasProperty(propertyId))
material.SetFloat(propertyId, value);
}
private static void AppendMaterialFloatTween(
DG.Tweening.Sequence sequence,
Material material,
float target,
string property,
float duration)
{
if (sequence != null && material != null && material.HasProperty(property))
sequence.Join(material.DOFloat(target, property, duration));
}
private void PrepareRuntimeMemoryMaterial()
{
if (!useSpriteShaderTuning || !HasMemoryGraphic())
return;
var baseMaterial = GetColorMemoryMaterial();
if (baseMaterial == null)
baseMaterial = fadeMaterial;
var shader = baseMaterial != null ? baseMaterial.shader : null;
if (shader == null || shader.name != "AibisDream/MemoryTuningSprite")
shader = Shader.Find("AibisDream/MemoryTuningSprite");
if (shader == null)
{
Debug.LogWarning("[MemoryProcess] 未找到 AibisDream/MemoryTuningSprite,调频将回退到当前材质表现。", this);
return;
}
runtimeMemoryMaterial = CreateRuntimeMemoryMaterial(baseMaterial, shader, "Color", 0f, 1f);
SetColorMemoryMaterial(runtimeMemoryMaterial);
fadeMaterial = runtimeMemoryMaterial;
var noColorBaseMaterial = GetNoColorMemoryMaterial();
if (noColorBaseMaterial != null)
{
runtimeNoColorMemoryMaterial = CreateRuntimeMemoryMaterial(
noColorBaseMaterial,
shader,
"NoColor",
1f,
1.8f);
SetNoColorMemoryMaterial(runtimeNoColorMemoryMaterial);
}
ResetSpriteTuning();
}
private static Material CreateRuntimeMemoryMaterial(
Material source,
Shader shader,
string suffix,
float grayscale,
float contrast)
{
var material = source != null ? new Material(source) : new Material(shader);
material.shader = shader;
material.name = $"{(source != null ? source.name : shader.name)}_RuntimeMemoryTuning_{suffix}";
SetFloatIfExists(material, MemoryGrayscaleId, grayscale);
SetFloatIfExists(material, MemoryContrastId, contrast);
return material;
}
private bool HasMemoryGraphic()
{
return memoryFinishedImage != null || legacyMemoryFinishedSpriteRenderer != null;
}
private void StartStandbyOverlay()
{
if (standbyOverlay == null || isProcessing)
return;
overlayTween?.Kill();
SetOverlayAlpha(standbyOverlayMinAlpha);
overlayTween = standbyOverlay
.DOFade(standbyOverlayMaxAlpha, Mathf.Max(0.01f, standbyOverlayPulseDuration))
.SetLoops(-1, LoopType.Yoyo)
.SetEase(Ease.InOutSine);
}
private void StopStandbyOverlay()
{
overlayTween?.Kill();
overlayTween = null;
}
private void ShowSearchOverlay()
{
StopStandbyOverlay();
SetOverlayAlpha(searchOverlayAlpha);
}
private void ClearSearchOverlay()
{
StopStandbyOverlay();
SetOverlayAlpha(0f);
}
private void SetOverlayAlpha(float alpha)
{
if (standbyOverlay == null)
return;
Color color = standbyOverlay.color;
color.a = Mathf.Clamp01(alpha);
standbyOverlay.color = color;
}
private void SetMemorySprite(Sprite sprite)
{
if (memoryFinishedImage != null)
memoryFinishedImage.sprite = sprite;
if (memoryFinishedNoColorImage != null)
memoryFinishedNoColorImage.sprite = sprite;
if (legacyMemoryFinishedSpriteRenderer != null)
legacyMemoryFinishedSpriteRenderer.sprite = sprite;
if (legacyMemoryFinishedImageNoColor != null)
legacyMemoryFinishedImageNoColor.sprite = sprite;
}
private void SetMemoryColor(Color color)
{
if (memoryFinishedImage != null)
memoryFinishedImage.color = color;
if (legacyMemoryFinishedSpriteRenderer != null)
legacyMemoryFinishedSpriteRenderer.color = color;
}
private Material GetColorMemoryMaterial()
{
if (memoryFinishedImage != null)
return memoryFinishedImage.material;
return legacyMemoryFinishedSpriteRenderer != null
? legacyMemoryFinishedSpriteRenderer.sharedMaterial
: null;
}
private Material GetNoColorMemoryMaterial()
{
if (memoryFinishedNoColorImage != null)
return memoryFinishedNoColorImage.material;
return legacyMemoryFinishedImageNoColor != null
? legacyMemoryFinishedImageNoColor.sharedMaterial
: null;
}
private void SetColorMemoryMaterial(Material material)
{
if (memoryFinishedImage != null)
memoryFinishedImage.material = material;
if (legacyMemoryFinishedSpriteRenderer != null)
legacyMemoryFinishedSpriteRenderer.sharedMaterial = material;
}
private void SetNoColorMemoryMaterial(Material material)
{
if (memoryFinishedNoColorImage != null)
memoryFinishedNoColorImage.material = material;
if (legacyMemoryFinishedImageNoColor != null)
legacyMemoryFinishedImageNoColor.sharedMaterial = material;
}
public bool IsProcessing()
{
return isProcessing;
}
}
}
}
@@ -19,15 +19,19 @@ namespace AibisDream.FixSystem
private Slider slider;
private PlayableDirector playableDirector;
private Volume _volume;
private bool driveVolume = true;
/// <summary>视觉 Lock/UnlockTimeline);true = 收起未开,false = 已打开。</summary>
private bool isLocked;
public float TargetValue { get; set; }
public float StartValue => defaultValue;
public float MaxValue => sliderMaxValue;
public bool IsFound { get; private set; }
public event Action OnFound;
public event Action OnLost;
public event Action<float> OnTuningChanged;
public void Init(Volume volume)
{
@@ -35,6 +39,7 @@ namespace AibisDream.FixSystem
playableDirector = GetComponent<PlayableDirector>();
_volume = volume;
driveVolume = true;
IsFound = false;
slider.onValueChanged.RemoveAllListeners();
slider.onValueChanged.AddListener(HandleValueChanged);
@@ -49,6 +54,13 @@ namespace AibisDream.FixSystem
/// <summary>仅控制是否可拖动,与 <see cref="UnlockSlider"/> / <see cref="LockSlider"/> 无关。</summary>
public void SetInteractable(bool interactable) => slider.interactable = interactable;
public void SetVolumeOutputEnabled(bool enabled)
{
driveVolume = enabled;
if (!driveVolume && _volume != null)
_volume.weight = 0f;
}
/// <summary>
/// 新一轮搜索前复位滑条与 IsFound(不播 Timeline、不改变视觉锁状态)。
/// 需先设置好 <see cref="TargetValue"/>。
@@ -116,9 +128,11 @@ namespace AibisDream.FixSystem
);
float t = Mathf.Clamp01(distance / maxDistance);
if (_volume != null)
if (_volume != null && driveVolume)
_volume.weight = Mathf.Lerp(0, 1, t);
OnTuningChanged?.Invoke(t);
return t;
}
@@ -1,40 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!84 &8400000
RenderTexture:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: maskTexture
m_ImageContentsHash:
serializedVersion: 2
Hash: 00000000000000000000000000000000
m_ForcedFallbackFormat: 4
m_DownscaleFallback: 0
m_IsAlphaChannelOptional: 0
serializedVersion: 5
m_Width: 1920
m_Height: 1080
m_AntiAliasing: 1
m_MipCount: -1
m_DepthStencilFormat: 94
m_ColorFormat: 8
m_MipMap: 0
m_GenerateMips: 1
m_SRGB: 0
m_UseDynamicScale: 0
m_BindMS: 0
m_EnableCompatibleFormat: 1
m_EnableRandomWrite: 0
m_TextureSettings:
serializedVersion: 2
m_FilterMode: 1
m_Aniso: 0
m_MipBias: 0
m_WrapU: 1
m_WrapV: 1
m_WrapW: 1
m_Dimension: 2
m_VolumeDepth: 1
m_ShadowSamplingMode: 2
@@ -1,140 +0,0 @@
fileFormatVersion: 2
guid: 5661e676977c3c348b0f50c7f1737dd3
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 12
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 1
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: WebGL
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Server
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID: 5e97eb03825dee720800000000000000
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:
@@ -29,8 +29,8 @@ namespace AibisDream.FixSystem
ufSystem.OpenUFView();
yield return CameraKit.Instance.SwitchCamera(CameraEnum.EyeDeep);
eyeSystem.OnEnter();
yield return UIManager.Instance.GetPanel<PlayToolPanel>().FadeOutAsync(0.5f);
BubbleSlotKit.Instance.LoadBubbles(BubbleSlotEnum.Eye);
yield return UIManager.Instance.GetPanel<PlayToolPanel>().FadeOutAsync(0.5f);
}
public IEnumerator Exit(FixTransition transition)
@@ -72,9 +72,8 @@ namespace AibisDream.FixSystem
yield return UIManager.Instance.GetPanel<PlayToolPanel>().FadeInAsync(0.5f);
yield return CameraKit.Instance.SwitchCamera(CameraEnum.EyeDeep);
eyeSystem.OnEnter();
yield return UIManager.Instance.GetPanel<PlayToolPanel>().FadeOutAsync(0.5f);
BubbleSlotKit.Instance.LoadBubbles(BubbleSlotEnum.Eye);
yield return UIManager.Instance.GetPanel<PlayToolPanel>().FadeOutAsync(0.5f);
}
public IEnumerator EnterImmediate(FixTransition transition)
@@ -1,8 +1,8 @@
fileFormatVersion: 2
guid: 43cd57e530f22b840b58612b2a536e20
NativeFormatImporter:
guid: 8f92d49bad6ceaa4983976ea6bb47c46
folderAsset: yes
DefaultImporter:
externalObjects: {}
mainObjectFileID: 8400000
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,185 @@
#if UNITY_EDITOR
using UnityEditor;
using UnityEngine;
namespace AibisDream
{
[CustomEditor(typeof(EyeSystem))]
public class EyeSystemEditor : Editor
{
private bool editorOverlayPreview;
private float editorBlinkPreview = 1f;
public override void OnInspectorGUI()
{
serializedObject.Update();
DrawPropertiesExcluding(serializedObject, "m_Script", "visualSettings", "replayFocusedEffectOnSettingsChange");
SerializedProperty visualSettings = serializedObject.FindProperty("visualSettings");
SerializedProperty replayOnChange = serializedObject.FindProperty("replayFocusedEffectOnSettingsChange");
if (visualSettings != null)
{
EditorGUILayout.Space(6f);
EditorGUILayout.LabelField("Visual Tuning", EditorStyles.boldLabel);
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(visualSettings, includeChildren: true);
bool visualChanged = EditorGUI.EndChangeCheck();
EditorGUILayout.Space(4f);
EditorGUILayout.PropertyField(replayOnChange);
EditorGUILayout.HelpBox(
"常驻视线 = 椭圆(eyeWidth / eyeHeight)。\n" +
"眨眼 = 同一椭圆纵向压扁(BlinkOpen 1→0),不是另一套眼皮形状。\n" +
"用「BlinkOpen 预览」滑条看闭合;eyeWidth / eyeHeight 在 BlinkOpen=1 时调。",
MessageType.Info);
DrawViewportOverlayControls(visualChanged);
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button("立即应用(Idle 预览)"))
{
serializedObject.ApplyModifiedProperties();
GetEyeSystem().ApplyVisualSettingsNow(previewIdleState: true);
}
if (GUILayout.Button("重播当前目标效果"))
{
serializedObject.ApplyModifiedProperties();
GetEyeSystem().ApplyVisualSettingsNow(replayFocusedEffect: true);
}
EditorGUILayout.EndHorizontal();
if (visualChanged)
{
serializedObject.ApplyModifiedProperties();
var system = GetEyeSystem();
system.ApplyVisualSettingsNow(
previewIdleState: !Application.isPlaying,
replayFocusedEffect: Application.isPlaying && replayOnChange.boolValue);
if (editorOverlayPreview)
{
RefreshEditorOverlayPreview(system);
}
return;
}
}
else
{
EditorGUILayout.HelpBox(
"visualSettings 未找到。请确认 EyeSystem.cs 已编译通过。",
MessageType.Warning);
}
serializedObject.ApplyModifiedProperties();
}
private void DrawViewportOverlayControls(bool visualChanged)
{
EditorGUILayout.Space(4f);
EditorGUILayout.LabelField("Viewport Overlay 预览", EditorStyles.boldLabel);
EditorGUI.BeginChangeCheck();
editorOverlayPreview = EditorGUILayout.Toggle("Edit 模式预览遮罩", editorOverlayPreview);
if (EditorGUI.EndChangeCheck() || visualChanged)
{
serializedObject.ApplyModifiedProperties();
var system = GetEyeSystem();
if (editorOverlayPreview)
{
RefreshEditorOverlayPreview(system);
}
else
{
EnsureOverlay(system).ExitEditorPreview();
editorBlinkPreview = 1f;
}
}
GUI.enabled = editorOverlayPreview;
EditorGUI.BeginChangeCheck();
editorBlinkPreview = EditorGUILayout.Slider("BlinkOpen 预览(眨眼)", editorBlinkPreview, 0f, 1f);
if (EditorGUI.EndChangeCheck())
{
var overlay = EnsureOverlay(GetEyeSystem());
overlay.SetBlinkOpenPreview(editorBlinkPreview);
}
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button("BlinkOpen = 睁开"))
{
editorBlinkPreview = 1f;
EnsureOverlay(GetEyeSystem()).SetBlinkOpenPreview(1f);
}
if (GUILayout.Button("BlinkOpen = 闭合"))
{
editorBlinkPreview = 0f;
EnsureOverlay(GetEyeSystem()).SetBlinkOpenPreview(0f);
}
EditorGUILayout.EndHorizontal();
GUI.enabled = true;
EditorGUILayout.BeginHorizontal();
GUI.enabled = Application.isPlaying;
if (GUILayout.Button("测试眨眼动画"))
{
serializedObject.ApplyModifiedProperties();
var overlay = EnsureOverlay(GetEyeSystem());
editorBlinkPreview = 1f;
overlay.SetBlinkOpenPreview(1f);
overlay.PlayBlink();
}
GUI.enabled = true;
if (GUILayout.Button("刷新遮罩参数"))
{
serializedObject.ApplyModifiedProperties();
RefreshEditorOverlayPreview(GetEyeSystem());
}
EditorGUILayout.EndHorizontal();
}
private void RefreshEditorOverlayPreview(EyeSystem system)
{
var overlay = EnsureOverlay(system);
overlay.EnterEditorPreview(system.VisualSettings);
overlay.SetBlinkOpenPreview(editorBlinkPreview);
}
private static EyeViewportOverlay EnsureOverlay(EyeSystem system)
{
var overlay = system.GetComponentInChildren<EyeViewportOverlay>(true);
if (overlay == null)
{
overlay = system.gameObject.AddComponent<EyeViewportOverlay>();
}
return overlay;
}
private EyeSystem GetEyeSystem()
{
return (EyeSystem)target;
}
private void OnDisable()
{
if (!editorOverlayPreview || target == null)
{
return;
}
editorOverlayPreview = false;
editorBlinkPreview = 1f;
var system = target as EyeSystem;
system?.GetComponentInChildren<EyeViewportOverlay>(true)?.ExitEditorPreview();
}
}
}
#endif
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 193d982b27c7dd34cb2aa48c07b1df09
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+337 -202
View File
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
@@ -19,20 +19,93 @@ namespace AibisDream
{
private const string MemorySpritePath = "Sprite/Memory/{0}.png";
[SerializeField] private List<EyeTarget> targets = new(); // 保存目标列表
public List<EyeTarget> Targets => targets; // 提供目标列表的公共访问
private EyeTarget currentTarget; // 当前选中的目标
public EyeTarget CurrentTarget => currentTarget; // 提供当前目标的公共访问
public RawImage eyeImage;
[SerializeField] private List<EyeTarget> targets = new();
public List<EyeTarget> Targets => targets;
private EyeTarget currentTarget;
public EyeTarget CurrentTarget => currentTarget;
[Header("Memory Overlay (UI)")]
public Image memoryRender;
public GameObject eyeView;
public ViewCameraManager cameraManager;
[Header("Focus Pan Bounds")]
[SerializeField] private Vector2 focusMinBounds = new(30.2f, -5f);
[SerializeField] private Vector2 focusMaxBounds = new(50.2f, 5f);
[Header("Visual Tuning")]
[SerializeField] private EyeVisualSettings visualSettings = new();
[SerializeField] private EyeViewportOverlay viewportOverlay;
[Tooltip("Play 模式下改 Visual Tuning 后,自动对当前目标重播一次 Focused 效果(如 ColorIn)。")]
[SerializeField] private bool replayFocusedEffectOnSettingsChange = true;
public EyeVisualSettings VisualSettings => visualSettings;
private IEyeStateEffect eyeStateEffect;
private EyeColorState _currentState = EyeColorState.CannotImagineColor;
private MouseFollowAndZoom mouseFollowAndZoom;
private Sequence glitchin;
/// <summary>获取当前 Eye 颜色阶段。</summary>
public EyeColorState GetCurrentColorState() => _currentState;
public float GetEffectTransitionDuration() => visualSettings.GetDurationForState(_currentState);
private void OnValidate()
{
ApplyVisualSettingsNow(
previewIdleState: !Application.isPlaying,
replayFocusedEffect: Application.isPlaying && replayFocusedEffectOnSettingsChange,
touchViewportOverlay: false);
}
/// <summary>
/// 将 Visual Tuning 推送到所有 EyeTarget。Edit 模式预览 idle;Play 模式可选重播当前目标效果。
/// </summary>
public void ApplyVisualSettingsNow(
bool previewIdleState = false,
bool replayFocusedEffect = false,
bool touchViewportOverlay = true)
{
if (targets == null)
{
return;
}
foreach (EyeTarget target in targets)
{
if (target == null)
{
continue;
}
target.BindVisualSettings(visualSettings);
if (previewIdleState)
{
target.ApplyIdlePreviewState();
}
}
if (touchViewportOverlay && Application.isPlaying)
{
EnsureViewportOverlay()?.ApplySettings(visualSettings);
}
if (!replayFocusedEffect || !Application.isPlaying || currentTarget == null || eyeStateEffect == null)
{
return;
}
currentTarget.KillVisualTweens();
eyeStateEffect.ApplyFocusedEffect(currentTarget, this);
}
private void ApplyVisualSettingsToTargets()
{
ApplyVisualSettingsNow();
}
public enum EyeColorState
{
CanImagineColor,
@@ -42,49 +115,85 @@ namespace AibisDream
HaveChip
}
private Tween hsvShiftTween;
private MouseFollowAndZoom mouseFollowAndZoom;
/// <summary>
/// 眼睛 RawImage 的故障/色相/饱和度等效果依赖 AllIn1SpriteShader(或兼容)材质上的属性。
/// 若未在 Inspector 指定 MaterialUnity 会使用 Default UI MaterialUI/Default),不含 _HsvShift 等属性。
/// </summary>
private bool TryGetEyeImageMaterialForEffects(out Material material)
private void Awake()
{
material = null;
if (eyeImage == null)
Init();
}
private EyeViewportOverlay EnsureViewportOverlay()
{
if (viewportOverlay != null)
{
Debug.LogError("[EyeSystem] eyeImage 未赋值。", this);
return false;
return viewportOverlay;
}
material = eyeImage.material;
if (material == null || !material.HasProperty("_HsvShift"))
viewportOverlay = GetComponentInChildren<EyeViewportOverlay>(true);
if (viewportOverlay == null)
{
Debug.LogError(
"[EyeSystem] eyeImage 需要指定 AllIn1SpriteShader(或兼容)材质;当前为 " +
(material != null && material.shader != null ? material.shader.name : "null") +
"。请在场景中选中该 RawImage,指定项目内 EyeMaterials / AllIn1 材质,勿留空(否则会使用 Default UI Material)。",
this);
viewportOverlay = gameObject.AddComponent<EyeViewportOverlay>();
}
return viewportOverlay;
}
private bool EnsureMouseFollowAndZoom()
{
if (mouseFollowAndZoom != null)
{
return true;
}
mouseFollowAndZoom = FindObjectOfType<MouseFollowAndZoom>(true);
if (mouseFollowAndZoom == null)
{
Debug.LogError("[EyeSystem] MouseFollowAndZoom 未找到,无法进入视觉模块。", this);
return false;
}
return true;
}
private void Awake()
private Vector2 GetFocusViewportSize()
{
Init();
var focusCamera = Camera.main;
if (focusCamera == null || !focusCamera.orthographic)
{
return Vector2.zero;
}
float height = focusCamera.orthographicSize * 2f;
return new Vector2(height * focusCamera.aspect, height);
}
private static Transform GetFocusTransform(EyeTarget target)
{
if (target == null)
{
return null;
}
return target.targetTransform ? target.targetTransform : target.transform;
}
public void FocusOnTarget(EyeTarget target, float duration)
{
Transform focusTransform = GetFocusTransform(target);
if (focusTransform == null || !EnsureMouseFollowAndZoom())
{
return;
}
mouseFollowAndZoom.MoveFocusTo(
focusTransform,
duration,
focusMinBounds,
focusMaxBounds,
GetFocusViewportSize());
}
public void Init()
{
FixSystemCenter.SystemDic.Register(this);
if (cameraManager != null)
{
cameraManager.gameObject.SetActive(false);
}
}
public EyeSnapshotDto CaptureEyeSnapshot()
@@ -107,61 +216,58 @@ namespace AibisDream
public void OnEnter()
{
// 先激活 ViewCamera
if (cameraManager != null)
if (!EnsureMouseFollowAndZoom())
{
cameraManager.gameObject.SetActive(true);
return;
}
mouseFollowAndZoom.SetIsActive(true);
mouseFollowAndZoom.SetIsLock(false);
// 激活 ViewCamera 后再打开 EyeView
OpenEyeView();
// 进入视图时的逻辑
Debug.Log("EyeView entered.");
mouseFollowAndZoom.ResetPanOrigin();
ApplyVisualSettingsToTargets();
InitializeAllTargets();
EnsureViewportOverlay()?.EnterModule(visualSettings);
}
public void OpenEyeView()
private void InitializeAllTargets()
{
eyeView.SetActive(true);
}
foreach (EyeTarget target in targets)
{
target.Init();
}
public void CloseEyeView()
{
eyeView.SetActive(false);
if (currentTarget != null)
{
eyeStateEffect?.InitializeEffect(currentTarget, this);
}
}
public void OnShow()
{
// 离开视图时的逻辑
Debug.Log("EyeView showed.");
}
public void OnExit()
{
EnsureViewportOverlay()?.ExitModule();
if (currentTarget != null)
{
eyeStateEffect?.CleanupEffect(CurrentTarget);
}
mouseFollowAndZoom.SetIsActive(false);
currentTarget = null;
// 先关闭 EyeView
CloseEyeView();
// 再关闭 ViewCamera
if (cameraManager != null)
if (EnsureMouseFollowAndZoom())
{
cameraManager.gameObject.SetActive(false);
mouseFollowAndZoom.SetIsActive(false);
}
// 离开视图时的逻辑
Debug.Log("EyeView exited.");
currentTarget = null;
}
private void Start()
{
ApplyVisualSettingsToTargets();
SetEyeColorState(_currentState);
mouseFollowAndZoom = FindObjectOfType<MouseFollowAndZoom>();
EnsureMouseFollowAndZoom();
}
public EyeTarget FindEyeTargetByName(string name)
@@ -180,9 +286,20 @@ namespace AibisDream
StartCoroutine(SetTargetCoroutine(newTarget.gameObject.name));
}
[YarnCommand("EyeBlink")]
public void EyeBlink(float duration = 0f)
{
EnsureViewportOverlay()?.PlayBlink(duration > 0f ? duration : null);
}
[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)
@@ -192,13 +309,13 @@ namespace AibisDream
}
YarnVariableStorage.Instance.SetValue("$currentEyeTarget", target.name);
StartCoroutine(EyeEffectFadeOut());
StartCoroutine(EyeEffect_FadeOut());
cameraManager.MoveCameraTo(target.targetTransform ? target.targetTransform : target.transform, 1f);
FocusOnTarget(target, 1f);
if (currentTarget != null)
{
currentTarget.BlurIn(eyeStateEffect.TransitionDuration, false);
currentTarget.BlurIn(visualSettings.targetSwitchDuration);
}
if (currentTarget == target)
@@ -206,16 +323,16 @@ namespace AibisDream
DialogController.Instance.StartDialogNode("重复看同一个目标");
currentTarget = target;
mouseFollowAndZoom.SetIndicatorTarget(currentTarget);
currentTarget.BlurOut(eyeStateEffect.TransitionDuration, false);
yield return new WaitForSeconds(eyeStateEffect.TransitionDuration);
currentTarget.BlurOut(visualSettings.targetSwitchDuration);
yield return new WaitForSeconds(visualSettings.targetSwitchDuration);
yield break;
}
currentTarget = target;
mouseFollowAndZoom.SetIndicatorTarget(currentTarget);
currentTarget.BlurOut(eyeStateEffect.TransitionDuration, false);
currentTarget.BlurOut(visualSettings.targetSwitchDuration);
yield return new WaitForSeconds(eyeStateEffect.TransitionDuration);
yield return new WaitForSeconds(visualSettings.targetSwitchDuration);
if (startDialogue)
{
@@ -226,48 +343,43 @@ namespace AibisDream
[YarnCommand("Set_MouseMovementLockState")]
public void SetMouseMovementLockState(bool isActive)
{
if (!EnsureMouseFollowAndZoom())
{
return;
}
mouseFollowAndZoom.SetIsLock(isActive);
}
[YarnCommand("EyeEffect_FadeIn")]
public IEnumerator EyeEffectFadeIn()
public IEnumerator EyeEffect_FadeIn()
{
if (currentTarget != null)
{
// AudioManager.RandomPlayInteraction("colorimagine");
AudioManager.Instance.PlaySfx("event:/ActionFB/color_imagine");
eyeStateEffect?.ApplyFocusedEffect(currentTarget);
eyeStateEffect?.ApplyFocusedEffect(currentTarget, this);
}
yield return new WaitForSeconds(eyeStateEffect.TransitionDuration);
yield return new WaitForSeconds(GetEffectTransitionDuration());
}
[YarnCommand("EyeEffect_FadeOut")]
public IEnumerator EyeEffectFadeOut()
public IEnumerator EyeEffect_FadeOut()
{
if (currentTarget != null)
{
// AudioManager.RandomPlayInteraction("colorimagine");
AudioManager.Instance.PlaySfx("event:/ActionFB/color_imagine");
eyeStateEffect?.ApplyUnfocusedEffect(currentTarget);
eyeStateEffect?.ApplyUnfocusedEffect(currentTarget, this);
}
yield return new WaitForSeconds(eyeStateEffect.TransitionDuration);
yield return new WaitForSeconds(GetEffectTransitionDuration());
}
[YarnCommand("EyeEffect_Init")]
public void EyeEffectInit()
{
foreach (EyeTarget target in targets)
{
target.Init();
}
if (currentTarget != null)
{
eyeStateEffect?.InitializeEffect(currentTarget, cameraManager);
}
ApplyVisualSettingsToTargets();
InitializeAllTargets();
}
[YarnCommand("EyeEffect_Clean")]
@@ -282,127 +394,71 @@ namespace AibisDream
[YarnCommand("set_EyeColorState")]
public void SetEyeColorStateForYarn(string state)
{
// Try to parse the string as an EyeColorState
if (!Enum.TryParse(state, true, out EyeColorState eyeState))
{
throw new ArgumentException("Invalid eye state: " + state, nameof(state));
}
// Start the coroutine for setting the eye state
SetEyeColorState(eyeState);
}
private void SetEyeColorState(EyeColorState state)
{
_currentState = state;
switch (state)
eyeStateEffect = state switch
{
case EyeColorState.CanImagineColor:
eyeStateEffect = new CanImagineColorEffect();
break;
case EyeColorState.CannotImagineColor:
eyeStateEffect = new CannotImagineColorEffect();
break;
case EyeColorState.EyeDisorder:
eyeStateEffect = new EyeDisorderEffect();
break;
case EyeColorState.CanSeeColor:
eyeStateEffect = new CanSeeColorEffect();
break;
case EyeColorState.HaveChip:
eyeStateEffect = new HaveChip();
break;
default:
throw new ArgumentException("Unknown eye state: " + state.ToString(), nameof(state));
}
EyeColorState.CanImagineColor => new CanImagineColorEffect(),
EyeColorState.CannotImagineColor => new CannotImagineColorEffect(),
EyeColorState.EyeDisorder => new EyeDisorderEffect(),
EyeColorState.CanSeeColor => new CanSeeColorEffect(),
EyeColorState.HaveChip => new HaveChip(),
_ => throw new ArgumentException("Unknown eye state: " + state, 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();
if (!TryJoinTargetSaturation(saturationSequence, targetSaturation, duration))
{
Debug.LogWarning("[EyeSystem] 无可用 EyeTarget 饱和度 tween。", this);
yield break;
}
// 同时启动材质的饱和度变化和 Volume 的weight变化
saturationSequence.Join(material.DOFloat(targetSaturation, "_HsvSaturation", duration));
// 获取 Tween 对象并添加到序列中
Tween saturationTween = ScreenEffectManager.Instance.TweenSaturation(targetSaturation, duration);
Tween saturationTween = ScreenEffectManager.Instance != null
? ScreenEffectManager.Instance.TweenSaturation(targetSaturation, duration)
: null;
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))
if (!TryStartUnifiedGlitch())
{
yield break;
Debug.LogWarning("[EyeSystem] 无可用 EyeTarget Glitch tween。", this);
}
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);
yield return new WaitForSeconds(visualSettings.glitchInDuration);
}
[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);
// 确保之前的动画被停止
TryStopUnifiedGlitch(duration > 0f ? duration : visualSettings.glitchOutDuration);
}
[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);
@@ -426,63 +482,37 @@ namespace AibisDream
}
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())
Sequence waveIn = CreateTargetRoundWaveSequence(1f, visualSettings.roundWaveInDuration);
if (waveIn != null)
{
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;
sequence.Append(waveIn);
}
// 添加一个 tween 来将 _RoundWaveStrength 的值回复为 0
sequence.Append(material.DOFloat(0, "_RoundWaveStrength", 0.5f));
sequence.AppendCallback(() => { EyeGlitch_out(visualSettings.glitchOutDuration); });
Sequence waveOut = CreateTargetRoundWaveSequence(0f, visualSettings.roundWaveOutDuration);
if (waveOut != null)
{
sequence.Append(waveOut);
}
// 开始这个序列
sequence.Play();
// 等待序列完成
yield return sequence.WaitForCompletion();
}
@@ -490,13 +520,118 @@ namespace AibisDream
public void GetColor()
{
SetEyeColorState(EyeColorState.CanImagineColor);
// 对目标列表中的每个目标执行 ColorBack 方法
foreach (EyeTarget target in targets)
{
target.ColorBack();
}
}
// 切换到 CanImagineColorEffect 状态
public void SetUnifiedUfWeights(float ascii, float dream, float memory)
{
foreach (EyeTarget target in targets)
{
target.SetUfWeights(ascii, dream, memory);
}
}
public Tween TweenUnifiedUfWeights(float ascii, float dream, float memory, float duration)
{
Sequence sequence = DOTween.Sequence();
bool hasTween = false;
foreach (EyeTarget target in targets)
{
Tween tween = target.TweenUfWeights(ascii, dream, memory, duration);
if (tween == null)
{
continue;
}
sequence.Join(tween);
hasTween = true;
}
return hasTween ? sequence : null;
}
private bool TryJoinTargetSaturation(Sequence sequence, float saturation, float duration)
{
bool hasTween = false;
foreach (EyeTarget target in targets)
{
Tween tween = target.TweenSaturation(saturation, duration);
if (tween == null)
{
continue;
}
sequence.Join(tween);
hasTween = true;
}
return hasTween;
}
private bool TryStartUnifiedGlitch()
{
glitchin?.Kill();
glitchin = DOTween.Sequence();
bool hasTween = false;
foreach (EyeTarget target in targets)
{
Sequence targetGlitch = target.StartUnifiedGlitch(visualSettings.glitchInDuration);
if (targetGlitch == null)
{
continue;
}
glitchin.Join(targetGlitch);
hasTween = true;
}
return hasTween;
}
private bool TryStopUnifiedGlitch(float duration)
{
bool hasTarget = false;
if (glitchin != null && glitchin.IsActive())
{
glitchin.Kill();
hasTarget = true;
}
foreach (EyeTarget target in targets)
{
if (!target.TryGetUnifiedVisual(out _))
{
continue;
}
target.StopUnifiedGlitch(duration);
hasTarget = true;
}
return hasTarget;
}
private Sequence CreateTargetRoundWaveSequence(float value, float duration)
{
Sequence sequence = DOTween.Sequence();
bool hasTween = false;
foreach (EyeTarget target in targets)
{
Tween tween = target.TweenRoundWave(value, duration);
if (tween != null)
{
sequence.Join(tween);
hasTween = true;
}
}
return hasTween ? sequence : null;
}
}
}
}
+342 -62
View File
@@ -1,149 +1,429 @@
using UnityEngine;
using DG.Tweening;
using AibisDream.Utility;
using System;
using AibisDream;
using AibisDream.Kit;
using AibisDream.Utility;
using DG.Tweening;
using UnityEngine;
public class EyeTarget : MonoBehaviour
{
public Transform targetTransform; // EyeTarget的位置
[SerializeField] private float burnRadiusMin = 0f; // 对应_BurnRadius的最小值
private static readonly int AlphaId = Shader.PropertyToID("_Alpha");
[SerializeField] private float burnRadiusMax = 1f; // 对应_BurnRadius的最大值
public Transform targetTransform;
private Material forwardMaterial;
[SerializeField] private SpriteRenderer backSpriteRenderer;
[SerializeField] private SpriteRenderer wrongColorSpriteRenderer;
[Header("Optional overlay sprite for UF imagination")]
[SerializeField] private SpriteRenderer imageSpriteRender;
private Material backMaterial;
private Material wrongColorMaterial;
[Header("Wrong-color overlay (e.g. plantError); auto-resolved from sibling if unset")]
[SerializeField] private SpriteRenderer wrongColorSpriteRenderer;
[SerializeField] private Shader unifiedSpriteShader;
[SerializeField] private EyeTargetVisual unifiedVisual;
public Color _color;
private EyeVisualSettings visualSettings = new();
private Material wrongColorMaterial;
private void Awake()
{
// 获取此对象的材质
SpriteRenderer spriteRenderer = GetComponent<SpriteRenderer>();
forwardMaterial = spriteRenderer.material;
forwardMaterial.SetFloat("_SourceGlowDissolveFade", burnRadiusMin);
var spriteRenderer = GetComponent<SpriteRenderer>();
if (spriteRenderer == null)
{
Debug.LogError($"[EyeTarget] {name} 缺少 SpriteRenderer。", this);
return;
}
backMaterial = backSpriteRenderer.material;
wrongColorMaterial = wrongColorSpriteRenderer.material;
;
EnsureWrongColorOverlay();
EnsureUnifiedVisual(spriteRenderer);
DisableLegacyChildRenderers();
DisableLegacySiblingRenderers();
}
public void BindVisualSettings(EyeVisualSettings settings)
{
visualSettings = settings ?? new EyeVisualSettings();
RefreshMaterialTuning();
}
public void RefreshMaterialTuning()
{
EnsureVisualReady();
unifiedVisual?.ApplyTuning(visualSettings);
}
public void ApplyIdlePreviewState()
{
EnsureVisualReady();
Init();
}
public void KillVisualTweens()
{
unifiedVisual?.KillActiveTweens();
wrongColorMaterial?.DOKill();
}
private void EnsureVisualReady()
{
var spriteRenderer = GetComponent<SpriteRenderer>();
if (spriteRenderer == null)
{
return;
}
if (unifiedVisual == null || !unifiedVisual.IsReady)
{
EnsureUnifiedVisual(spriteRenderer);
}
}
public void Init()
{
SetToMinBurnRadius();
SetToMaxBlur(false);
SetToMinColorReveal();
SetToMaxBlur();
SetToNoWrongColor();
SetNoImage();
}
public void ImageIn(float duration)
{
imageSpriteRender.DOFade(1, duration);
unifiedVisual?.TweenImageAlpha(1f, duration);
}
public void ImageOut(float duration)
{
imageSpriteRender.DOFade(0, duration);
unifiedVisual?.TweenImageAlpha(0f, duration);
}
public void SetNoImage()
{
imageSpriteRender.SetAlpha(0);
unifiedVisual?.SetImageAlpha(0f);
}
public void SetToMinBurnRadius()
public void SetToMinColorReveal()
{
forwardMaterial.SetFloat("_SourceGlowDissolveFade", burnRadiusMin);
unifiedVisual?.SetColorReveal(visualSettings.colorRevealMin);
}
public void SetToMaxBurnRadius()
public void SetToMaxColorReveal()
{
forwardMaterial.SetFloat("_SourceGlowDissolveFade", burnRadiusMax);
unifiedVisual?.SetColorReveal(visualSettings.colorRevealMax);
}
public void SetToMaxBlur(bool isForwardMaterial = true)
public void SetToMaxBlur()
{
Material targetMaterial = isForwardMaterial ? forwardMaterial : backMaterial;
targetMaterial.SetFloat("_GaussianBlurFade", 1f);
unifiedVisual?.SetBlur(visualSettings.idleBlurAmount);
}
public void SetToMinBlur(bool isForwardMaterial = true)
public void SetToMinBlur()
{
Material targetMaterial = isForwardMaterial ? forwardMaterial : backMaterial;
targetMaterial.SetFloat("_GaussianBlurFade", 0f);
unifiedVisual?.SetBlur(visualSettings.focusedBlurAmount);
}
public void SetToNoWrongColor()
{
wrongColorMaterial.SetFloat("_Alpha", 0f);
unifiedVisual?.SetWrongColor(0f);
SetWrongColorOverlayAlpha(0f);
}
public void SetToWrongColor()
{
wrongColorMaterial.SetFloat("_Alpha", 1f);
unifiedVisual?.SetWrongColor(1f);
SetWrongColorOverlayAlpha(1f);
}
public void FadeOut(float duration)
{
// 使用DoTween库来改变_BurnRadius,使目标褪色
forwardMaterial.DOFloat(burnRadiusMin, "_SourceGlowDissolveFade", duration);
forwardMaterial.DOFloat(1f, "_GaussianBlurFade", duration).SetEase(Ease.InSine);
forwardMaterial.DOFloat(1f, "_UVDistortFade", duration / 2).SetEase(Ease.InSine).SetLoops(2, LoopType.Yoyo);
;
unifiedVisual?.TweenColorReveal(visualSettings.colorRevealMin, duration)?.SetEase(Ease.InSine);
unifiedVisual?.TweenBlur(visualSettings.idleBlurAmount, duration)?.SetEase(Ease.InSine);
unifiedVisual?.PulseDistortion(visualSettings.colorPulseDistortion, duration);
}
public void ColorIn(float duration)
{
forwardMaterial.DOFloat(burnRadiusMax, "_SourceGlowDissolveFade", duration).SetEase(Ease.InOutSine);
forwardMaterial.DOFloat(0f, "_GaussianBlurFade", duration).SetEase(Ease.InOutSine);
forwardMaterial.DOFloat(1f, "_UVDistortFade", duration / 2).SetEase(Ease.InOutSine).SetLoops(2, LoopType.Yoyo);
;
unifiedVisual?.TweenColorReveal(visualSettings.colorRevealMax, duration)?.SetEase(Ease.InOutSine);
unifiedVisual?.TweenBlur(visualSettings.focusedBlurAmount, duration)?.SetEase(Ease.InOutSine);
unifiedVisual?.PulseDistortion(visualSettings.colorPulseDistortion, duration);
}
public void WrongColorIn(float duration)
{
wrongColorMaterial.DOFloat(1f, "_Alpha", duration).SetEase(Ease.InOutSine);
unifiedVisual?.TweenWrongColor(1f, duration)?.SetEase(Ease.InOutSine);
TweenWrongColorOverlay(1f, duration);
}
public void WrongColorOut(float duration)
{
wrongColorMaterial.DOFloat(0f, "_Alpha", duration).SetEase(Ease.InOutSine);
unifiedVisual?.TweenWrongColor(0f, duration)?.SetEase(Ease.InOutSine);
TweenWrongColorOverlay(0f, duration);
}
public void BlurIn(float duration, bool isForwardMaterial = true)
public void BlurIn(float duration)
{
// AudioManager.PlayLoopAudio("visualturn");
AudioManager.Instance.PlaySfx("event:/ActionFB/visualturn");
Material targetMaterial = isForwardMaterial ? forwardMaterial : backMaterial;
targetMaterial.DOFloat(1f, "_GaussianBlurFade", duration).SetEase(Ease.InOutSine)
.OnComplete(() => { AudioManager.Instance.StopSfx("event:/ActionFB/visualturn"); });
var tween = unifiedVisual?.TweenBlur(visualSettings.idleBlurAmount, duration);
tween?.SetEase(Ease.InOutSine).OnComplete(StopVisualTurnSfx);
}
public void BlurOut(float duration, bool isForwardMaterial = true)
public void BlurOut(float duration)
{
// AudioManager.PlayLoopAudio("visualturn");
AudioManager.Instance.PlaySfx("event:/ActionFB/visualturn");
Material targetMaterial = isForwardMaterial ? forwardMaterial : backMaterial;
targetMaterial.DOFloat(0f, "_GaussianBlurFade", duration).SetEase(Ease.InOutSine)
.OnComplete(() => { AudioManager.Instance.StopSfx("event:/ActionFB/visualturn"); });
var tween = unifiedVisual?.TweenBlur(visualSettings.focusedBlurAmount, duration);
tween?.SetEase(Ease.InOutSine).OnComplete(StopVisualTurnSfx);
}
public void ColorBack()
{
SetToMaxBurnRadius();
SetToMaxColorReveal();
SetToNoWrongColor();
SetToMinBlur();
}
public void ColorLost()
{
SetToMinBurnRadius();
//SetToNoWrongColor();
SetToMinColorReveal();
SetToMaxBlur();
}
}
public bool TryGetUnifiedVisual(out EyeTargetVisual visual)
{
visual = unifiedVisual;
return visual != null && visual.IsReady;
}
public void SetSaturation(float saturation)
{
unifiedVisual?.SetSaturation(saturation);
}
public Tween TweenSaturation(float saturation, float duration)
{
return unifiedVisual != null ? unifiedVisual.TweenSaturation(saturation, duration) : null;
}
public DG.Tweening.Sequence StartUnifiedGlitch(float duration = 1f)
{
return unifiedVisual != null ? unifiedVisual.StartGlitch(duration) : null;
}
public void StopUnifiedGlitch(float duration)
{
unifiedVisual?.StopGlitch(duration);
}
public Tween TweenRoundWave(float value, float duration)
{
return unifiedVisual != null ? unifiedVisual.TweenRoundWave(value, duration) : null;
}
public void SetUfWeights(float ascii, float dream, float memory)
{
unifiedVisual?.SetUfWeights(ascii, dream, memory);
}
public Tween TweenUfWeights(float ascii, float dream, float memory, float duration)
{
return unifiedVisual != null ? unifiedVisual.TweenUfWeights(ascii, dream, memory, duration) : null;
}
private void EnsureUnifiedVisual(SpriteRenderer spriteRenderer)
{
Shader shader = unifiedSpriteShader != null
? unifiedSpriteShader
: Shader.Find("AIBIS/EyeUnifiedSprite");
if (shader == null)
{
Debug.LogError($"[EyeTarget] 找不到 AIBIS/EyeUnifiedSprite shader{name}", this);
return;
}
Material currentMaterial = Application.isPlaying
? spriteRenderer.material
: spriteRenderer.sharedMaterial;
bool usesUnifiedShader = currentMaterial != null
&& currentMaterial.shader != null
&& currentMaterial.shader.name == shader.name;
if (currentMaterial == null || !usesUnifiedShader || !currentMaterial.HasProperty("_BlurAmount"))
{
Texture mainTex = currentMaterial != null && currentMaterial.HasProperty("_MainTex")
? currentMaterial.GetTexture("_MainTex")
: null;
var runtimeMaterial = new Material(shader)
{
name = $"{gameObject.name}_EyeUnified_Runtime"
};
if (mainTex != null)
{
runtimeMaterial.SetTexture("_MainTex", mainTex);
}
if (Application.isPlaying)
{
spriteRenderer.material = runtimeMaterial;
}
else
{
spriteRenderer.sharedMaterial = runtimeMaterial;
}
}
if (unifiedVisual == null)
{
unifiedVisual = GetComponent<EyeTargetVisual>();
}
if (unifiedVisual == null)
{
unifiedVisual = gameObject.AddComponent<EyeTargetVisual>();
}
unifiedVisual.Configure(spriteRenderer, imageSpriteRender);
unifiedVisual.ApplyTuning(visualSettings);
}
private void DisableLegacyChildRenderers()
{
foreach (var childRenderer in GetComponentsInChildren<SpriteRenderer>(true))
{
if (childRenderer.gameObject == gameObject)
{
continue;
}
if (imageSpriteRender != null && childRenderer == imageSpriteRender)
{
continue;
}
childRenderer.enabled = false;
}
}
/// <summary>
/// 禁用同组下旧版 overlay(如 posterNoColor / computernocolor),避免盖住 unified 效果。
/// </summary>
private void EnsureWrongColorOverlay()
{
if (wrongColorSpriteRenderer == null && transform.parent != null)
{
string errorName = $"{gameObject.name}Error";
foreach (var renderer in transform.parent.GetComponentsInChildren<SpriteRenderer>(true))
{
if (string.Equals(renderer.gameObject.name, errorName, StringComparison.OrdinalIgnoreCase))
{
wrongColorSpriteRenderer = renderer;
break;
}
}
}
if (wrongColorSpriteRenderer == null)
{
return;
}
wrongColorMaterial = Application.isPlaying
? wrongColorSpriteRenderer.material
: wrongColorSpriteRenderer.sharedMaterial;
wrongColorSpriteRenderer.enabled = true;
SetWrongColorOverlayAlpha(0f);
}
private void SetWrongColorOverlayAlpha(float alpha)
{
if (wrongColorSpriteRenderer == null)
{
return;
}
alpha = Mathf.Clamp01(alpha);
if (alpha > 0f)
{
wrongColorSpriteRenderer.gameObject.SetActive(true);
wrongColorSpriteRenderer.enabled = true;
}
if (wrongColorMaterial != null && wrongColorMaterial.HasProperty(AlphaId))
{
wrongColorMaterial.SetFloat(AlphaId, alpha);
}
if (alpha <= 0f)
{
wrongColorSpriteRenderer.gameObject.SetActive(false);
}
}
private void TweenWrongColorOverlay(float alpha, float duration)
{
if (wrongColorSpriteRenderer == null || wrongColorMaterial == null || !wrongColorMaterial.HasProperty(AlphaId))
{
return;
}
if (alpha > 0f)
{
wrongColorSpriteRenderer.gameObject.SetActive(true);
wrongColorSpriteRenderer.enabled = true;
}
wrongColorMaterial.DOKill();
wrongColorMaterial
.DOFloat(alpha, AlphaId, duration)
.SetEase(Ease.InOutSine)
.OnComplete(() =>
{
if (alpha <= 0f && wrongColorSpriteRenderer != null)
{
wrongColorSpriteRenderer.gameObject.SetActive(false);
}
});
}
private static bool IsWrongColorOverlayRenderer(SpriteRenderer renderer)
{
return renderer != null
&& renderer.gameObject.name.EndsWith("Error", StringComparison.OrdinalIgnoreCase);
}
private void DisableLegacySiblingRenderers()
{
Transform groupRoot = transform.parent;
if (groupRoot == null)
{
return;
}
foreach (var renderer in groupRoot.GetComponentsInChildren<SpriteRenderer>(true))
{
if (renderer.gameObject == gameObject)
{
continue;
}
if (renderer.GetComponent<EyeTarget>() != null)
{
continue;
}
if (renderer.GetComponentInParent<EyeTarget>() != null)
{
continue;
}
if (IsWrongColorOverlayRenderer(renderer))
{
continue;
}
renderer.enabled = false;
}
}
private static void StopVisualTurnSfx()
{
AudioManager.Instance.StopSfx("event:/ActionFB/visualturn");
}
}
@@ -0,0 +1,340 @@
using DG.Tweening;
using UnityEngine;
using AibisDream.Utility;
namespace AibisDream
{
/// <summary>
/// Drives the unified EyeUnifiedSprite path for Peipei eye targets.
/// </summary>
public class EyeTargetVisual : MonoBehaviour
{
private static readonly int ColorRevealId = Shader.PropertyToID("_ColorReveal");
private static readonly int GrayAmountId = Shader.PropertyToID("_GrayAmount");
private static readonly int BlurAmountId = Shader.PropertyToID("_BlurAmount");
private static readonly int BlurSizeId = Shader.PropertyToID("_BlurSize");
private static readonly int DistortAmountId = Shader.PropertyToID("_DistortAmount");
private static readonly int WrongColorAmountId = Shader.PropertyToID("_WrongColorAmount");
private static readonly int SpriteFadeId = Shader.PropertyToID("_SpriteFade");
private static readonly int AsciiAmountId = Shader.PropertyToID("_AsciiAmount");
private static readonly int DreamAmountId = Shader.PropertyToID("_DreamAmount");
private static readonly int MemoryAmountId = Shader.PropertyToID("_MemoryAmount");
private static readonly int ScanlineAmountId = Shader.PropertyToID("_ScanlineAmount");
private static readonly int GlitchAmountId = Shader.PropertyToID("_GlitchAmount");
private static readonly int HsvShiftId = Shader.PropertyToID("_HsvShift");
private static readonly int HsvSaturationId = Shader.PropertyToID("_HsvSaturation");
private static readonly int RoundWaveStrengthId = Shader.PropertyToID("_RoundWaveStrength");
private static readonly int RevealWidthId = Shader.PropertyToID("_RevealWidth");
private static readonly int RevealRotationId = Shader.PropertyToID("_RevealRotation");
private static readonly int RevealNoiseId = Shader.PropertyToID("_RevealNoise");
private static readonly int WrongColorId = Shader.PropertyToID("_WrongColor");
[SerializeField] private SpriteRenderer spriteRenderer;
[SerializeField] private SpriteRenderer imageSpriteRenderer;
[SerializeField] private bool driveImageLayer = true;
private Material material;
private float activeBlurSize = 0.008f;
public bool IsReady => material != null && material.HasProperty(ColorRevealId);
public bool CanDriveImageLayer => driveImageLayer && imageSpriteRenderer != null;
public Material Material => material;
private void Awake()
{
EnsureMaterial();
}
public bool EnsureMaterial()
{
if (spriteRenderer == null)
{
spriteRenderer = GetComponent<SpriteRenderer>();
}
if (spriteRenderer == null)
{
return false;
}
material = Application.isPlaying
? spriteRenderer.material
: spriteRenderer.sharedMaterial;
return IsReady;
}
public void Configure(SpriteRenderer mainRenderer, SpriteRenderer imageRenderer)
{
spriteRenderer = mainRenderer;
imageSpriteRenderer = imageRenderer;
EnsureMaterial();
}
public void ApplyTuning(EyeVisualSettings settings)
{
if (settings == null)
{
return;
}
activeBlurSize = settings.blurSize;
if (!EnsureMaterial())
{
return;
}
material.SetFloat(RevealWidthId, settings.revealWidth);
material.SetFloat(RevealRotationId, settings.revealRotation);
material.SetFloat(RevealNoiseId, settings.revealNoise);
material.SetColor(WrongColorId, settings.wrongColor);
SetFloatRaw(HsvSaturationId, settings.hsvSaturation);
SyncBlurSizeToMaterial();
}
public void SyncBlurSizeToMaterial()
{
if (!EnsureMaterial() || !material.HasProperty(BlurSizeId))
{
return;
}
float blur = material.GetFloat(BlurAmountId);
material.SetFloat(BlurSizeId, blur > 0.01f ? activeBlurSize : 0f);
}
public void KillActiveTweens()
{
if (material != null)
{
material.DOKill();
}
if (driveImageLayer && imageSpriteRenderer != null)
{
imageSpriteRenderer.DOKill();
}
}
public void ResetVisual(float reveal)
{
if (!EnsureMaterial())
{
return;
}
SetFloat(ColorRevealId, reveal);
SetFloat(GrayAmountId, 1f - reveal);
SetFloat(BlurAmountId, 1f);
if (material.HasProperty(BlurSizeId))
{
material.SetFloat(BlurSizeId, activeBlurSize);
}
SetFloat(DistortAmountId, 0f);
SetFloat(WrongColorAmountId, 0f);
SetFloat(SpriteFadeId, 1f);
SetFloat(AsciiAmountId, 0f);
SetFloat(DreamAmountId, 0f);
SetFloat(MemoryAmountId, 0f);
SetFloat(ScanlineAmountId, 0f);
SetFloat(GlitchAmountId, 0f);
SetFloat(HsvShiftId, 0f);
SetFloat(HsvSaturationId, 1f);
SetFloat(RoundWaveStrengthId, 0f);
SetImageAlpha(0f);
}
public void SetColorReveal(float value)
{
SetFloat(ColorRevealId, value);
SetFloat(GrayAmountId, 1f - value);
}
public Tween TweenColorReveal(float value, float duration)
{
if (!EnsureMaterial())
{
return null;
}
value = Mathf.Clamp01(value);
Sequence sequence = DOTween.Sequence();
sequence.Join(material.DOFloat(value, ColorRevealId, duration));
sequence.Join(material.DOFloat(1f - value, GrayAmountId, duration));
return sequence;
}
public void SetBlur(float value)
{
SetFloat(BlurAmountId, value);
if (EnsureMaterial() && material.HasProperty(BlurSizeId))
{
material.SetFloat(BlurSizeId, value > 0.01f ? activeBlurSize : 0f);
}
}
public Tween TweenBlur(float value, float duration)
{
return TweenFloat(BlurAmountId, value, duration);
}
public void SetWrongColor(float value)
{
SetFloat(WrongColorAmountId, value);
}
public Tween TweenWrongColor(float value, float duration)
{
return TweenFloat(WrongColorAmountId, value, duration);
}
public Tween PulseDistortion(float amount, float duration)
{
if (!EnsureMaterial())
{
return null;
}
return material.DOFloat(amount, DistortAmountId, Mathf.Max(0.01f, duration * 0.5f))
.SetEase(Ease.InOutSine)
.SetLoops(2, LoopType.Yoyo);
}
public void SetImageAlpha(float alpha)
{
if (!driveImageLayer || imageSpriteRenderer == null)
{
return;
}
alpha = Mathf.Clamp01(alpha);
if (alpha > 0f)
{
imageSpriteRenderer.gameObject.SetActive(true);
imageSpriteRenderer.enabled = true;
}
imageSpriteRenderer.SetAlpha(alpha);
if (alpha <= 0f)
{
imageSpriteRenderer.gameObject.SetActive(false);
}
}
public Tween TweenImageAlpha(float alpha, float duration)
{
if (!driveImageLayer || imageSpriteRenderer == null)
{
return null;
}
alpha = Mathf.Clamp01(alpha);
if (alpha > 0f)
{
imageSpriteRenderer.gameObject.SetActive(true);
imageSpriteRenderer.enabled = true;
}
return imageSpriteRenderer.DOFade(alpha, duration)
.OnComplete(() =>
{
if (alpha <= 0f && imageSpriteRenderer != null)
{
imageSpriteRenderer.gameObject.SetActive(false);
}
});
}
public void SetUfWeights(float ascii, float dream, float memory)
{
SetFloat(AsciiAmountId, ascii);
SetFloat(DreamAmountId, dream);
SetFloat(MemoryAmountId, memory);
SetFloat(ScanlineAmountId, Mathf.Max(ascii, memory));
}
public Tween TweenUfWeights(float ascii, float dream, float memory, float duration)
{
if (!EnsureMaterial())
{
return null;
}
Sequence sequence = DOTween.Sequence();
sequence.Join(material.DOFloat(Mathf.Clamp01(ascii), AsciiAmountId, duration));
sequence.Join(material.DOFloat(Mathf.Clamp01(dream), DreamAmountId, duration));
sequence.Join(material.DOFloat(Mathf.Clamp01(memory), MemoryAmountId, duration));
sequence.Join(material.DOFloat(Mathf.Clamp01(Mathf.Max(ascii, memory)), ScanlineAmountId, duration));
return sequence;
}
public void SetSaturation(float saturation)
{
SetFloatRaw(HsvSaturationId, Mathf.Clamp(saturation, 0f, 2f));
}
public Tween TweenSaturation(float saturation, float duration)
{
return TweenFloat(HsvSaturationId, Mathf.Clamp(saturation, 0f, 2f), duration);
}
public Sequence StartGlitch(float duration = 1f)
{
if (!EnsureMaterial())
{
return null;
}
duration = Mathf.Max(0.01f, duration);
Sequence sequence = DOTween.Sequence();
sequence.Join(material.DOFloat(1f, GlitchAmountId, duration));
sequence.Join(material.DOFloat(0.02f, DistortAmountId, duration));
sequence.Join(material.DOFloat(360f, HsvShiftId, duration).SetLoops(-1, LoopType.Yoyo));
return sequence;
}
public void StopGlitch(float duration)
{
TweenFloat(GlitchAmountId, 0f, duration);
TweenFloat(DistortAmountId, 0f, duration);
TweenFloat(HsvShiftId, 0f, duration);
}
public Tween TweenRoundWave(float value, float duration)
{
return TweenFloat(RoundWaveStrengthId, value, duration);
}
private Tween TweenFloat(int propertyId, float value, float duration)
{
if (!EnsureMaterial() || !material.HasProperty(propertyId))
{
return null;
}
return material.DOFloat(value, propertyId, duration);
}
private void SetFloat(int propertyId, float value)
{
if (!EnsureMaterial() || !material.HasProperty(propertyId))
{
return;
}
material.SetFloat(propertyId, Mathf.Clamp01(value));
}
private void SetFloatRaw(int propertyId, float value)
{
if (!EnsureMaterial() || !material.HasProperty(propertyId))
{
return;
}
material.SetFloat(propertyId, value);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a0c7e3c9e6a546b49887860b3f26cf91
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,403 @@
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;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b95035b93d484ef4da0b45fcf2c2a380
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,89 @@
using System;
using UnityEngine;
namespace AibisDream
{
/// <summary>
/// Central tuning for Peipei eye-module visuals. Edit on EyeManager (EyeSystem).
/// </summary>
[Serializable]
public class EyeVisualSettings
{
[Header("Color Reveal")]
[Tooltip("Unfocused / gray state (0 = gray, 1 = full color).")]
[Range(0f, 1f)] public float colorRevealMin;
[Tooltip("Focused / imagined color state.")]
[Range(0f, 1f)] public float colorRevealMax = 1f;
[Header("Transition Duration (seconds)")]
public float imagineColorDuration = 1f;
public float cannotImagineDuration = 1f;
public float eyeDisorderDuration = 2f;
public float canSeeColorDuration = 1f;
public float targetSwitchDuration = 1f;
[Header("Blur")]
[Tooltip("Blur when unfocused or module idle.")]
[Range(0f, 1f)] public float idleBlurAmount = 1f;
[Tooltip("Blur when focused and sharp.")]
[Range(0f, 1f)] public float focusedBlurAmount;
[Tooltip("Shader _BlurSize when blur is active.")]
[Range(0f, 0.02f)] public float blurSize = 0.008f;
[Header("Color Sweep (shader)")]
[Tooltip("Width of the directional color sweep during ColorIn/FadeOut.")]
[Range(0.001f, 1f)] public float revealWidth = 0.18f;
[Tooltip("Sweep direction in radians (0 = left-to-right).")]
[Range(0f, 6.28318f)] public float revealRotation;
[Range(0f, 1f)] public float revealNoise = 0.08f;
[Header("Distortion Pulse")]
[Tooltip("Peak _DistortAmount during ColorIn / FadeOut.")]
[Range(0f, 0.08f)] public float colorPulseDistortion = 0.035f;
[Header("Wrong Color (Eye Disorder)")]
public Color wrongColor = new(1f, 0.16f, 0.08f, 1f);
[Header("HSV")]
[Range(0f, 2f)] public float hsvSaturation = 1f;
[Header("Glitch / Memory Wave")]
public float glitchInDuration = 1f;
public float roundWaveInDuration = 2f;
public float roundWaveOutDuration = 0.5f;
public float glitchOutDuration = 0.5f;
[Header("Viewport Overlay (no post-processing)")]
public bool enableViewportOverlay = true;
[Tooltip("Horizontal half-size of the resting sight almond (left/right darkness).")]
[Range(0.22f, 0.58f)] public float eyeWidth = 0.46f;
[Tooltip("Vertical half-size of the resting sight almond (top/bottom darkness).")]
[Range(0.15f, 0.72f)] public float eyeHeight = 0.28f;
[Tooltip("Softness of the elliptical sight boundary.")]
[Range(0.001f, 0.2f)] public float edgeSoftness = 0.022f;
[Tooltip("Off by default so you can tune the sight without blinking.")]
public bool autoBlink;
public Vector2 autoBlinkInterval = new(8f, 15f);
public float blinkCloseDuration = 0.09f;
public float blinkOpenDuration = 0.14f;
public float GetDurationForState(EyeSystem.EyeColorState state) => state switch
{
EyeSystem.EyeColorState.CanImagineColor => imagineColorDuration,
EyeSystem.EyeColorState.CannotImagineColor => cannotImagineDuration,
EyeSystem.EyeColorState.EyeDisorder => eyeDisorderDuration,
EyeSystem.EyeColorState.CanSeeColor => canSeeColorDuration,
_ => imagineColorDuration
};
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2767162520353a0458684a571ce59532
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,179 +1,133 @@
using AibisDream;
public interface IEyeStateEffect
{
float TransitionDuration { get; }
void InitializeEffect(EyeTarget target, ViewCameraManager cameraManager);
void InitializeEffect(EyeTarget target, EyeSystem eyeSystem);
void CleanupEffect(EyeTarget target);
void ApplyFocusedEffect(EyeTarget target);
void ApplyUnfocusedEffect(EyeTarget target);
void ApplyFocusedEffect(EyeTarget target, EyeSystem eyeSystem);
void ApplyUnfocusedEffect(EyeTarget target, EyeSystem eyeSystem);
}
public class CanImagineColorEffect : IEyeStateEffect
{
public float TransitionDuration => 1f;
public void InitializeEffect(EyeTarget target, ViewCameraManager cameraManager)
public void InitializeEffect(EyeTarget target, EyeSystem eyeSystem)
{
target.SetToNoWrongColor();
target.SetToMaxBurnRadius();
target.SetToMaxColorReveal();
target.SetToMinBlur();
if (target.targetTransform)
{
cameraManager.SetCameraPosition(target.targetTransform);
}
else
{
cameraManager.SetCameraPosition(target.transform);
}
eyeSystem?.FocusOnTarget(target, 0f);
}
public void CleanupEffect(EyeTarget target)
{
target.SetToMinBurnRadius();
target.SetToMinColorReveal();
target.SetToMaxBlur();
// Add cleanup logic here
}
public void ApplyFocusedEffect(EyeTarget target)
public void ApplyFocusedEffect(EyeTarget target, EyeSystem eyeSystem)
{
target.ColorIn(TransitionDuration);
target.ColorIn(eyeSystem.GetEffectTransitionDuration());
}
public void ApplyUnfocusedEffect(EyeTarget target)
public void ApplyUnfocusedEffect(EyeTarget target, EyeSystem eyeSystem)
{
target.FadeOut(TransitionDuration);
target.FadeOut(eyeSystem.GetEffectTransitionDuration());
}
}
public class CannotImagineColorEffect : IEyeStateEffect
{
public float TransitionDuration { get; } = 1f;
public void InitializeEffect(EyeTarget target, ViewCameraManager cameraManager)
public void InitializeEffect(EyeTarget target, EyeSystem eyeSystem)
{
target.SetToMinBlur(false);
target.SetToMinBurnRadius();
target.SetToMinBlur();
target.SetToMinColorReveal();
target.SetToNoWrongColor();
if (target.targetTransform)
{
cameraManager.SetCameraPosition(target.targetTransform);
}
else
{
cameraManager.SetCameraPosition(target.transform);
}
eyeSystem?.FocusOnTarget(target, 0f);
}
public void CleanupEffect(EyeTarget target)
{
// Add cleanup logic here
target.SetToMaxBlur(false);
target.SetToMaxBlur();
}
public void ApplyFocusedEffect(EyeTarget target)
public void ApplyFocusedEffect(EyeTarget target, EyeSystem eyeSystem)
{
target.BlurOut(TransitionDuration, false);
target.BlurOut(eyeSystem.GetEffectTransitionDuration());
}
public void ApplyUnfocusedEffect(EyeTarget target)
public void ApplyUnfocusedEffect(EyeTarget target, EyeSystem eyeSystem)
{
target.BlurIn(TransitionDuration, false);
target.BlurIn(eyeSystem.GetEffectTransitionDuration());
}
}
public class EyeDisorderEffect : IEyeStateEffect
{
public float TransitionDuration { get; } = 2f;
public void InitializeEffect(EyeTarget target, ViewCameraManager cameraManager)
public void InitializeEffect(EyeTarget target, EyeSystem eyeSystem)
{
target.SetToMinBurnRadius();
// Add initialization logic here
if (target.targetTransform)
{
cameraManager.SetCameraPosition(target.targetTransform);
}
else
{
cameraManager.SetCameraPosition(target.transform);
}
target.SetToMinColorReveal();
eyeSystem?.FocusOnTarget(target, 0f);
target.SetToWrongColor();
target.SetToMinBlur(false);
}
public void CleanupEffect(EyeTarget target)
{
// Add cleanup logic here
target.SetToNoWrongColor();
}
public void ApplyFocusedEffect(EyeTarget target)
{
target.WrongColorIn(TransitionDuration);
}
public void ApplyUnfocusedEffect(EyeTarget target)
{
target.WrongColorOut(TransitionDuration);
}
}
public class CanSeeColorEffect : IEyeStateEffect
{
public float TransitionDuration { get; } = 1f;
public void InitializeEffect(EyeTarget target, ViewCameraManager cameraManager)
{
target.SetToNoWrongColor();
target.SetToMaxBurnRadius();
if (target.targetTransform)
{
cameraManager.SetCameraPosition(target.targetTransform);
}
else
{
cameraManager.SetCameraPosition(target.transform);
}
target.SetToMinBlur();
}
public void CleanupEffect(EyeTarget target)
{
// Add cleanup logic here
target.SetToNoWrongColor();
}
public void ApplyFocusedEffect(EyeTarget target, EyeSystem eyeSystem)
{
target.WrongColorIn(eyeSystem.GetEffectTransitionDuration());
}
public void ApplyUnfocusedEffect(EyeTarget target, EyeSystem eyeSystem)
{
target.WrongColorOut(eyeSystem.GetEffectTransitionDuration());
}
}
public class CanSeeColorEffect : IEyeStateEffect
{
public void InitializeEffect(EyeTarget target, EyeSystem eyeSystem)
{
target.SetToNoWrongColor();
target.SetToMaxColorReveal();
eyeSystem?.FocusOnTarget(target, 0f);
target.SetToMinBlur();
}
public void CleanupEffect(EyeTarget target)
{
target.SetToMaxBlur();
}
public void ApplyFocusedEffect(EyeTarget target)
public void ApplyFocusedEffect(EyeTarget target, EyeSystem eyeSystem)
{
target.BlurOut(TransitionDuration);
target.BlurOut(eyeSystem.GetEffectTransitionDuration());
}
public void ApplyUnfocusedEffect(EyeTarget target)
public void ApplyUnfocusedEffect(EyeTarget target, EyeSystem eyeSystem)
{
target.BlurIn(TransitionDuration);
target.BlurIn(eyeSystem.GetEffectTransitionDuration());
}
}
public class HaveChip : IEyeStateEffect
{
public float TransitionDuration { get; } = 1f;
public void InitializeEffect(EyeTarget target, ViewCameraManager cameraManager)
public void InitializeEffect(EyeTarget target, EyeSystem eyeSystem)
{
}
public void CleanupEffect(EyeTarget target)
{
// Add cleanup logic here
}
public void ApplyFocusedEffect(EyeTarget target)
public void ApplyFocusedEffect(EyeTarget target, EyeSystem eyeSystem)
{
}
public void ApplyUnfocusedEffect(EyeTarget target)
public void ApplyUnfocusedEffect(EyeTarget target, EyeSystem eyeSystem)
{
}
}
}
+145 -66
View File
@@ -1,5 +1,5 @@
using System.Collections;
using System.Collections.Generic;
using System;
using System.Collections;
using UnityEngine;
using AibisDream;
using AibisDream.FixSystem;
@@ -7,12 +7,10 @@ using UnityEngine.Rendering;
using Yarn.Unity;
using DG.Tweening;
using VolFx;
using System;
using AibisDream.Kit;
public class UFSystem : MonoBehaviour
{
// Start is called before the first frame update
private GameObject _UFCover;
private GameObject _UAudioule;
@@ -22,17 +20,18 @@ public class UFSystem : MonoBehaviour
public SpriteRenderer face;
private bool isLoopImage = false;
private bool isLoopImage;
[Header("Global UF Cover")]
[SerializeField] private Volume FullAscillVolume;
[Header("Target Post-Processing Volumes")]
[SerializeField] private Volume imageAscillVolume;
[SerializeField] private Volume imageVolume;
[SerializeField] private Volume finalVolume;
private AsciiVol ascii;
private Tween scaleTween; // 存储 Tween 动画
private Tween scaleTween;
public enum UFState
{
@@ -45,21 +44,54 @@ public class UFSystem : MonoBehaviour
{
FixSystemCenter.SystemDic.Register(this);
eyeSystem = FindObjectOfType<EyeSystem>();
ClearUnifiedUfSpriteWeights();
}
public void TweenWeight(Volume postProcessingVolume, float targetWeight, float duration)
{
// 动态调整权重
if (postProcessingVolume == null)
{
return;
}
DOTween.To(() => postProcessingVolume.weight, x => postProcessingVolume.weight = x, targetWeight, duration);
}
private void ClearUnifiedUfSpriteWeights()
{
eyeSystem?.SetUnifiedUfWeights(0f, 0f, 0f);
}
private EyeTarget ResolveSpriteTarget()
{
if (eyeSystem == null)
{
return null;
}
if (eyeSystem.CurrentTarget != null)
{
return eyeSystem.CurrentTarget;
}
if (YarnVariableStorage.Instance != null
&& YarnVariableStorage.Instance.TryGetValue("$currentEyeTarget", out string targetName)
&& !string.IsNullOrEmpty(targetName))
{
return eyeSystem.FindEyeTargetByName(targetName);
}
return null;
}
[YarnCommand("UF_spriteIn")]
public IEnumerator UF_spriteIn(float duration = 1.5f)
{
if (eyeSystem.CurrentTarget != null)
var target = ResolveSpriteTarget();
if (target != null)
{
AudioManager.Instance.PlaySfx("event:/ActionFB/color_imagine");
eyeSystem.CurrentTarget.ImageIn(duration);
target.ImageIn(duration);
}
yield return new WaitForSeconds(duration);
@@ -68,10 +100,11 @@ public class UFSystem : MonoBehaviour
[YarnCommand("UF_spriteOut")]
public IEnumerator UF_spriteOut(float duration = 1.5f)
{
if (eyeSystem.CurrentTarget != null)
var target = ResolveSpriteTarget();
if (target != null)
{
AudioManager.Instance.PlaySfx("event:/ActionFB/color_imagine");
eyeSystem.CurrentTarget.ImageOut(duration);
target.ImageOut(duration);
}
yield return new WaitForSeconds(duration);
@@ -119,58 +152,72 @@ public class UFSystem : MonoBehaviour
public IEnumerator UF_ImaginationEffectIn(float duration = 1.5f)
{
AudioManager.Instance.PlaySfx("event:/ActionFB/color_imagine");
TweenWeight(imageVolume, 1, duration);
yield return new WaitForSeconds(0);
ClearUnifiedUfSpriteWeights();
TweenWeight(imageVolume, 1f, duration);
yield return new WaitForSeconds(0f);
}
[YarnCommand("UF_ImaginationEffectOut")]
public IEnumerator UF_ImaginationEffectOut(float duration = 1.5f)
{
TweenWeight(imageVolume, 0, duration);
TweenWeight(imageVolume, 0f, duration);
AudioManager.Instance.PlaySfx("event:/ActionFB/color_imagine");
yield return new WaitForSeconds(0);
yield return new WaitForSeconds(0f);
}
[YarnCommand("UF_MemoryEffectIn")]
public IEnumerator UF_MemoryEffectIn(float duration = 1.5f)
{
AudioManager.Instance.PlaySfx("event:/ActionFB/color_imagine");
TweenWeight(finalVolume, 1, duration);
yield return new WaitForSeconds(0);
ClearUnifiedUfSpriteWeights();
TweenWeight(finalVolume, 1f, duration);
yield return new WaitForSeconds(0f);
}
[YarnCommand("UF_MemoryEffectOut")]
public IEnumerator UF_MemoryEffectOut(float duration = 1.5f)
{
TweenWeight(finalVolume, 0, duration);
TweenWeight(finalVolume, 0f, duration);
AudioManager.Instance.PlaySfx("event:/ActionFB/color_imagine");
yield return new WaitForSeconds(0);
yield return new WaitForSeconds(0f);
}
[YarnCommand("UF_EyeTargetAscillIn")]
public IEnumerator UF_EyeTargetAscillIn(float duration = 1.5f)
{
AudioManager.Instance.PlaySfx("event:/ActionFB/color_imagine");
TweenWeight(imageAscillVolume, 1, duration);
yield return new WaitForSeconds(0);
ClearUnifiedUfSpriteWeights();
TweenWeight(imageAscillVolume, 1f, duration);
yield return new WaitForSeconds(0f);
}
[YarnCommand("UF_EyeTargetAscillOut")]
public IEnumerator UF_EyeTargetAscillOut(float duration = 1.5f)
{
TweenWeight(imageAscillVolume, 0, duration);
TweenWeight(imageAscillVolume, 0f, duration);
AudioManager.Instance.PlaySfx("event:/ActionFB/color_imagine");
yield return new WaitForSeconds(0);
yield return new WaitForSeconds(0f);
}
[YarnCommand("UF_SetBorkenValue")]
public void UF_Borken(int value)
{
FullAscillVolume.profile.TryGet<AsciiVol>(out ascii);
ascii.m_Depth.value = value;
ascii.m_Ascii.value = Color.red;
}
if (FullAscillVolume == null)
{
return;
}
if (!FullAscillVolume.gameObject.activeInHierarchy)
{
FullAscillVolume.gameObject.SetActive(true);
}
if (FullAscillVolume.profile.TryGet(out ascii))
{
ascii.m_Depth.value = value;
ascii.m_Ascii.value = Color.red;
}
}
[YarnCommand("StartUFEffect_loop")]
public void UFImageLoop()
@@ -187,28 +234,39 @@ public class UFSystem : MonoBehaviour
[YarnCommand("UF_fightEffect")]
public void UF_fightEffect()
{
ClearUnifiedUfSpriteWeights();
scaleTween = DOTween.To(() => 0f, t =>
{
// 根据 t(0 到 1 的变化)更新多个值
imageVolume.weight = Mathf.Lerp(0f, 1f, t); // 例如:imageVolume 的 weight
imageAscillVolume.weight = Mathf.Lerp(0f, 1f, t); // 假设 saturation 是另一个值
finalVolume.weight = Mathf.Lerp(1f, 0f, t); // 假设 contrast 是另一个值
if (imageVolume != null)
{
imageVolume.weight = Mathf.Lerp(0f, 1f, t);
}
if (imageAscillVolume != null)
{
imageAscillVolume.weight = Mathf.Lerp(0f, 1f, t);
}
if (finalVolume != null)
{
finalVolume.weight = Mathf.Lerp(1f, 0f, t);
}
}, 1f, 1f)
.SetEase(Ease.InOutSine) // 平滑缓动
.SetLoops(-1, LoopType.Yoyo); // 无限循环
.SetEase(Ease.InOutSine)
.SetLoops(-1, LoopType.Yoyo);
}
[YarnCommand("UF_StopfightEffect")]
public void UF_StopfightEffect()
{
scaleTween.Kill();
scaleTween?.Kill();
}
public IEnumerator UFImageLoopProcess()
{
int index = 0;
isLoopImage = true;
while (isLoopImage) // 无限循环
while (isLoopImage)
{
eyeSystem.StartCoroutine(eyeSystem.SetTargetCoroutine(eyeSystem.Targets[index].name, false));
yield return new WaitForSeconds(0.5f);
@@ -222,36 +280,61 @@ public class UFSystem : MonoBehaviour
eyeSystem.Targets[index].ImageOut(0.5f);
StartCoroutine(UF_MemoryEffectOut(0.5f));
index++;
// 如果到达列表末尾,则回到开头
if (index >= eyeSystem.Targets.Count - 2)
{
index = 0;
}
//yield return new WaitForSeconds(1f); // 每隔1秒打印一次
}
}
public void OpenUFView()
{
if (!FullAscillVolume.gameObject.activeInHierarchy)
ClearUnifiedUfSpriteWeights();
if (FullAscillVolume != null && !FullAscillVolume.gameObject.activeInHierarchy)
{
FullAscillVolume.gameObject.SetActive(true);
imageAscillVolume.weight = 1;
imageVolume.weight = 0;
finalVolume.weight = 0;
}
if (imageAscillVolume != null)
{
imageAscillVolume.weight = 1f;
}
if (imageVolume != null)
{
imageVolume.weight = 0f;
}
if (finalVolume != null)
{
finalVolume.weight = 0f;
}
}
public void CloseUFView()
{
if (FullAscillVolume.gameObject.activeInHierarchy)
if (FullAscillVolume != null && FullAscillVolume.gameObject.activeInHierarchy)
{
FullAscillVolume.gameObject.SetActive(false);
imageAscillVolume.weight = 1;
imageVolume.weight = 0;
finalVolume.weight = 0;
//scaleTween.Kill();
}
if (imageAscillVolume != null)
{
imageAscillVolume.weight = 1f;
}
if (imageVolume != null)
{
imageVolume.weight = 0f;
}
if (finalVolume != null)
{
finalVolume.weight = 0f;
}
ClearUnifiedUfSpriteWeights();
}
public void OpenCover()
@@ -277,36 +360,32 @@ public class UFSystem : MonoBehaviour
[YarnCommand("UF_SwitchState")]
public IEnumerator SwitchState(string stateName, float duration)
{
UFState state;
if (!Enum.TryParse(stateName, true, out state))
if (!Enum.TryParse(stateName, true, out UFState state))
{
Debug.LogError($"Invalid state name: {stateName}");
yield break;
}
AudioManager.Instance.PlaySfx("event:/ActionFB/color_imagine");
ClearUnifiedUfSpriteWeights();
switch (state)
{
case UFState.UF:
AudioManager.Instance.PlaySfx("event:/ActionFB/color_imagine");
TweenWeight(imageAscillVolume, 1, duration);
TweenWeight(imageVolume, 0, duration);
TweenWeight(finalVolume, 0, duration);
TweenWeight(imageAscillVolume, 1f, duration);
TweenWeight(imageVolume, 0f, duration);
TweenWeight(finalVolume, 0f, duration);
break;
case UFState.imagination:
AudioManager.Instance.PlaySfx("event:/ActionFB/color_imagine");
TweenWeight(imageAscillVolume, 0, duration);
TweenWeight(imageVolume, 1, duration);
TweenWeight(finalVolume, 0, duration);
TweenWeight(imageAscillVolume, 0f, duration);
TweenWeight(imageVolume, 1f, duration);
TweenWeight(finalVolume, 0f, duration);
break;
case UFState.memory:
AudioManager.Instance.PlaySfx("event:/ActionFB/color_imagine");
TweenWeight(imageAscillVolume, 0, duration);
TweenWeight(imageVolume, 0, duration);
TweenWeight(finalVolume, 1, duration);
TweenWeight(imageAscillVolume, 0f, duration);
TweenWeight(imageVolume, 0f, duration);
TweenWeight(finalVolume, 1f, duration);
break;
default:
Debug.LogError($"Unhandled state: {state}");
yield break;
@@ -314,4 +393,4 @@ public class UFSystem : MonoBehaviour
yield return new WaitForSeconds(duration);
}
}
}
+8 -8
View File
@@ -120,7 +120,7 @@ namespace AibisDream.UI
/// 显示物体
/// </summary>
/// <returns>协程</returns>
public IEnumerator ShowObj(string picName)
public IEnumerator ShowObj(string picName, float duration = 1)
{
_showObjPanel.gameObject.SetActive(true);
@@ -151,16 +151,16 @@ namespace AibisDream.UI
// 淡入
_showObjImage.sprite = newSprite;
yield return _showObjImage.FadeInAsync(1);
yield return _showObjImage.FadeInAsync(duration);
}
public IEnumerator HideObj()
public IEnumerator HideObj(float duration = 1)
{
yield return _showObjImage.FadeOutAsync(1);
yield return _showObjImage.FadeOutAsync(duration);
_showObjPanel.SetActive(false);
}
public IEnumerator ShowFullScreen(string picName)
public IEnumerator ShowFullScreen(string picName, float duration = 1)
{
var key = string.Format(ObjSpritePath, NormalizeObjPicName(picName));
var handle = ResourceSystem.LoadAsync<Sprite>(key);
@@ -174,12 +174,12 @@ namespace AibisDream.UI
_fullScreenImage.sprite = handle.Result;
// 淡入
yield return _fullScreenImage.FadeInAsync(1);
yield return _fullScreenImage.FadeInAsync(duration);
}
public IEnumerator HideFullScreen()
public IEnumerator HideFullScreen(float duration = 1)
{
return _fullScreenImage.FadeOutAsync(1);
yield return _fullScreenImage.FadeOutAsync(duration);
}
public IEnumerator OpenProgressWindow(string title, string description, float duration)