Unity 仮想ジョイスティック (イベント ディスパッチャーの実装)

目次

UIを構築する

コードを書く

UIを構築する

 ①ボタン認識の検出範囲としてControlAreaという名前の画像を作成し、透明度を1に設定します(透明度が0の場合はイベント検出は行われません)

②下図のように画像を追加し、親子関係に名前を付けます適切な素材を見つけて、対応する画像にドラッグして適切なサイズを設定します(好みと素材のサイズに応じて自分で設定します)

③ジョイスティックのアンカーポイント(花)を選択し、解像度の変更によってボタンの位置が変わらないように左下隅に配置することに注意してください。中心点(ピボット)の位置は(0.5、 0.5) 以下の図に示すように

 

脚本を実現する

① シングルトン UIManger を使用していくつかのパブリック メソッドを管理する

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);
    }

}

②イベントディスパッチャー

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("广播错误!!!"); }
        }
    }
}

③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);
    }
}

④移動対象オブジェクトにテストスクリプトを実装する

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;

    } 
}

おすすめ

転載: blog.csdn.net/m0_52021450/article/details/123443561