今天整理需求,有一些UI拖拽的功能,之前看到项目是用Rect.Contains()来判断UI的区域,但是这个方法需要根据分辨率处理。
我看到很多文章写得都有点复杂了,各种计算各种转换,我的脚本非常简单,同事支持点击和拖拽,希望可以帮到需要的小伙伴。
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
public enum OptionType
{
Click,
Drag,
}
public class OptionButton : MonoBehaviour, IPointerDownHandler
{
public OptionType optionType;
private RectTransform rect;
[Tooltip("目标对象")]
public RectTransform targetRect;
[Tooltip("移动时候的层级对象")]
public Transform MoveParent;
private Transform Parent;
private Vector2 initPos;
private bool isClick;
[Tooltip("答案")]
public string Answer;
public Action<OptionButton, string> ChooseAnswerCallBack;
void Awake()
{
rect = GetComponent<RectTransform>();
Parent = transform.parent;
initPos = rect.localPosition;
}
public void Init()
{
Parent = transform.parent;
initPos = rect.localPosition;
}
public void Restore()
{
transform.SetParent(Parent);
rect.localPosition = initPos;
}
void Update()
{
if (optionType == OptionType.Drag)
{
//#if UNITY_EDITOR || UNITY_STANDALONE
if (Input.GetMouseButton(0) && isClick)
{
//如果Canvas的渲染模式是Overlay,注意用position
//rect.position = Input.mousePosition;
//如果Canvas的渲染模式是Camera
Vector3 worldPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
Camera camera = GameController.Instance.Camera;
if (camera == null)
{
camera = Assets.Script.Test.GameController.Instance.Camera;
}
rect.localPosition = camera.transform.InverseTransformPoint(worldPos);
}
if (Input.GetMouseButtonUp(0))
{
TouchUp();
}
//#else
//if (Input.GetTouch(0).phase == TouchPhase.Moved && isClick)
//{
//rect.position = Input.GetTouch(0).position;
//}
//if (Input.GetTouch(0).phase == TouchPhase.Canceled)
//{
//TouchUp();
//}
//#endif
}
}
public void OnPointerDown(PointerEventData eventData)
{
if (optionType == OptionType.Drag)
{
isClick = true;
if (MoveParent)
{
transform.SetParent(MoveParent);
}
transform.SetAsLastSibling();
}
else if (optionType == OptionType.Click)
{
ChooseAnswerCallBack?.Invoke(this, Answer);
}
}
private void TouchUp()
{
isClick = false;
if (CheckMoveToTarget())
{
//TODO拖到目标时的处理
ChooseAnswerCallBack?.Invoke(this, Answer);
//Restore();
}
else
{
Restore();
}
}
/// <summary>
/// 检测是否拖动到目标
/// </summary>
/// <returns></returns>
private bool CheckMoveToTarget()
{
if (targetRect)
{
return RectTransformUtility.RectangleContainsScreenPoint(targetRect, rect.position);
}
else
{
return false;
}
}
private void OnDestroy()
{
ChooseAnswerCallBack = null;
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
[CustomEditor(typeof(OptionButton))]
public class OptionButtonEditor : Editor
{
private SerializedObject obj;
private OptionButton optionButton;
private SerializedProperty type;
private SerializedProperty targetRect;
private SerializedProperty MoveParent;
private SerializedProperty Answer;
void OnEnable()
{
obj = new SerializedObject(target);
targetRect = obj.FindProperty("targetRect");
MoveParent = obj.FindProperty("MoveParent");
Answer = obj.FindProperty("Answer");
}
public override void OnInspectorGUI()
{
optionButton = (OptionButton)target;
optionButton.optionType = (OptionType)EditorGUILayout.EnumPopup("OptionType", optionButton.optionType);
if (optionButton.optionType == OptionType.Drag)
{
EditorGUILayout.PropertyField(targetRect);
EditorGUILayout.PropertyField(MoveParent);
}
EditorGUILayout.PropertyField(Answer);
obj.ApplyModifiedProperties();
if (GUI.changed)
{
AssetDatabase.SaveAssets();
EditorUtility.SetDirty(target);
AssetDatabase.Refresh();
}
}
}
利用这个方式我们还可以不通过UI的射线检测就可以判断点击事件,可以不用Button的点击事件了,从而优化了一部分性能。
注1:拖动的对象必须打开射线检测,目标对象不需要射线检测。