faiculty 2020-01-31
在家里闲着没事,在网上看到一个好玩的需求,在亮点之间画一条直线。
听起来很简单,unity就提供了很多的API,不过大部分是以屏幕画一条线类似的方式做的。如果我们需要让部队等 一个集群排列成一条斜线呢?
其实这也和屏幕渲染一条直线的道理是一样的。
屏幕要画一条直线的话,其实也是在屏幕的像素坐标系里面x,y进行赋值,把连续数转换成离散数,放大看的时候,就会看到一条直线其实是一条梯子状的线。
在实际的场景中可以把我们的地图当成一个屏幕,地图最小格子单位当成一个屏幕的像素点,这样就相当于构建了一套屏幕坐标系了
在网上找了很多的资料,但是都说的特别复杂,我找到一篇说的比较简练清楚的。
cnblogs.com/feiquan/archive/2018/03/04/8506283.html
(此图来源于网络) 
图中格子即为最小像素单位,设一条直线从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°的效果

>45°的情况

接下来上代码
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;
}
}