Files
Cielonos/Assets/Scripts/MainGame/Characters/Player/View/OcclusionFadeSubmodule.cs

181 lines
6.6 KiB
C#
Raw Normal View History

2025-11-25 08:19:33 -05:00
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;
2025-12-17 04:19:38 -05:00
playerCenter = owner.player.bodyPartsSc.flexibleCenterPoint;
2025-11-25 08:19:33 -05:00
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);
}
}
}
}