![703645c69444532f8082bd73d74869fa.png](https://i-blog.csdnimg.cn/blog_migrate/5c8739ae6f66e04ee70930c9c0a202be.jpeg)
前言
雷达图是游戏中常见的图表表现之一,比如用来呈现英雄的属性等等,本文将在 Unity 中实现一个带均匀描边、可响应点击的雷达图。
Graphic类
Unity 的原生 UI 系统 UGUI 提供了名为 Graphic 的基类用于实现各自 UI组件的。该类中有名为 OnPopulateMesh 的方法,只要向参数 VertexHelper 传递正确的顶点数组与下标数组,即可构造出我们期望的网格。
网格的构建
雷达图可以视为一个正多边形各个顶点沿着径向缩放得到,因此我们只要遍历一遍各个角度并且根据当前角度的权重计算就能得到对应的顶点,最后构造下标数组,即可得到所需网格。构造顶点代码如下
![b2a32f4ce25e634abd04e6ee8b3730b2.png](https://i-blog.csdnimg.cn/blog_migrate/e0c5f73fdca46afc59458c68123e5d8e.png)
这样对于边数为 N 的雷达图,我们会得到包括中心点在内的 N + 1 个顶点,然后是构造下标数组,代码如下。
![0f6da2f18e143c28ad5805eb2b6f58ed.png](https://i-blog.csdnimg.cn/blog_migrate/12a8c8f8bd3b7f4ce6d9c08854e80222.png)
只是简单地把相邻的顶点两两与中心点组合成三角形,注意这里最后复用了第一个顶点。
响应点击
Unity 的 UI 组件,响应点击需要实现接口 ICanvasRaycastFilter 的 IsRaycastLocationValid 方法。如果命中多边形,也就是点击坐标落在多边形内,则返回 true ,否则返回 false。首先需要将方法参数里的屏幕坐标转换到ui的局部坐标。
![6b940c6f0cf5db89277710990e7d3850.png](https://i-blog.csdnimg.cn/blog_migrate/e963839347b64a1b8ada147c763b704b.png)
然后检测坐标是否落在多边形内,对于点击的坐标,我们检测从它出发发射一条指向多边形外的某个顶点的射线,对于多边形的每一条边,检测是否相交,最后统计相交的边的数目,如果为奇数,则代表该点落在多边形内,否则落在多边形外。更多详情可以参考下面的问题。
一个线条自交叉的封闭图形,怎样判断一个点位于图形内部还是外部?www.zhihu.com![74d6193224c2da2322052568570f9a56.png](https://i-blog.csdnimg.cn/blog_migrate/945d527b8d9cb4a9e84fb2d113020fb6.png)
代码如下
![7ef12dd851152734af3056b1ef86d379.png](https://i-blog.csdnimg.cn/blog_migrate/38678c5f33ab3f00f59f95d2edff1c2d.png)
![b884f950b5fd4cd1b1c3fa24bddb7f2e.png](https://i-blog.csdnimg.cn/blog_migrate/9f4eaae54b1b954f5ffe111f2e026dc5.jpeg)
做完这些就实现了一个可响应点击的雷达图了。
![dce6be129316de829f307a37e6233c3f.gif](https://i-blog.csdnimg.cn/blog_migrate/41879e940c8802f5638b1282c3831712.gif)
描边
一开始觉得这就不算个问题,搞两个权重一样但是半径不一样的雷达图不就能实现了嘛。试了试发现,问题没有这么简单。
![a5ae1bbc74a6e199f4022f3ee2e05617.png](https://i-blog.csdnimg.cn/blog_migrate/210ab5e307f1944d1f7ad8f651f5fb81.png)
因为权重不均匀,所以会导致描边也不均匀,为了实现均匀的描边,还需要额外的工作。
解决办法是额外进行边的绘制。对于多边形的每一个顶点 C ,我们按逆时针方向找出它的上一个 A 与下一个顶点 B ,并且得到它相邻的两条边的向外的垂线,把 A 与 B 沿着垂线向外延伸得到 E 与 F,把 E 与 F 沿着边的方向延伸,相交于点 P,对每个顶点重复上述步骤,就能得到外框的所有点了。
![4c276afd6549ed718744a06462e7c889.png](https://i-blog.csdnimg.cn/blog_migrate/8502944669370a2df7bff4dc18c53537.png)
顶点构造代码
![990eceb2303eb84e1153b244b0924205.png](https://i-blog.csdnimg.cn/blog_migrate/872070a11c0658d0f2954f4031dd1b3c.jpeg)
下标数组构造
![13bee31097f4cef5945a9f182569efdc.png](https://i-blog.csdnimg.cn/blog_migrate/4b9edd65bc92764dd5b63f50d13b9575.png)
获得交点
![38bd20b8e5617bc862338e5e3d3bebeb.png](https://i-blog.csdnimg.cn/blog_migrate/0101a153a6cf842ea854508f1fa28c23.png)
获得垂线
![aa9348855ea616339b7949593b705950.png](https://i-blog.csdnimg.cn/blog_migrate/c936cbb22ea273217b8b5b29b434a95c.png)
最后看效果
![4c0509a596fa65bf6c2c35db368a91e7.png](https://i-blog.csdnimg.cn/blog_migrate/2e9b7766cb81a6292378d65e70abe9a9.jpeg)
总结
网上 Unity 实现 雷达图的代码能找到不少,但是我发现基本都止步于网格的构造,并没有将点击响应的计算以及均匀描边的实现,有的更过分,甚至连 uv 都不肯算,没办法只能自己撸一个,把一些中学几何的东西又捡起来过了一遍,总的来说,这个过程还是有所收获的。