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