切割接入

This commit is contained in:
2025-05-20 19:28:58 +08:00
parent cfe8f8e5c1
commit 9fb81dcdd2
16 changed files with 2221 additions and 2955 deletions
@@ -4,12 +4,14 @@ using UnityEngine;
using UnityEngine.Rendering.Universal;
using UnityEngine.VFX;
using DG.Tweening;
using AibisDream.FixSystem;
using AibisDream; // 添加OpenSystem的命名空间
public class GearFollower : MonoBehaviour
{
[Header("参数")]
public SpriteRenderer targetSpriteRenderer;
public float snapDistance = 0.3f;
public float snapDistance = 1.0f;
public float detachDistance = 1.0f;
public float tractionSpeed = 2f;
public float edgePointsScale = 1.2f; // 边缘点缩放倍率
@@ -42,7 +44,7 @@ public class GearFollower : MonoBehaviour
public float scaleUpDuration = 1f; // 放大动画持续时间
public float fadeOutDelay = 1f; // 淡出前的延迟时间
public AudioClip cutCompleteSound; // 切割完成音效
private float totalEdgeLength = 0f; // 总边缘长度
private float cutLength = 0f; // 已切割长度
private bool isCutComplete = false;
@@ -86,6 +88,16 @@ public class GearFollower : MonoBehaviour
void Start()
{
}
public void Init(SpriteRenderer targetSpriteRenderer)
{
SpriteRenderer gearRenderer = GetComponent<SpriteRenderer>();
if (gearRenderer != null)
{
gearRenderer.color = new Color(1, 1, 1, 1);
}
this.targetSpriteRenderer = null;
this.targetSpriteRenderer = targetSpriteRenderer;
mainCamera = Camera.main;
LoadEdgePointsFromSprite();
CalculateTotalEdgeLength();
@@ -118,7 +130,24 @@ public class GearFollower : MonoBehaviour
}
void Update()
{
Vector2 mouseWorldPos = mainCamera.ScreenToWorldPoint(Input.mousePosition);
}
public void GearUpdate()
{
// 获取鼠标在屏幕上的位置
Vector3 mouseScreenPos = Input.mousePosition;
// 将屏幕坐标转换为世界坐标
Vector3 mouseWorldPos = mainCamera.ScreenToWorldPoint(new Vector3(mouseScreenPos.x, mouseScreenPos.y, -mainCamera.transform.position.z));
mouseWorldPos.z = 0; // 确保z坐标为0
Debug.Log($"鼠标屏幕坐标: {mouseScreenPos}");
Debug.Log($"鼠标世界坐标: {mouseWorldPos}");
Debug.Log($"齿轮当前位置: {transform.position}");
if (targetSpriteRenderer != null)
{
Debug.Log($"目标精灵位置: {targetSpriteRenderer.transform.position}");
Debug.Log($"目标精灵边界: {targetSpriteRenderer.bounds}");
}
if (Input.GetMouseButton(0))
{
@@ -181,7 +210,7 @@ public class GearFollower : MonoBehaviour
{
fadeOutTimer += Time.deltaTime;
float alpha = 1f - (fadeOutTimer / fadeOutDuration);
if (targetSpriteRenderer != null)
{
Color color = targetSpriteRenderer.color;
@@ -220,6 +249,7 @@ public class GearFollower : MonoBehaviour
Debug.LogError("targetSpriteRenderer 为空");
return;
}
List<Vector2> shapePoints = new List<Vector2>();
targetSpriteRenderer.sprite.GetPhysicsShape(0, shapePoints);
@@ -228,33 +258,70 @@ public class GearFollower : MonoBehaviour
Debug.LogError("目标Sprite没有物理形状点");
return;
}
Debug.Log($"原始形状点数量: {shapePoints.Count}");
float desiredSegmentLength = 0.1f;
edgePoints.Clear();
originalEdgePoints.Clear();
Vector2 center = Vector2.zero;
foreach (Vector2 point in shapePoints)
{
center += point;
}
center /= shapePoints.Count;
// 获取精灵的世界坐标
Vector3 spriteWorldPos = targetSpriteRenderer.transform.position;
Debug.Log($"精灵世界坐标: {spriteWorldPos}");
// 计算精灵的局部边界
Bounds localBounds = targetSpriteRenderer.bounds;
Vector3 localCenter = localBounds.center;
Vector3 localSize = localBounds.size;
// 计算缩放后的边界
float maxScale = Mathf.Max(localSize.x, localSize.y) * edgePointsScale;
float minScale = Mathf.Min(localSize.x, localSize.y) * edgePointsScale;
Debug.Log($"精灵局部边界: 中心={localCenter}, 大小={localSize}");
Debug.Log($"缩放后边界: 最大={maxScale}, 最小={minScale}");
for (int i = 0; i < shapePoints.Count; i++)
{
Vector2 p1 = targetSpriteRenderer.transform.TransformPoint(shapePoints[i]);
Vector2 p2 = targetSpriteRenderer.transform.TransformPoint(shapePoints[(i + 1) % shapePoints.Count]);
// 将局部坐标转换为世界坐标
Vector3 localPoint1 = shapePoints[i];
Vector3 localPoint2 = shapePoints[(i + 1) % shapePoints.Count];
Vector2 scaledP1 = center + (p1 - center) * edgePointsScale;
Vector2 scaledP2 = center + (p2 - center) * edgePointsScale;
// 转换为世界坐标
Vector3 worldPoint1 = targetSpriteRenderer.transform.TransformPoint(localPoint1);
Vector3 worldPoint2 = targetSpriteRenderer.transform.TransformPoint(localPoint2);
float segmentLength = Vector2.Distance(scaledP1, scaledP2);
// 确保坐标在合理范围内
worldPoint1 = ClampPosition(worldPoint1, spriteWorldPos, maxScale);
worldPoint2 = ClampPosition(worldPoint2, spriteWorldPos, maxScale);
Debug.Log($"点 {i} 局部坐标: {localPoint1} -> 世界坐标: {worldPoint1}");
// 计算相对于精灵中心点的缩放
Vector3 scaledPoint1 = spriteWorldPos + (worldPoint1 - spriteWorldPos) * edgePointsScale;
Vector3 scaledPoint2 = spriteWorldPos + (worldPoint2 - spriteWorldPos) * edgePointsScale;
// 确保缩放后的点也在合理范围内
scaledPoint1 = ClampPosition(scaledPoint1, spriteWorldPos, maxScale);
scaledPoint2 = ClampPosition(scaledPoint2, spriteWorldPos, maxScale);
Debug.Log($"点 {i} 缩放后世界坐标: {scaledPoint1}");
float segmentLength = Vector3.Distance(scaledPoint1, scaledPoint2);
int steps = Mathf.Max(1, Mathf.CeilToInt(segmentLength / desiredSegmentLength));
for (int s = 0; s < steps; s++)
{
float t = (float)s / steps;
Vector2 interpolated = Vector2.Lerp(scaledP1, scaledP2, t);
Vector2 originalInterpolated = Vector2.Lerp(p1, p2, t);
edgePoints.Add(interpolated);
originalEdgePoints.Add(originalInterpolated);
Vector3 interpolated = Vector3.Lerp(scaledPoint1, scaledPoint2, t);
Vector3 originalInterpolated = Vector3.Lerp(worldPoint1, worldPoint2, t);
// 确保插值点也在合理范围内
interpolated = ClampPosition(interpolated, spriteWorldPos, maxScale);
originalInterpolated = ClampPosition(originalInterpolated, spriteWorldPos, maxScale);
// 存储为Vector2,但保持世界坐标
edgePoints.Add(new Vector2(interpolated.x, interpolated.y));
originalEdgePoints.Add(new Vector2(originalInterpolated.x, originalInterpolated.y));
}
}
@@ -263,22 +330,48 @@ public class GearFollower : MonoBehaviour
edgePoints.Add(edgePoints[0]);
originalEdgePoints.Add(originalEdgePoints[0]);
}
Debug.Log($"生成的边缘点数量: {edgePoints.Count}");
Debug.Log($"第一个边缘点世界坐标: {edgePoints[0]}");
Debug.Log($"最后一个边缘点世界坐标: {edgePoints[edgePoints.Count - 1]}");
}
// 添加一个辅助方法来限制坐标范围
private Vector3 ClampPosition(Vector3 position, Vector3 center, float maxDistance)
{
Vector3 offset = position - center;
float distance = offset.magnitude;
if (distance > maxDistance)
{
offset = offset.normalized * maxDistance;
position = center + offset;
}
return position;
}
private void UpdateFollowState(Vector2 mousePos)
{
transform.position = mousePos;
isInTractionMode = false;
int nearestIndex = FindNearestEdgePointIndex(mousePos, out float dist);
Debug.Log($"最近边缘点索引: {nearestIndex}, 距离: {dist}");
Debug.Log($"最近边缘点位置: {edgePoints[nearestIndex]}");
Debug.Log($"当前snapDistance: {snapDistance}");
if (dist < snapDistance)
{
currentEdgeIndex = nearestIndex;
Vector2 prev = edgePoints[WrapIndex(currentEdgeIndex - 1)];
Vector2 next = edgePoints[WrapIndex(currentEdgeIndex + 1)];
Debug.Log($"前一个点: {prev}, 后一个点: {next}");
float distToPrev = Vector2.Distance(mousePos, prev);
float distToNext = Vector2.Distance(mousePos, next);
Debug.Log($"到前一个点距离: {distToPrev}, 到后一个点距离: {distToNext}");
tractionDirection = (distToNext < distToPrev) ? 1 : -1;
@@ -454,12 +547,14 @@ public class GearFollower : MonoBehaviour
sparkVFX.Stop();
}
}
private int FindNearestEdgePointIndex(Vector2 pos, out float minDist)
{
int nearestIndex = 0;
minDist = float.MaxValue;
// 使用世界坐标进行距离计算
for (int i = 0; i < edgePoints.Count; i++)
{
float dist = Vector2.Distance(pos, edgePoints[i]);
@@ -509,22 +604,29 @@ public class GearFollower : MonoBehaviour
{
totalEdgeLength += Vector2.Distance(edgePoints[i], edgePoints[i + 1]);
}
Debug.Log($"计算总边缘长度: {totalEdgeLength}");
}
private void UpdateCutProgress(Vector2 from, Vector2 to)
{
cutLength += Vector2.Distance(from, to);
float progress = cutLength / totalEdgeLength;
// 计算当前段的切割长度
float segmentLength = Vector2.Distance(from, to);
cutLength += segmentLength;
// 记录已切割的边缘点
cutEdgeIndices.Add(currentEdgeIndex);
// 计算切割进度
float progress = cutLength / totalEdgeLength;
Debug.Log($"当前切割长度: {cutLength}, 总长度: {totalEdgeLength}, 进度: {progress * 100}%");
// 检查是否满足切割完成条件
if (!isCutComplete && progress >= cutCompletionThreshold && progress >= minCutLength)
if (!isCutComplete && progress >= cutCompletionThreshold)
{
// 检查是否形成了连续的切割路径
bool hasContinuousCut = CheckContinuousCut();
Debug.Log($"连续切割检查: {hasContinuousCut}, 已切割点数: {cutEdgeIndices.Count}, 总点数: {edgePoints.Count}");
if (hasContinuousCut)
{
isCutComplete = true;
@@ -544,32 +646,64 @@ public class GearFollower : MonoBehaviour
// 检查是否有连续的切割点
int continuousCount = 1;
int maxContinuousCount = 1;
int totalPoints = edgePoints.Count;
for (int i = 1; i < sortedIndices.Count; i++)
// 检查所有可能的连续段
for (int startIndex = 0; startIndex < sortedIndices.Count; startIndex++)
{
if (sortedIndices[i] == sortedIndices[i - 1] + 1)
continuousCount = 1;
int currentIndex = sortedIndices[startIndex];
// 向前检查
for (int i = 1; i < sortedIndices.Count; i++)
{
continuousCount++;
maxContinuousCount = Mathf.Max(maxContinuousCount, continuousCount);
int nextIndex = (currentIndex + i) % totalPoints;
if (sortedIndices.Contains(nextIndex))
{
continuousCount++;
}
else
{
break;
}
}
else
// 向后检查
for (int i = 1; i < sortedIndices.Count; i++)
{
continuousCount = 1;
int prevIndex = (currentIndex - i + totalPoints) % totalPoints;
if (sortedIndices.Contains(prevIndex))
{
continuousCount++;
}
else
{
break;
}
}
maxContinuousCount = Mathf.Max(maxContinuousCount, continuousCount);
}
// 如果连续切割点的数量超过总点数的50%,认为形成了有效的切割路径
return maxContinuousCount >= edgePoints.Count * 0.5f;
// 计算连续切割点的比例
float continuousRatio = (float)maxContinuousCount / totalPoints;
Debug.Log($"最大连续切割点数: {maxContinuousCount}, 总点数: {totalPoints}, 比例: {continuousRatio * 100}%");
// 如果连续切割点的数量超过总点数的70%,认为形成了有效的切割路径
return continuousRatio >= 0.7f;
}
private void OnCutComplete()
{
// 停止切割状态
isCutting = false;
isInTractionMode = false;
enabled = false;
// 清理特效
var cuttingManager = FixSystemCenter.SystemDic.Get<CuttingManager>();
if (cuttingManager != null)
{
cuttingManager.OnCutComplete();
}
}
public void FinishCutting()
{
// 清理特效
if (cutLight != null)
{
cutLight.enabled = false;
@@ -582,47 +716,42 @@ public class GearFollower : MonoBehaviour
{
cutLineRenderer.positionCount = 0;
}
timedCutPoints.Clear();
// 计算随机方向
float randomAngle = Random.Range(-cutCompleteTiltAngle, cutCompleteTiltAngle);
Vector2 randomOffset = Random.insideUnitCircle * cutCompleteOffset;
// 目标倾斜和位移
if (targetSpriteRenderer != null)
// 立即应用倾斜和位移
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)
{
// 计算随机方向
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(() =>
{
cutCompleteSequence.Join(gearRenderer.DOFade(0f, fadeOutDuration));
}
// 动画完成后销毁物体
cutCompleteSequence.OnComplete(() => {
Destroy(gameObject);
if (targetSpriteRenderer != null)
Destroy(this.gameObject);
var cuttingManager = FixSystemCenter.SystemDic.Get<CuttingManager>();
if (cuttingManager != null)
{
Destroy(targetSpriteRenderer.gameObject);
cuttingManager.UpdateModule();
}
});
}
}
private void OnDestroy()