基于Unity~UGUI的简单UI框架(附UIFramework源码)


框架源码地址: UIFramework
litjson.dll下载地址: litjson


不使用UI框架存在的一些问题


1.随着游戏系统的复杂,UI控件越来越多,各个UI直接的通讯,以及UI与GameObject之间的通讯形成一张复杂的蜘蛛网,
拖着拖着,有时候我都忘了哪个对象跟哪个对象关联了。如果是别人要看我的程序,我估计他找半天都找不到UI逻辑的入口。
2.耦合性非常严重,如果要改变需求,更改某个UI或者更改某个游戏对象,那么你需要再手动全部与该对象关联的物件重新更改一次。
3.作为强迫症的程序员,最受不了程序代码的混乱。这种组织方式十分“不优雅”,看着很乱。
鉴于以上各种情况,我们需要寻找一种新的,科学的,高效的UI管理方式


UI框架总揽:


这里写图片描述


创建测试面板(非框架部分)


将面板以Prefab形式放入Resources文件夹下面,便于框架加载面板
UIPanel


创建json文件和UIPanelType类来保存所有的面板信息


UIPanelType.json:

{
	"panelInfoList":
	[
		{"panelType":"MainMenu","path":"UIPanel/MainMenuPanel"},
		{"panelType":"ItemMessage","path":"UIPanel/ItemMessagePanel"},
		{"panelType":"Knapsack","path":"UIPanel/KnapsackPanel"},
		{"panelType":"Shop","path":"UIPanel/ShopPanel"},
		{"panelType":"Skill","path":"UIPanel/SkillPanel"},
		{"panelType":"System","path":"UIPanel/SystemPanel"},
		{"panelType":"Task","path":"UIPanel/TaskPanel"}
	]
}

UIPanelType.cs:

public class UIPanelType {
    public const string MainMenu = "MainMenu";
    public const string Knapsack = "Knapsack";
    public const string ItemMessage = "ItemMessage";
    public const string Shop = "Shop";
    public const string Skill = "Skill";
    public const string System = "System";
    public const string Task = "Task";
}


开发UIManger来解析面板信息json文件(框架核心:UIManager)


UIManager为框架的核心,全局只有一个UIManager,因此UIManager要使用单例模式
现阶段的完整的UIManager.cs(后续还会继续完善):

using System.Collections.Generic;
using System.Collections;
using System;
using UnityEngine;
using LitJson;
public class UIPanelManager
{
    private static UIPanelManager _instance;

    public static UIPanelManager Instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = new UIPanelManager();
            }
            return _instance;
        }
    }

    private Dictionary<string, string> panelPathDict;
    //private Dictionary<UIPanelType,>
    //private Stack<>

    private UIPanelManager()
    {
        ParseUIPanelTypeJson();
    }

    private void ParseUIPanelTypeJson()
    {
        panelPathDict = new Dictionary<string, string>();
        TextAsset textUIPanelType = Resources.Load<TextAsset>("UIPanelTypeJson");
        UIPanelInfoList panelInfoList = JsonMapper.ToObject<UIPanelInfoList>(textUIPanelType.text);

        foreach (UIPanelInfo panelInfo in panelInfoList.panelInfoList)
        {
            panelPathDict.Add(panelInfo.panelType, panelInfo.path);
            Debug.Log(panelInfo.panelType + ":" + panelInfo.path);
        }
    }
}

开发BasePanel抽象类(各个UIPanel的基类)


BasePanel给每个Panel定义了四个事件:
OnEnter:面板进入时调用
OnPause:面板停止时调用(鼠标与面板的交互停止)
OnResume:面板恢复使用时调用(鼠标与面板的交互恢复)
OnExit:面板退出时调用

using UnityEngine;

public abstract class BasePanel : MonoBehaviour
{
    public abstract void OnEnter();

    public abstract void OnPause();

    public abstract void OnResume();

    public abstract void OnExit();

}

给各个面板添加UIPanel类(非框架部分)
这里写图片描述

using UnityEngine;

public class MainMenuPanel : BasePanel
{

    public override void OnEnter()
    {
    }

    public override void OnPause()
    {
    }

    public override void OnResume()
    {
    }

    public override void OnExit()
    {
    }
}


对UI面板Prefab实例化的创建与管理


获取Panel(如果panel没有被实例化,进行实例化,并且存储到已经实例化好的panel字典中)

private BasePanel GetPanel(string panelType)
    {
        BasePanel panel = panelDict.GetValue(panelType);

        //如果没有实例化面板,寻找路径进行实例化,并且存储到已经实例化好的字典面板中
        if (panel == null)
        {
            string path = panelPathDict.GetValue(panelType);
            Transform panelTransform = GameObject.Instantiate(Resources.Load<GameObject>(path), canvasTransform).transform;
            panel = panelTransform.GetComponent<BasePanel>();
            panelDict.Add(panelType, panel);
        }
        return panel;
    }

字典的扩展:

using System.Collections.Generic;

public static class DictTool
{
    public static Tvalue GetValue<Tkey, Tvalue>(this Dictionary<Tkey, Tvalue> dict, Tkey key)
    {
        Tvalue value = default(Tvalue);
        dict.TryGetValue(key, out value);
        return value;
    }
}

现阶段的完整的UIManager.cs(后续还会继续完善):

using System.Collections.Generic;
using System.Collections;
using System;
using UnityEngine;
using LitJson;
public class UIPanelManager
{
    private static UIPanelManager _instance;
    private Transform canvasTransform;
    private Transform CanvasTransform
    {
        get
        {
            if (canvasTransform == null)
            {
                canvasTransform = GameObject.Find("Canvas").transform;
            }
            return canvasTransform;
        }
    }
    public static UIPanelManager Instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = new UIPanelManager();
            }


            return _instance;
        }
    }

    private Dictionary<string, string> panelPathDict;
    private Dictionary<string, BasePanel> panelDict;
    //private Stack<>

    private UIPanelManager()
    {
        ParseUIPanelTypeJson();
    }

    private BasePanel GetPanel(string panelType)
    {

        if (panelDict == null)
        {
            panelDict = new Dictionary<string, BasePanel>();
        }

        BasePanel panel = panelDict.GetValue(panelType);

        //如果没有实例化面板,寻找路径进行实例化,并且存储到已经实例化好的字典面板中
        if (panel == null)
        {
            string path = panelPathDict.GetValue(panelType);
            GameObject panelGo = GameObject.Instantiate(Resources.Load<GameObject>(path), CanvasTransform, false);
            panel = panelGo.GetComponent<BasePanel>();
            panelDict.Add(panelType, panel);
        }
        return panel;
    }

    //解析json文件
    private void ParseUIPanelTypeJson()
    {
        panelPathDict = new Dictionary<string, string>();
        TextAsset textUIPanelType = Resources.Load<TextAsset>("UIPanelTypeJson");
        UIPanelInfoList panelInfoList = JsonMapper.ToObject<UIPanelInfoList>(textUIPanelType.text);

        foreach (UIPanelInfo panelInfo in panelInfoList.panelInfoList)
        {
            panelPathDict.Add(panelInfo.panelType, panelInfo.path);
            //Debug.Log(panelInfo.panelType + ":" + panelInfo.path);
        }
    }
}


开发界面存储栈,并进行出栈和入栈操作


【设计理念】
1.鼠标只允许和一个界面进行交互,当一个界面显示后,上一个界面要停止和鼠标的交互
2.界面显示,进行入栈操作(Push,OnEnter),停止上一个界面(OnPause)
3.界面隐藏,进行出栈操作(Pop,OnExit),恢复上一个界面(OnResume)

 private Stack<BasePanel> panelStack;
    public void PushPanel(string panelType)
    {
        if (panelStack == null)
        {
            panelStack = new Stack<BasePanel>();
        }

        //停止上一个界面
        if (panelStack.Count > 0)
        {
            BasePanel topPanel = panelStack.Peek();
            topPanel.OnPause();
        }

        BasePanel panel = GetPanel(panelType);
        panelStack.Push(panel);
        panel.OnEnter();
    }

    public void PopPanel()
    {
        if (panelStack == null)
        {
            panelStack = new Stack<BasePanel>();
        }
        if (panelStack.Count <= 0)
        {
            return;
        }

        //退出栈顶面板
        BasePanel topPanel = panelStack.Pop();
        topPanel.OnExit();

        //恢复上一个面板
        if (panelStack.Count > 0)
        {
            BasePanel panel = panelStack.Peek();
            panel.OnResume();
        }

    }

控制Panel之间的跳转(非框架部分)


完善面板中的一些操作,实现面板之间的跳转,此部分不是框架部分,省略!
MainMenuPanel.cs:

using UnityEngine;
using UnityEngine.UI;

public class MainMenuPanel : BasePanel
{
    private Button TaskButton;
    private Button KnapsackButton;
    private Button SkillButton;
    private Button ShopButton;
    private Button SystemButton;
    private CanvasGroup canvasGroup;
    void Awake()
    {
        TaskButton = transform.Find("IconPanel/TaskButton").GetComponent<Button>();
        KnapsackButton = transform.Find("IconPanel/KnapsackButton").GetComponent<Button>();
        SkillButton = transform.Find("IconPanel/SkillButton").GetComponent<Button>();
        ShopButton = transform.Find("IconPanel/ShopButton").GetComponent<Button>();
        SystemButton = transform.Find("IconPanel/SystemButton").GetComponent<Button>();
        canvasGroup = transform.GetComponent<CanvasGroup>();

        TaskButton.onClick.AddListener(OnTaskButtonClick);
        KnapsackButton.onClick.AddListener(OnKnapsackButtonClick);
        SkillButton.onClick.AddListener(OnSkillButtonClick);
        ShopButton.onClick.AddListener(OnShopButtonClick);
        SystemButton.onClick.AddListener(OnSystemButtonClick);
    }

    void OnDestory()
    {
        TaskButton.onClick.RemoveListener(OnTaskButtonClick);
        KnapsackButton.onClick.RemoveListener(OnKnapsackButtonClick);
        SkillButton.onClick.RemoveListener(OnSkillButtonClick);
        ShopButton.onClick.RemoveListener(OnShopButtonClick);
        SystemButton.onClick.RemoveListener(OnSystemButtonClick);
    }
    public override void OnEnter()
    {
    }

    public override void OnPause()
    {
        canvasGroup.blocksRaycasts = false;
    }

    public override void OnResume()
    {
        canvasGroup.blocksRaycasts = true;
    }

    public override void OnExit()
    {
    }

    private void OnTaskButtonClick()
    {
        UIPanelManager.Instance.PushPanel(UIPanelType.Task);
    }
    private void OnKnapsackButtonClick()
    {
        UIPanelManager.Instance.PushPanel(UIPanelType.Knapsack);
    }
    private void OnSkillButtonClick()
    {
        UIPanelManager.Instance.PushPanel(UIPanelType.Skill);
    }
    private void OnShopButtonClick()
    {
        UIPanelManager.Instance.PushPanel(UIPanelType.Shop);
    }
    private void OnSystemButtonClick()
    {
        UIPanelManager.Instance.PushPanel(UIPanelType.System);
    }
}


UI框架(完结)


UI框架
【BasePanel(基类):所有面板继承自BasePanel】
定义了四个事件:
OnEnter:面板进入时调用
OnPause:面板停止时调用(鼠标与面板的交互停止)
OnResume:面板恢复使用时调用(鼠标与面板的交互恢复)
OnExit:面板退出时调用

using UnityEngine;

public abstract class BasePanel : MonoBehaviour
{
    public abstract void OnEnter();
    public abstract void OnPause();
    public abstract void OnResume();
    public abstract void OnExit();

}

【GameRoot】
实例化游戏中的主面板

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;

public class GameRoot : MonoBehaviour {
	void Start () {
        UIPanelManager panelManager = UIPanelManager.Instance;
        panelManager.PushPanel(UIPanelType.MainMenu);
	}
}

【UIPanelManager(框架的核心,单例)】
进行解析面板信息json文件,实例化面板Prefab,面板的入栈和出栈等等一系列操作

using System.Collections.Generic;
using System.Collections;
using System;
using UnityEngine;
using LitJson;
public class UIPanelManager
{
    private static UIPanelManager _instance;
    private Transform canvasTransform;
    private Transform CanvasTransform
    {
        get
        {
            if (canvasTransform == null)
            {
                canvasTransform = GameObject.Find("Canvas").transform;
            }
            return canvasTransform;
        }
    }
    public static UIPanelManager Instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = new UIPanelManager();
            }


            return _instance;
        }
    }

    private Dictionary<string, string> panelPathDict;
    private Dictionary<string, BasePanel> panelDict;
    private Stack<BasePanel> panelStack;

    private UIPanelManager()
    {
        ParseUIPanelTypeJson();
    }

    public void PushPanel(string panelType)
    {
        if (panelStack == null)
        {
            panelStack = new Stack<BasePanel>();
        }

        //停止上一个界面
        if (panelStack.Count > 0)
        {
            BasePanel topPanel = panelStack.Peek();
            topPanel.OnPause();
        }

        BasePanel panel = GetPanel(panelType);
        panelStack.Push(panel);
        panel.OnEnter();
    }

    public void PopPanel()
    {
        if (panelStack == null)
        {
            panelStack = new Stack<BasePanel>();
        }
        if (panelStack.Count <= 0)
        {
            return;
        }

        //退出栈顶面板
        BasePanel topPanel = panelStack.Pop();
        topPanel.OnExit();

        //恢复上一个面板
        if (panelStack.Count > 0)
        {
            BasePanel panel = panelStack.Peek();
            panel.OnResume();
        }

    }

    private BasePanel GetPanel(string panelType)
    {

        if (panelDict == null)
        {
            panelDict = new Dictionary<string, BasePanel>();
        }

        BasePanel panel = panelDict.GetValue(panelType);

        //如果没有实例化面板,寻找路径进行实例化,并且存储到已经实例化好的字典面板中
        if (panel == null)
        {
            string path = panelPathDict.GetValue(panelType);
            GameObject panelGo = GameObject.Instantiate(Resources.Load<GameObject>(path), CanvasTransform, false);
            panel = panelGo.GetComponent<BasePanel>();
            panelDict.Add(panelType, panel);
        }
        return panel;
    }

    //解析json文件
    private void ParseUIPanelTypeJson()
    {
        panelPathDict = new Dictionary<string, string>();
        TextAsset textUIPanelType = Resources.Load<TextAsset>("UIPanelTypeJson");
        UIPanelInfoList panelInfoList = JsonMapper.ToObject<UIPanelInfoList>(textUIPanelType.text);

        foreach (UIPanelInfo panelInfo in panelInfoList.panelInfoList)
        {
            panelPathDict.Add(panelInfo.panelType, panelInfo.path);
            //Debug.Log(panelInfo.panelType + ":" + panelInfo.path);
        }
    }
}

【litjson】
json文件解析的工具

【UIPanelTypeJson(保存面板信息的json文件)】
1.此文件需要加载,保存到Resources文件下
2.保存了面板的类型(相当于名称)和面板Prefab的路径(实例化面板时要用到)
3.每添加一个面板,都要在此文件添加该面板对应的信息

{
	"panelInfoList":
	[
		{"panelType":"MainMenu","path":"UIPanel/MainMenuPanel"},
		{"panelType":"ItemMessage","path":"UIPanel/ItemMessagePanel"},
		{"panelType":"Knapsack","path":"UIPanel/KnapsackPanel"},
		{"panelType":"Shop","path":"UIPanel/ShopPanel"},
		{"panelType":"Skill","path":"UIPanel/SkillPanel"},
		{"panelType":"System","path":"UIPanel/SystemPanel"},
		{"panelType":"Task","path":"UIPanel/TaskPanel"}
	]
}

【DictTool】
Dictionnary的拓展工具类

using System.Collections.Generic;

public static class DictTool
{
    public static Tvalue GetValue<Tkey, Tvalue>(this Dictionary<Tkey, Tvalue> dict, Tkey key)
    {
        Tvalue value = default(Tvalue);
        dict.TryGetValue(key, out value);
        return value;
    }
}

【UIPanelInfo(面板的信息类)】
包括面板的名称和面板Prefab的路径
用于和json文件进行映射

using System;
[Serializable]
public class UIPanelInfo
{
    public string panelType;
    public string path;

    public UIPanelInfo()
    {

    }
}

【UIPanelInfoList(面板信息集合类)】
用于和json文件进行映射

using System;
using System.Collections.Generic;

[Serializable]
public class UIPanelInfoList
{
    public List<UIPanelInfo> panelInfoList;

    public UIPanelInfoList() { }
}

【UIPanelType(面板的类型)】
用来记录面板的类型(名称)
每添加一个面板都要在此类里面添加对应面板的类型

public class UIPanelType {
    public const string MainMenu = "MainMenu";
    public const string Knapsack = "Knapsack";
    public const string ItemMessage = "ItemMessage";
    public const string Shop = "Shop";
    public const string Skill = "Skill";
    public const string System = "System";
    public const string Task = "Task";
}


赞赏与支持


【收藏与点赞】
觉得写的不错的可以收藏点赞转发噢~

【请作者喝杯咖啡】
觉得写的不错的可以请作者喝杯咖啡噢~
在这里插入图片描述


(完)

  • 95
    点赞
  • 402
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 26
    评论
Unity中,UGUI的EventTrigger是一个非常有用的组件,它可以用于实现UI拖拽和位置交换的功能。 首先,需要在要实现拖拽和位置交换的UI元素上添加EventTrigger组件。可以通过代码或者在Inspector面板中进行操作。然后,需要添加相应的事件触发器,例如拖拽开始、拖拽移动、拖拽结束等事件。 接下来,需要编写拖拽的逻辑代码。可以使用Unity提供的接口来处理拖拽事件,例如OnBeginDrag、OnDrag和OnEndDrag。在OnBeginDrag事件中,可以获取到拖拽起始位置,并将拖拽中的UI元素设置为可拖拽状态。在OnDrag事件中,可以实时获取到拖拽的位置,并将UI元素跟随鼠标或手指移动。在OnEndDrag事件中,可以获取到拖拽结束位置,并将UI元素设置回初始位置。 要实现位置交换功能,可以在UI元素上添加Collider组件,并根据拖拽的起始和结束位置来计算是否需要进行位置交换。可以使用RaycastHit来判断拖拽位置是否与其他UI元素重合,并记录下交换元素的信息。然后,根据交换元素的信息,可以将两个UI元素的位置进行互换。 最后,为了保证拖拽和位置交换功能的流畅性和用户体验,还可以添加一些动画效果和交互反馈。例如,在拖拽开始时可以添加一些拖拽阴影效果,使拖拽的UI元素看起来更加立体和真实。在位置交换时,可以添加一些过渡动画,使UI元素的位置变换更加平滑和自然。 总之,利用Unity中UGUI的EventTrigger组件,结合适当的逻辑代码和交互反馈,可以很方便地实现UI拖拽和位置交换功能。这种功能可以在游戏开发中广泛应用,例如拼图游戏、物品交换系统等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 26
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

cchoop

有用的话请杯肥宅水

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值