Files
Cielonos/Assets/Scripts/MainGame/Characters/Player/View/OcclusionFadeSubmodule.cs
SoulliesOfficial d15957c719 更新
2025-12-17 04:19:38 -05:00

181 lines
6.6 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Collections.Generic;
using UnityEngine;
namespace Cielonos.MainGame.Characters
{
public partial class OcclusionFadeSubmodule
{
// --- 内部类:用于管理正在褪色的物体状态 ---
private class FadingObject
{
public Renderer renderer;
public MaterialPropertyBlock propBlock;
public float currentAlpha;
public float lastHitTime;
public FadingObject(Renderer r)
{
renderer = r;
propBlock = new MaterialPropertyBlock();
// 初始获取当前可能的Fade值默认为1
currentAlpha = 1.0f;
lastHitTime = Time.time;
}
}
}
public partial class OcclusionFadeSubmodule : SubmoduleBase<PlayerViewSubcontroller>
{
[Header("References")] [Tooltip("主角头部的Transform")]
public Transform playerHead;
[Tooltip("主角中心点的Transform")] public Transform playerCenter;
[Tooltip("主角脚部的Transform")] public Transform playerFeet;
[Tooltip("主摄像机如果不填则自动获取MainCamera")] public Camera targetCamera;
[Header("Settings")] [Tooltip("遮挡层级:只检测这些层级的物体(例如 Wall, Default")]
public LayerMask obstructionLayer;
[Header("Fading Settings")] [Tooltip("物体变透明的速度 (建议快一点,比如 10-15)")]
public float fadeOutSpeed = 5f;
[Tooltip("物体恢复实体的速度 (建议慢一点,比如 2-5)")] public float fadeInSpeed = 2f;
[Tooltip("被遮挡时的目标透明度")] [Range(0f, 1f)]
public float targetAlpha = 0.5f;
[Header("Stability")]
[Tooltip("防抖动缓冲时间:当物体不再遮挡视线后,延迟多少秒才开始恢复实体?")]
public float recoveryDelay = 0.1f;
[Tooltip("射线球体半径使用SphereCast代替Raycast可以让检测更宽容防止细小缝隙穿帮")]
public float checkRadius = 0.1f;
// 字典Renderer -> 状态对象
private Dictionary<Renderer, FadingObject> _fadingObjects = new Dictionary<Renderer, FadingObject>();
// 缓存 ID
private static readonly int FadeID = Shader.PropertyToID("_Fade");
// 射线检测缓存数组 (NonAlloc优化避免每帧GC)
private RaycastHit[] _hitsBuffer = new RaycastHit[20];
private List<LineRenderer> _debugLines = new List<LineRenderer>();
public OcclusionFadeSubmodule(PlayerViewSubcontroller owner) : base(owner)
{
obstructionLayer = LayerMask.GetMask("FadableEnvironment");
targetCamera = owner.playerCamera;
playerHead = owner.player.bodyPartsSc.head;
playerCenter = owner.player.bodyPartsSc.flexibleCenterPoint;
playerFeet = owner.player.bodyPartsSc.footPoint;
}
public void Update()
{
if (playerHead == null || playerCenter == null || playerFeet == null || targetCamera == null) return;
// 2. 执行射线检测
Vector3 cameraPos = targetCamera.transform.position;
// 检测头部路径
CastRayAndMark(cameraPos, playerHead.position);
// 检测中心路径
CastRayAndMark(cameraPos, playerCenter.position);
// 检测脚部路径
CastRayAndMark(cameraPos, playerFeet.position);
// 3. 处理淡入淡出逻辑
ProcessFading();
}
private void CastRayAndMark(Vector3 start, Vector3 end)
{
Vector3 direction = end - start;
float distance = direction.magnitude;
int hitCount = Physics.SphereCastNonAlloc(
start,
checkRadius,
direction.normalized,
_hitsBuffer,
distance,
obstructionLayer,
QueryTriggerInteraction.Ignore
);
for (int i = 0; i < hitCount; i++)
{
RaycastHit hit = _hitsBuffer[i];
Renderer r = hit.collider.GetComponent<Renderer>();
if (r == null || hit.transform.root == playerHead.root) continue;
// 如果是新物体,加入字典
if (!_fadingObjects.ContainsKey(r))
{
if (r.sharedMaterial != null && r.sharedMaterial.HasProperty(FadeID))
{
_fadingObjects.Add(r, new FadingObject(r));
}
}
// 更新击中时间
if (_fadingObjects.TryGetValue(r, out FadingObject obj))
{
obj.lastHitTime = Time.time; // 刷新时间戳
}
}
}
private void ProcessFading()
{
List<Renderer> keysToRemove = new List<Renderer>();
float currentTime = Time.time;
foreach (var kvp in _fadingObjects)
{
FadingObject obj = kvp.Value;
Renderer r = obj.renderer;
if (r == null)
{
keysToRemove.Add(kvp.Key);
continue;
}
// --- 核心逻辑修改 ---
// 判断是否处于"遮挡状态"或"缓冲期"
// 只要当前时间 < 最后击中时间 + 延迟时间,就认为它还应该保持透明
bool isObstructedOrBuffered = currentTime < (obj.lastHitTime + recoveryDelay);
float target = isObstructedOrBuffered ? targetAlpha : 1.0f;
// 根据目标选择速度:变透明用快速度,恢复用慢速度
float speed = isObstructedOrBuffered ? fadeOutSpeed : fadeInSpeed;
// 执行插值
obj.currentAlpha = Mathf.MoveTowards(obj.currentAlpha, target, Time.deltaTime * speed);
// 应用属性
r.GetPropertyBlock(obj.propBlock);
obj.propBlock.SetFloat(FadeID, obj.currentAlpha);
r.SetPropertyBlock(obj.propBlock);
// 清理逻辑:只有当完全恢复,且早已过了缓冲期才移除
if (!isObstructedOrBuffered && Mathf.Approximately(obj.currentAlpha, 1.0f))
{
keysToRemove.Add(r);
}
}
foreach (var key in keysToRemove)
{
_fadingObjects.Remove(key);
}
}
}
}