Unity UGUI 问题汇总
前言
前段时间做了一个纯UI界面切换的项目,切换逻辑又比较复杂,几十个页面不同的动效,每个页面又有大量的图片和文字,使用UGUI与DOTween插件,遇到的一些问题记录下来。
问题
DOTween在UGUI中的移动问题
第一种情况
当一个UI父物体的RectTransform为全屏,如下图
则他的子物体PosX
、PosY
,PosZ
为本身的localPosition
也就是SomeoneGameObject.transform.localPosition
的值,故而使用SomeoneGameObject.transform.DOLocalMove
是没有问题的。
移动代码为
//移动localPosition
SomeoneGameObject.transform.DOLocalMove(SomeoneGameObject.transform.localPosition, 2f);
第二种情况
父物体的RectTransform
不为全屏模式,如下图。
此时子物体的PosX
、PosY
,PosZ
为本身 SomeoneGameObject.GetComponent<RectTransform>().anchoredPosition
的值。
此时的移动可以使用
//获取移动UI的RectTransform
RectTransform rectTransform = someoneObject.GetComponent<RectTransform>();
//获取当前的anchoredPosition
Vector2 myPos = rectTransform.anchoredPosition;
//设置结束位置
Vector2 endPos = Vector2.zero;
//使用DoTween让V2变量变化
DOTween.To(() => myPos, x => myPos= x, endPos, 1).OnUpdate(() =>
{
//每帧进行位置赋值
rectTransform.anchoredPosition = vector2;
});
显示隐藏大界面时使用CanvasGroup而不是SetActive
SetActive
会导致卡顿(当一个大界面在Enable时,需要重建大量内部数据,如Mask、Scroll View,导致了异常的卡顿,会卡主线程),而设置CanvasGroup
则没有这个问题,带来的影响只是加载场景变慢一点,但是场景加载完毕后是流畅的。
//拿到CanvasGroup组件
CanvasGroup canvasGroup = SomeTransform.GetComponent<CanvasGroup>();
//获取当前透明度
float myFloat = canvasGroup.alpha;
//动画期间屏蔽点击
canvasGroup.interactable = false;
//动画期间使UI不阻挡鼠标(点击)事件
canvasGroup.blocksRaycasts = false;
//让myFloat从当前透明度过渡到1,用时2s
DOTween.To(() => myFloat, c => myFloat = c, 1f, 2).OnUpdate(() => {
//每帧都要重新赋值透明度
canvasGroup.alpha = myFloat;
}).OnComplete(() => {
//动画期间屏蔽点击
canvasGroup.interactable = true;
//动画期间使UI不阻挡鼠标(点击)事件
canvasGroup.blocksRaycasts = true;
}).SetEase(Ease.InOutQuad);
便捷的控制层级
当一个界面出现时,可以使用SomeoneGameObject.transform.SetAsLastSibling();
将其列为最前方的一个,也就是Hierarchy中最靠下面的一个,反之SomeoneGameObject.transform.SetAsFirstSibling();
会被放置到当前目录树的最顶上,也就是最下层。
CanvasGroup交叉显隐问题
当一个界面包含CanvasGroup
而其子界面也包含CanvasGroup
时,当父物体CanvasGroup
与子物体CanvasGroup
的alpha
同时改变的时候,需要注意,每个UI最终的透明度是由此值和自身的alpha
数值相乘得到。此时可以勾选子物体CanvasGroup的Ignore Parent Groups
忽略父级对象中的CanvasGroup的设置。
对添加Mask组件的UI进行透明度变化
需要取消勾选Show Mask Graphic
,移除对Mask的渲染
异步场景切换延迟
当异步加载场景开始之前建议在IEnumerator
中加一个小等待,尤其是大场景加载之前,否则会导致UI出不来或者逻辑卡顿问题(会卡到主线程,当你的场景足够大)。
附上一个简单的异步场景加载:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
/// <summary>
/// 场景管理器
/// </summary>
public class ScenesManager : Singleton<ScenesManager>
{
/// <summary>
/// 将当前进度扔出去的方法Action
/// </summary>
public Action<int> loadProgressEvent;
/// <summary>
/// 场景加载结束Action
/// </summary>
public Action loadEndEvent;
/// <summary>
/// 加载场景的Async
/// </summary>
AsyncOperation loadSceneAsync;
/// <summary>
/// 虚假进度
/// </summary>
private int falseNumber;
/// <summary>
/// 外界获取的虚假进度
/// </summary>
public int FalseNumber
{
get {
return falseNumber;
}
set {
falseNumber = value;
loadProgressEvent?.Invoke(falseNumber);
}
}
/// <summary>
/// 要加载的场景名称
/// </summary>
private string loadSceneName;
/// <summary>
/// 真实进度
/// </summary>
private int trueNum = 0;
/// <summary>
/// 异步加载某一个场景
/// </summary>
/// <param name="scenename"></param>
public void LoadScenes(string scenename)
{
loadSceneName = scenename;
StartCoroutine(LoadScenesEnumerator());
}
/// <summary>
/// 加载
/// </summary>
/// <returns></returns>
IEnumerator LoadScenesEnumerator()
{
//这里不等一下会卡主线
yield return new WaitForSeconds(0.1f);
loadSceneAsync = SceneManager.LoadSceneAsync(loadSceneName, LoadSceneMode.Single);
// 禁止载入后自己切换
loadSceneAsync.allowSceneActivation = false;
while (loadSceneAsync.progress < 0.9f)
{
//加载进度
trueNum = (int)(loadSceneAsync.progress * 100);
while (FalseNumber < trueNum)
{
FalseNumber=FalseNumber+1;
//等待时长
yield return new WaitForSeconds(0.1f);
}
}
trueNum = 100;
//增加平滑效果
while (FalseNumber < trueNum)
{
FalseNumber = FalseNumber + 1;
//等待时长
yield return new WaitForSeconds(0);
}
//加载场景激活 当虚假值完成后,才算加载完成
loadSceneAsync.allowSceneActivation = true;
yield return new WaitForSeconds(0.1f);
LoadEndFunction();
}
/// <summary>
/// 加载结束
/// </summary>
private void LoadEndFunction()
{
loadEndEvent?.Invoke();
loadSceneAsync = null;
//进度变0
trueNum = 0;
FalseNumber = 0;
}
}