1-8 获取鼠标点击位置
在之前的章节中,我们完成了一个正六边形地图系统的基本框架。但是目前这个地图系统是无法与玩家产生任何交互的。在一般策略游戏中,地图系统最基本的交互方式,就是响应玩家的鼠标点击事件。即玩家鼠标左键单击一个地图单元,这个地图单元便会改变颜色,或者播放一段预置的动画,来响应玩家的操作。我们可以通过鼠标向场景中发射一条射线的方式,来检测鼠标是否点击在了某个地图单元上。
目前,我们先把交互代码放在HexGrid.cs
脚本里,随着项目在之后的章节中不断完善,将会把与玩家交互的代码移动到其他的脚本中。代码如下:
1 | private void Update() |
现在,我们完成了鼠标左键单击后发射一条射线的功能,这条射线如果穿过了一个带有Collider组件的模型,那么将会返回一个Vector3的位置信息。但是,现在的地图单元是没有Collider组件的,我们在HexMesh.cs脚本中为它添加MeshCollider组件。代码如下:
1 | //为了检测射线碰撞Collider |
为Hex Mesh这个物体添加了MeshCollider
组件后,我们需要将创建的Mesh数据赋值给MeshCollider
组件,这样它就可以根据Mesh信息成成碰撞网格了。代码如下:
1 | public void Triangulate(HexCell[] cells) |
这样,射线穿过Hex Mesh这个物体时,通过MeshCollider
组件就可以返回射线的碰撞信息了。
可能会有小伙伴问,为什么不使用更简单高效的Box Collider
?这是因为Box Collider
不能很准确的吻合正六边形地图单元的轮廓,尤其是在3个地图单元相邻的情况下,Box Collider
可能会重叠到一起,最终导致我们无法判断鼠标到底点击在了哪个地图单元上。而且随着项目的不断深入,我们的地形单元并不会一直保持在同意水平面上。所以使用MeshCollider
会更加方便计算和判断。
以上代码完成后,我们在Unity的Scene窗口中点击任意一个地图单元,Console窗口中就会输出鼠标点击的坐标信息了。但是这个信息并没有体现出我们具体点击的是哪一个地图单元,所以需要将鼠标的点击的坐标信息,转换成六边形的坐标信息。这个步骤需要在HexCoordinates.cs中进行。
我们需要在首先要在HexGrid.TouchCell方法内,添加对HexCoordinates.cs中发发的调用,这个方法命名为FromPosition,专门用来将射线触碰MeshCollider的坐标,转换成正六边形地图的坐标。代码如下:
1 | private void TouchCell(Vector3 position) |
现在,我们来思考FromPosition这个方法要处理的事情。要将原有的射线触碰Collider信息转换成正六边形地图信息,可以将X、Y、Z几个轴向分开处理。这里首先处理X轴坐标,只需要将转换前的坐标除以地图单元的宽度即可,而且当Z值为0的时候,X和Y是护卫相反数的。在这里我们先假设Z值为0,所以很容易就能得出X和Y的值。代码如下:
1 | public static HexCoordinates FromPosition(Vector3 position) |
接下来,Z不为0的时候,我们需要对X和Y进行偏移,才能得出正确的结果。代码如下:
1 | float offset = position.z / (HexMetrics.outerRadius * 3f); |
当计算出X和Y的值后,我们可以利用X+Y+Z=0这个特性,求出Z的坐标。然后将这些坐标进行四舍五入,就可以得到转换后的正六边形地图坐标了。代码如下:
1 | //对得出的坐标进行四舍五入,得到转换后的Hexmap坐标 |
以上这些步骤虽然看起来没什么问题,但是仔细想一下就会发现,这样计算的最终坐标,很可能相加并不为0.让我们来加一段验证这个想法的代码:
1 | //对得出的坐标进行四舍五入,得到转换后的Hexmap坐标 |
我们再次运行程序的时候发现,这个报错信息确实会弹出。而且是发生在鼠标点击的位置接近正六边形边界的时候。所以应该是在四舍五的过程中出现了问题,因为离地图单元的中心越远,四舍五入时舍去的值就越多,所以我们做一个合理的假设:舍去值更大的坐标是错误的。
知道了产生错误的原因,解决起来就比较简单了。解决方法就是废弃具有最大舍去增量的坐标值,然后用其它的两个坐标去重新构建它。这里我们只需要去重建X和Z,不需要关注Y,因为Y本来就是由X和Z求得的。代码如下:
1 | if (iX + iY + iZ != 0) |
通过判断,我们重新计算四舍五入中,舍去的值最多的那个坐标。这样我们就得到了最终正确结果。
在下一章中,我们利用本章判断鼠标点击在哪个地图单元上的功能,给被点击的地图单元改变颜色,让地图系统拥有最基本的交互。