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
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