UI拖拽

实现接口

1
IBeginDragHandler, IEndDragHandler,IDragHandler

该方法是BeginDrag接口的实现方法,在指定游戏对象被拖拽的时候启用,对于鼠标的位置追踪则可以直接使用 eventData.position 来进行移动,不需要进行一些列转换

一般被拖动的UI会受到自身所在层级影响,因此在一开始就需要重新设置一下被拖动UI的父对象

1
2
3
4
5
6
7
8
public void OnBeginDrag(PointerEventData eventData)
{
originParent = transform.parent;

this.transform.SetParent(transform.parent.parent.parent);
this.transform.position = eventData.position;
canvasGroup.blocksRaycasts = false;//射线可以穿透
}

该方法是EndDrag接口的实现方法,在停止拖拽的时候启用

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
public void OnEndDrag(PointerEventData eventData)
{
if (eventData.pointerCurrentRaycast.gameObject.name.EndsWith("Slot"))
{
Debug.Log("----找到合适的位置,准备交换");
//this.parentPanel.SwitchItemIndex(this.gameObject,eventData.currentInputModule.gameObject); //数据层面的交换
Transform trans = eventData.pointerCurrentRaycast.gameObject.transform;
this.transform.SetParent(trans.parent);
this.transform.position = trans.position;
trans.SetParent(originParent);
trans.position = originParent.position;
}
else if (eventData.pointerCurrentRaycast.gameObject.name.StartsWith("Slot"))
{
Transform trans = eventData.pointerCurrentRaycast.gameObject.transform;
this.transform.SetParent(trans);
this.transform.position = trans.position;

}
else
{
Debug.Log("----没有移动到合适的位置,回归原位");
this.transform.SetParent(originParent);
this.transform.position = originParent.position;
RectTransform rect = transform as RectTransform;
rect.sizeDelta = this.sizeData;
}
canvasGroup.blocksRaycasts = true;
}

可以使用该方法对射线检测到的物体进行判断

1
eventData.pointerCurrentRaycast.gameObject

拖拽过程中如果不是新生成一个游戏对象作为拖拽副本的话,采用重新设置parent,一来二去会对最后的大小造成影响,因此建议在操作完成后对各自的position进行一设置,position可以是目标地点本来存在的同类游戏对象的position,也可以是目标地的父对象的position

1
2
3
Transform trans = eventData.pointerCurrentRaycast.gameObject.transform; 
this.transform.SetParent(trans);
this.transform.position = trans.position;

配合组件

image-20240822115517698

要注意名字名为 Blocks Raycasts 参数的使用,当为true时,发出的射线不可穿透拖动的物体,fasle反之

射线判断

区域

区域计算

1
2
3
4
5
6
7
8
9
10
11
12
13
//计算UI大小,进行鼠标拖拽检查
RectTransform rectransfrom = itemEquipUI.transform as RectTransform;
float w = rectransfrom.rect.width;
float h = rectransfrom.rect.height;
float x = rectransfrom.position.x;
float y = rectransfrom.position.y;
float xL = x - w / 2;
float xR = x + w / 2;
float yL = y - h / 2;
float yR = y + h / 2;

itemEuqipUIArea.Item1 = new Vector2(xL, xR);
itemEuqipUIArea.Item2 = new Vector2(yL, yR);

方法调用

1
2
3
4
5
6
if (eventData.position.x.InRange(parentPanel.itemEuqipUIArea.Item1.x, parentPanel.itemEuqipUIArea.Item1.y)
&& eventData.position.y.InRange(parentPanel.itemEuqipUIArea.Item2.x, parentPanel.itemEuqipUIArea.Item2.y))
{
Debug.Log("可以装备Item");
EquipItem();
}

游戏对象

可以获取拖动UI下方,鼠标正下方的游戏对象

1
eventData.pointerCurrentRaycast.gameObject

注意,该操作的使用前提是

1
canvasGroup.blocksRaycasts = false;//射线可以穿透

一般会在拖动开始就将该属性设置为false,但是为了下一次拖拽的稳定可靠,一般会在结束拖动的时候重新设置为true,需要注意要把顺序放置的方法最后,提前设置可能无法获取穿透下方的游戏对象

1
2
3
4
5
public void OnEndDrag(PointerEventData eventData)
{
。。。。。。
canvasGroup.blocksRaycasts = true;
}

相关配置

layout参数配置

使用下图右侧栏目 参数进行配置,可以得到左边的效果。

image-20240819112946234

下图的是在外部大框架没有使用content size filter的情况下使用的,建议使用第一行的第二个参数,会让子元素在空间内均匀排布

image-20240819112723044

示例代码

生成拖动副本

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
    /// <summary>
/// 开始拖拽
/// </summary>
/// <param name="eventData"></param>
/// <exception cref="System.NotImplementedException"></exception>
public void OnBeginDrag(PointerEventData eventData)
{
itemOnDrag = Instantiate(this.gameObject, this.parentPanel.transform);
itemOnDrag.transform.position = eventData.position;
}

/// <summary>
/// 拖拽中
/// </summary>
/// <param name="eventData"></param>
/// <exception cref="System.NotImplementedException"></exception>
void IDragHandler.OnDrag(PointerEventData eventData)
{
itemOnDrag.transform.position = eventData.position;

Debug.Log(eventData.position);
}

/// <summary>
/// 结束拖拽
/// </summary>
/// <param name="eventData"></param>
/// <exception cref="System.NotImplementedException"></exception>
void IEndDragHandler.OnEndDrag(PointerEventData eventData)
{

if (eventData.position.x.InRange(parentPanel.itemEuqipUIArea.Item1.x, parentPanel.itemEuqipUIArea.Item1.y)
&& eventData.position.y.InRange(parentPanel.itemEuqipUIArea.Item2.x, parentPanel.itemEuqipUIArea.Item2.y))
{
Debug.Log("可以装备Item");
EquipItem();
}
else if (eventData.position.x.InRange(parentPanel.itemContentUIArea.Item1.x, parentPanel.itemContentUIArea.Item1.y)
&& eventData.position.y.InRange(parentPanel.itemContentUIArea.Item2.x, parentPanel.itemContentUIArea.Item2.y))
{
Debug.Log("可以卸下Item");
RelieveItem();
}
else
{
Debug.Log("可以丢弃Item");
DestoryItem();
}

Destroy(itemOnDrag);
}

本体移动

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
58
59
    /// <summary>
/// 开始拖拽
/// </summary>
/// <param name="eventData"></param>
/// <exception cref="System.NotImplementedException"></exception>
public void OnBeginDrag(PointerEventData eventData)
{
originParent = transform.parent;

this.transform.SetParent(transform.parent.parent.parent);
this.transform.position = eventData.position;
canvasGroup.blocksRaycasts = false;//射线可以穿透
}

/// <summary>
/// 拖拽中
/// </summary>
/// <param name="eventData"></param>
/// <exception cref="System.NotImplementedException"></exception>
public void OnDrag(PointerEventData eventData)
{
this.transform.position = eventData.position;
Debug.Log("-----射线当前检测到的对象"+eventData.pointerCurrentRaycast.gameObject.name);
}

/// <summary>
/// 结束拖拽
/// </summary>
/// <param name="eventData"></param>
/// <exception cref="System.NotImplementedException"></exception>
public void OnEndDrag(PointerEventData eventData)
{
if (eventData.pointerCurrentRaycast.gameObject.name.EndsWith("Slot"))
{
Debug.Log("----找到合适的位置,准备交换");
//this.parentPanel.SwitchItemIndex(this.gameObject,eventData.currentInputModule.gameObject); //数据层面的交换
Transform trans = eventData.pointerCurrentRaycast.gameObject.transform;
this.transform.SetParent(trans.parent);
this.transform.position = trans.position;
trans.SetParent(originParent);
trans.position = originParent.position;
}
else if (eventData.pointerCurrentRaycast.gameObject.name.StartsWith("Slot"))
{
Transform trans = eventData.pointerCurrentRaycast.gameObject.transform;
this.transform.SetParent(trans);
this.transform.position = trans.position;

}
else
{
Debug.Log("----没有移动到合适的位置,回归原位");
this.transform.SetParent(originParent);
this.transform.position = originParent.position;
RectTransform rect = transform as RectTransform;
rect.sizeDelta = this.sizeData;
}
canvasGroup.blocksRaycasts = true;
}