181 lines
6.6 KiB
C#
181 lines
6.6 KiB
C#
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.centerPoint;
|
||
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);
|
||
}
|
||
}
|
||
}
|
||
} |