提交
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user