6-7 检测鼠标拖拽

  在上一章中,我们完成了河流编辑器的UI部分,通过点选Toggle就可以选择当前是否可以编辑河流。在这一章中,我们继续为创建河流做前期的一些工作,首先是通过什么样的交互形式来创建河流。这里我们通过参考其他的地图编辑器功能,选择了使用鼠标拖动的形式来创建河流。也就是拖动鼠标经过若干个地图单元,地图系统就会跟着鼠标的轨迹在这些地图单元上创建河流。
  要实现这个功能,就需要同时获得地图单元的位置和鼠标在这个地图单元移动的方位,回到HexMapEditor.cs中,在脚本中添加获取这两个数据的方法。代码如下:

HexMapEditor.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
public class HexMapEditor : MonoBehaviour
{


//判断当前是否处于拖拽状态
private bool isDrag;

//判断鼠标在当前cell的移动方位
private HexDirection dragDirection;

//当鼠标到下一个cell 的时候,这里记录上一个cell
private HexCell previousCell;
}

  最初的时候是没有拖拽事件的,也就没有上一个地图单元的记录。所以当没有输入信息或者没有与地图交互时,需要设置其为null。代码如下:

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
public class HexMapEditor : MonoBehaviour
{


private void Update()
{
if (Input.GetMouseButtonUp(0) && !EventSystem.current.IsPointerOverGameObject())
{
HandleInput();
}
else
{
//当鼠标左键没有按下的时候,记录上一个经过的cell为空
previousCell = null;
}
}

private void HandleInput()
{


if (Physics.Raycast(_inputRay, out _hit))
{

}
else
{
//当鼠标的射线未触碰到地图的时候,上一个经过的cell为空
previousCell = null;
}
}
}

  当前地图单元是根据射线碰撞点找到的,当在这一帧里结束编辑时,它就会变成下一次Update里的上一个地图单元。代码如下:

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
public class HexMapEditor : MonoBehaviour
{


private void HandleInput()
{


if (Physics.Raycast(_inputRay, out _hit))
{
//记录当前射线碰撞到的cell
HexCell currentCell = hexGrid.GetCell(_hit.point);
//记录当前正在编辑的cell
EditCells(currentCell);
//目前上一个cell与当前cell是一个
previousCell = currentCell;
}
else
{

}
}
}

  确认了当前射线碰撞到的地图单元之后,我们可以与前一个地图单元(如果有的话)进行比较,当发现是两个不同的单元格时,就说明可能存在有效拖动并需要去检测,要不就是没有拖拽事件。代码如下:

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
public class HexMapEditor : MonoBehaviour
{


private void HandleInput()
{


if (Physics.Raycast(_inputRay, out _hit))
{
HexCell currentCell = hexGrid.GetCell(_hit.point);

//判断当前cell与之前的cell是否为同一个
//如果是,就是在拖拽
if (previousCell && previousCell != currentCell)
{
ValidateDrag(currentCell);
}
else
{
isDrag = false;
}


}
else
{

}
}
}

  如何证实确实是拖拽事件?通过检测当前地图单元是否与前一个地图单元相邻,循环遍历前一个地图单元所有相邻地图单元来进行检测,如果找到了与当前地图单元相吻合的结果,就能同时确认拖拽的方向。代码如下:

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
public class HexMapEditor : MonoBehaviour
{


/// <summary>
/// 确认拖拽方向
/// </summary>
/// <param name="currentCell">当前射线所碰撞到的cell</param>
private void ValidateDrag(HexCell currentCell)
{
//循环遍历当前cell的6个方位
for (dragDirection = HexDirection.NE; dragDirection <= HexDirection.NW; dragDirection++)
{
//如果之前cell某个方位上的cell,与当前射线触碰到的cell相同,就证明发生了碰撞
//可以这样理解,当真实发生拖拽了,会记录下previousCell
//鼠标移动至新的cell后,开始检测previousCell在对应方位上是不是有新的cell
//如果有,就证明鼠标从previousCell拖拽移动到了新的cell上
if (previousCell.GetNeighbor(dragDirection) == currentCell)
{
isDrag = true;
return;
}
}
isDrag = false;
}
}

这样逐帧检测这不会产生拖拽抖动么?
当你移动鼠标穿过地图单元边界时,可能会在单元格之间快速来回摆动,这确实会导致拖拽抖动,但情况没那么糟。
可以通过记录上一次拖拽事件来减缓抖动,然后防止下一次直接向相反方向拖拽即可。

  至此,我们完成了通过检测鼠标左键按下后,是否产生了拖拽的动作。在下一章中,我们将继续完善移除和添加河流的部分代码,并最终可以在Inspector的Debug模式下看到效果。

Github代码