Unity 贝塞尔曲线

Unity 贝塞尔曲线的创建

效果

请添加图片描述

贝塞尔曲线原理

常用的贝塞尔曲线,就是二维图形应用程序的数学曲线。 曲线定义:起始点、终止点、控制点。通过调整控制点,贝塞尔曲线的形状会发生变化。

一阶直线

如何将下图中 C C C点在设定时间内,从 A A A点移动到 B B B点?这里设定一个时间 t , t ∈ ( 0 , 1 ) t ,t \in(0,1) t,t(0,1),

设定 t t t时间内 O A ⃗ \vec{OA} OA 等比缩短成 O E ⃗ \vec{OE} OE , O B ⃗ \vec{OB} OB 的单位向量延长到 O F ⃗ \vec{OF} OF
O E ⃗ = O A ⃗ ( 1 − t ) \vec{OE}=\vec{OA}(1-t) OE =OA (1t)
O F ⃗ = O B ⃗ × t \vec{OF}=\vec{OB}\times t OF =OB ×t
O C ⃗ = O A ⃗ ( 1 − t ) + O B ⃗ × t \vec{OC}=\vec{OA}(1-t)+\vec{OB}\times t OC =OA (1t)+OB ×t

这样就可以得到 C C C点在设定时间内,从 A A A点移动到 B B B点的插值。

 Vector3 AB = (1 - t) * OA + t * OB;
二阶曲线

二阶是基于一阶的算法,也就是分别处理 A B ⃗ \vec{AB} AB B C ⃗ \vec{BC} BC

 Vector3 AB = (1 - t) * OA + t * OB;
 Vector3 BC = (1 - t) * OB + t * OC;

再将 A B ⃗ \vec{AB} AB B C ⃗ \vec{BC} BC 也进行一阶的算法处理

 Vector3 ABC = (1 - t) * AB + t * BC;
三阶曲线

三阶和二价类似,不断累加。

   Vector3 AB = (1 - t) * OA + t * OB;
   Vector3 BC = (1 - t) * OB + t * OC;
   Vector3 CD = (1 - t) * OC + t * OD;

   Vector3 ABC = (1 - t) * AB + t * BC;
   Vector3 BCD = (1 - t) * BC + t * CD;

   result = (1 - t) * ABC + t * BCD;

后面四道高阶以此类推。日常常用的就是三阶,即四个点确定一条曲线。

在unity中的应用

目的是通过贝塞尔曲线,得到曲线上的点位,以便建立运动路径数据。

通过界面的编辑扩展工具,建立贝塞尔工具。

#if UNITY_EDITOR
    public class BesselPathCreat : ScriptableWizard
    {
        public string Name = "Path";
        // 点的半径
        public float radius = 1;
        // 曲线取点的密度
        public int densityCurve = 1;
        /// <summary>
        /// 绘制曲线控制点 -- 此脚本的子物体
        /// </summary>
        public List<GameObject> PathPointList = new List<GameObject>();

        DrawGizmosLine drawGizmosLint;
        GameObject game;

        [MenuItem("FrameWorkSong//FrameWork/3.创建贝塞尔路径", false, 3)]
        static void CreateBasselPath()
        {
            ScriptableWizard.DisplayWizard<BesselPathCreat>("CreateBasselPath", "创建", "加点");

        }
        /// <summary>
        /// 创建按钮触发
        /// </summary>
        void OnWizardCreate()
        {
            if (PathPointList.Count > 4)
            {
                var level = ScriptableObject.CreateInstance<BasselPathTemplet>();
                level.BasselPathPoints = drawGizmosLint.CurvePoints;
                AssetDatabase.CreateAsset(level, @"Assets/FrameWorkSong/Data/" + Name + ".asset");//在传入的路径中创建资源
                AssetDatabase.SaveAssets(); //存储资源
                AssetDatabase.Refresh(); //刷新

            }


            DestroyObject();
        }
        /// <summary>
        /// 关闭触发
        /// </summary>
        void DestroyObject()
        {
            if (game)
            {
                DestroyImmediate(game);
            }
            for (int i = 0; i < PathPointList.Count; i++)
            {
                DestroyImmediate(PathPointList[i]);
            }
        }
        void OnWizardUpdate()
        {
            
        }
        /// <summary>
        /// 加点按钮触发
        /// </summary>
        // When the user presses the "Apply" button OnWizardOtherButton is called.
        void OnWizardOtherButton()
        {

            if (PathPointList.Count == 0)
            {
                game = new GameObject();
                drawGizmosLint = game.AddComponent<DrawGizmosLine>();
            }
            BasselPath basselPath = new BasselPath();
            DrawGizmosPointLine drawGizmos = basselPath.MianPiont.AddComponent<DrawGizmosPointLine>();
            PathPointList.Add(basselPath.FrontPiont);
            PathPointList.Add(basselPath.MianPiont);
            PathPointList.Add(basselPath.BackePiont);
            drawGizmos.SelfPoint = basselPath.Piont;
            drawGizmosLint.SelfPoint = PathPointList;
            drawGizmosLint.densityCurve = densityCurve;
            drawGizmosLint.radius = radius;
        }
        void OnDestroy()
        {
            DestroyObject();
        }


    }
#endif

这里用到了,类派生以创建编辑器向导。详情可以查阅官方api https://docs.unity3d.com/cn/current/ScriptReference/ScriptableWizard.html

贝塞尔的创建方法

 public class BasselPath
    {
        List<GameObject> piont = new List<GameObject>();
        GameObject mianPiont;
        GameObject frontPiont;
        GameObject backePiont;
        public BasselPath()
        {
            mianPiont = new GameObject();
            mianPiont.transform.position = Vector3.zero;
            frontPiont = new GameObject();
            frontPiont.transform.position = Vector3.zero + (Vector3.right * 5);

            backePiont = new GameObject();
            backePiont.transform.position = Vector3.zero + (Vector3.left * 5);

            piont.Add(frontPiont);
            piont.Add(mianPiont);
            piont.Add(backePiont);
            backePiont.transform.SetParent(mianPiont.transform);
            frontPiont.transform.SetParent(mianPiont.transform);
            mianPiont.AddComponent<DrawGizmosPoint>();
            frontPiont.AddComponent<DrawGizmosPoint>();
            backePiont.AddComponent<DrawGizmosPoint>();
        }

        public GameObject MianPiont { get => mianPiont; set => mianPiont = value; }
        public GameObject FrontPiont { get => frontPiont; }
        public GameObject BackePiont { get => backePiont; }
        public List<GameObject> Piont { get => piont; }
    }



    /// <summary>
    /// 绘制节点
    /// </summary>
    public class DrawGizmosPoint : MonoBehaviour
    {
        public float radius = 1;
        private void OnDrawGizmos()
        {
            //绘制点
            Gizmos.color = Color.blue;
            Gizmos.DrawSphere(transform.position, radius * 0.5f);
        }
    }
    /// <summary>
    /// 绘制节点线
    /// </summary>
    public class DrawGizmosPointLine : MonoBehaviour
    {
        public float radius = 1;
        public List<GameObject> SelfPoint = new List<GameObject>();
        private void OnDrawGizmos()
        {
            List<Vector3> controlPointPos = SelfPoint.Select(point => point.transform.position).ToList();



            //绘制曲线控制点连线
            Gizmos.color = Color.red;
            for (int i = 0; i < controlPointPos.Count - 1; i += 3)
            {
                Gizmos.DrawLine(controlPointPos[i], controlPointPos[i + 1]);
                Gizmos.DrawLine(controlPointPos[i + 1], controlPointPos[i + 2]);
            }
        }
    }
    /// <summary>
    /// 绘制曲线
    /// </summary>
    public class DrawGizmosLine : MonoBehaviour
    {
        public float radius;
        public int densityCurve;
        public List<GameObject> SelfPoint;
        public List<Vector3> CurvePoints;
        private void OnDrawGizmos()
        {
            List<Vector3> controlPointPos = SelfPoint.Select(point => point.transform.position).ToList();
            if (controlPointPos != null)
            {
                CurvePoints = GetDrawingPoints(controlPointPos, densityCurve);
            }

            //绘制曲线
            if (CurvePoints.Count >= 4)
            {
                Gizmos.color = Color.green;
                //点密度
                foreach (var item in CurvePoints)
                {
                    Gizmos.DrawSphere(item, radius * 0.5f);
                }
                //曲线
                for (int i = 0; i < CurvePoints.Count - 1; i++)
                {
                    Gizmos.DrawLine(CurvePoints[i], CurvePoints[i + 1]);
                }
            }


        }
        /// <summary>
        /// 获取绘制点
        /// </summary>
        /// <param name="controlPoints"></param>
        /// <param name="segmentsPerCurve"></param>
        /// <returns></returns>
        public List<Vector3> GetDrawingPoints(List<Vector3> controlPoints, int segmentsPerCurve)
        {
            List<Vector3> points = new List<Vector3>();
            // 下一段的起始点和上段终点是一个,所以是 i+=3
            for (int i = 1; i < controlPoints.Count - 4; i += 3)
            {

                var p0 = controlPoints[i];
                var p1 = controlPoints[i + 1];
                var p2 = controlPoints[i + 2];
                var p3 = controlPoints[i + 3];
                float dis = Vector3.Distance(p0, p3);
                int count = Mathf.CeilToInt(segmentsPerCurve * dis);
                if (count < segmentsPerCurve)
                {
                    count = segmentsPerCurve;
                }

                for (int j = 0; j <= count; j++)
                {
                    var t = j / (float)count;
                    points.Add(CalculateBezierPoint(t, p0, p1, p2, p3));
                }
            }
            return points;
        }
        // 三阶公式
        Vector3 CalculateBezierPoint(float t, Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3)
        {
            Vector3 result;

            Vector3 p0p1 = (1 - t) * p0 + t * p1;
            Vector3 p1p2 = (1 - t) * p1 + t * p2;
            Vector3 p2p3 = (1 - t) * p2 + t * p3;

            Vector3 p0p1p2 = (1 - t) * p0p1 + t * p1p2;
            Vector3 p1p2p3 = (1 - t) * p1p2 + t * p2p3;

            result = (1 - t) * p0p1p2 + t * p1p2p3;
            return result;
        }

    }


  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小生云木

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值