[Unity 3d] 60 行代码实现通用模态提示窗口
2022-09-09 本文已影响0人
雨落随风
在本文,笔者将教大家使用 60 行不到的代码实现一个通用的模态提示窗口(modal notification window)
前言:
在软件工程中,提示窗口往往必不可少,越多的提示代表越好的用户体验,那如何用最精悍的代码,实现相对够用的模态提示窗口呢?
如何量化“相对够用”:
- 首先,能够方便弹窗展示提示语
- 其次,能够以组合形式展示 “确定”、“取消” 按钮,并捕获用户的选择。
- 然后,有按钮的模态窗口,点击空白区域需要震动消息窗口实现提醒。
- 接着,没有按钮的模态窗口,点击空白区域关闭,或者震动消息窗口而不关闭(由逻辑内决定何时关闭)
- 最后,这个提示弹窗最好能够很方便的被调用、复用;拒绝回调(async、await 实现)。
演示:
在贴出细节实现前,先看看实际使用效果:
notification动画中的配套测试代码,是不是很简单?
using UnityEngine;
using static zFramework.UI.NotificationManager;
public class TestNotification : MonoBehaviour
{
public void ShowMessage() => _ = ShowAsync("这个消息没有按钮,点空白区域关闭"); //也可以使用 await 等待
public async void ShowAutoMessage()
{
_= ShowAsync("这个消息没有按钮、用户不能关闭,空白区域震窗、2 秒后自动关闭", interactable: false);
await UniTask.Delay(2000); // 也可以是其他判断关闭提示窗的逻辑
CloseNotification();
}
public async void ShowMessageWithConfirm()
{
await ShowAsync("带确认按钮的消息,点空白区域关闭会震窗", true);
Debug.Log($"{nameof(TestNotification)}: 点击了确定按钮!");
}
public async void ShowMessageWithCancel()
{
await ShowAsync("带取消按钮的消息,点空白区域关闭会震窗", enablecancel: true);
Debug.Log($"{nameof(TestNotification)}: 点击了取消按钮!");
}
public async void ShowMessageWith2Button()
{
var index = await ShowAsync("这个消息有 2 个按钮,点空白区域震窗", enableConfirm: true, enablecancel: true);
Debug.Log($"{nameof(TestNotification)}: 用户点击了{(index == 0 ? "确定" : "取消")}");
}
}
实现:
先构建一个通知 Notifacation
using DG.Tweening;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
[RequireComponent(typeof(CanvasGroup))]
public class Notification : MonoBehaviour
{
public TextMeshProUGUI text;
public Transform window;
public Button mask_button;
public Button confirm_button;
public Button cancel_button;
public CanvasGroup canvasGroup;
public void Init(string message, bool enableConfirm, bool enablecancel, bool interactable)
{
text.text = message;
confirm_button.gameObject.SetActive(interactable&&enableConfirm);
cancel_button.gameObject.SetActive(interactable && enablecancel);
mask_button.onClick.AddListener(() =>
{
if (enablecancel || enableConfirm|| !interactable)
window.DOShakePosition(0.5f, 5);
else
Destroy(gameObject);
});
canvasGroup.blocksRaycasts = false;
window.localScale = Vector3.one * 0.2f;
}
}
再实现一个通知管理器 NotificationManager
using Cysharp.Threading.Tasks;
using DG.Tweening;
using UnityEngine;
namespace zFramework.UI
{
public class NotificationManager : MonoBehaviour
{
public Notification notificationPrefab;
static NotificationManager instance;
static Notification notification;
void Awake() => instance = this;
public static void CloseNotification() => Destroy(notification.gameObject);
public static async UniTask<int> ShowAsync(string message, bool enableConfirm = false, bool enablecancel = false, bool interactable = true)
{
notification = Instantiate(instance.notificationPrefab, instance.transform, false);
notification.Init(message, enableConfirm, enablecancel, interactable);
await notification.window.DOScale(1, 0.5F);
notification.canvasGroup.blocksRaycasts = true;
try
{
var cancellation = notification.gameObject.GetCancellationTokenOnDestroy();
var index = await UniTask.WhenAny(notification.confirm_button.OnClickAsync(cancellation), notification.cancel_button.OnClickAsync(cancellation));
Destroy(notification.gameObject);
return index;
}
catch (System.Exception)
{
return 0;
}
}
}
}
Notification UI搭建最后 UI 配套
Notification 测试环境搭建
写到最后:
- 功能如此完善代码却简约至此,夫复何求?
- 笔者使用了 Dotween、 UniTask 以及 UniTask的 Dotween扩展,通过本文,也能更直观感受到基于 async/await 实现的异步当成同步写的魅力,可以跟回调地狱 say 拜拜了您嘞!
- 在测试代码中,笔者使用了
using static NotificationManager
,这是一个非常优雅的语法糖,可以像使用本地方法一样使用NotificationManager
中的公共方法或其他成员。 - 在测试代码中,笔者使用了弃元运算符(下划线),用于跳过等待,可以实现多个异步操作并行的直观感受。
- void 关键字在与 async await 配合使用时,一般用于做入口异步方法,也能够保证函数签名符合 UGUI 组件的事件所需要的方法类型。
版权所有,转载请注明出处..