简介
能实现用鼠标拖动UI位置,滚轮缩放UI尺寸的功能。支持在缩放的时候让UI向光标位置靠拢,不过没有做平滑处理。
可以满足简单的需求,至少能避免一些界面遮挡视野带来的不悦。
使用要求
目标对象必须具有Rect Transform组件。我的环境是unity2021+vs2019
解释文档
CanMove:允许UI被光标拖拽移动。若您不希望UI被移动,请务必同时关闭“IsFocusWhenScale”聚焦功能,避免UI位置的移动。
CanScale:允许使用滚轮缩放UI的大小。此功能通过增减UI物体的localScale实现。
ScaleSpeed:缩放速度,即滚轮每个段落将改变UI的localScale大小。如在ScaleSpeed为0.1f时,正向滚动滚轮一个段落将使UI的localScale增加0.1f。
LimitSize.x:缩放后最小尺寸
LimitSize.y:缩放后最大尺寸
IsFucusWhenScale:是否启用聚焦(在缩放时,使UI向光标靠近)
FocusSpeed:聚焦速度,绝对值越大,聚焦越快。默认为正值。为负值时,缩放会远离光标。
AllowOverScreen:当光标在画面外时,是否允许操作生效。特别地,当您不希望用户将UI拖拽至画面外时,禁用此选项。
ResetOnDisable:当UI物件被禁用或处于非活动状态时,是否将其位置和尺寸复原。当您使用Destroy销毁该UI物件或使用SetActive(false)隐藏它(无论在这之前activeSelf是否已经为false),亦或是运行时在编辑器中手动隐藏该Ui,都会触发此功能。
代码
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
[RequireComponent(typeof(RectTransform))]
public class UIMove : MonoBehaviour, IDragHandler, IBeginDragHandler, IScrollHandler
{
[Header("移动")]
[SerializeField]
private bool canMove = true;
[Header("缩放")]
[SerializeField]
private bool canScale = false;
/// <summary>
/// 滚轮滚动一个段落缩放的本地大小倍数
/// </summary>
[SerializeField]
[Tooltip("缩放的速度")]
private float scaleSpeed = 0.1f;
[SerializeField]
[Tooltip("x:最小尺寸 y:最大尺寸")]
private Vector2 limitSize = new Vector2(0.5f,2);
[Header("聚焦")]
[Tooltip("在缩放时,使UI中心向光标靠近")]
[SerializeField]
private bool isFocusWhenScale = true;
/// <summary>
/// 缩放时,靠近的速度
/// </summary>
[SerializeField]
private float focusSpeed = 0.3f;
[Header("其他")]
[SerializeField]
[Tooltip("允许光标超出屏幕后生效")]
private bool AllowOverScreen = false;
[SerializeField]
[Tooltip("非活动(Disable)时复原位置和尺寸")]
private bool ResetOnDisable = true;
RectTransform rt;
//拖拽前UI的位置
Vector3 startObjectPos;
//每次拖拽开始前光标的位置
Vector3 startMousePos;
//每次拖拽过程中光标的位置
Vector3 nowMousePos;
//UI的原始尺寸的位置,用于复原
Vector3 _originScale;
Vector3 _originPoint;
void Start()
{
rt = gameObject.GetComponent<RectTransform>();
_originPoint = rt.localPosition;
_originScale = rt.localScale;
}
public void OnScroll(PointerEventData eventData)
{
if(canScale) SetSize(eventData);
}
public void SetSize(PointerEventData eventData)
{
float speed = focusSpeed / rt.localScale.x;
//注意eventData.enterEventCamera与eventData.pressEventCamera的不同,在这里踩了坑,导致第一次缩放出问题
RectTransformUtility.ScreenPointToWorldPointInRectangle(rt, eventData.position, eventData.enterEventCamera, out nowMousePos);
float OffsetX = nowMousePos.x - transform.position.x;
float OffsetY = nowMousePos.y - transform.position.y;
float PivotX = OffsetX / rt.rect.width / transform.localScale.x;
float PivotY = OffsetY / rt.rect.height / transform.localScale.y;
if (isFocusWhenScale)
{
rt.pivot += new Vector2(PivotX, PivotY);
transform.position += new Vector3(OffsetX, OffsetY, 0) * speed;
}
if (Input.GetAxis("Mouse ScrollWheel") > 0)
{
transform.localScale += (transform.localScale.x >= limitSize.y ? Vector3.zero : Vector3.one * scaleSpeed);
}
else if (Input.GetAxis("Mouse ScrollWheel") < 0)
{
transform.localScale += (transform.localScale.x <= limitSize.x ? Vector3.zero : Vector3.one * -scaleSpeed);
}
}
public void OnBeginDrag(PointerEventData eventData)
{
//记录拖拽前UI位置,鼠标位置
startObjectPos = rt.position;
RectTransformUtility.ScreenPointToWorldPointInRectangle(rt, eventData.position, eventData.pressEventCamera, out startMousePos);
}
public void OnDrag(PointerEventData eventData)
{
if (canMove) SetDraggedPosition(eventData);
}
private void SetDraggedPosition(PointerEventData eventData)
{
//检测鼠标位置是否在UI内
if (RectTransformUtility.ScreenPointToWorldPointInRectangle(rt, eventData.position, eventData.pressEventCamera, out nowMousePos))
{
//记录光标移动的路程,加到UI上,实现位置跟随
Vector3 targetPos = startObjectPos + nowMousePos - startMousePos;
//若光标移动到了屏幕外,根据设AllowOverScreen决定是否跟随
if (AllowedMove())
{
rt.position = targetPos;
}
}
}
/// <summary>
/// 检测鼠标是否在屏幕内
/// </summary>
/// <param name="targetPos"></param>
/// <param name="targetlocalScale"></param>
/// <returns></returns>
private bool IsOverScreen()
{
bool isOver;
isOver = Input.mousePosition.x > Screen.width || Input.mousePosition.x < 0
|| Input.mousePosition.y > Screen.height || Input.mousePosition.y < 0;
return isOver;
}
private bool AllowedMove()
{
return !IsOverScreen() || AllowOverScreen;
}
/// <summary>
/// 将其位置和尺寸重置到初始状态
/// </summary>
public void ResetPosAndScale()
{
this.transform.localPosition = _originPoint;
this.transform.localScale = _originScale;
}
/// <summary>
/// 隐藏时复原位置和尺寸
/// </summary>
public void OnDisable()
{
if(ResetOnDisable) ResetPosAndScale();
}
}
文章版权归作者所有





暂无评论内容