using System.Collections.Generic; using Lean.Pool; using Sirenix.OdinInspector; using UnityEngine; namespace SLSUtilities.Effects { public class VFXRaycastInteraction : MonoBehaviour, IPoolable { [Header("核心设置")] [Tooltip("在特效生成时是否立刻启用渐变,注意,必须通过对象池生成。\n为true时,渐变效果从targetLight的默认intensity开始,否则需要调用EnableFade方法启用渐变")] public bool playOnSpawn = true; public float life = 1f; public List checkPoints; public int currentCheckPointIndex = 0; public bool isEnabling = true; public Transform startPoint; // 刀尖的位置(用于发射射线) public LayerMask collisionLayers; // 地面图层 public float rayLength = 0.5f; // 射线长度(稍微比刀刃离地距离长一点) [Header("特效资源")] public GameObject sparkPrefab; // 火星特效 Prefab public GameObject decalPrefab; // 划痕 Decal Prefab [HideInEditorMode] [SerializeField] private bool isFading; [HideInEditorMode] [SerializeField] private float time; private Vector3 _lastTipPosition; private void Reset() { startPoint = transform; collisionLayers = LayerMask.GetMask("Wall", "Ground"); } public void OnSpawn() { if (playOnSpawn && !isEnabling) { isEnabling = true; time = 0f; currentCheckPointIndex = 0; } _lastTipPosition = startPoint.position; } public void OnDespawn() { isEnabling = false; } void Update() { time += Time.deltaTime; if (time >= life) { isEnabling = false; } if (!isEnabling || Time.timeScale == 0 || checkPoints.Count == 0) return; // 1. 计算刀尖速度方向(用于决定划痕朝向) Vector3 velocity = (startPoint.position - _lastTipPosition) / Time.deltaTime; _lastTipPosition = startPoint.position; //1. 检查是否到达下一个检查点 if (currentCheckPointIndex < checkPoints.Count) { if (time >= checkPoints[currentCheckPointIndex]) { currentCheckPointIndex++; } else { return; // 未到达检查点,跳过本次更新 } } else { return; // 所有检查点已处理完,跳过本次更新 } // 2. 发射射线检测地面 // 这里我们向下发射射线。如果你的游戏支持墙壁划痕,可以改为 velocity.normalized RaycastHit hit; if (Physics.Raycast(startPoint.position, startPoint.forward, out hit, rayLength, collisionLayers)) { SpawnEffects(hit, velocity); } } void SpawnEffects(RaycastHit hit, Vector3 slashVelocity) { Vector3 projectedSlashDir = Vector3.ProjectOnPlane(slashVelocity, hit.normal).normalized; // --- 处理火星 (Spark) --- if (sparkPrefab != null) { // 关键点:使用 Quaternion.LookRotation(hit.normal) // 这会让火星特效的 Z轴(发射方向)严格对准地面法线(垂直向上),right方向和挥动方向对齐 LeanPool.Spawn(sparkPrefab, hit.point, Quaternion.LookRotation(hit.normal, Vector3.Cross(projectedSlashDir, hit.normal))); } // --- 处理划痕 (Decal) --- if (decalPrefab != null) { // 计算划痕的朝向: // 我们希望划痕贴在地面上(法线对齐 hit.normal) // 同时划痕的延伸方向要对齐刀的挥动方向(slashVelocity) if (projectedSlashDir != Vector3.zero) { // 计算 Decal 的旋转,Decal的forward和地面法线对齐,right方向和挥动方向对齐 Quaternion decalRotation = Quaternion.LookRotation(hit.normal, Vector3.Cross(projectedSlashDir, hit.normal)); // 生成 Decal,稍微抬高一点点避免 Z-Fighting GameObject decal = LeanPool.Spawn(decalPrefab, hit.point + hit.normal * 0.01f, decalRotation); // 记得在 Prefab 里设置自动销毁,或者在这里写 Destroy LeanPool.Despawn(decal, 5f); } } } // 用于在 Scene 窗口调试射线,方便你调整 rayLength void OnDrawGizmos() { if (startPoint != null) { Gizmos.color = Color.yellow; Gizmos.DrawLine(startPoint.position, startPoint.position + transform.forward * rayLength); } } } }