Unity实现任意两点之间画一条直线——bresenham算法(直线的处理)

faiculty 2020-01-31

    在家里闲着没事,在网上看到一个好玩的需求,在亮点之间画一条直线。

 听起来很简单,unity就提供了很多的API,不过大部分是以屏幕画一条线类似的方式做的。如果我们需要让部队等 一个集群排列成一条斜线呢?

 其实这也和屏幕渲染一条直线的道理是一样的。

 屏幕要画一条直线的话,其实也是在屏幕的像素坐标系里面x,y进行赋值,把连续数转换成离散数,放大看的时候,就会看到一条直线其实是一条梯子状的线。

 在实际的场景中可以把我们的地图当成一个屏幕,地图最小格子单位当成一个屏幕的像素点,这样就相当于构建了一套屏幕坐标系了

 在网上找了很多的资料,但是都说的特别复杂,我找到一篇说的比较简练清楚的。

 cnblogs.com/feiquan/archive/2018/03/04/8506283.html   

 

(此图来源于网络)   Unity实现任意两点之间画一条直线——bresenham算法(直线的处理)

     图中格子即为最小像素单位,设一条直线从p1到达p2 , p1的坐标为(0,0),作一个以p2,p1为顶点的直角三角形,可得tan ∠p1 = (p2.y - p1.y) / (p2.x - p1.x) 

         可算得  float value =   tan  ∠p1 

 通过∠p1的斜率可得出直线上某一个点的y值为是多少

 因为格子已经是最小单位了,而y的高度可能为小数,所以需要做离散化处理

 定义一个值当做校准指标,这里我们按0.5来算,每个点在经过像素单位的时候,将y小于0.5的按0来计算,并存储,抹除掉的这个值我们统计起来。  

   float slope = 0

   每计算一次点的坐标 slope = y + slope 

 当slope > 0.5的时候,y的坐标向上移一格,并清零。进行下一次的统计

 直到遍历完成

 注意,这里需要考虑2种情况,这条直线在某个象限里面,角度小于45°的时候是以是y轴作为偏差值处理,也就是 0< tan ∠p1 <= 1的情况,在大于45°的情况,以此类推,就要把x当做偏差值处理了,不明白的小伙伴可以想象成,如果这个角度大于45°的话,把坐标系反转一下,就是一个竖着的小于45°的角了然后直接照搬上述方式代入计算。

        代码只处理了0-90度情况,同理可以推导出 2/3/4象限的排列坐标,根据象限将x或者y取绝对值即可。

 效果如图    

<45°的效果

Unity实现任意两点之间画一条直线——bresenham算法(直线的处理)

>45°的情况

Unity实现任意两点之间画一条直线——bresenham算法(直线的处理)

 接下来上代码

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Bresenham : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        List<Vector2> mList =   GetPointList(new Vector2(0,0),new Vector2(10,128));
        for (int i = 0; i < mList.Count; i++)
        {
            GameObject cube =  GameObject.CreatePrimitive(PrimitiveType.Cube);
            Vector2 pos =  mList[i];
            cube.transform.position = new Vector3(pos.x,pos.y,0);
        }
    }
    public List<Vector2> GetPointList(Vector2 startPos,Vector2 endPos)
    {

        //构建直角三角形
        //distance_y为对边
        //distance_x为直角边
        float distance_x = endPos.x - startPos.x ; 
        float distance_y = endPos.y - startPos.y ;

        float slope_x =  distance_y / distance_x ; //tan 求斜率
        float slope_y =  distance_x/distance_y ;//当slope_x > 1  

        Vector2 pos = startPos;
        float y = startPos.y;
        float x = startPos.x ;
        int num = 0 ;
        float sumValue = 0; //累计值
        List<Vector2> posList =  new List<Vector2>();
        posList.Add(startPos);        //45度内
        if (slope_x > 0 && slope_x <= 1)
        {
            for (int i = 0; i < distance_x; i++)
            {   
            
               
                y = posList[num].y;
                x = x + 1 ;
                sumValue = sumValue + slope_x;
                if (sumValue > 0.5f)
                {
                    sumValue = 0;
                    y += 1;
                }
                num += 1;
                posList.Add(new Vector2(x,y));
            }
        }else if(slope_x > 1 )
            {
                for (int i = 0; i < distance_y; i++)
                {
                    y = y + 1;
                    x = posList[num].x ;
                    sumValue = sumValue + slope_y;
                    if (sumValue > 0.5f)
                    {
                        sumValue = 0;
                        x += 1;
                    }
                    num += 1;
                    posList.Add(new Vector2(x,y));
                }
                
            }
        
        return posList;
    }
}