虚拟摇杆实现笔记(事件触发器的使用、UGUI 坐标转换)
一、核心逻辑
通过 EventTrigger 监听摇杆的拖拽/结束拖拽事件,控制摇杆位置,再将摇杆偏移量转为玩家移动方向,实现虚拟摇杆控制角色移动。
主要学习:事件触发器的使用、UGUI 坐标转换
二、代码结构与注释
1. 摇杆事件注册
csharp
//从外部获取摇杆物体上的EventTrigger组件
public EventTrigger et;
// 为摇杆添加拖拽事件
EventTrigger.Entry entry = new EventTrigger.Entry();
entry.eventID = EventTriggerType.Drag; // 拖拽事件ID(枚举类型)
entry.callback.AddListener(JoyDrag); // 绑定拖拽回调方法
et.triggers.Add(entry);
// 为摇杆添加结束拖拽事件
entry = new EventTrigger.Entry();
entry.eventID = EventTriggerType.EndDrag; // 结束拖拽事件ID
entry.callback.AddListener(EndJoyDrag); // 绑定结束拖拽回调方法
et.triggers.Add(entry);
2. 拖拽事件回调(控制摇杆位置+玩家移动)
csharp
// 摇杆拖拽时执行
private void JoyDrag(BaseEventData data)
{
// 把基础事件数据转为指针事件数据(获取拖拽相关信息)
PointerEventData eventData = data as PointerEventData;
// 旧方法:通过当前摇杆位置 + 拖拽偏移量更新位置
// 缺点:受UI相机、Canvas模式影响,多分辨率适配较差
// imgJoy.position += new Vector3(eventData.delta.x, eventData.delta.y, 0);
// 新方法:使用RectTransformUtility完成屏幕坐标转UI局部坐标,适配性更强
Vector2 nowPos;
// 将鼠标/触摸屏幕坐标,转换为摇杆父物体下的局部坐标
RectTransformUtility.ScreenPointToLocalPointInRectangle(
imgJoy.parent as RectTransform, // 目标父物体RectTransform
eventData.position, // 原始屏幕坐标
eventData.enterEventCamera, // 渲染UI的相机
out nowPos // 输出转换后的局部坐标
);
// 赋值局部坐标,实现摇杆精准跟随拖拽点
imgJoy.localPosition = nowPos;
// 防止摇杆超出范围:限制摇杆的锚点偏移量最大为110
if (imgJoy.anchoredPosition.magnitude > 110)
{
// 将摇杆位置归一化(保持方向不变),再乘以最大范围,拉回临界位置
imgJoy.anchoredPosition = imgJoy.anchoredPosition.normalized * 110;
}
// 调用玩家移动方法,传入摇杆当前偏移量作为移动方向
player.Move(imgJoy.anchoredPosition);
}
3. 结束拖拽回调(摇杆复位+玩家停止移动)
csharp
// 摇杆拖拽结束时执行
private void EndJoyDrag(BaseEventData data)
{
// 摇杆锚点位置回归中心点(Vector2.zero)
imgJoy.anchoredPosition = Vector2.zero;
// 传入零向量,让玩家停止移动
player.Move(Vector2.zero);
}
4. 玩家移动控制(Update 帧更新)
csharp
void Update()
{
// 如果当前移动方向不为零(摇杆被拖动)
if (nowMoveDir != Vector3.zero)
{
// 角色沿自身前方移动,速度由 moveSpeed 控制,乘以 Time.deltaTime 保证帧率无关
this.transform.Translate(Vector3.forward * (moveSpeed * Time.deltaTime));
// 角色平滑转向目标方向(Slerp实现缓动效果,rotateSpeed控制转向速度)
this.transform.rotation = Quaternion.Slerp(
this.transform.rotation,
Quaternion.LookRotation(nowMoveDir),
rotateSpeed * Time.deltaTime
);
}
}
5. 移动方向设置(供摇杆调用)
csharp
// 外部调用:将摇杆二维偏移量转为角色三维移动方向
public void Move(Vector2 dir)
{
nowMoveDir.x = dir.x; // 摇杆X偏移量 → 角色水平方向
nowMoveDir.y = 0; // Y轴(竖直方向)固定为0,不影响高度
nowMoveDir.z = dir.y; // 摇杆Y偏移量 → 角色前后方向
}
三、关键知识点
1. EventTrigger 事件绑定(标准四步流程)
a. EventTrigger.Entry entry = new EventTrigger.Entry(); 创建事件条目
b. entry.eventID = EventTriggerType.Drag; 绑定事件枚举类型
c. entry.callback.AddListener(JoyDrag); 绑定拖拽回调方法
d. et.triggers.Add(entry); 把事件添加到EventTrigger组件,事件正式生效
2. 两种摇杆位置更新方式对比
-
旧方案 :
position + delta偏移叠加直接累加拖拽增量,逻辑简单,但依赖屏幕空间,不同Canvas渲染模式、分辨率下容易错位。
-
新方案 :
RectTransformUtility.ScreenPointToLocalPointInRectangle坐标转换将屏幕坐标转为UI父物体局部坐标,适配所有UI模式,拖拽跟随更精准,是UGUI拖拽通用写法。
参数说明:目标父物体RectTransform、屏幕坐标、UI相机、输出局部坐标。
3. 摇杆范围限制
anchoredPosition:UI元素相对于锚点的偏移量,适合做摇杆位置控制magnitude:向量长度,用于判断摇杆是否超出最大范围normalized:向量归一化,保持方向不变,仅缩放长度到1,再乘以最大范围实现"拉回"限位
4. 方向转换逻辑
- 摇杆的二维
Vector2偏移量 → 角色三维Vector3移动方向(X→水平,Y→前后,Y轴固定为0) Quaternion.LookRotation:根据方向向量生成目标旋转Quaternion.Slerp:实现平滑转向,避免角色瞬间转头
5. 帧率无关移动
- 所有移动/旋转逻辑乘以
Time.deltaTime,保证不同帧率下移动速度保持一致