unity3D技术分享

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

2019-07-28  本文已影响4人  WOTTOW

在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]);
                }
            }
          
        }
    }
上一篇 下一篇

猜你喜欢

热点阅读