This commit is contained in:
2025-05-16 21:01:05 +08:00
parent 7be5180ac1
commit c9218275d7
@@ -8,19 +8,32 @@ public class GearFollower : MonoBehaviour
public SpriteRenderer targetSpriteRenderer; // 目标图片
public float snapDistance = 0.3f; // 距离边缘多少时吸附
public float detachDistance = 1.0f; // 超过多少距离脱离牵引
public float tractionSpeed = 2f; // 牵引时沿边缘移动速度
public float tractionSpeed = 5f; // 牵引时沿边缘移动速度
public float rotationSpeed = 360f; // 齿轮旋转速度(度/秒)
public float smoothTime = 0.1f; // 平滑过渡时间
public float edgeSmoothTime = 0.05f; // 边缘移动平滑时间
public float edgeSmoothTime = 0.02f; // 边缘移动平滑时间
public float edgeScaleFactor = 1.1f; // 边缘点采样放大倍数
public int interpolationPoints = 0; // 每两个采样点之间插入的点数
public float cutTimeThreshold = 0.5f; // 切割时间阈值
public Color normalColor = Color.white; // 正常状态颜色
public Color tractionColor = Color.yellow; // 牵引状态颜色
public Color pathColor = new Color(1f, 1f, 1f, 0.3f); // 路径颜色
public Color cutPathColor = new Color(0f, 1f, 0f, 0.5f); // 已切割路径颜色
public float pathWidth = 0.05f; // 路径宽度
[Header("反馈效果")]
public float speedMultiplier = 1f; // 切割时速度倍率
private List<Vector2> edgePoints; // 边缘路径点
private int currentEdgeIndex = 0; // 当前吸附边缘点索引
private SpriteRenderer gearSpriteRenderer; // 齿轮的SpriteRenderer
private Vector2 currentVelocity; // 用于平滑移动
private float currentRotationVelocity; // 用于平滑旋转
private LineRenderer pathRenderer; // 路径渲染器
private float currentPathProgress; // 当前路径进度
private List<bool> cutStates; // 记录每个点是否被切割
private List<float> cutTimes; // 记录每个点的切割时间
private float currentCutTime; // 当前点的切割时间
private enum State { Follow, Traction }
private State currentState = State.Follow;
@@ -29,6 +42,15 @@ public class GearFollower : MonoBehaviour
private Vector2 targetEdgePosition; // 目标边缘位置
private float currentEdgeProgress; // 当前边缘移动进度
private Vector2 imageCenter; // 图片中心点
private float imageRadius; // 图片半径
private Vector2 lastMousePosition; // 上一帧的鼠标位置
private float currentSpeed; // 当前速度
private float targetSpeed; // 目标速度
private float screenShakeTimer; // 屏幕震动计时器
private ParticleSystem cutParticleSystem; // 切割粒子系统
private AudioSource cutAudioSource; // 切割音效
void Start()
{
@@ -38,12 +60,33 @@ public class GearFollower : MonoBehaviour
{
Debug.LogError("齿轮对象缺少SpriteRenderer组件");
}
// 计算图片中心点和半径
if (targetSpriteRenderer != null)
{
imageCenter = targetSpriteRenderer.bounds.center;
// 使用bounds的size计算半径(取较大的值)
imageRadius = Mathf.Max(targetSpriteRenderer.bounds.size.x, targetSpriteRenderer.bounds.size.y) * 0.5f;
}
// 添加并设置LineRenderer
pathRenderer = gameObject.AddComponent<LineRenderer>();
pathRenderer.startWidth = pathWidth;
pathRenderer.endWidth = pathWidth;
pathRenderer.startColor = pathColor;
pathRenderer.endColor = pathColor;
pathRenderer.material = new Material(Shader.Find("Sprites/Default"));
pathRenderer.sortingOrder = 0;
pathRenderer.enabled = false;
LoadEdgePointsFromSprite();
}
void Update()
{
Vector2 mouseWorldPos = mainCamera.ScreenToWorldPoint(Input.mousePosition);
Vector2 mouseDelta = mouseWorldPos - lastMousePosition;
lastMousePosition = mouseWorldPos;
switch (currentState)
{
@@ -54,11 +97,13 @@ public class GearFollower : MonoBehaviour
UpdateTractionState(mouseWorldPos);
break;
}
// 更新齿轮旋转
UpdateGearRotation();
// 更新视觉反馈
UpdateVisualFeedback();
// 更新路径显示
UpdatePathDisplay();
}
// 更新齿轮旋转
@@ -68,6 +113,23 @@ public class GearFollower : MonoBehaviour
{
// 在牵引状态下按住左键时旋转
transform.Rotate(0, 0, rotationSpeed * Time.deltaTime);
// 更新当前点的切割时间
currentCutTime += Time.deltaTime;
if (currentCutTime >= cutTimeThreshold)
{
if (!cutStates[currentEdgeIndex])
{
// 触发切割效果
//TriggerCutEffects();
}
cutStates[currentEdgeIndex] = true;
cutTimes[currentEdgeIndex] = currentCutTime;
}
}
else
{
currentCutTime = 0f;
}
}
@@ -83,7 +145,7 @@ public class GearFollower : MonoBehaviour
// 从Sprite的PhysicsShape获取边缘点,构成闭环路径
private void LoadEdgePointsFromSprite()
{
edgePoints = new List<Vector2>();
List<Vector2> originalPoints = new List<Vector2>();
if (targetSpriteRenderer == null)
{
Debug.LogError("targetSpriteRenderer 为空");
@@ -108,21 +170,59 @@ public class GearFollower : MonoBehaviour
center /= shapePoints.Count;
// 转换为世界坐标并应用放大
edgePoints = shapePoints
originalPoints = shapePoints
.Select(p => {
// 计算从中心点到当前点的向量
Vector2 dirFromCenter = p - center;
// 应用放大
Vector2 scaledPoint = center + dirFromCenter * edgeScaleFactor;
// 转换为世界坐标
return (Vector2)targetSpriteRenderer.transform.TransformPoint(scaledPoint);
})
.ToList();
// 假设形状是闭合的,确保首尾连通
if (edgePoints[0] != edgePoints[edgePoints.Count - 1])
// 确保首尾连通
if (originalPoints[0] != originalPoints[originalPoints.Count - 1])
{
edgePoints.Add(edgePoints[0]);
originalPoints.Add(originalPoints[0]);
}
// 生成插值点
edgePoints = new List<Vector2>();
for (int i = 0; i < originalPoints.Count - 1; i++)
{
Vector2 start = originalPoints[i];
Vector2 end = originalPoints[i + 1];
// 添加起始点
edgePoints.Add(start);
// 在两点之间插入插值点
for (int j = 1; j <= interpolationPoints; j++)
{
float t = j / (float)(interpolationPoints + 1);
Vector2 interpolatedPoint = Vector2.Lerp(start, end, t);
edgePoints.Add(interpolatedPoint);
}
}
// 添加最后一个点
edgePoints.Add(originalPoints[originalPoints.Count - 1]);
// 初始化切割状态数组
cutStates = new List<bool>(new bool[edgePoints.Count]);
cutTimes = new List<float>(new float[edgePoints.Count]);
// 更新路径渲染器
UpdatePathRenderer();
}
private void UpdatePathRenderer()
{
if (pathRenderer != null)
{
pathRenderer.positionCount = edgePoints.Count;
for (int i = 0; i < edgePoints.Count; i++)
{
pathRenderer.SetPosition(i, new Vector3(edgePoints[i].x, edgePoints[i].y, 0));
}
}
}
@@ -139,11 +239,16 @@ public class GearFollower : MonoBehaviour
{
currentEdgeIndex = nearestIndex;
currentState = State.Traction;
// 切换到牵引状态时,调整渲染顺序
currentPathProgress = 0f;
// 立即吸附到最近的边缘点
transform.position = edgePoints[currentEdgeIndex];
if (gearSpriteRenderer != null)
{
gearSpriteRenderer.sortingOrder = 1;
}
pathRenderer.enabled = true;
}
}
@@ -152,85 +257,100 @@ public class GearFollower : MonoBehaviour
{
Vector2 gearPos = (Vector2)transform.position;
// 计算鼠标齿轮距离,超过detachDistance则脱离牵引
if (Vector2.Distance(mousePos, gearPos) > detachDistance)
// 计算鼠标齿轮距离
float distToGear = Vector2.Distance(mousePos, gearPos);
// 计算鼠标到图片中心的距离
float distToCenter = Vector2.Distance(mousePos, imageCenter);
// 如果鼠标在图片内部(距离中心小于半径),或者距离齿轮太近,则不脱离吸附
if (distToGear > detachDistance && distToCenter > imageRadius)
{
currentState = State.Follow;
// 切换回跟随状态时,调整渲染顺序
if (gearSpriteRenderer != null)
{
gearSpriteRenderer.sortingOrder = 5;
}
pathRenderer.enabled = false;
return;
}
// 判断是否按住左键
if (!Input.GetMouseButton(0))
{
// 未按键,齿轮固定在边缘点
transform.position = Vector2.SmoothDamp(transform.position, edgePoints[currentEdgeIndex], ref currentVelocity, smoothTime);
}
else
{
// 按住左键,齿轮沿边缘点链移动
MoveAlongEdge(mousePos);
// 未按键,齿轮固定在当前位置
return;
}
// 沿路径移动
MoveAlongPath(mousePos);
}
// 沿边缘移动的逻辑
private void MoveAlongEdge(Vector2 mousePos)
// 沿路径移动的逻辑
private void MoveAlongPath(Vector2 mousePos)
{
// 找到当前边缘点前后点索引,注意闭环处理
int prevIndex = (currentEdgeIndex - 1 + edgePoints.Count) % edgePoints.Count;
int nextIndex = (currentEdgeIndex + 1) % edgePoints.Count;
// 计算鼠标到当前齿轮位置的距离
float distToGear = Vector2.Distance(mousePos, transform.position);
Vector2 prevPoint = edgePoints[prevIndex];
Vector2 currPoint = edgePoints[currentEdgeIndex];
Vector2 nextPoint = edgePoints[nextIndex];
// 获取当前段落的起点和终点
Vector2 startPoint = edgePoints[currentEdgeIndex];
Vector2 endPoint = edgePoints[(currentEdgeIndex + 1) % edgePoints.Count];
// 计算向前和向后移动方向
Vector2 dirToPrev = (prevPoint - currPoint).normalized;
Vector2 dirToNext = (nextPoint - currPoint).normalized;
// 计算鼠标到当前段落两端点的距离
float distToStart = Vector2.Distance(mousePos, startPoint);
float distToEnd = Vector2.Distance(mousePos, endPoint);
// 计算鼠标到当前点的向量
Vector2 mouseToCurrent = mousePos - currPoint;
// 计算移动方向
float moveDirection = 0f;
// 计算鼠标在前后方向上的投影
float projToPrev = Vector2.Dot(mouseToCurrent, dirToPrev);
float projToNext = Vector2.Dot(mouseToCurrent, dirToNext);
// 获取鼠标相对于当前段落的向量
Vector2 mouseToStart = mousePos - startPoint;
Vector2 segmentVector = endPoint - startPoint;
// 计算叉积来判断方向
float crossProduct = mouseToStart.x * segmentVector.y - mouseToStart.y * segmentVector.x;
// 根据叉积的符号决定移动方向
moveDirection = crossProduct > 0 ? -1f : 1f;
// 根据投影决定移动方向
Vector2 targetDir;
if (Mathf.Abs(projToPrev) > Mathf.Abs(projToNext))
// 计算移动速度
float moveSpeed = tractionSpeed * (cutStates[currentEdgeIndex] ? speedMultiplier : 1f);
// 使用固定速度更新移动
float maxProgressChange = moveSpeed * Time.deltaTime;
float targetProgress = currentPathProgress + moveDirection * maxProgressChange;
// 处理段落切换
if (targetProgress >= 1f)
{
targetDir = dirToPrev;
currentEdgeIndex = prevIndex;
targetEdgePosition = prevPoint;
currentEdgeIndex = (currentEdgeIndex + 1) % edgePoints.Count;
targetProgress = 0f;
currentCutTime = 0f; // 重置切割时间
}
else
else if (targetProgress <= 0f)
{
targetDir = dirToNext;
currentEdgeIndex = nextIndex;
targetEdgePosition = nextPoint;
// 修改逆时针移动的处理逻辑
currentEdgeIndex = (currentEdgeIndex - 1 + edgePoints.Count) % edgePoints.Count;
targetProgress = 1f;
currentCutTime = 0f; // 重置切割时间
// 确保不会出现负值
if (currentEdgeIndex < 0)
{
currentEdgeIndex = edgePoints.Count - 1;
}
}
// 计算目标位置(在边缘上)
Vector2 currentPos = (Vector2)transform.position;
Vector2 targetPos = Vector2.Lerp(currPoint, targetEdgePosition, currentEdgeProgress);
// 更新进度
currentPathProgress = targetProgress;
// 计算目标位置
startPoint = edgePoints[currentEdgeIndex];
endPoint = edgePoints[(currentEdgeIndex + 1) % edgePoints.Count];
Vector2 targetPos = Vector2.Lerp(startPoint, endPoint, currentPathProgress);
// 使用平滑移动
transform.position = Vector2.SmoothDamp(currentPos, targetPos, ref currentVelocity, edgeSmoothTime);
// 更新边缘移动进度
float moveDistance = tractionSpeed * Time.deltaTime;
float totalDistance = Vector2.Distance(currPoint, targetEdgePosition);
currentEdgeProgress += moveDistance / totalDistance;
// 如果到达目标点,重置进度
if (currentEdgeProgress >= 1f)
{
currentEdgeProgress = 0f;
}
transform.position = Vector2.SmoothDamp(transform.position, targetPos, ref currentVelocity, edgeSmoothTime);
}
// 找到距离pos最近的边缘点索引和距离
@@ -250,4 +370,20 @@ public class GearFollower : MonoBehaviour
}
return nearestIndex;
}
// 更新路径显示
private void UpdatePathDisplay()
{
if (pathRenderer != null && pathRenderer.enabled)
{
for (int i = 0; i < edgePoints.Count; i++)
{
Color segmentColor = cutStates[i] ? cutPathColor : pathColor;
pathRenderer.startColor = segmentColor;
pathRenderer.endColor = segmentColor;
}
}
}
}