using System.Collections.Generic; using UnityEngine; using Cielonos.MainGame.Characters; namespace Cielonos.MainGame.Effects { public enum AfterImageSpawnMode { TimeInterval, DistanceInterval } /// /// 残影发生器,挂载在角色身上,用于在高速移动时动态生成姿态残影。 /// public class AfterImageGenerator : MonoBehaviour { private CharacterBase character; private List skinnedRenderers = new List(); private bool isSpawning = false; private float timer; private Vector3 lastSpawnPos; [Header("AfterImage Settings")] [Tooltip("残影所使用的菲涅尔发光材质")] public Material afterImageMaterial; [Tooltip("单个残影的持续消失时间(秒)")] public float afterImageDuration = 0.3f; [Tooltip("残影生成模式:时间间隔 或 距离间隔")] public AfterImageSpawnMode spawnMode = AfterImageSpawnMode.TimeInterval; [Tooltip("生成残影的时间间隔(秒,TimeInterval 模式)")] public float spawnInterval = 0.03f; [Tooltip("生成残影的距离间隔(米,DistanceInterval 模式)")] public float spawnDistance = 0.8f; private void Start() { character = GetComponent(); GetComponentsInChildren(true, skinnedRenderers); } public void StartSpawning() { isSpawning = true; timer = 0f; // 时间模式下启动时立即生成一个 lastSpawnPos = transform.position; // 距离模式下重置起始点 if (spawnMode == AfterImageSpawnMode.DistanceInterval) { CreateAfterImageSnapshot(); // 距离模式启动时也立即生成第一个 } } public void StopSpawning() { isSpawning = false; } #region Dynamic Configuration APIs public void SetSpawnMode(AfterImageSpawnMode mode) => spawnMode = mode; public void SetSpawnInterval(float interval) => spawnInterval = interval; public void SetSpawnDistance(float distance) => spawnDistance = distance; public void SetAfterImageDuration(float duration) => afterImageDuration = duration; public void SetMaterial(Material mat) => afterImageMaterial = mat; #endregion private void Update() { if (!isSpawning || character == null) return; if (spawnMode == AfterImageSpawnMode.TimeInterval) { timer -= character.selfTimeSm.DeltaTime; if (timer <= 0f) { CreateAfterImageSnapshot(); timer = spawnInterval; } } else if (spawnMode == AfterImageSpawnMode.DistanceInterval) { Vector3 currentPos = transform.position; float dist = Vector3.Distance(lastSpawnPos, currentPos); if (dist >= spawnDistance) { CreateAfterImageSnapshot(); lastSpawnPos = currentPos; } } } /// /// 手动生成一个当前姿态的残影 /// public void CreateAfterImageSnapshot() { if (afterImageMaterial == null) return; // 创建残影根容器 GameObject afterImageObj = new GameObject($"{gameObject.name}_AfterImage"); List filters = new List(); List renderers = new List(); foreach (var skinnedRenderer in skinnedRenderers) { // 仅烘焙当前处于显示状态的蒙皮部件 if (skinnedRenderer == null || !skinnedRenderer.gameObject.activeInHierarchy || !skinnedRenderer.enabled) continue; // 创建对应蒙皮部件的静态 Mesh 副本 GameObject subObj = new GameObject(skinnedRenderer.name); subObj.transform.SetParent(afterImageObj.transform); subObj.transform.position = skinnedRenderer.transform.position; subObj.transform.rotation = skinnedRenderer.transform.rotation; subObj.transform.localScale = skinnedRenderer.transform.lossyScale; // 烘焙蒙皮网格的当前姿态 Mesh bakedMesh = new Mesh(); skinnedRenderer.BakeMesh(bakedMesh); MeshFilter filter = subObj.AddComponent(); filter.mesh = bakedMesh; filters.Add(filter); MeshRenderer renderer = subObj.AddComponent(); renderers.Add(renderer); } // 如果没有成功烘焙出任何网格,直接销毁根容器 if (filters.Count == 0) { Destroy(afterImageObj); return; } // 初始化渐隐控制 AfterImageItem afterImageItem = afterImageObj.AddComponent(); afterImageItem.Initialize(filters.ToArray(), renderers.ToArray(), afterImageMaterial, afterImageDuration); } } }