Unity virtual joystick (event dispatcher implementation)

Table of contents

Build the UI

Write code

Build the UI

 ①Create an image named ControlArea as the detection range of button recognition and set the transparency to 1 (transparency of 0 does not trigger event detection)

② Add an image and name it to the parent-child relationship as shown in the figure below Find a suitable material and drag it to the corresponding image to set the appropriate size (set it yourself according to your preferences and the size of the material)

③Pay attention to select the anchor point (flower) of the joystick and place it in the lower left corner to prevent the position of the button from changing due to the change of resolution. The position of the center point (pivot) is set to (0.5, 0.5) as shown in the figure below

 

Realize the script

① Use singleton UIManger to manage some public methods

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;

public class UIManger : MonoBehaviour
{
    public static UIManger Instance;

    public static UIManger GetInstance()
    {
        if (Instance == null) 
        {
            Instance = new UIManger();
        }
        return Instance;
    }

    public static void AddCustomEnventListener(UIBehaviour contral, EventTriggerType type, UnityAction<BaseEventData> callback) 
    {
        EventTrigger trigger = contral.GetComponent<EventTrigger>();
        if (trigger == null)
            trigger = contral.gameObject.AddComponent<EventTrigger>();

        EventTrigger.Entry entry = new EventTrigger.Entry();

        entry.eventID = type;
        entry.callback.AddListener(callback);
        trigger.triggers.Add(entry);
    }

}

②Event dispatcher

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;

//事件分发器
public class EventCenter : MonoBehaviour
{
    //声明一个带参数的委托
    public  delegate void CallBack<T>(T t);
    //声明一个字典 事件码与事件进行绑定
    public static Dictionary<string, Delegate> m_EventTable = new Dictionary<string, Delegate>();

    /// <summary>
    /// 添加监听的方法
    /// </summary>
    /// <typeparam name="T">数据类型</typeparam>
    /// <param name="str">事件码</param>
    /// <param name="callBack">泛型为委托</param>
    public static void Addlistener<T>(string str , CallBack<T> callBack)
    {
        //
        if (!m_EventTable.ContainsKey(str))
        {
            m_EventTable.Add(str,null);
        }
        Delegate d = m_EventTable[str];
        if (d != null && d.GetType() != callBack.GetType()) 
        {
            throw new Exception("监听添加错误!!");
        }
        //订阅事件
        m_EventTable[str] = (CallBack<T>)m_EventTable[str] + callBack;

    }
    /// <summary>
    /// 移除监听的方法
    /// </summary>
    /// <typeparam name="T">数据类型</typeparam>
    /// <param name="str">事件码</param>
    /// <param name="callBack">泛型委托</param>
    public static void RemoveListener<T>(string str, CallBack<T> callBack) 
    {
        if (m_EventTable.ContainsKey(str))
        {
            Delegate d = m_EventTable[str];
            if (d == null)
            {
                throw new Exception(string.Format("监听移除失败,事件{0}没有委托", str));
            }
            else if (d.GetType() != callBack.GetType())
            {
                throw new Exception(string.Format("监听移除失败,事件类型不对应{0},{1}", str.GetType(), callBack.GetType()));
            }

        }
        else
        {
            throw new Exception(string.Format("监听移除失败,没有事件码{0}", str));
        }
        //取消订阅
        m_EventTable[str] = (CallBack<T>)m_EventTable[str] - callBack;
        if (m_EventTable[str] == null) 
        {
            m_EventTable.Remove(str);
        }
    }
    /// <summary>
    /// 广播
    /// </summary>
    /// <typeparam name="T">数据类型</typeparam>
    /// <param name="str">事件码</param>
    /// <param name="arg">T 的参数</param>
    public static void BroadCast<T>(string str,T arg) 
    {
        Delegate d;
        if (m_EventTable.TryGetValue(str, out d))
        {
            CallBack<T> callBack = d as CallBack<T>;

            if (d != null)
            {
                callBack(arg);
            }
            else { throw new Exception("广播错误!!!"); }
        }
    }
}

③Add the following script to ControlArea

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;

public class JoystickContral : UIManger
{
    
    public float MaxL;

    private Image ControlBK;
    private Image Joystick;
    private Image Handle;
    // Start is called before the first frame update
    void Start()
    {
        //根据按钮的中心到按钮父物体边界的距离
        MaxL = 75;
        //控制按钮监听的范围
        ControlBK = this.GetComponent<Image>();
        
        Joystick = transform.Find("Joystick").GetComponent<Image>();
        
        Handle =  transform.GetChild(0).GetChild(0).GetComponent<Image>();
        
        //添加自定义事件监听的方法 把对应的函数和 事件 关联起来
        UIManger.AddCustomEnventListener(ControlBK, EventTriggerType.PointerDown, PointerDown);
        UIManger.AddCustomEnventListener(ControlBK, EventTriggerType.PointerUp, PointerUp);
        UIManger.AddCustomEnventListener(ControlBK, EventTriggerType.Drag, Drag);
    }


    private void PointerDown(BaseEventData data) 
    {
        //Debug.Log("我被按下了");
    }
    private void PointerUp(BaseEventData data)
    {
        Handle.transform.localPosition = Vector3.zero;
        //Debug.Log("我抬起来了");
        EventCenter.BroadCast<Vector2>("JC", Vector2.zero);
    }
    private void Drag(BaseEventData data)
    {
        Vector2 localPos;
        //将一个屏幕空间点转换为 RectTransform 的本地空间中位于其矩形平面上的一个位置(将其转换为ControlBK 范围内的位置)
        RectTransformUtility.ScreenPointToLocalPointInRectangle(
            Joystick.rectTransform,//你想改变位置对象(handle)的 父对象(joystick)
            (data as PointerEventData).position,//得到当前鼠标的位置
            (data as PointerEventData).enterEventCamera,//UI用的摄像机
            out localPos);//的到一个转换来的 相对坐标
        Handle.transform.localPosition = localPos;

        if (localPos.magnitude > MaxL)
            Handle.transform.localPosition = localPos.normalized * MaxL;

        //EventCenter.Addlistener<Vector2>("yaogan", localPos.normalized);
        EventCenter.BroadCast<Vector2>("JC", localPos.normalized);
    }
}

④The test script is mounted on the object to be moved

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class text : MonoBehaviour
{
    private Vector3 dir;
    // Start is called before the first frame update
    private void Awake()
    {
        EventCenter.Addlistener<Vector2>("JC", CheckDirChange);
    }
    private void OnDestroy()
    {
        EventCenter.RemoveListener<Vector2>("JC", CheckDirChange);
    }
    // Update is called once per frame
    void Update()
    {
        this.transform.Translate(dir*Time.deltaTime, Space.World);
    }

    private void CheckDirChange(Vector2 dir) 
    {
        this.dir.x = dir.x;
        this.dir.z = dir.y;

    } 
}

Guess you like

Origin blog.csdn.net/m0_52021450/article/details/123443561