[Unity Daily Inspiration] The first issue: IPointer_?_Handler interface realizes interesting mouse interaction

In this issue, a new column, Up&Up, is set up, which is dedicated to some of the fun ideas I use in projects on weekdays, or the re-enactment of interesting functions that have not yet been realized.

The first phase: IPointerClickHandler, IPointerEnterHandler, IPointerExitHandler, etc. in EventSystems... detection and function control of mouse callback events.

Table of contents

1. Summary of interface and its function method

〇EventSystems

①IPointerClickHandler

②IPointerEnter/ExitHandler

③IPointerUp/DownHandler

2. Actual cases

①Suspended prompt UI

②Drag UI

③3D object response


1. Summary of interface and its function method

〇EventSystems

EventSystems are mainly responsible for handling input, raycasting and sending events.

According to the literal meaning, it can also be seen that ES is responsible for handling events in the Unity scene. A scene should contain only one EventSystem.

When the EventSystem starts, it searches for   any  BaseInputModule attached to the same GameObject and adds it to an internal list. Here BaseInputModule (basic input module class) all relational input modules in EventSystem inherit from this class.

On update, each additional module receives an  UpdateModules  call where the module can modify internal state. After all modules have been updated, the active module executes a Process call. Custom module processing is now available.

To take the simplest example, whenever developers create a new UI, they will automatically create a new object named EventSystem. Without this object, various operations on the UI will be invalid.

①IPointerClickHandler

Interface to implement (if you want to receive  OnPointerClick  callback)

Use the IPointerClickHandler interface to handle click input using the OnPointerClick callback. Make sure an event system exists in the scene to support click detection. For click detection on non-UI game objects, make sure to  attach a PhysicsRaycaster  to the camera.

An example of use is shown in Figure 1-1 below. Refer to EventSystems in the head, and PointerClickHandler in the back of the Mono behavior class. Alt+enter can quickly implement the interface, and you should be able to type it yourself. And the effect we want to achieve next is to get some kind of feedback when the mouse clicks on the object or UI.

Figure 1-1 IPointerClickHandler interface

So we write the logic under the implemented OnPointerClick method. After writing, add collision boxes for both objects in the scene, add PhysicsRaycaster to the camera according to the official statement, and add the written script to the object you want to achieve click detection, as shown in Figure 1-2, 1-3 .

 Figure 1-2 Collision addition

  Figure 1-3 PhysicsRaycaster added

The specific test code is as follows, use Debug to understand the principle

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

public class ESIPointerTest : MonoBehaviour, IPointerClickHandler
{
    public void OnPointerClick(PointerEventData eventData)
    {
        Debug.Log("你点击到了:" + name + " " + tag);
    }
}

Run and click on two objects to view the effect, as shown in Figure 1-4.

Figure 1-4 OnPointerClick method feedback

②IPointerEnter/ExitHandler

Interface to implement (if you wish to receive  an OnPointerExit  callback)

Used to detect when the mouse starts hovering over a game object. To detect when the mouse stops hovering over a game object, use  IPointerExitHandler .

Note: Enter and Exit themselves do not need to appear at the same time.

The detection principle is actually the same as the collision detection, similar to Tigger or Collision's OnCollisionEnter, OnTriggerEnter, etc., except that the monitoring object of the collision box trigger is replaced by the mouse, which is equivalent to writing a camera to launch the mouse position ray detection.

Also use Debug to understand, the following code.

public class ESIPointerTest : MonoBehaviour, IPointerClickHandler, IPointerEnterHandler, IPointerExitHandler
{
    public void OnPointerClick(PointerEventData eventData)
    {
        Debug.Log("你点击到了:" + name + " " + tag);
    }

    public void OnPointerEnter(PointerEventData eventData)
    {
        Debug.Log("你的鼠标悬停在了:" + name + " " + tag + " 开始于:" + Time.time);
    }

    public void OnPointerExit(PointerEventData eventData)
    {
        Debug.Log("你的鼠标离开了:" + name + " " + tag + " 结束于:" + Time.time);
    }
}

The specific effect is as shown in the figure below.

 Figure 2-1, 3-1 mouse detection

③IPointerUp/DownHandler

Interface to implement (if you wish to receive  OnPointerUp  callbacks)

Note : In order to receive  the OnPointerUp  callback, you must also implement  the IPointerDownHandler  interface, that is, the Up and Down interfaces must be implemented at the same time.

Interface to implement (if you wish to receive  OnPointerDown  callbacks)

Detects ongoing mouse clicks until the mouse button is released. Use  IPointerUpHandler  to handle mouse button release. Unlike OnCollision/TriggerStay, it can be seen that the press is not continuously detected for a little more than a second.


2. Actual cases

Ok, now that we have an understanding of the effects and implementation principles of the above interfaces, let's try to implement a few cases.

①Suspended prompt UI

The effect is shown in Figure 1-1 below:

 Figure 1-1 Floating UI

Principle analysis:

Ⅰ To realize the generation of floating UI, use the RectTransform type variable to store and manipulate the position, size and anchoring of the rectangle (floating square UI).

    public static TipUI instance;
    private RectTransform childRectTrans;
    private float rectRefreshTime;
    public float rectIntervaTime = 0.1f; //矩形UI间隔时间,间隔时间越长事件结束后停留越久
    private bool isUI; //判断是否是UI

    void Awake()
    {
        instance = this;

        childRectTrans = transform.GetChild(0).GetComponent<RectTransform>();
        rectRefreshTime = 0.0f;
        isUI = false;
        DontDestroyOnLoad(gameObject); //防止在改变场景的时候销毁 即额外保存可以引用在别的场景
    }

Ⅱ To realize the mouse following, you need to use Input.mousePosition to control it, and detect the activation state at the same time, and deactivate it after moving out of the detection range.

    /// <summary>
    /// 设置Tip显示位置
    /// </summary>
    private void SetTipPos() {
        childRectTrans.position = Input.mousePosition;
    }

    /// <summary>
    /// 获取当前Tip激活状态
    /// </summary>
    /// <returns></returns>
    public bool GetActive() {
        return childRectTrans.gameObject.activeSelf;
    }

Ⅲ Realize the adjustment logic of judging whether the screen is out of bounds, whether it is in the upper part or lower part of the screen, then get the XY values ​​on the screen, judge which part of the screen the mouse position is in, and feedback the center position of the rectangle pivot to the floating UI before creating it.

    /// <summary>
    /// 设置Tip中心
    /// </summary>
    private void SetTipPivot() {
        int tempPivotX = ((Input.mousePosition.x <= Screen.height / 2.0f) ? 0 : 1);
        int tempPivotY = ((Input.mousePosition.x <= Screen.width / 2.0f) ? 0 : 1);
        if (childRectTrans.pivot.x != tempPivotX || childRectTrans.pivot.y != tempPivotY) {
            childRectTrans.pivot = new Vector2(tempPivotX, tempPivotY);
        }
    }

Ⅳ To hide or display the floating window TipUI, order two functions to set SetActive(true/false) according to the IPointer event introduced earlier in the article and the OnMouse event of the object.

    /// <summary>
    /// 隐藏Tip
    /// </summary>
    /// <param name="_isUI"></param>
    public void HideTip(bool _isUI) {
        if (!_isUI && (isUI && childRectTrans.gameObject.activeSelf)) return;
        childRectTrans.GetChild(0).GetComponent<UnityEngine.UI.Text>().text = "";
        childRectTrans.gameObject.SetActive(false);
    }

    /// <summary>
    /// 展示Tip
    /// </summary>
    /// <param name="_infoStr"></param>
    /// <param name="_isUI"></param>
    public void ShowTip(string _infoStr, bool _isUI) {
        if (!_isUI && (isUI && childRectTrans.gameObject.activeSelf)) return;
        childRectTrans.GetChild(0).GetComponent<UnityEngine.UI.Text>().text = _infoStr;
        childRectTrans.gameObject.SetActive(true);
        isUI = _isUI;
        rectRefreshTime = Time.time;
        SetTipPivot();
        SetTipPos();
    }

Ⅴ To realize the text on the floating window UI, you need to define additional UI information classes, and call IPointer and OnMouse at the same time to add scripts for an object that wants to realize the floating window.

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

public class TipUIInfo : MonoBehaviour,IPointerEnterHandler,IPointerExitHandler
{
    public string infoStr;

    /// <summary>
    ///  用于检测UI的悬停移开
    /// </summary>
    /// <param name="eventData"></param>
    public void OnPointerEnter(PointerEventData eventData)
    {
        Debug.LogWarning("OnPointerEnter物体和UI都可以检测,但不连续");
        TipUI.instance.ShowTip(infoStr, true);
    }

    public void OnPointerExit(PointerEventData eventData)
    {
        TipUI.instance.HideTip(true);
    }

    /// <summary>
    /// 检测物体的悬停移开
    /// </summary>
    private void OnMouseOver()
    {
        Debug.LogWarning("OnMouseOver仅检测物体,是连续的");
        if (!TipUI.instance.GetActive())
        {
            TipUI.instance.ShowTip(infoStr, false);
        }

    }

    private void OnMouseExit()
    {
        TipUI.instance.HideTip(false);
    }
}

The method of use is to mount TipUIInfo on the object or UI that wants to realize the floating UI, as shown in Figure 1-2 to set the shape of the floating UI, where UITip is the Canvas without GraphicRaycast as shown in Figure 1-3, and TipImage is an icon. After adding the two components in Figure 1-4, TipText is a text.

 Figure 1-2 UITip prefabrication

Figure 1-3 Canvas as UITip

 Figure 1-4 TipImage settings

The complete code is as follows

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

/// <summary>
/// 显示悬浮UI,优先级顺序-> UI -> 物体
/// </summary>
public class TipUI : MonoBehaviour
{
    public static TipUI instance;
    private RectTransform childRectTrans;
    private float rectRefreshTime;
    public float rectIntervaTime = 0.1f; //矩形UI间隔时间,间隔时间越长事件结束后停留越久
    private bool isUI; //判断是否是UI

    void Awake()
    {
        instance = this;

        childRectTrans = transform.GetChild(0).GetComponent<RectTransform>();
        rectRefreshTime = 0.0f;
        isUI = false;
        DontDestroyOnLoad(gameObject); //防止在改变场景的时候销毁 即额外保存可以引用在别的场景
    }

    void Start()
    {
        childRectTrans.gameObject.SetActive(false);
        Debug.Log("Rect:" + childRectTrans);
    }

    void Update()
    {
        if (childRectTrans.gameObject.activeSelf) {
            if (Time.time >= rectRefreshTime + rectIntervaTime) {
                rectRefreshTime = Time.time;
                SetTipPivot();
            }
            SetTipPos();
        }
    }

    /// <summary>
    /// 设置Tip中心
    /// </summary>
    private void SetTipPivot() {
        int tempPivotX = ((Input.mousePosition.x <= Screen.height / 2.0f) ? 0 : 1);
        int tempPivotY = ((Input.mousePosition.x <= Screen.width / 2.0f) ? 0 : 1);
        if (childRectTrans.pivot.x != tempPivotX || childRectTrans.pivot.y != tempPivotY) {
            childRectTrans.pivot = new Vector2(tempPivotX, tempPivotY);
        }
    }

    /// <summary>
    /// 设置Tip显示位置
    /// </summary>
    private void SetTipPos() {
        childRectTrans.position = Input.mousePosition;
    }

    /// <summary>
    /// 获取当前Tip激活状态
    /// </summary>
    /// <returns></returns>
    public bool GetActive() {
        return childRectTrans.gameObject.activeSelf;
    }

    /// <summary>
    /// 隐藏Tip
    /// </summary>
    /// <param name="_isUI"></param>
    public void HideTip(bool _isUI) {
        if (!_isUI && (isUI && childRectTrans.gameObject.activeSelf)) return;
        childRectTrans.GetChild(0).GetComponent<UnityEngine.UI.Text>().text = "";
        childRectTrans.gameObject.SetActive(false);
    }

    /// <summary>
    /// 展示Tip
    /// </summary>
    /// <param name="_infoStr"></param>
    /// <param name="_isUI"></param>
    public void ShowTip(string _infoStr, bool _isUI) {
        if (!_isUI && (isUI && childRectTrans.gameObject.activeSelf)) return;
        childRectTrans.GetChild(0).GetComponent<UnityEngine.UI.Text>().text = _infoStr;
        childRectTrans.gameObject.SetActive(true);
        isUI = _isUI;
        rectRefreshTime = Time.time;
        SetTipPivot();
        SetTipPos();
    }
}

②Drag UI

Different from the several IPointer interfaces mentioned earlier, there are also interfaces of the IBegin..IDrag.. class, which are inherited from the event system interface of IEventSystemHandler. The specific implementation effect is shown in Figure 2-1.

Figure 2-1 UI drag and drop

Just hang the script, the complete code is as follows:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
public class DragUI : MonoBehaviour, IPointerClickHandler, IBeginDragHandler, IDragHandler, IEndDragHandler
{
    private RectTransform rectTransform;
    void Start()
    {
        rectTransform = GetComponent<RectTransform>();
    }

    public void OnBeginDrag(PointerEventData eventData)
    {
        Debug.Log("开始拖拽");
    }

    public void OnDrag(PointerEventData eventData)
    {
        //以备反馈点输出
        Vector3 uiPosition;

        //将一个屏幕空间点转换为世界空间中位于给定 RectTransform 平面上的一个位置
        RectTransformUtility.ScreenPointToWorldPointInRectangle(rectTransform, eventData.position, eventData.enterEventCamera, out uiPosition);

        //将赋值位置的uiPosition反馈回当前具有RectTransform的UI.Position
        rectTransform.position = uiPosition;
    }

    public void OnEndDrag(PointerEventData eventData)
    {
        Debug.Log("结束拖拽");
    }

    public void OnPointerClick(PointerEventData eventData)
    {
        Debug.LogWarning("检测到点击");
    }
}

③3D object response

pending upgrade

Guess you like

Origin blog.csdn.net/PinaColadaONE/article/details/124322308