This commit is contained in:
2025-05-19 21:27:58 +08:00
parent 34aa1b7bf4
commit cfe8f8e5c1
14 changed files with 4761 additions and 1300 deletions
@@ -3,6 +3,7 @@ using Shapes;
using UnityEngine;
using UnityEngine.Rendering.Universal;
using UnityEngine.VFX;
using DG.Tweening;
public class GearFollower : MonoBehaviour
{
@@ -29,6 +30,31 @@ public class GearFollower : MonoBehaviour
public float maxEmissionRate = 50f; // 最大发射速率
public float emissionChangeSpeed = 100f; // 发射速率变化速度
[Header("切割完成效果")]
public float cutCompletionThreshold = 0.95f; // 切割完成阈值(切割路径占边缘的比例)
public float minCutLength = 0.5f; // 最小切割长度(相对于总长度的比例)
public float targetShakeAmount = 0.02f; // 目标抖动幅度
public float cutCompleteTiltAngle = 15f; // 切割完成后的倾斜角度
public float cutCompleteOffset = 0.2f; // 切割完成后的位移
public float cutCompleteScale = 1.2f; // 切割完成后的放大倍数
public float fadeOutDuration = 1f; // 淡出持续时间
public float cutCompleteDelay = 1f; // 切割完成后的延迟时间
public float scaleUpDuration = 1f; // 放大动画持续时间
public float fadeOutDelay = 1f; // 淡出前的延迟时间
public AudioClip cutCompleteSound; // 切割完成音效
private float totalEdgeLength = 0f; // 总边缘长度
private float cutLength = 0f; // 已切割长度
private bool isCutComplete = false;
private bool isFadingOut = false;
private float fadeOutTimer = 0f;
private Vector3 originalTargetPosition;
private Quaternion originalTargetRotation;
private Vector3 originalTargetScale;
private AudioSource audioSource;
private Sequence cutCompleteSequence; // DOTween序列
private HashSet<int> cutEdgeIndices = new HashSet<int>(); // 记录已切割的边缘点索引
private float currentEmissionRate = 0f;
private Vector3 originalLocalPosition;
private List<Vector2> edgePoints;
@@ -62,6 +88,7 @@ public class GearFollower : MonoBehaviour
{
mainCamera = Camera.main;
LoadEdgePointsFromSprite();
CalculateTotalEdgeLength();
if (cutLineRenderer != null)
{
cutLineRenderer.positionCount = 0;
@@ -75,6 +102,19 @@ public class GearFollower : MonoBehaviour
cutLight.intensity = 0f;
cutLight.enabled = false;
}
// 初始化目标相关变量
if (targetSpriteRenderer != null)
{
originalTargetPosition = targetSpriteRenderer.transform.position;
originalTargetRotation = targetSpriteRenderer.transform.rotation;
originalTargetScale = targetSpriteRenderer.transform.localScale;
}
// 添加音频源组件
audioSource = gameObject.AddComponent<AudioSource>();
audioSource.playOnAwake = false;
audioSource.spatialBlend = 0f; // 2D音效
}
void Update()
{
@@ -105,6 +145,13 @@ public class GearFollower : MonoBehaviour
shakeOffset = (Vector3)baseShake;
transform.position = targetPosition + shakeOffset;
// 目标抖动
if (targetSpriteRenderer != null && !isCutComplete)
{
Vector2 targetShake = Random.insideUnitCircle * targetShakeAmount;
targetSpriteRenderer.transform.position = originalTargetPosition + (Vector3)targetShake;
}
// 根据震动幅度控制灯光强度
if (cutLight != null)
{
@@ -112,10 +159,6 @@ public class GearFollower : MonoBehaviour
float intensity = shakeOffset.magnitude / shakeAmount; // 得到 [0,1] 比例
cutLight.intensity = intensity * lightMaxIntensity;
}
// if (sparkVFX != null)
// {
// sparkVFX.SendEvent("OnPlay"); // 这里的 "OnBurst" 是你在VFX Graph里定义的事件名,需要一致
// }
}
else
{
@@ -131,10 +174,39 @@ public class GearFollower : MonoBehaviour
cutLight.intensity = 0f;
cutLight.enabled = false;
}
// if (sparkVFX != null)
// {
// sparkVFX.SendEvent("OnStop"); // 这里的 "OnBurst" 是你在VFX Graph里定义的事件名,需要一致
// }
}
// 处理淡出效果
if (isFadingOut)
{
fadeOutTimer += Time.deltaTime;
float alpha = 1f - (fadeOutTimer / fadeOutDuration);
if (targetSpriteRenderer != null)
{
Color color = targetSpriteRenderer.color;
color.a = alpha;
targetSpriteRenderer.color = color;
}
// 齿轮也淡出
SpriteRenderer gearRenderer = GetComponent<SpriteRenderer>();
if (gearRenderer != null)
{
Color gearColor = gearRenderer.color;
gearColor.a = alpha;
gearRenderer.color = gearColor;
}
if (fadeOutTimer >= fadeOutDuration)
{
// 淡出完成后销毁物体
Destroy(gameObject);
if (targetSpriteRenderer != null)
{
Destroy(targetSpriteRenderer.gameObject);
}
}
}
}
@@ -273,6 +345,7 @@ public class GearFollower : MonoBehaviour
if (distance <= maxStep)
{
targetPosition = targetPoint;
UpdateCutProgress(from, targetPoint); // 更新切割进度
if (Vector2.Distance(targetPoint, to) < 0.01f)
{
@@ -428,4 +501,136 @@ public class GearFollower : MonoBehaviour
var segmentController = segObj.AddComponent<HeatLineSegmentController>();
segmentController.duration = segmentDuration;
}
private void CalculateTotalEdgeLength()
{
totalEdgeLength = 0f;
for (int i = 0; i < edgePoints.Count - 1; i++)
{
totalEdgeLength += Vector2.Distance(edgePoints[i], edgePoints[i + 1]);
}
}
private void UpdateCutProgress(Vector2 from, Vector2 to)
{
cutLength += Vector2.Distance(from, to);
float progress = cutLength / totalEdgeLength;
// 记录已切割的边缘点
cutEdgeIndices.Add(currentEdgeIndex);
// 检查是否满足切割完成条件
if (!isCutComplete && progress >= cutCompletionThreshold && progress >= minCutLength)
{
// 检查是否形成了连续的切割路径
bool hasContinuousCut = CheckContinuousCut();
if (hasContinuousCut)
{
isCutComplete = true;
OnCutComplete();
}
}
}
private bool CheckContinuousCut()
{
if (cutEdgeIndices.Count < 2) return false;
// 将索引转换为有序列表
List<int> sortedIndices = new List<int>(cutEdgeIndices);
sortedIndices.Sort();
// 检查是否有连续的切割点
int continuousCount = 1;
int maxContinuousCount = 1;
for (int i = 1; i < sortedIndices.Count; i++)
{
if (sortedIndices[i] == sortedIndices[i - 1] + 1)
{
continuousCount++;
maxContinuousCount = Mathf.Max(maxContinuousCount, continuousCount);
}
else
{
continuousCount = 1;
}
}
// 如果连续切割点的数量超过总点数的50%,认为形成了有效的切割路径
return maxContinuousCount >= edgePoints.Count * 0.5f;
}
private void OnCutComplete()
{
// 停止切割状态
isCutting = false;
isInTractionMode = false;
enabled = false;
// 清理特效
if (cutLight != null)
{
cutLight.enabled = false;
}
if (sparkVFX != null)
{
sparkVFX.Stop();
}
if (cutLineRenderer != null)
{
cutLineRenderer.positionCount = 0;
}
timedCutPoints.Clear();
// 目标倾斜和位移
if (targetSpriteRenderer != null)
{
// 计算随机方向
float randomAngle = Random.Range(-cutCompleteTiltAngle, cutCompleteTiltAngle);
Vector2 randomOffset = Random.insideUnitCircle * cutCompleteOffset;
// 立即应用倾斜和位移
targetSpriteRenderer.transform.rotation = Quaternion.Euler(0, 0, randomAngle);
targetSpriteRenderer.transform.position = originalTargetPosition + (Vector3)randomOffset;
// 创建动画序列
cutCompleteSequence = DOTween.Sequence();
// 等待一段时间后开始放大
cutCompleteSequence.AppendInterval(cutCompleteDelay);
cutCompleteSequence.Append(targetSpriteRenderer.transform.DOScale(originalTargetScale * cutCompleteScale, scaleUpDuration)
.SetEase(Ease.OutQuad));
// 等待一段时间后开始淡出
cutCompleteSequence.AppendInterval(fadeOutDelay);
cutCompleteSequence.Append(targetSpriteRenderer.DOFade(0f, fadeOutDuration));
// 齿轮也同时淡出
SpriteRenderer gearRenderer = GetComponent<SpriteRenderer>();
if (gearRenderer != null)
{
cutCompleteSequence.Join(gearRenderer.DOFade(0f, fadeOutDuration));
}
// 动画完成后销毁物体
cutCompleteSequence.OnComplete(() => {
Destroy(gameObject);
if (targetSpriteRenderer != null)
{
Destroy(targetSpriteRenderer.gameObject);
}
});
}
}
private void OnDestroy()
{
// 清理DOTween序列
if (cutCompleteSequence != null)
{
cutCompleteSequence.Kill();
}
}
}