一、原因
前一阵突然想到Unity里面的UI,在我的印象里面没有直接控制UI界面的方法,去实现在UI边侧界面的进入退出时的淡入淡出推进效果。
想来目前主流有两种方法:
1.用动画状态机设置其变化转化,然后通过bool值的代码控制。
2.直接对面板整体做UI动画,然后用代码控制其按钮在点击事件时出现对应UI效果。
其实在我看来这两种方法是一样的,于我个人更倾向于第三种方法:代码全功能实现。
原因有以下几点:
1.状态机的灵活性不如代码实现,动态调整动画参数较困难。
- 时间复杂度:状态机的过渡和更新通常是常数时间复杂度O(1)。
- 空间复杂度:需要额外的内存来存储动画状态和过渡信息。
2.UI动画参数在运行时不易调整,多个动画需要分别管理,增加维护成本。
- 时间复杂度:动画播放通常是常数时间复杂度O(1)。
- 空间复杂度:需要存储动画曲线和关键帧数据。
3.全代码实现(说实话,真不想造轮子了,能不能让Unity官方将对应UI及功能集成到编辑引擎内部啊!)
- 时间复杂度:取决于实现,通常为O(n),其中n为动画帧数。
- 空间复杂度:主要是代码占用,通常较小。
二、实践操作过程
首先我们搭建一下测试场景:
1.首先在默认场景下直接造一个面板(Panel)
然后在面板下创建两个图片(test-FImage、test-RImage)模拟两侧UI面板,为了更好的模拟面板效果,我给它们加上了canvas group组件。
接着添加一个测试的button
最后创建一个空物体用来挂载我们的C#脚本。
三、代码编写
using UnityEngine;
using UnityEngine.UI;
public class PanelFadeController : MonoBehaviour
{
public CanvasGroup leftPanel; // 左侧面板的CanvasGroup组件
public CanvasGroup rightPanel; // 右侧面板的CanvasGroup组件
public Button toggleButton; // 用于触发面板动画的按钮
public float fadeDuration = 1f; // 淡入淡出的持续时间
public Vector3 leftPanelStartOffset; // 左侧面板的初始偏移
public Vector3 rightPanelStartOffset; // 右侧面板的初始偏移
private bool isVisible = false; // 面板当前的可见状态
private Vector3 leftPanelInitialPosition; // 左侧面板的初始位置
private Vector3 rightPanelInitialPosition; // 右侧面板的初始位置
void Start()
{
if (toggleButton != null)
{
toggleButton.onClick.AddListener(TogglePanels); // 为按钮添加点击事件监听器
}
if (leftPanel != null)
{
leftPanelInitialPosition = leftPanel.transform.localPosition; // 存储左侧面板的初始位置
}
if (rightPanel != null)
{
rightPanelInitialPosition = rightPanel.transform.localPosition; // 存储右侧面板的初始位置
}
}
void TogglePanels()
{
isVisible = !isVisible; // 切换面板的可见状态
StopAllCoroutines(); // 停止所有正在运行的协程
StartCoroutine(FadeAndMovePanels(isVisible)); // 启动新的协程进行淡入淡出和移动
}
private System.Collections.IEnumerator FadeAndMovePanels(bool show)
{
if (show)
{
SetPanelActive(true); // 如果是淡入,则激活面板
}
float startAlpha = show ? 0 : 1; // 设置初始透明度
float endAlpha = show ? 1 : 0; // 设置结束透明度
float elapsedTime = 0f; // 记录经过的时间
// 计算左侧面板的起始和结束位置
Vector3 leftStartPos = show ? leftPanelInitialPosition + leftPanelStartOffset : leftPanelInitialPosition;
Vector3 leftEndPos = show ? leftPanelInitialPosition : leftPanelInitialPosition + leftPanelStartOffset;
// 计算右侧面板的起始和结束位置
Vector3 rightStartPos = show ? rightPanelInitialPosition + rightPanelStartOffset : rightPanelInitialPosition;
Vector3 rightEndPos = show ? rightPanelInitialPosition : rightPanelInitialPosition + rightPanelStartOffset;
while (elapsedTime < fadeDuration)
{
elapsedTime += Time.deltaTime; // 增加经过的时间
float alpha = Mathf.Lerp(startAlpha, endAlpha, elapsedTime / fadeDuration); // 计算当前透明度
SetPanelAlpha(alpha); // 设置面板透明度
if (leftPanel != null)
{
// 计算并设置左侧面板的位置
leftPanel.transform.localPosition = Vector3.Lerp(leftStartPos, leftEndPos, elapsedTime / fadeDuration);
}
if (rightPanel != null)
{
// 计算并设置右侧面板的位置
rightPanel.transform.localPosition = Vector3.Lerp(rightStartPos, rightEndPos, elapsedTime / fadeDuration);
}
yield return null; // 等待下一帧
}
SetPanelAlpha(endAlpha); // 确保最终透明度设置正确
if (!show)
{
SetPanelActive(false); // 如果是淡出,则失活面板
}
}
private void SetPanelAlpha(float alpha)
{
if (leftPanel != null)
{
leftPanel.alpha = alpha; // 设置左侧面板的透明度
leftPanel.blocksRaycasts = alpha > 0; // 根据透明度设置是否阻挡射线
}
if (rightPanel != null)
{
rightPanel.alpha = alpha; // 设置右侧面板的透明度
rightPanel.blocksRaycasts = alpha > 0; // 根据透明度设置是否阻挡射线
}
}
private void SetPanelActive(bool active)
{
if (leftPanel != null)
{
leftPanel.gameObject.SetActive(active); // 激活或失活左侧面板
}
if (rightPanel != null)
{
rightPanel.gameObject.SetActive(active); // 激活或失活右侧面板
}
}
}
以上代码都添加了详细解释说明。
值得注意的是:
代码中的左右面板偏移量参数:以左右为例,向右移动参数为正,向左移动参数为负。轴向对应移动UI的轴向;其它轴和方向同理。
将对应组件拖拽到脚本对应变量位置如下图:
四、结果展示
pillow-界面淡入淡出