Unity 之 使用原生UGUI实现随手移动摇杆功能经典实例

请添加图片描述

实现效果

本文最终实现效果:
请添加图片描述


一,实现思路

1.1 原理解析

做一个实验看一下使用ScrollRect组件实现摇杆的原理。

  1. Hierarchy面板右键 UI -> Scroll View 创建一个滚动视图,这个组件经常被应用于排行榜,选角色之类的可滑动的界面。

  2. Scroll View -> Viewport -> Content 添加一个Image组件

  3. 运行场景,鼠标点击并拖动中间部分,即可看到如下效果:

看到这里基本了解实现思路了吧,其实就是通过Scroll Rect组件的Context和Viewport的关系来进行模拟的。

更多关于ScrollRect的使用方法和实战应用,可以查看:Unity 之 UGUI Scroll Rect滚动矩形组件详解


1.2 思路概述

  1. 随手指落下位置
    思路:其实就是根据手指第一次落下的屏幕坐标,修改摇杆的初始位置;手抬起时再将摇杆位置还原
    知识点:获取手指按下和抬起的回调,将手指落下坐标转换为屏幕UI坐标

  2. 摇杆移动
    思路:使用Scroll Rect的移动回调,来控制中间的虚拟摇杆进行位置变化
    注意的点:使用OnDrag进行回调,并来控制虚拟摇杆的标移动位置不要超出背景

  3. 移动回调
    思路:当使用摇杆时使用Update进行实时回调
    注意的点:使用OnEndDrag进行回调,还原要个位置


二,实现代码

2.1 随手落下

通过锚点和RectTransformUtility坐标转换方法进行位置设置。

using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

[RequireComponent(typeof(EventTrigger))]
public class JoystickTouch : ScrollRect
{
    
        
    protected override void Start()
    {
    
                
        EventTrigger trigger = GetComponent<EventTrigger>();
        EventTrigger.Entry entryPointerUp = new EventTrigger.Entry();
        entryPointerUp.eventID = EventTriggerType.PointerUp;
        entryPointerUp.callback.AddListener((data) => {
    
     OnPointerUp((PointerEventData)data); });
        trigger.triggers.Add(entryPointerUp);

        EventTrigger.Entry entryPointerDown = new EventTrigger.Entry();
        entryPointerDown.eventID = EventTriggerType.PointerDown;
        entryPointerDown.callback.AddListener((data) => {
    
     OnPointerDown((PointerEventData)data); });
        trigger.triggers.Add(entryPointerDown);
    }
    
    // 随手落下设置摇杆位置
    private void OnPointerDown(PointerEventData eventData)
    {
    
    
        Vector2 LocalPosition;
        RectTransformUtility.ScreenPointToLocalPointInRectangle(this.GetComponent<RectTransform>(),
            eventData.position, eventData.pressEventCamera, out LocalPosition);
        this.viewport.localPosition = LocalPosition;
    }

    // 抬起还原位置
    private void OnPointerUp(PointerEventData eventData)
    {
    
    
        this.viewport.anchoredPosition3D = Vector3.zero;
    }

}

2.2 摇杆转动

还是在上面的类中重写OnDrag方法,进行虚拟摇杆中间位置的控制。

public class JoystickTouch : ScrollRect
{
    
        
    // 半径 -- 控制拖拽区域
    private float mRadius;
    
    protected override void Start()
    {
    
    
        mRadius = this.content.sizeDelta.x * 0.5f;
    }
    
    public override void OnDrag(PointerEventData eventData)
    {
    
    
        base.OnDrag(eventData);
        joyIsCanUse = true;
        //虚拟摇杆移动
        Vector3 contentPosition = this.content.anchoredPosition;
        if (contentPosition.magnitude > mRadius)
        {
    
    
            contentPosition = contentPosition.normalized * mRadius;
            SetContentAnchoredPosition(contentPosition);
        }

        // 摇杆内部按钮旋转
        //if (content.anchoredPosition.y != 0)
        //{
    
    
        //    content.eulerAngles = new Vector3(0, 0,
        //        Vector3.Angle(Vector3.right, content.anchoredPosition) * content.anchoredPosition.y /
        //        Mathf.Abs(content.anchoredPosition.y) - 90);
        //}
    }
}

三,源码分享

3.1 场景搭建

创建三个Image一个作为一个的子物体,依次为:接收点击背景面积,摇杆背景板,摇杆中的虚拟按钮。
第一个Image挂载新建脚本JoystickTouch
场景搭建如下:


3.2 完整代码

using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

[RequireComponent(typeof(EventTrigger))]
public class JoystickTouch : ScrollRect
{
    
    
    /// <summary>
    /// 拖动差值
    /// </summary>
    public Vector2 offsetValue;
    
    // 半径 -- 控制拖拽区域
    private float mRadius;
    
    /// <summary>
    /// 移动中回调
    /// </summary>
    public System.Action<RectTransform> JoystickMoveHandle;
    /// <summary>
    /// 移动结束回调
    /// </summary>
    public System.Action<RectTransform> JoystickEndHandle;

    /// <summary>
    /// 摇杆是否处于可用状态
    /// </summary>
    public bool joyIsCanUse = false;
    
    protected override void Start()
    {
    
    
        mRadius = this.content.sizeDelta.x * 0.5f;
            
        EventTrigger trigger = GetComponent<EventTrigger>();
        EventTrigger.Entry entryPointerUp = new EventTrigger.Entry();
        entryPointerUp.eventID = EventTriggerType.PointerUp;
        entryPointerUp.callback.AddListener((data) => {
    
     OnPointerUp((PointerEventData)data); });
        trigger.triggers.Add(entryPointerUp);

        EventTrigger.Entry entryPointerDown = new EventTrigger.Entry();
        entryPointerDown.eventID = EventTriggerType.PointerDown;
        entryPointerDown.callback.AddListener((data) => {
    
     OnPointerDown((PointerEventData)data); });
        trigger.triggers.Add(entryPointerDown);
    }
    
    protected override void OnEnable()
    {
    
    
        joyIsCanUse = false;
        offsetValue = Vector2.zero;
    }
    
    public override void OnDrag(PointerEventData eventData)
    {
    
    
        base.OnDrag(eventData);
        joyIsCanUse = true;
        //虚拟摇杆移动
        Vector3 contentPosition = this.content.anchoredPosition;
        if (contentPosition.magnitude > mRadius)
        {
    
    
            contentPosition = contentPosition.normalized * mRadius;
            SetContentAnchoredPosition(contentPosition);
        }

        // 摇杆内部按钮旋转
        //if (content.anchoredPosition.y != 0)
        //{
    
    
        //    content.eulerAngles = new Vector3(0, 0,
        //        Vector3.Angle(Vector3.right, content.anchoredPosition) * content.anchoredPosition.y /
        //        Mathf.Abs(content.anchoredPosition.y) - 90);
        //}
    }

    private void FixedUpdate()
    {
    
    
        if (joyIsCanUse)
        {
    
    
            JoystickMoveHandle?.Invoke(this.content);
            offsetValue = this.content.anchoredPosition3D;
        }
    }

    public override void OnEndDrag(PointerEventData eventData)
    {
    
    
        base.OnEndDrag(eventData);
        joyIsCanUse = false;
        offsetValue = Vector2.zero;
        JoystickEndHandle?.Invoke(this.content);
    }
    
    // 随手落下设置摇杆位置
    private void OnPointerDown(PointerEventData eventData)
    {
    
    
        Vector2 LocalPosition;
        RectTransformUtility.ScreenPointToLocalPointInRectangle(this.GetComponent<RectTransform>(),
            eventData.position, eventData.pressEventCamera, out LocalPosition);
        this.viewport.localPosition = LocalPosition;
    }

    // 抬起还原位置
    private void OnPointerUp(PointerEventData eventData)
    {
    
    
        this.viewport.anchoredPosition3D = Vector3.zero;
    }

}

3.3 实现效果

按钮素材图片:


实现效果:


工程下载:源码和步骤都在上面分享过了,若还有什么不明白的,可以 点击链接下载 ,积分不够的童鞋关注下方卡片,回复:“摇杆” 即可获得开篇Demo源码~

猜你喜欢

转载自blog.csdn.net/Czhenya/article/details/129621067