Unity开发中经常有点击Button,弹窗提示界面,然后点击任意区域关闭提示界面并且提示界面下一层的事件依然可以触发;需要点击事件向下传递;UGUI对此支持相对好处理,NGUI本身对此支持不好;这里提供一个方法:
public class UIClickOutWidget : MonoBehaviour
{
public UIWidget BGWidget;//界面widget
public UIWidget TargetWidget;//触发按钮widget
public float ClickDistance = 10f; //判断点击的范围
bool beganInWidget = false; //记录触屏开始是否在BGWidget内(当在BGWidget内,需要点击才关闭,滑动则不关闭)
Vector3 beganPos = Vector3.zero;//记录触屏开始的坐标
// Update is called once per frame
void Update ()
{
if (!gameObject.activeSelf)
return;
if(Application.platform == RuntimePlatform.Android || Application.platform == RuntimePlatform.IPhonePlayer)
{
if (Input.touchCount > 0)
{
Touch touch = Input.GetTouch(0);
if (touch.phase == TouchPhase.Began || touch.phase == TouchPhase.Ended || touch.phase == TouchPhase.Canceled)
{
if(touch.phase == TouchPhase.Began)
{
beganPos = touch.position;
beganInWidget = IsClickInWidget(touch.position, BGWidget);
//触屏开始,如果不在界面BGWidget内,则直接关闭
if(!beganInWidget)
{
//如果点击在触发按钮上,则不关闭;需要通过触发按钮关闭
bool isInTarWidget = IsClickInWidget(touch.position, TargetWidget);
if (!isInTarWidget)
DeactiveUI();
}
}
else
{
//触屏结束,是点击则关闭界面
if(IsClick(beganPos, touch.position))
{
//如果点击在触发按钮上,则有触发按钮关闭;此处不关闭
bool isInTarWidget = IsClickInWidget(touch.position, TargetWidget);
if (!isInTarWidget)
DeactiveUI();
}
else if(!beganInWidget)//触屏结束,不是点击并且起始点不在BGWidget内,则关闭
{
//如果点击在触发按钮上,则有触发按钮关闭;此处不关闭
bool isInTarWidget = IsClickInWidget(touch.position, TargetWidget);
if (!isInTarWidget)
DeactiveUI();
}
}
}
}
}
else
{
if (Input.GetMouseButtonUp(0) || Input.GetMouseButtonDown(0))
{
if (Input.GetMouseButtonDown(0))
{
beganPos = Input.mousePosition;
beganInWidget = IsClickInWidget(Input.mousePosition, BGWidget);
//触屏开始,如果不在界面BGWidget内,则直接关闭
if (!beganInWidget)
{
//如果点击在触发按钮上,则不关闭;需要通过触发按钮关闭
bool isInTarWidget = IsClickInWidget(Input.mousePosition, TargetWidget);
if(!isInTarWidget)
DeactiveUI();
}
}
else
{
//触屏结束,是点击则关闭界面
if (IsClick(beganPos, Input.mousePosition))
{
//如果点击在触发按钮上,则有触发按钮关闭;此处不关闭
bool isInTarWidget = IsClickInWidget(Input.mousePosition, TargetWidget);
if (!isInTarWidget)
DeactiveUI();
}
else if (!beganInWidget)//触屏结束,不是点击并且起始点不在BGWidget内,则关闭
{
//如果点击在触发按钮上,则有触发按钮关闭;此处不关闭
bool isInTarWidget = IsClickInWidget(Input.mousePosition, TargetWidget);
if (!isInTarWidget)
DeactiveUI();
}
}
}
}
}
void DeactiveUI()
{
gameObject.SetActive(false);
--添加需要关闭的UI界面
beganPos = Vector3.zero;
beganInWidget = false;
}
bool IsClick(Vector3 beganPos, Vector3 endPos)
{
if (beganPos == Vector3.zero)
return true;
Vector3 temp = endPos - beganPos;
return Mathf.Abs(temp.x) < ClickDistance && Mathf.Abs(temp.y) < ClickDistance;
}
//判断点击坐标是否在wdiget范围内
bool IsClickInWidget(Vector3 pos, UIWidget widget)
{
if (widget == null)
return false;
Vector3 localPos = widget.transform.localPosition;
Vector3 screenPos = UICamera.currentCamera.ScreenToWorldPoint(pos);
Vector3 mousePos = widget.transform.parent.InverseTransformPoint(screenPos);
float minX = localPos.x - widget.width / 2;
float maxX = localPos.x + widget.width / 2;
float minY = localPos.y - widget.height / 2;
float maxY = localPos.y + widget.height / 2;
if (widget.pivot == UIWidget.Pivot.Left || widget.pivot == UIWidget.Pivot.BottomLeft || widget.pivot == UIWidget.Pivot.TopLeft)
{
minX = localPos.x;
maxX = localPos.x + widget.width;
}
else if(widget.pivot == UIWidget.Pivot.Right || widget.pivot == UIWidget.Pivot.BottomRight || widget.pivot == UIWidget.Pivot.TopRight)
{
minX = localPos.x - widget.width;
maxX = localPos.x;
}
if (widget.pivot == UIWidget.Pivot.Bottom || widget.pivot == UIWidget.Pivot.BottomLeft || widget.pivot == UIWidget.Pivot.BottomRight)
{
minY = localPos.y;
maxY = localPos.y + widget.height;
}
else if (widget.pivot == UIWidget.Pivot.Top || widget.pivot == UIWidget.Pivot.TopLeft || widget.pivot == UIWidget.Pivot.TopRight)
{
minY = localPos.y - widget.height;
maxY = localPos.y;
}
if (mousePos.x < maxX && mousePos.x > minX && mousePos.y < maxY && mousePos.y > minY)
return true;
return false;
}
}
总体思路:去掉背景点击BoxCollider,改由触摸事件来关闭界面;这样就可以达到关闭界面的同时触发下一层的功能的目的;
这个方法有缺陷:因为基于触摸事件所以当此界面上层有覆盖界面时依然会执行逻辑;对此可能加入UIPanel的depth进行判断;不知道还有什么好的解决方法!待完善