美文网首页unity3D技术分享
unity 初步了解Bezier之创建Bezier路线(一)

unity 初步了解Bezier之创建Bezier路线(一)

作者: WOTTOW | 来源:发表于2019-07-28 21:04 被阅读4次

    在unity中曲线的运用是比较广泛的,如:人物沿着指定的移动轨迹,怪物的指定移动路线,汽车的在指定的轨道运动等。
    绘制Bezier路线:需要了解一下什么是贝塞尔,同时需要unity 的Gizmos与Handles,我这里就不介绍了。

    整体思路:
    1. 使用贝塞尔的三阶方程,目的:绘制路线的主要步骤。
    2. 在Inspector面板创建一些空物体来表示路径点
    3. 在Bezier在不考虑Loop时,需要的点是:4,7,10,13........
    4.代码创建辅助点,用来调节路径的弧度。
    5.计算段数,第一段是特殊的需要四个点,后面的都是以三个点为一段
    6.计算每一段的距离
    7.控制点的移动,动态更新点之间的距离
    8.增加点
    9.删除点

    Bezier_A.gif
    Bezier_B.gif

    脚本主要分为2个
    1.主要是绘制辅助点,以及拖动辅助点、绘制点之间的线段
    2.绘制贝塞尔的主要逻辑,及绘制贝塞尔线

    主要逻辑
     public  List<Transform> AnchorPoint;                                //添加锚点
    
        public List<Vector3> AllPoints = new List<Vector3>();               //所有点
        public List<float> segmentDistance = new List<float>();             //计算每段的长度
    
        public float precision = 15f;                                       //精度
    
        //variable
        private float dis;                                                  //计算距离的辅助变量
        private Vector3 currentpos;                                         //当前的坐标
        private Vector3[] Pos; 
        public int anchorPointNum;
        public int CurrentanchorPointNum;
    
        //Help
        public bool Limit = false;
    
        public int numSegments
        {
            get
            {
                return (AllPoints.Count - 4) / 3 + 1;
            }
        }
    
        public int numAllPoints
        {
            get {
                return AllPoints.Count;
            }
        }
    
        /// <summary>
        /// 创建最初的路径
        /// </summary>
        public void CreatePath()
        {
            if (AllPoints.Count < 1)
            {
                InitPathPoint(AnchorPoint[0].position);
                  InitComputeDistance();
                    anchorPointNum = AnchorPoint.Count;
                CurrentanchorPointNum = AnchorPoint.Count;
    
                if (AnchorPoint.Count > 2)
                {
                    for (int i = 2; i < AnchorPoint.Count; i++)
                    {
                        AddPointsToList(AnchorPoint[i].position);
                        ComputeDistance(i);
                    }
                }
            }
        }
    
        /// <summary>
        /// 删除点
        /// </summary>
        public void DelectPoint()
        {
            if (AnchorPoint.Count>1&&CurrentanchorPointNum - AnchorPoint.Count > 0)
            {
                CurrentanchorPointNum = AnchorPoint.Count;
                Limit = true;
                segmentDistance.RemoveAt(segmentDistance.Count - 1);
                for (int i = 0; i < 3; i++)
                {
                    AllPoints.RemoveAt(AllPoints.Count - 1);
                    anchorPointNum = AnchorPoint.Count;
                }
                Limit = false;
            }
        }
    
        /// <summary>
        /// 移动点
        /// </summary>
        /// pos 是新的坐标
        public void MovePoint(int index, Vector3 Pos)
        {
            Vector3 deltaMove = Pos - AllPoints[index];             //新坐标与旧坐标的插值
            AllPoints[index] = Pos;                                 //把旧坐标覆盖掉 
    
            if (index % 3 == 0)                                     //判断是否是中心点
            {
                if (index + 1 < AllPoints.Count)                    //中心点的两坐标的辅助点
                {
                    AllPoints[index + 1] += deltaMove;
                }
                if (index - 1 > 0)
                {
                    AllPoints[index - 1] += deltaMove;
                }
                //Debug.Log("中心点");
            }
            else
            {
                bool nextPointIsAnchor = (index + 1) % 3 == 0;                                              //区分其中的一个点
                int correspondingControlIndex = (nextPointIsAnchor) ? index + 2 : index - 2;                //对应点的index
                int anchorIndex = (nextPointIsAnchor) ? index + 1 : index - 1;                              //对应的中心点
    
                if (correspondingControlIndex > 0 && correspondingControlIndex < AllPoints.Count)
                {
                    float dst = (AllPoints[anchorIndex] - AllPoints[correspondingControlIndex]).magnitude;    //计算点与中心点的距离      
                    Vector3 dir = (AllPoints[anchorIndex] - Pos).normalized;                                  //计算向量的方向
                    AllPoints[correspondingControlIndex] = AllPoints[anchorIndex] + dir * dst;                //增加的新坐标    
                }
                //Debug.Log("辅助点");
            }
    
            UpdateDistance(index);
        }
    
        /// <summary>
        /// 获取在路径上的坐标点
        /// </summary>
        public Vector3[] GetPointInPath(int index)
        {
            Vector3[] A = { AllPoints[index * 3], AllPoints[index * 3 + 1], AllPoints[index * 3 + 2], AllPoints[index * 3 + 3] };
            return A;
        }
    
        /// <summary>
        /// 添加新点
        /// </summary>
        public void AddPathPoint()
        {
            if (AnchorPoint.Count > 2
                && anchorPointNum - 1 - numSegments > 0
                && AnchorPoint[AnchorPoint.Count - 1].position != AnchorPoint[AnchorPoint.Count - 2].position
                && !Limit)
            {
                AddPointsToList(AnchorPoint[AnchorPoint.Count - 1].position);
                ComputeDistance(AnchorPoint.Count - 1);
                CurrentanchorPointNum = AnchorPoint.Count;
            }
            else
            {
                anchorPointNum = AnchorPoint.Count;
            }
        }
    
        /// <summary>
        /// 添加点到List中
        /// </summary>
        public void AddPointsToList(Vector3 anchorPos)
        {
            AllPoints.Add(AllPoints[AllPoints.Count - 1] * 2 - AllPoints[AllPoints.Count - 2]);
            AllPoints.Add((AllPoints[AllPoints.Count - 1] + anchorPos) * .5f);
            AllPoints.Add(anchorPos);
        }
    
        /// <summary>
        ///更新距离 
        /// </summary>
        public void UpdateDistance(int index) 
        {
            //判断是第几段的
            int j = 0;
            if (index < 4)
            {
                j = 0;
            }
            else
            {
                j = Mathf.CeilToInt((index - 4) / 3f);
            }
    
                 dis = 0;
                for (int i = 0; i < 4; i++)
                {
                    dis += Vector3.Distance(AllPoints[index], AllPoints[(int)Mathf.Repeat(i + 1, AllPoints.Count - 1)]);
                }
                segmentDistance[j]=dis;
        }
    
        /// <summary>
        ///计算距离 
        /// </summary>
        public void ComputeDistance(int index)
        {
            dis = 0;
            for (int i = 0; i < 4; i++)
            {
                dis += Vector3.Distance(AllPoints[index], AllPoints[(int)Mathf.Repeat(i + 1, AllPoints.Count - 1)]);
            }
            segmentDistance.Add(dis);
        }
    
        // 初始化Path
        /// <summary>
        /// 计算初始点的距离3
        /// </summary>
        public void InitComputeDistance()
        {
            for (int i = 0; i < AllPoints.Count; i++)
            {
                dis += Vector3.Distance(AllPoints[0], AllPoints[(int)Mathf.Repeat(i + 1, AllPoints.Count - 1)]);
            }
            segmentDistance.Add(dis);
        }
    
        /// <summary>
        /// 初始化路劲点1
        /// </summary>
        public void InitPathPoint(Vector3 center)
        {
            AllPoints = new List<Vector3>
            {
               AnchorPoint[0].position,
               AnchorPoint[0].position +  (Vector3.right + Vector3.down) * precision,
               AnchorPoint[1].position +(Vector3.left + Vector3.left)* precision,
               AnchorPoint[1].position
            };
        }
    
        /// <summary>
        /// 贝塞尔方程
        /// </summary>
        public Vector3 EvaluateCurve(Vector3 a1, Vector3 c1, Vector3 c2, Vector3 a2, float t)
        {
            t = Mathf.Clamp01(t);
            return (1 - t) * (1 - t) * (1 - t) * a1 + 3 * (1 - t) * (1 - t) * t * c1 + 3 * (1 - t) * t * t * c2 + t * t * t * a2;
        }
    
        /// <summary>
        /// 绘制线段
        /// </summary>
        private void OnDrawGizmos()
        {
            Gizmos.color = Color.green;
            if (AllPoints.Count > 0 && !Limit)
            {
                currentpos = AllPoints[0];
                for (int j = 0; j < numSegments; j++)
                {
                    for (int i = 0; i < segmentDistance[j]; i++)
                    {
                        float t = i / (float)segmentDistance[j];
                        Vector3 NextPoint = EvaluateCurve(AllPoints[j * 3], AllPoints[j * 3 + 1], AllPoints[j * 3 + 2], AllPoints[j * 3 + 3], t);
                        Gizmos.DrawLine(currentpos, NextPoint);
                        currentpos = NextPoint;
                    }
                }
            }
        }
    
    
    此脚本需要继承Editor,并关联上面的脚本
     bool Limit = false;
        Bezier bezier;
        private void OnEnable()
        {
            bezier = (Bezier)target;
            bezier.CreatePath();
        }
    
        private void OnSceneGUI()
        {
            Handles.color = Color.red;                                      //设置Handles的颜色
            if (bezier.AllPoints.Count > 0)                                 //如果辅助点 
            {
                bezier.AddPathPoint();
                bezier.DelectPoint();
                for (int i = 0; i < bezier.numAllPoints; i++)            //绘制所有点 
                {
                    Vector3 newPos = Handles.FreeMoveHandle(bezier.AllPoints[i], Quaternion.identity, 1, Vector3.zero, Handles.SphereHandleCap);
                    if (bezier.AllPoints[i] != newPos)
                    {
                        bezier.MovePoint(i, newPos);
                    }
                }
    
                Handles.color = Color.black;
                if (bezier.numSegments > 0)
                {
                    for (int i = 0; i < bezier.numSegments; i++)
                    {
                        Vector3[] points = bezier.GetPointInPath(i);
                        Handles.DrawLine(points[0], points[1]);
                        Handles.DrawLine(points[2], points[3]);
                    }
                }
              
            }
        }
    

    相关文章

      网友评论

        本文标题:unity 初步了解Bezier之创建Bezier路线(一)

        本文链接:https://www.haomeiwen.com/subject/bevprctx.html