概述
在游戏开发中,常常会遇到一些通用的界面逻辑,它不论在什么范例的游戏中都会出现。为了避免重复造轮子,本文总结并提供了一些常用UI界面的实现逻辑。希望可以帮助大家快速开发通用界面模块,也可以在次底子上举行扩展修改,以适应你项目标需求。
工程链接:GitCode - 全球开发者的开源社区,开源代码托管平台
二次确认界面

- using UnityEngine;
- using UnityEngine.Events;
- using UnityEngine.UI;
- public class ConfirmDialog : MonoBehaviour
- {
- private Text txt_title;
- private Text txt_content;
- private Button btn_Yes;
- private Button btn_No;
- void Start()
- {
- var root = gameObject.transform;
- txt_title = root.Find("txt_Title").GetComponent<Text>();
- txt_content = root.Find("txt_Content").GetComponent<Text>();
- btn_Yes = root.Find("btn/btn_Yes").GetComponent<Button>();
- btn_No = root.Find("btn/btn_No").GetComponent<Button>();
- txt_title.text = "提示";
-
- //测试代码
- InitDialog("好好学习,天天向上!",
- () => {Debug.Log("Yes"); },
- () => {Debug.Log("No"); });
- }
- /// <summary>
- /// 重载一:使用默认标题 “提示”
- /// </summary>
- /// <param name="content">需要确认的内容</param>
- /// <param name="yesAction">确认按钮回调</param>
- /// <param name="noAction">取消按钮回调</param>
- public void InitDialog(string content, UnityAction yesAction = null, UnityAction noAction = null)
- {
- txt_title.text = "提示";
- CoreLogic(content, yesAction, noAction);
- }
- /// <summary>
- /// 重载一:使用自定义标题
- /// </summary>
- /// <param name="title">自定义标题</param>
- /// <param name="content">需要确认的内容</param>
- /// <param name="yesAction">确认按钮回调</param>
- /// <param name="noAction">取消按钮回调</param>
- public void InitDialog(string title, string content, UnityAction yesAction = null, UnityAction noAction = null)
- {
- txt_title.text = title;
- CoreLogic(content, yesAction, noAction);
- }
- //公共逻辑提取
- private void CoreLogic(string content, UnityAction yesAction = null, UnityAction noAction = null)
- {
- txt_content.text = content;
- BindBtnLogic(btn_Yes, yesAction);
- BindBtnLogic(btn_No, noAction);
- btn_Yes.gameObject.SetActive(yesAction != null);
- btn_No.gameObject.SetActive(noAction != null);
- }
- //绑定按钮点击回调
- private void BindBtnLogic(Button btn, UnityAction action)
- {
- btn.onClick.RemoveAllListeners();
- if (action != null)
- {
- btn.onClick.AddListener(action);
- }
- }
- }
复制代码 切页标签

通过按钮来实现。固然使用Toggle也可以实现,但是在实际开发中会发现使用toggle不好控制选中变乱的触发和选中状态表现。通过按钮来自定义组件可以更好地控制逻辑的调用和标签的显示。
- using System.Collections.Generic;
- using UnityEngine;
- using UnityEngine.Events;
- using UnityEngine.UI;
- class TabNode
- {
- public int index;
- public GameObject offBg;
- public GameObject onBg;
- public Text offTxt;
- public Text onTxt;
- public Button btn;
- public UnityAction callback;
- }
- public class SwitchPageTab : MonoBehaviour
- {
- public Transform tabRoot;//标签组的父节点
- public GameObject tabObj;//标签页预制体模板
- private int _selectIndex;//选中的标签页索引
- private List<TabNode> _objList = new List<TabNode>();
- private Dictionary<int, UnityAction> _callbackDic = new Dictionary<int, UnityAction>();
-
- private void Start()
- {
- _selectIndex = -1;
-
- InitCount(4);
- BindSelectCallback(0, "背包", (() =>
- {
- Debug.Log("查看背包");
- }));
- BindSelectCallback(1, "英雄", (() =>
- {
- Debug.Log("查看英雄");
- }));
- BindSelectCallback(2, "商店", (() =>
- {
- Debug.Log("查看商店");
- }));
- BindSelectCallback(3, "活动", (() =>
- {
- Debug.Log("查看活动");
- }));
-
-
- OnSelectLogic(0);
- }
- /// <summary>
- /// 初始化调用
- /// </summary>
- /// <param name="count">标签的数量</param>
- public void InitCount(int count)
- {
- _objList.Clear();
- ClearAllChild(tabRoot);
- for (var i = 0; i < count; i++)
- {
- var obj = Instantiate(tabObj, tabRoot);
- obj.SetActive(true);
- var trans = obj.transform;
- var node = new TabNode
- {
- offTxt = trans.Find("btn/offBg/offTxt").GetComponent<Text>(),
- onTxt = trans.Find("btn/onBg/onTxt").GetComponent<Text>(),
- onBg = trans.Find("btn/onBg").gameObject,
- offBg = trans.Find("btn/offBg").gameObject,
- btn = trans.Find("btn").GetComponent<Button>(),
- };
- var index = i;
- BindBtnLogic(node.btn, () =>
- {
- OnSelectLogic(index);
- });
- _objList.Add(node);
- }
- }
-
- /// <summary>
- /// 绑定指定页签索引的回调函数
- /// </summary>
- /// <param name="index">页签索引</param>
- /// <param name="txt">页签问本</param>
- /// <param name="callback">选中回调</param>
- public void BindSelectCallback(int index,string txt,UnityAction callback)
- {
- if (_callbackDic.ContainsKey(index))
- {
- Debug.LogError("已经注册过了!");
- return;
- }
- if (callback == null)
- {
- Debug.LogError("回调为空!");
- return;
- }
- if (index < 0 || index > _objList.Count)
- {
- Debug.LogError("索引越界!");
- return;
- }
- var node = _objList[index];
- node.onTxt.text = txt;
- node.offTxt.text = txt;
- _callbackDic.Add(index,callback);
- }
-
- /// <summary>
- /// 调用指定索引对应的回调函数
- /// </summary>
- /// <param name="index"></param>
- private void OnSelectLogic(int index)
- {
- if (index == _selectIndex)
- {
- return;
- }
- _selectIndex = index;
-
- var isExist = _callbackDic.TryGetValue(_selectIndex, out UnityAction callback);
- if (isExist)
- {
- callback?.Invoke();
- SetSelectStatus(index);
- }
- }
-
- /// <summary>
- /// 控制指定页签的UI表现
- /// </summary>
- /// <param name="index"></param>
- private void SetSelectStatus(int index)
- {
- var count = _objList.Count;
- for (var i = 0; i < count; i++)
- {
- var isActive = index == i;
- var node = _objList[i];
- node.onBg.SetActive(isActive);
- node.offBg.SetActive(!isActive);
- }
- }
-
- //清除指定父节点下的所有子物体
- private void ClearAllChild(Transform parentRoot)
- {
- var childCount = parentRoot.childCount;
- for (var i = childCount - 1; i >= 0; i--)
- {
- var child = parentRoot.GetChild(i);
- DestroyImmediate(child.gameObject);
- }
- }
- //绑定按钮点击回调
- private void BindBtnLogic(Button btn, UnityAction action)
- {
- btn.onClick.RemoveAllListeners();
- if (action != null)
- {
- btn.onClick.AddListener(action);
- }
- }
- }
复制代码
飘字提示
简易版本

- using UnityEngine;
- using UnityEngine.Pool;
- using DG.Tweening;
- using UnityEngine.UI;
- public class SimpleTip : MonoBehaviour
- {
- //提示栏预制体
- public GameObject tipObj;
- //提示栏显示的父节点
- public Transform tipRoot;
- //对象池
- private ObjectPool<GameObject> tipPool;
- //飞行高度
- private float flyHeight = 500;
-
- void Start()
- {
- InitTipPool();
- }
- void Update()
- {
- if (Input.GetKeyDown(KeyCode.Space))
- {
- ShowTip("货币不足!");
- }
- }
- void ShowTip(string tipStr)
- {
- var obj = tipPool.Get();
- var rectValue = obj.GetComponent<RectTransform>();
- var group = obj.GetComponent<CanvasGroup>();
- var txt = obj.transform.Find("txt").GetComponent<Text>();
- txt.text = tipStr;
- obj.SetActive(true);
- group.alpha = 1;
- ResetLocal(obj.transform);
- rectValue.DOAnchorPosY(flyHeight, 1f).OnComplete(() =>
- {
- group.DOFade(0, 0.1f).OnComplete(() =>
- {
- tipPool.Release(obj);
- });
- });
- }
-
- //初始化对象池
- void InitTipPool()
- {
- tipPool = new ObjectPool<GameObject>(() =>
- {
- //创建新对象调用
- var obj = Instantiate(tipObj, tipRoot);
- obj.SetActive(false);
- return obj;
- },
- (go) =>
- {
- //获取对象调用
- go.SetActive(true);
- ResetLocal(go.transform);
- },
- (go) =>
- {
- // 在对象放回池子时调用
- go.SetActive(false);
- ResetLocal(go.transform);
- go.transform.SetParent(tipRoot);
- },
- (go) =>
- {
- Destroy(go);
- });
- }
-
- //重置本地信息
- void ResetLocal(Transform trans)
- {
- trans.localPosition = Vector3.zero;
- trans.localEulerAngles = Vector3.zero;
- trans.localScale = Vector3.one;
- }
- }
复制代码 升级版本

- using System.Collections.Generic;
- using DG.Tweening;
- using UnityEngine;
- using UnityEngine.UI;
- using UnityEngine.Pool;
- public class GoodTip : MonoBehaviour
- {
- //提示栏显示的父节点
- public Transform root;
- //提示栏模板预制体
- public GameObject tipObj;
- //对象池节点
- public Transform objectPool;
-
- //最多显示的提示栏数量,超过就隐藏
- private int limitCount = 5;
- //提示栏之间的偏移
- private float offset = 20;
- //提示飞行高度
- private float flyHeight = 100;
- //提示栏生成数量,只用于逻辑运算
- private int tipCount = 0;
- //提示栏高度
- private float tipHeight;
- private Queue<GameObject> visualTipQueue = new Queue<GameObject>();
- //是否可继续生成提示栏,防止频繁点击造成异常
- private bool isOk = true;
- private float timer = 0f;
- private bool startTimer = false;
- private float displayTime = 0.65f;//提示停留展示时间
- private ObjectPool<GameObject> tipPool;
-
- void Start()
- {
- var rect = tipObj.GetComponent<RectTransform>();
- tipHeight = rect.rect.height;
-
- InitTipPool();
- }
- private void Update()
- {
- if (startTimer)
- {
- //定时统一清理提示消息
- timer += Time.deltaTime;
- if (timer > displayTime)
- {
- ClearAllMsg();
- timer = 0f;
- startTimer = false;
- }
- }
- if (Input.GetKeyDown(KeyCode.Space))
- {
- ShowTip("货币不足!");
- }
- }
-
- public void ShowTip(string tip)
- {
- if (!isOk)
- {
- return;
- }
- startTimer = false;
- isOk = false;
- var obj = tipPool.Get();
- var rect1 = obj.GetComponent<RectTransform>();
- var group = obj.GetComponent<CanvasGroup>();
-
- var sequence = DOTween.Sequence();
- if (visualTipQueue.Count > 0)
- {
- sequence.AppendCallback(() =>
- {
- foreach (var item in visualTipQueue)
- {
- var rectValue = item.GetComponent<RectTransform>();
- rectValue.DOAnchorPosY(rectValue.anchoredPosition.y+tipHeight+offset, 0.2f);
- }
- });
- sequence.AppendInterval(0.2f);
- }
- sequence.AppendCallback(() =>
- {
- group.alpha = 1;
- obj.transform.SetParent(root);
- obj.transform.localScale = new Vector3(0, 0, 1);
- obj.SetActive(true);
- rect1.anchoredPosition = Vector2.zero;
- visualTipQueue.Enqueue(obj);
-
- tipCount++;
- var txt = obj.transform.Find("txt").GetComponent<Text>();
- txt.text = tip;
-
- if (tipCount > limitCount)
- {
- var result = visualTipQueue.Dequeue();
- tipPool.Release(result);
- tipCount--;
- }
- });
- sequence.Append(obj.transform.DOScale(Vector3.one, 0.1f));
- sequence.AppendInterval(0.1f);
- sequence.OnComplete(() =>
- {
- timer = 0f;
- isOk = true;
- startTimer = true;
- });
- }
-
- //初始化对象池
- void InitTipPool()
- {
- tipPool = new ObjectPool<GameObject>(() =>
- {
- //创建新对象调用
- var obj = Instantiate(tipObj, objectPool);
- obj.SetActive(false);
- return obj;
- },
- (go) =>
- {
- //获取对象调用
- go.SetActive(true);
- ResetLocal(go.transform);
- },
- (go) =>
- {
- // 在对象放回池子时调用
- go.SetActive(false);
- ResetLocal(go.transform);
- go.transform.SetParent(objectPool);
- },
- (go) =>
- {
- Destroy(go);
- });
- }
-
- //重置本地信息
- void ResetLocal(Transform trans)
- {
- trans.localPosition = Vector3.zero;
- trans.localEulerAngles = Vector3.zero;
- trans.localScale = Vector3.one;
- }
-
- //清空消息
- public void ClearAllMsg()
- {
- var childCount = root.childCount;
- for (var i = 0; i < childCount; i++)
- {
- var child = root.GetChild(i);
- var group = child.GetComponent<CanvasGroup>();
- var rectValue = child.GetComponent<RectTransform>();
- var sequence = DOTween.Sequence();
- sequence.AppendInterval(0.1f * i);
- sequence.Append(rectValue.DOAnchorPosY(rectValue.anchoredPosition.y + tipHeight+flyHeight, 0.2f));
- sequence.Append(group.DOFade(0, 0.1f).OnComplete(() =>
- {
- visualTipQueue.Dequeue();
- tipPool.Release(child.gameObject);
- tipCount--;
- }));
- }
- }
- }
复制代码
左右切换按钮组
本组件一般出现在查看好汉界面,点击左右两个按钮切换查看按钮的具体信息。在好汉列表中,第一个好汉的左按钮不显示,末了一个好汉的右按钮不显示。

- using UnityEngine;
- using UnityEngine.Events;
- using UnityEngine.UI;
- public class SwitchCheck : MonoBehaviour
- {
- public Button btn_Left;
- public Button btn_Right;
- public Text txt_Check;
- private int sumCount;
- private int curIndex;
- private UnityAction<int> callback;//外部逻辑回调
-
- void Start()
- {
- curIndex = 0;
- InitGroup(10, (index) =>
- {
- txt_Check.text = $"{index}/{sumCount}";
- });
- CheckBtnActive();
- BindBtnLogic(btn_Left, () =>
- {
- var nextIndex = curIndex - 1;
- if (nextIndex < 0)
- {
- return;
- }
- curIndex = nextIndex;
- CheckBtnActive();
- });
-
- BindBtnLogic(btn_Right, () =>
- {
- var nextIndex = curIndex + 1;
- if (nextIndex >= sumCount)
- {
- return;
- }
- curIndex = nextIndex;
- CheckBtnActive();
- });
- }
-
- public void InitGroup(int _sumCount,UnityAction<int> _callback)
- {
- sumCount = _sumCount;
- callback = _callback;
- }
-
- //按钮显隐逻辑
- private void CheckBtnActive()
- {
- if (sumCount <= 1)
- {
- btn_Left.gameObject.SetActive(false);
- btn_Right.gameObject.SetActive(false);
- }
- else
- {
- btn_Left.gameObject.SetActive(curIndex >= 1);
- btn_Right.gameObject.SetActive(curIndex <= sumCount-2);
- }
- var showIndex = curIndex + 1;
- callback?.Invoke(showIndex);
- }
-
- //绑定按钮点击回调
- private void BindBtnLogic(Button btn, UnityAction action)
- {
- btn.onClick.RemoveAllListeners();
- if (action != null)
- {
- btn.onClick.AddListener(action);
- }
- }
- }
复制代码 帮助说明界面
- using System.Text;
- using UnityEngine;
- using UnityEngine.UI;
- public class ComDesc : MonoBehaviour
- {
- public Text txt_Title;
- public Text txt_Desc;
- public RectTransform content;
- void Update()
- {
- if (Input.GetKeyDown(KeyCode.Space))
- {
- SetDesc("帮助", "好好学习,天天向上");
- }
-
- if (Input.GetKeyDown(KeyCode.A))
- {
- var str = "好好学习,天天向上";
- StringBuilder sb = new StringBuilder();
- for (var i = 1; i < 100; i++)
- {
- sb.Append(str);
- }
- SetDesc("帮助", sb.ToString());
- }
- }
- /// <summary>
- /// 设置说明描述
- /// </summary>
- /// <param name="title">界面标题</param>
- /// <param name="desc">说明文本</param>
- public void SetDesc(string title,string desc)
- {
- txt_Title.text = title;
- txt_Desc.text = desc;
- LayoutRebuilder.ForceRebuildLayoutImmediate(content);
- }
- }
复制代码 跑马灯消息提示

有消息队列缓存,等待队列中所有消息播放完后,提示才消失。
- using System.Collections.Generic;
- using DG.Tweening;
- using UnityEngine;
- using TMPro;
- using UnityEngine.Events;
- using UnityEngine.UI;
- public class Marquee : MonoBehaviour
- {
- public TMP_Text tmpTxt;
- public RectTransform maskNode;
- public CanvasGroup canvasGroup;
- private float maskWidth;
- private float unitTime = 0.2f;//计算动画时间自定义标准
- private Queue<MsgNode> marqueeMsg = new Queue<MsgNode>();
- private List<int> idList = new List<int>();
- private bool isPlay;//是否正在播放消息
-
- private class MsgNode
- {
- public int id;
- public string msg;
- public int loopCount;
- }
-
- void Start()
- {
- maskWidth = maskNode.rect.width;
- }
- // Update is called once per frame
- void Update()
- {
- if (Input.GetKeyDown(KeyCode.Space))
- {
- var id = Random.Range(1,100);
- var str = $"id:{id}好好学习,天天向上>>";
- AddMarqueeMsg(id,str,1);
- }
-
- if(marqueeMsg.Count > 0) {
- if (!isPlay)
- {
- isPlay = true;
- tmpTxt.rectTransform.anchoredPosition = Vector2.zero;
- var data = marqueeMsg.Peek();
- idList.Remove(data.id);
- DisplayMarqueeMsg(data.msg,data.loopCount, () =>
- {
- marqueeMsg.Dequeue();
- if (marqueeMsg.Count == 0)
- {
- canvasGroup.alpha = 0;
- }
- isPlay = false;
- });
- }
- }
- }
- /// <summary>
- /// 在跑马灯消息队列中添加消息
- /// </summary>
- /// <param name="msgId">消息记录的唯一id</param>
- /// <param name="msg">消息内容</param>
- /// <param name="loopCount">循环播放时间</param>
- public void AddMarqueeMsg(int msgId, string msg, int loopCount)
- {
- if (idList.Contains(msgId))
- {
- Debug.LogError("消息已在预播队列");
- return;
- }
-
- if (canvasGroup.alpha < 0.95f)
- {
- canvasGroup.alpha = 1;
- }
-
- idList.Add(msgId);
- marqueeMsg.Enqueue(new MsgNode
- {
- id = msgId,
- msg = msg,
- loopCount = loopCount
- });
- }
-
- /// <summary>
- /// 跑马灯消息播放
- /// </summary>
- /// <param name="msgId">消息记录的唯一id</param>
- /// <param name="msg">消息内容</param>
- /// <param name="loopCount">循环播放时间</param>
- public void DisplayMarqueeMsg(string msg,int loopCount,UnityAction callback)
- {
- tmpTxt.text = msg;
- LayoutRebuilder.ForceRebuildLayoutImmediate(tmpTxt.rectTransform);
- var width = tmpTxt.rectTransform.rect.width+maskWidth;
- var duration = GetDuration(width);
- tmpTxt.rectTransform.DOAnchorPosX(-width, duration)
- .SetEase(Ease.Linear)
- .SetLoops(loopCount, LoopType.Restart)
- .OnComplete(() =>
- {
- callback?.Invoke();
- });
- }
- //根据消息长度计算动画匀速运行时间
- private float GetDuration(float width)
- {
- var offset1 = (int)width / 100;
- var offset2 = width % 100 == 0 ?0:1;
- var offset = offset1 + offset2;
- return offset * unitTime;
- }
- }
复制代码 免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |