1-10 简单的地图编辑器

  在上一章中,我们实现了为被点击的地图单元添加指定的颜色。现在我们将这个功能拓展一下,制作一个简单的地图编辑器。在HexGrid.cs脚本中找到TouchCell方法,这个方法主要用来判断鼠标的点击位置并改变被点击地图单元的颜色。这里我们要对TouchCell方法进行一些修改,添加一个参数,使其可以接受任何颜色的信息,并将其访问修饰符改为public。代码如下:

HexGrid.cs
1
2
3
4
5
6
7
8
9
public void ColorCell(Vector3 _position, Color _color)
{
_position = transform.InverseTransformPoint(_position);
HexCoordinates _coordinates = HexCoordinates.FromPosition(_position);
int _index = _coordinates.X + _coordinates.Z * width + _coordinates.Z / 2;
HexCell _cell = cells[_index];
_cell.color = _color;
hexMesh.Triangulate(cells);
}

  接下来,创建HexMapEditor脚本,将HexGrid.cs脚本Update方法中的内容和HandleInput方法移动到这里。然后创建一个存储颜色的数组用来保存多个备选颜色,创建一个私有的颜色变量用来保存已选中的颜色值。最后添加一个公共方法来选择颜色。代码如下:

HexMapEditor.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
using UnityEngine;

public class HexMapEditor : MonoBehaviour
{
//备选颜色数组
public Color[] colors;

//HexGrid实例,用来调用其中的ColorCell方法
public HexGrid hexGrid;

//已选中的颜色
private Color activeColor;

//这里使用指定的camera代替 Camera.main方式,避免遍历场景中的Object
[SerializeField] private Camera mainCamera;

private void Awake()
{
//为activeColor赋初始值
SelectColor(0);
}

private void Update()
{
if (Input.GetMouseButtonUp(0))
{
HandleInput();
}
}

/// <summary>
/// 鼠标左键单击会调用此方法,以鼠标为发射点,经过主摄像机练成射线
/// 检测射线穿过Collider的位置
/// 此方法移动到了HexMapEditor中
/// </summary>
private void HandleInput()
{
//射线起点为鼠标位置,经过主摄像机
Ray _inputRay = mainCamera.ScreenPointToRay(Input.mousePosition);

//检测射线是否碰撞到了collider
RaycastHit _hit;
if (Physics.Raycast(_inputRay, out _hit))
{
hexGrid.ColorCell(_hit.point, activeColor);
}
}

/// <summary>
/// 为选中颜色activeColor 赋值
/// </summary>
/// <param name="_index">备选颜色数组colors 中的颜色值索引</param>
public void SelectColor(int _index)
{
activeColor = colors[_index];
}
}

  新建一个canvas,重命名为Hex Map Editor不需要对其进行修改,保持默认即可。然后将HexMapEditor脚本挂载到canvas上,为Colors数组添加几个不同的颜色。最后将挂载Hex Grid.cs脚本的物体Hex Grid拖入到HexMapEditor中变量Hex Grid栏内。效果如下:
  

  这里注意,在创建cancas的时候,可以调整Canvas Scaler的UI Scale Mode属性为Scale With Screen Size,这样输入屏幕的长度和宽度,就能更好的匹配UI的大小了。

  在canvas下创建一个Panel,重命名为Color Panel,挂载Toggle Group组件,效果如下图:

  接着,在Color Panel下创建若干个单选按钮(Components/ UI/toggle),其数量为HexMapEditor中colors数组的长度。效果如下图:

  完成以上步骤后,选中Color Panel下面的所有子物体,其中Toggle组件中有Group一栏,将Color Panel的Toggle Group组件拖入其中。这样,就能把这些Toggle组件关联到一起了,当一个被打开的时候,另外的Toggle组件会自动关闭。效果如下图:

  关于Toggle组件中事件调用的时机问题:

  当我们每次选中或取消选中一个Toggle按钮时,Toggle组件中挂载的事件都会被调用。也就是说,当我们在Toggle按钮组中切换颜色的时候,SelectColor()方法会被调用两次。不过不用担心,因为每次Toggle组件被选中的事件调用总在最后,所以结果一定是正确的。或者我们可以通过判断当前组件是否被选中(Toggle组件Is On属性),来决定是否执行SelectColor()方法。

  在每个Toggle组件中,有On Value Changed事件,将这个这件绑定到HexMapEditor.SelectColor方法上,并设置其参数,即Toggle组件表示的颜色与HexMapEditor.Color数组的颜色索引相同。效果如下图:

  最后,让我们将Color Panel覆盖到正六边形地图之上。这时候点击一个Toggle按钮,会发现每一次鼠标点击,不仅改变了Toggle按钮的状态,还同时改变了其后面地图单元的颜色。这说明鼠标在一次点击的时候不仅UI相应了鼠标点击,同时地图系统也响应了鼠标点击。我们可以使用事件系统来将两个事件分开处理,也就是当鼠标悬停在UI上时,六边形地图不会响应鼠标的点击事件。代码如下:

HexMapEditor.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
using UnityEngine;
using UnityEngine.EventSystems;

public class HexMapEditor : MonoBehaviour
{


private void Update()
{
//通过IsPointerOverGameObject区分点击在UI或cell上
//IsPointerOverGameObject点击在UI上时候为true。其他为false
if (Input.GetMouseButtonUp(0) && !EventSystem.current.IsPointerOverGameObject())
{
HandleInput();
}
}


}

  这样,我们就完成了一个很简单的地图编辑器,在一组单选按钮组中选择一个颜色后,点击任意的地图单元,这个地图单元就会变成相应的颜色。虽然在改变颜色的过程中,我们是通过重新构建整个六边形地图做到的,但是目前市局效果和逻辑是正确的。
  在接下来的章节中,我们会先将注意力放在视觉效果部分,首先要让地图单元之间的颜色过渡更加自然,然后添加地形的高低起伏。

Github代码