Files
Cielonos/Assets/Scripts/MainGame/Effects/AfterImage/AfterImageGenerator.cs
SoulliesOfficial ddd387ef35 做不出来
2026-06-30 01:48:58 -04:00

147 lines
5.3 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Collections.Generic;
using UnityEngine;
using Cielonos.MainGame.Characters;
namespace Cielonos.MainGame.Effects
{
public enum AfterImageSpawnMode
{
TimeInterval,
DistanceInterval
}
/// <summary>
/// 残影发生器,挂载在角色身上,用于在高速移动时动态生成姿态残影。
/// </summary>
public class AfterImageGenerator : MonoBehaviour
{
private CharacterBase character;
private List<SkinnedMeshRenderer> skinnedRenderers = new List<SkinnedMeshRenderer>();
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<CharacterBase>();
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;
}
}
}
/// <summary>
/// 手动生成一个当前姿态的残影
/// </summary>
public void CreateAfterImageSnapshot()
{
if (afterImageMaterial == null) return;
// 创建残影根容器
GameObject afterImageObj = new GameObject($"{gameObject.name}_AfterImage");
List<MeshFilter> filters = new List<MeshFilter>();
List<MeshRenderer> renderers = new List<MeshRenderer>();
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<MeshFilter>();
filter.mesh = bakedMesh;
filters.Add(filter);
MeshRenderer renderer = subObj.AddComponent<MeshRenderer>();
renderers.Add(renderer);
}
// 如果没有成功烘焙出任何网格,直接销毁根容器
if (filters.Count == 0)
{
Destroy(afterImageObj);
return;
}
// 初始化渐隐控制
AfterImageItem afterImageItem = afterImageObj.AddComponent<AfterImageItem>();
afterImageItem.Initialize(filters.ToArray(), renderers.ToArray(), afterImageMaterial, afterImageDuration);
}
}
}