标题1111
以下是基于Unity实现的MoveCurve脚本,允许物体沿三次贝塞尔曲线移动,并提供场景中的可视化路径:
using UnityEngine;
[RequireComponent(typeof(Transform))]
public class MoveCurve : MonoBehaviour
{
[Header("Control Points")]
[Tooltip("Control points for the cubic Bezier curve (4 required)")]
public Transform[] controlPoints;
[Header("Movement Settings")]
[Tooltip("Duration of the movement in seconds")]
public float duration = 3f;
[Tooltip("Whether to loop the movement")]
public bool loop = true;
[Tooltip("Rotate object to face movement direction")]
public bool faceMovementDirection = false;
private float elapsedTime;
private Vector3 previousPosition;
void Start()
{
ValidateControlPoints();
previousPosition = transform.position;
}
void Update()
{
if (controlPoints.Length < 4) return;
elapsedTime += Time.deltaTime;
float t = Mathf.Clamp01(elapsedTime / duration);
UpdatePosition(t);
HandleRotation();
if (t >= 1f) HandleCompletion();
}
private void UpdatePosition(float t)
{
Vector3 newPosition = CalculateBezierPoint(t,
controlPoints[0].position,
controlPoints[1].position,
controlPoints[2].position,
controlPoints[3].position);
transform.position = newPosition;
}
private void HandleRotation()
{
if (faceMovementDirection)
{
Vector3 direction = transform.position - previousPosition;
if (direction != Vector3.zero)
{
transform.rotation = Quaternion.LookRotation(direction);
}
previousPosition = transform.position;
}
}
private void HandleCompletion()
{
if (loop)
{
elapsedTime = 0f;
}
else
{
enabled = false;
}
}
private Vector3 CalculateBezierPoint(float t, Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3)
{
float u = 1 - t;
float uu = u * u;
float uuu = uu * u;
float tt = t * t;
float ttt = tt * t;
return uuu * p0 +
3 * uu * t * p1 +
3 * u * tt * p2 +
ttt * p3;
}
private void ValidateControlPoints()
{
if (controlPoints == null || controlPoints.Length < 4)
{
Debug.LogError("MoveCurve requires 4 control points!");
enabled = false;
}
}
void OnDrawGizmos()
{
if (controlPoints == null || controlPoints.Length < 4) return;
Gizmos.color = Color.green;
const int segments = 20;
Vector3 prevPoint = controlPoints[0].position;
for (int i = 1; i <= segments; i++)
{
float t = i / (float)segments;
Vector3 currentPoint = CalculateBezierPoint(t,
controlPoints[0].position,
controlPoints[1].position,
controlPoints[2].position,
controlPoints[3].position);
Gizmos.DrawLine(prevPoint, currentPoint);
prevPoint = currentPoint;
}
}
}
使用说明:
- 将脚本附加到需要移动的游戏对象上
- 在Inspector中配置:
- Control Points:拖入4个控制点的Transform(按顺序:起点、控制点1、控制点2、终点)
- Duration:完成整个路径所需时间(秒)
- Loop:是否循环移动
- Face Movement Direction:是否让物体面向移动方向
- 在Scene视图中可以看到绿色的路径预览线
功能特点:
- 基于三次贝塞尔曲线的平滑移动
- 可调节移动速度和循环模式
- 实时场景路径可视化
- 可选的面朝移动方向功能
- 自动参数验证和错误处理
扩展建议:
-
要创建更复杂的路径,可以:
- 在场景中排列多个贝塞尔曲线段
- 使用空物体作为路径节点
- 添加多个MoveCurve组件并顺序激活
-
要添加匀速移动支持,可以通过:
- 预计算曲线长度
- 建立弧长参数化表
- 调整时间参数t基于实际移动距离
-
要创建自定义编辑器工具:
- 添加路径自动生成功能
- 创建控制点自动布局系统
- 添加速度曲线编辑功能
该实现提供了基础的曲线移动功能,开发者可以根据具体需求进行扩展和定制。
2222
以下是封装后的独立曲线移动系统实现,分为核心逻辑类和MonoBehaviour适配器:
using UnityEngine;
using System;
/// <summary>
/// 三次贝塞尔曲线移动系统(非MonoBehaviour)
/// </summary>
public class CubicBezierMover
{
#region 配置参数
public Vector3[] ControlPoints {
get; private set; }
public float Duration {
get; set; } = 3f;
public bool IsLooping {
get; set; } = true;
public bool FaceMovementDirection {
get; set; } = false;
#endregion
#region 运行时状态
private float elapsedTime;
private bool isMoving;
private Vector3 previousPosition;
private Action<Vector3> positionUpdater;
private Action<Quaternion> rotationUpdater;
#endregion
#region 事件
public event Action OnMovementStarted;
public event Action OnMovementCompleted;
public event Action OnLoopCompleted;
#endregion
/// <summary>
/// 初始化移动器
/// </summary>
/// <param name="controlPoints">四个控制点(起点、控制点1、控制点2、终点)</param>
/// <param name="positionUpdater">位置更新回调</param>
/// <param name="rotationUpdater">旋转更新回调(可选)</param>
public CubicBezierMover(Vector3[] controlPoints,
Action<Vector3> positionUpdater,
Action<Quaternion> rotationUpdater = null)
{
ValidateControlPoints(controlPoints);
ControlPoints = controlPoints;
this.positionUpdater = positionUpdater;
this.rotationUpdater = rotationUpdater;
}
/// <summary>
/// 开始/继续移动
/// </summary>
public void BeginMovement()
{
isMoving = true;
OnMovementStarted?.Invoke();
}
/// <summary>
/// 暂停移动
/// </summary>
public void PauseMovement()
{
isMoving = false;
}
/// <summary>
/// 重置移动状态
/// </summary>
public void ResetMovement()
{
elapsedTime = 0f;
previousPosition = GetPosition(0);
UpdateTransform(0);
}
/// <summary>
/// 更新移动逻辑
/// </summary>
/// <param name="deltaTime">时间增量(秒)</param>
public void UpdateMovement(float deltaTime)
{
if (!isMoving || ControlPoints.Length < 4) return;
elapsedTime += deltaTime;
float t = Mathf.Clamp01(elapsedTime / Duration);
UpdateTransform(t);
if (t >= 1f)
{
HandleCompletion();
}
}
#region 核心逻辑
private void UpdateTransform(float t)
{
Vector3 newPos = CalculateBezierPoint(t);
positionUpdater?.Invoke(newPos);
if (FaceMovementDirection && rotationUpdater != null)
{
UpdateRotation(newPos);
}
}
private void UpdateRotation(Vector3 newPosition)
{
Vector3 direction = newPosition - previousPosition;
if (direction != Vector3.zero)
{
rotationUpdater?.Invoke(Quaternion.LookRotation(direction));
}
previousPosition = newPosition;
}
private void HandleCompletion()
{
OnLoopCompleted?.Invoke();
if (IsLooping)
{
elapsedTime = 0f;
}
else
{
isMoving = false;
OnMovementCompleted?.Invoke();
}
}
public Vector3 GetPosition(float t)
{
return CalculateBezierPoint(Mathf.Clamp01(t));
}
private Vector3 CalculateBezierPoint(float t)
{
float u = 1 - t;
float uu = u * u;
float uuu = uu * u;
float tt = t * t;
float ttt = tt * t;
return
uuu * ControlPoints[0] +
3 * uu * t * ControlPoints[1] +
3 * u * tt * ControlPoints[2] +
ttt * ControlPoints[3];
}
#endregion
#region 校验方法
private void ValidateControlPoints(Vector3[] points)
{
if (points == null || points.Length != 4)
{
throw new ArgumentException("贝塞尔曲线需要4个控制点");
}
}
#endregion
}
/// <summary>
/// MonoBehaviour适配器组件(可选)
/// </summary>
public class BezierMoverComponent : MonoBehaviour
{
[Header("控制点")]
[Tooltip("按顺序:起点、控制点1、控制点2、终点")]
public Transform[] controlTransforms;
[Header("移动设置")]
public float duration = 3f;
public bool loop = true;
public bool faceMovementDirection = false;
private CubicBezierMover mover;
void Start()
{
InitializeMover();
}
void Update()
{
if (mover != null)
{
// 更新控制点实际位置
Vector3[] points = new Vector3[4];
for (int i = 0; i < 4; i++)
{
points[i] = controlTransforms[i].position;
}
mover.ControlPoints = points;
mover.UpdateMovement(Time.deltaTime);
}
}
private void InitializeMover()
{
if (controlTransforms.Length != 4)
{
Debug.LogError("需要4个控制点");
enabled = false;
return;
}
Vector3[] points = new Vector3[4];
for (int i = 0; i < 4; i++)
{
points[i] = controlTransforms[i].position;
}
mover = new CubicBezierMover(
points,
UpdatePosition,
faceMovementDirection ? UpdateRotation : null
);
mover.Duration = duration;
mover.IsLooping = loop;
mover.FaceMovementDirection = faceMovementDirection;
mover.OnMovementStarted += () => Debug.Log("移动开始");
mover.OnMovementCompleted += () => Debug.Log("移动完成");
mover.OnLoopCompleted += () => Debug.Log("循环完成");
mover.BeginMovement();
}
private void UpdatePosition(Vector3 newPosition)
{
transform.position = newPosition;
}
private void UpdateRotation(Quaternion newRotation)
{
transform.rotation = newRotation;
}
// 示例控制方法
public void Pause() => mover.PauseMovement();
public void Resume() => mover.BeginMovement();
public void Restart() => mover.ResetMovement();
}
/// <summary>
/// 路径可视化组件(独立)
/// </summary>
public class BezierPathVisualizer : MonoBehaviour
{
public CubicBezierMover mover;
public int resolution = 20;
public Color pathColor = Color.green;
void OnDrawGizmos()
{
if (mover == null || mover.ControlPoints == null) return;
Gizmos.color = pathColor;
Vector3 prevPoint = mover.ControlPoints[0];
for (int i = 1; i <= resolution; i++)
{
float t = i / (float)resolution;
Vector3 currentPoint = mover.GetPosition(t);
Gizmos.DrawLine(prevPoint, currentPoint);
prevPoint = currentPoint;
}
}
}
结构说明
-
CubicBezierMover(核心逻辑类)
- 完全独立,不继承MonoBehaviour
- 通过构造函数注入:
- 控制点(Vector3数组)
- 位置更新回调
- 可选旋转更新回调
- 提供完整控制接口:
- BeginMovement() 开始/继续移动
- PauseMovement() 暂停
- ResetMovement() 重置
- UpdateMovement() 手动更新
-
BezierMoverComponent(适配器组件)
- 处理与Unity的集成
- 提供Inspector配置界面
- 自动转换Transform控制点为Vector3数组
- 示例事件处理
-
BezierPathVisualizer(独立可视化组件)
- 可附加到任意物体
- 自动绘制贝塞尔曲线路径
使用方式
纯代码调用示例:
// 初始化控制点
Vector3[] points = new Vector3[] {
new Vector3(0,0,0),
new Vector3(1,2,0),
new Vector3(3,2,0),
new Vector3(4,0,0)
};
// 创建移动器
var mover = new CubicBezierMover(
points,
pos => Debug.Log($"新位置:{
pos}"), // 位置更新回调
rot => Debug.Log($"新旋转:{
rot}") // 旋转更新回调
);
// 配置参数
mover.Duration = 5f;
mover.IsLooping = true;
// 订阅事件
mover.OnMovementStarted += () => Debug.Log("移动开始");
mover.OnLoopCompleted += () => Debug.Log("完成一次循环");
// 开始移动
mover.BeginMovement();
// 在游戏循环中更新
void Update()
{
mover.UpdateMovement(Time.deltaTime);
}
组件化使用:
- 将
BezierMoverComponent
附加到游戏对象 - 在Inspector中配置控制点
- 可选附加
BezierPathVisualizer
用于路径可视化
优势特点
-
关注点分离
- 核心算法与Unity解耦
- 可视化与逻辑分离
- 更新机制可自定义
-
灵活调用方式
- 可完全代码驱动
- 可通过组件快速集成
- 支持多实例并行
-
扩展性强
- 方便添加匀速移动算法
- 容易实现路径拼接
- 可结合其他移动系统
-
线程安全设计
- 无Unity API依赖
- 核心类可在非主线程使用(需自行处理位置同步)
-
事件驱动架构
- 提供完整生命周期事件
- 支持多订阅者模式
扩展建议
- 添加匀速移动支持
public class CubicBezierMover
{
// 在类中添加以下成员
private bool useConstantSpeed = false;
private AnimationCurve arcLengthMap;
public void EnableConstantSpeed()
{
// 预计算弧长参数化表
// 创建时间t到实际弧长的映射
// 在UpdateMovement中使用映射表转换时间参数
}
}
- 实现路径拼接
public class CompositePathMover
{
private List<CubicBezierMover> segments;
private int currentSegment;
public void AddSegment(CubicBezierMover segment)
{
segment.OnMovementCompleted += () => {
currentSegment++;
if(currentSegment < segments.Count){
segments[currentSegment].BeginMovement();
}
};
}
}
- 添加编辑器扩展
#if UNITY_EDITOR
[CustomEditor(typeof(BezierMoverComponent))]
public class BezierMoverEditor : Editor
{
void OnSceneGUI()
{
// 添加控制点手柄
// 实现路径实时编辑
}
}
#endif