You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Unity Inspector中显示游戏对象的贝塞尔加速度曲线图表?

在Unity Inspector中显示自定义贝塞尔曲线预览的实现方法

核心思路

通过Unity的**自定义编辑器(Custom Editor)**扩展功能,在Inspector中绘制自定义的速度-时间曲线预览,实时响应参数修改。具体分为两步:编写存储曲线参数的运行时脚本,再编写编辑器脚本实现曲线绘制。

步骤1:编写运行时参数脚本

创建一个IceMovement脚本,存储贝塞尔曲线的控制点参数,并实现速度计算逻辑:

using UnityEngine;

public class IceMovement : MonoBehaviour
{
    // 第二控制点:(到达最大速度的时间, 最大速度)
    public Vector2 maxSpeedPoint = new Vector2(2f, 5f);
    // 第三控制点:用于调整曲线形状
    public Vector2 controlPoint = new Vector2(1f, 1f);

    // 根据时间t计算当前速度
    public float GetSpeedAtTime(float t)
    {
        t = Mathf.Clamp01(t / maxSpeedPoint.x);
        // 三点贝塞尔公式:P0(0,0), P1(controlPoint), P2(maxSpeedPoint)
        float oneMinusT = 1f - t;
        return 2 * oneMinusT * t * controlPoint.y + t * t * maxSpeedPoint.y;
    }
}

步骤2:编写自定义编辑器脚本

在项目中创建Editor文件夹(必须命名为Editor,Unity才会识别为编辑器脚本),创建IceMovementEditor脚本,实现Inspector中的曲线绘制:

using UnityEditor;
using UnityEngine;

[CustomEditor(typeof(IceMovement))]
public class IceMovementEditor : Editor
{
    private IceMovement _movement;
    private Texture2D _curveTexture;

    private void OnEnable()
    {
        _movement = (IceMovement)target;
        _curveTexture = new Texture2D(400, 200);
        UpdateCurvePreview();
    }

    public override void OnInspectorGUI()
    {
        // 先绘制默认的参数面板
        DrawDefaultInspector();

        // 添加曲线预览区域
        EditorGUILayout.Space();
        EditorGUILayout.LabelField("速度-时间曲线预览", EditorStyles.boldLabel);
        Rect previewRect = EditorGUILayout.GetControlRect(false, 200);
        EditorGUI.DrawPreviewTexture(previewRect, _curveTexture);

        // 参数修改时实时更新曲线
        if (GUI.changed)
        {
            UpdateCurvePreview();
        }
    }

    private void UpdateCurvePreview()
    {
        // 清空纹理为白色背景
        Color[] pixels = _curveTexture.GetPixels();
        System.Array.Fill(pixels, Color.white);
        _curveTexture.SetPixels(pixels);

        // 绘制坐标轴
        DrawLine(new Vector2(20, _curveTexture.height - 20), new Vector2(_curveTexture.width - 20, _curveTexture.height - 20), Color.black);
        DrawLine(new Vector2(20, _curveTexture.height - 20), new Vector2(20, 20), Color.black);

        float maxTime = _movement.maxSpeedPoint.x;
        float maxSpeed = _movement.maxSpeedPoint.y;

        // 避免参数为0的异常情况
        if (maxTime <= 0 || maxSpeed <= 0)
        {
            _curveTexture.Apply();
            return;
        }

        // 采样绘制贝塞尔曲线
        int sampleCount = 100;
        Vector2 prevPoint = Vector2.zero;
        bool isFirstPoint = true;

        for (int i = 0; i <= sampleCount; i++)
        {
            float t = (float)i / sampleCount * maxTime;
            float speed = _movement.GetSpeedAtTime(t);

            // 将数值转换为纹理坐标
            float x = 20 + (t / maxTime) * (_curveTexture.width - 40);
            float y = _curveTexture.height - 20 - (speed / maxSpeed) * (_curveTexture.height - 40);
            Vector2 currentPoint = new Vector2(x, y);

            if (!isFirstPoint)
            {
                DrawLine(prevPoint, currentPoint, Color.blue);
            }
            prevPoint = currentPoint;
            isFirstPoint = false;
        }

        // 绘制控制点:红色为第三控制点,绿色为最大速度点
        Vector2 controlTexPos = new Vector2(20 + (_movement.controlPoint.x / maxTime) * (_curveTexture.width - 40),
            _curveTexture.height - 20 - (_movement.controlPoint.y / maxSpeed) * (_curveTexture.height - 40));
        DrawCircle(controlTexPos, 3, Color.red);

        Vector2 maxSpeedTexPos = new Vector2(20 + (_movement.maxSpeedPoint.x / maxTime) * (_curveTexture.width - 40),
            _curveTexture.height - 20 - (_movement.maxSpeedPoint.y / maxSpeed) * (_curveTexture.height - 40));
        DrawCircle(maxSpeedTexPos, 3, Color.green);

        _curveTexture.Apply();
    }

    // 辅助方法:绘制直线
    private void DrawLine(Vector2 start, Vector2 end, Color color)
    {
        float dx = end.x - start.x;
        float dy = end.y - start.y;
        float length = Mathf.Sqrt(dx * dx + dy * dy);
        float step = 1f / length;

        for (float t = 0; t <= 1f; t += step)
        {
            int x = Mathf.RoundToInt(start.x + dx * t);
            int y = Mathf.RoundToInt(start.y + dy * t);
            if (x >= 0 && x < _curveTexture.width && y >= 0 && y < _curveTexture.height)
            {
                _curveTexture.SetPixel(x, y, color);
            }
        }
    }

    // 辅助方法:绘制圆点
    private void DrawCircle(Vector2 center, float radius, Color color)
    {
        for (float angle = 0; angle < 360; angle += 5)
        {
            float rad = angle * Mathf.Deg2Rad;
            int x = Mathf.RoundToInt(center.x + radius * Mathf.Cos(rad));
            int y = Mathf.RoundToInt(center.y + radius * Mathf.Sin(rad));
            if (x >= 0 && x < _curveTexture.width && y >= 0 && y < _curveTexture.height)
            {
                _curveTexture.SetPixel(x, y, color);
            }
        }
    }
}

使用说明

  1. 将IceMovement脚本挂载到需要控制的游戏对象上。
  2. 在Inspector面板中,修改maxSpeedPoint和controlPoint的数值时,下方的曲线预览会实时更新:
    • 绿色点代表最大速度到达点(时间-速度)
    • 红色点代表第三控制点,用于调整曲线的弯曲程度
    • 蓝色曲线就是实时计算的速度-时间变化曲线

额外优化建议

  • 可以给参数添加[Tooltip]属性,方便理解每个参数的作用
  • 增加坐标轴刻度标注,让曲线预览更直观
  • 限制参数的最小值(比如用[Min(0.1f)]属性),避免出现除零异常

内容的提问来源于stack exchange,提问作者ambu360

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 16:14:55