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; } }