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);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|