Unity MVC设计模式与UI背包界面制作

MVC设计模式非常适合UI的架构,UI界面相当于View,UI转换控制相当于Controller,UI上面的数据变换相当于Model。MVC设计模式在软件设计中无处不在,结合其他设计模式或设计思想,同一设计方案中,对于更好的MVC模式的追求几乎是没有尽头的。SO,作为Unity行业新人,本文旨在讨论关于MVC设计模式在UI背包界面制作过程中,相关的思路及应用,顺便附上本次实际应用中的代码及思路。

写在前边:

1.本文使用Unity版本为Unity 2019.1.14f1 (64-bit),VS版本为Microsoft Visual Basic 2015
2.UI使用Unity默认UGUI
3.涉及服务器数据与静态数据,使用JSON进行存取 ,并在C#中使用LitJson工具进行编译
4.文章内知识综合各类文章、读物学习总结而来,并非完全原创,以下为创作过程中使用知识点较多的文章:
站内优秀文章:
Unity (C#) 使用 LitJson 处理 JSON 数据
unity基于MVC的ui框架(一)
MVC的理解和优缺点的总结
Unity拖动背包物品/技能图标位置互换

正式开始

一、图示MVC结构

Model–view–controller (usually known as MVC) is a software design pattern commonly used for developing user interfaces that divides the related program logic into three interconnected elements. This is done to separate internal representations of information from the ways information is presented to and accepted from the user.——Wikipedia

机翻:模型-视图-控制器(通常称为MVC)是一种软件设计模式,通常用于开发用户界面,它将相关的程序逻辑划分为三个相互关联的元素。这样做是为了将信息的内部表示与向用户显示信息和从用户接受信息的方式分开。

下图是来自维基百科对于MVC基本交互模型的图示

二、UI背包界面效果构想图

具体到本次UI背包界面制作,将会围绕下图进行展开

三、正式开始

  • 新建Canvas搭建UI视图

根据构想图搭建UI视图,具体Hierachy面板层级部署如下:
(对比上文构想图)

————————————————————————————————————————————————————

  • 新建Canvas搭建UI视图

  • 根据上图五个部分拆分需要实现的功能

  • 第一部分包括:两个button组件(V),控制BackPack层视图的开启和关闭(C)

  • 第二部分(search)包括:一个图片,一个InputFiled组件(V),根据道具名称(M),搜索道具(C)

  • 第三部分(Types)包括:一个Toggle组件(V),根据道具类型(M),筛选道具(C)

  • 第四部分(Items)包括:一个Scrollbar组件,一个固定物品框图片(V),一个可变图标图片(M)

  • 第五部分包括:一个button组件(V),实现物品栏的简单整理(C)

————————————————————————————————————————————————————

  • 新建Canvas搭建UI视图
  • 根据上图五个部分拆分需要实现的功能
  • 根据需求拆分MVC

总体而言,我们需要通过传入Model到Controller相应去修改View

综合上述五部分:

  • M部分——包括道具栏中的道具的数据,通过JSON来进行读写,实现数据的CURD操作,即
    Create增加数据,Update修改数据,Read读取数据 以及 Delete删除数据
  • V部分——图片,字符,按键等各个UI组件的显示
  • C部分——理论上需要实现View的部分,都需要传入Model到Controller部分,去修改View

同时需要注意:

  • 使用MVC的目的是将M和V实现代码分离(理想情况),从而使同一个程序可以使用不同的表现形式。C存在的目的则是确保M和V的同步,一旦M改变,V应该同步更新。更好的调节M和V的搭配。

  • View里会包含Model信息,不可避免的还要包括一些业务逻辑。
    在MVC模型里,更关注的Model的不变,而同时有多个对Model的不同显示,即View

————————————————————————————————————————————————————

  • 新建Canvas搭建UI视图
  • 根据上图五个部分拆分需要实现的功能
  • 根据需求拆分MVC
  • 编写代码

1.ItemStaticDataManager.cs 和 ItemDynamicDataManger.cs

建立两个类来装载道具的表数据和服务器数据,对应Model部分

下图为本次示例中的部分JSON格式表数据截图(放在Resources/JSON/Items目录下),根据表数据表头内容来编写静态数据脚本ItemStaticDataManager.cs

静态类数据代码 ItemStaticDataManager.cs:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using LitJson;/// <summary>
/// 静态数据管理类
/// 键值对Dictionart类型读取JSON数据中的道具表,定义一个类来获取值Value
/// </summary>
public class ItemStaticData
{public int ItemID;public string Name;public string Description;public string Icon;public int Type;public int Quality;public int CanSell;public int CellGold;public int UseEffect;public int UseNumerical;
}public class ItemStaticDataManager
{private static ItemStaticDataManager instance;public static ItemStaticDataManager Instance{get{if(instance == null){instance = new ItemStaticDataManager();}return instance;}}//定义Dictionary类型变量itemStaticDataDicprivate Dictionary<int, ItemStaticData> itemStaticDataDic;//读取JSON表中数据,并用list接收ItemStaticData//LitJson反序列化:JsonMapper.ToObject<T>()public ItemStaticDataManager(){itemStaticDataDic = new Dictionary<int, ItemStaticData>();TextAsset temp = Resources.Load<TextAsset>("JSON/Items");List<ItemStaticData> list = JsonMapper.ToObject<List<ItemStaticData>>(temp.text);//遍历list,获取ItemStaticData,itemStaticDataDic获取键值for (int i = 0; i < list.Count; i++){itemStaticDataDic.Add(list[i].ItemID, list[i]);}}/// <summary>/// 根据id访问ItemStaticData类的ItemStaticData方法,获取反序列化后的数据(Dictionary类型)/// </summary>/// <param name="id"></param>/// <returns></returns>public ItemStaticData GetItemByID(int id){if (itemStaticDataDic.ContainsKey(id)){return itemStaticDataDic[id];            }else{Debug.LogError("没有这个Item:" + id);return null;}}}
服务器类数据代码 ItemDynamicDataManger.cs:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using LitJson;/// <summary>
///封装的服务器数据类,包括道具ID和数量,即ItemID和Count
/// </summary>
public class ItemServerData
{public int id;public int count;public ItemServerData() { }public ItemServerData(int id, int count){this.id = id;this.count = count;}
}
/// <summary>
/// 数据:服务器数据+表数据
/// </summary>
public class ItemDynamicDataManger
{private static ItemDynamicDataManger instance;public static ItemDynamicDataManger Instance{get{if (instance == null){instance = new ItemDynamicDataManger();}return instance;}}//服务器数据类listpublic List<ItemServerData> list = null;private string bagDataPath = "D:/Unity4.2/bagData.json";//伪服务器数据,手动修改背包初始物品private ItemDynamicDataManger()     {//1.读本地玩家数据表---》服务器数据//2.服务器数据和表数据通过id进行组合,生成完整数据      if (!File.Exists(bagDataPath)){list = new List<ItemServerData>();//前是十个道具是武器,各一个ItemServerData[] data = new ItemServerData[10];for (int i = 0; i < data.Length; i++){data[i] = new ItemServerData(i + 1, 1);list.Add(data[i]);}//11-14为药水,各99个ItemServerData data11 = new ItemServerData(11, 99);list.Add(data11);ItemServerData data12 = new ItemServerData(12,99);list.Add(data12);ItemServerData data13 = new ItemServerData(13, 99);list.Add(data13);ItemServerData data14 = new ItemServerData(14, 99);list.Add(data14);//将list序列化string json = JsonMapper.ToJson(list);File.WriteAllText(bagDataPath, json, System.Text.Encoding.UTF8);}else{//读取JSON文档,并反序列化string json = File.ReadAllText(bagDataPath);list = JsonMapper.ToObject<List<ItemServerData>>(json);}           }// 获取背包中所有的装备数据public List<ItemServerData> GetAllData(){return list;}
}

2.BackPackPanel.cs

  • 根据上边的思路,为整个BackPack的Panel新建一个脚本BackPackPanel.cs,用来显示整个BackPack中的各个动态的子级视图。即View部分:
  • 首先通过服务器类数据确认有哪些道具,以及道具的数量,加载出预制体中的ItemCell和ItemCell_Empty(V)
  • 然后再根据静态类数据加载出每个道具的图标(C)
背包Panel视图 BackPackPanel.cs代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
//挂载到 BackPack
public class BackPackPanel : MonoBehaviour
{   public Transform itemContent;private List<ItemServerData> bagDataList;public static BackPackPanel Instance;void Awake(){Instance = this;}void Start(){bagDataList = ItemDynamicDataManger.Instance.GetAllData();ShowItems(bagDataList);}/// <summary>/// 根据ServerData显示所有图标/// </summary>/// <param name="dataList"></param>private GameObject itemCellMod;private GameObject Cell_Empty;public void ShowItems(List<ItemServerData> dataList){//每次Show先清除原有列表for (int i = 0; i < itemContent.childCount; i++){Destroy(itemContent.GetChild(i).gameObject);}//加载出放在预制体目录的ItemCell,原层级图中ItemCell和ItemCell_Empty提前手动删除if (itemCellMod == null){itemCellMod = Resources.Load<GameObject>("Prefabs/UI/ItemCell");}//遍历获取服务器数据列表List<ItemServerData>for (int i = 0; i < dataList.Count; i++){GameObject item = Instantiate(itemCellMod, itemContent);item.transform.localScale = Vector3.one;item.transform.localPosition = Vector3.zero;item.name = itemCellMod.name;//显示每一行也就是每一个物品的图标ItemCellController ic = item.GetComponent<ItemCellController>();ic.Show(dataList[i]);}//定义格子数量为16,没有图标的格子显示ItemCell_Emptyfor (int i = 0; i < 16 - dataList.Count; i++){if (Cell_Empty == null){Cell_Empty = Resources.Load<GameObject>("Prefabs/UI/ItemCell_Empty");}GameObject Item_Empty = Instantiate(Cell_Empty, itemContent);Item_Empty.transform.localScale = Vector3.one;Item_Empty.transform.localPosition = Vector3.zero;Item_Empty.name = Cell_Empty.name;}}
}

3. ItemCellController.cs

上边提到的根据静态类数据加载出每个道具的图标(C),这里注意图集名称和数据列表中一一对应

图标控制器 ItemCellController.cs代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.U2D;public class ItemCellController : MonoBehaviour
{public Image icon;  //表内数据取得Iconpublic Text countText;  //服务器数据取得数量private ItemServerData mServerData;//加载图集,加载图集中的图标 //icon :  UI/SpriteAtlas/Prop#0001 public void Show(ItemServerData data){mServerData = data;ItemStaticData itemStaticData = ItemStaticDataManager.Instance.GetItemByID(data.id);if (itemStaticData == null){Debug.LogError("没有数据:" + data.id);return;}//加载图集,#分隔 图集#图标string[] icons = itemStaticData.Icon.Split('#');SpriteAtlas prop = Resources.Load<SpriteAtlas>("UI/SpriteAtlas/"+ icons[0]);icon.sprite = prop.GetSprite(icons[1]);//加载道具数量countText.text = data.count + "";}
}

————————————————————————————————————————————————————

  • 新建Canvas搭建UI视图
  • 根据上图五个部分拆分需要实现的功能
  • 根据需求拆分MVC
  • 编写代码
  • 查看效果

进行到这里就可以先运行项目查看一下图标的显示效果了(当然控制物品栏打开的操作这里依然没有讨论,需要的朋友还请自己添加)

四、体验MVC模式开发UI背包的优点

如果只看上边完成的这部分UI,似乎展示背包中物品数据这样一个简单的功能,只要用一个类就能完成所有代码的书写,为什么还要如此大费周折,分析各个部分来进行MVC模式的设计呢?下边我们通过为上边完成的这个简单背包添加新的功能,来体验一下MVC模式下,开发UI背包的优点。

1.为背包添加搜索功能

  • 当玩家在搜索栏中输入文字后,立马展示背包中名称包含该文字的道具

上文已分析过,第二部分(search)包括:一个图片,一个InputFiled组件(V),根据道具名称(M),搜索道具(C)

第一步:分析新增需求,在动态数据类ItemDynamicDataManger.cs中,新增根据玩家输入,查找道具栏道具的方法

 //新增至 ItemDynamicDataManger.cs//根据名称筛选道具public List<ItemServerData> GetItemByName(string name){if (name == string.Empty){return list;}List<ItemServerData> nameList = new List<ItemServerData>();for (int i = 0; i < list.Count; i++){ItemStaticData data = ItemStaticDataManager.Instance.GetItemByID(list[i].id);if (data.Name.Contains(name)){nameList.Add(list[i]);}}return nameList;}

第二步:在层级图中,找到对应第二部分的UI效果图中,search部分的游戏物体,新增SearchController.cs

search部分 searchController.cs代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;//挂载到 search
public class SearchController : MonoBehaviour
{public void SearchEquip(string name)    {List<ItemServerData> list = ItemDynamicDataManger.Instance.GetItemByName(name);BackPackPanel.Instance.ShowItems(list);     }
}

搜索功能完成效果:

2.为背包添加按类型筛选道具功能

  • 当玩家在道具栏上方点击道具类型后,展示背包中该类型下的所有道具

上文已分析过,第三部分(Types)包括:一个Toggle组件(V),根据道具类型(M),筛选道具(C)

第一步:分析新增需求,在动态数据类ItemDynamicDataManger.cs中,新增根据玩家点击,筛选道具栏道具的方法

 //新增至 ItemDynamicDataManger.cs//根据Types筛选道具public List<ItemServerData> GetItemByType(int type){if (type == 0){return list;}else{List<ItemServerData> typeList = new List<ItemServerData>();for (int i = 0; i < list.Count; i++){if (ItemStaticDataManager.Instance.GetItemByID(list[i].id).Type == type){typeList.Add(list[i]);}}return typeList;}}

第二步:在层级图中,找到对应第三部分的UI效果图中,Types部分的游戏物体,新增TypesController.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;//挂载到 Types
public class TypesController : MonoBehaviour
{public ToggleGroup group;int selectType = 0;public void SelectType(bool isOn){if (!isOn){return;}foreach (Toggle t in group.ActiveToggles()){switch (t.gameObject.name){case "All":selectType = 0;break;case "consumables":selectType = 1;break;case "equip":selectType = 2;break;default:break;}}//Debug.Log("selectType:" + selectType);List<ItemServerData> list = ItemDynamicDataManger.Instance.GetItemByType(selectType);BackPackPanel.Instance.ShowItems(list);}
}

第三步:Content中新增ToggleGroup组件,并拖入All/equip/consumables中,三个ToggleGroup成员的OnValueChanged组件记得添加新增的事件

筛选道具功能完成效果:

3.为背包添加整理功能

  • 当玩家在道具栏下方点击”整理“按钮后,整理道具栏中道具间的空位

上文已分析过,第五部分包括:一个button组件(V),实现物品栏的简单整理(C)

在添加整理功能前,为了方便展示功能,先加入一个可以通过鼠标拖动来改变道具位置的方法DragImage.cs,详细方法请移步站内优秀文章: Unity拖动背包物品/技能图标位置互换

第一步:分析新增需求,因为道具栏中显示的道具和道具的数量并未发生改变,所以和在动态数据类ItemDynamicDataManger.cs中,无需新增方法

第二步:在层级图中,找到对应第五部分的UI效果图中,Clear部分的游戏物体,新增ClearCellsController.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//挂载到 Clear
public class ClearCellsController : MonoBehaviour
{public Transform Content;private GameObject Cell_Empty;//整理//删除背包空显示public void ClearCells(){int deleteNum = 0;for (int i = 0; i < Content.childCount; i++){Transform t = Content.GetChild(i);if (t.childCount == 0){Destroy(t.gameObject);deleteNum++;}}//用空格填补移走的位置for (int i = 0; i < deleteNum; i++){if (Cell_Empty == null){Cell_Empty = Resources.Load<GameObject>("Prefabs/UI/ItemCell_Empty");}GameObject Item_Empty = Instantiate(Cell_Empty, Content);Item_Empty.transform.localScale = Vector3.one;Item_Empty.transform.localPosition = Vector3.zero;Item_Empty.name = Cell_Empty.name;}}
}

完成代码后记得给按钮添加事件

整理功能完成效果:

写在最后:

通过第四部分为我们搭建好的UI背包来新增的三个功能,不难看出,在MVC设计模式的基础下写好的脚本中,每次新增功能,我们只需要考虑:

  • Model部分的数据是否发生了改变(也就是是否需要新增或改变方法),View部分的内容是否需要跟着进行改变

  • 在View部分并无新增显示时(这里指已经搭建好的UI模版未发生新增游戏物体),如何通过对Controller部分类的改进或者新增,实现View部分的实时跟进,而无需对View层进行直接的改动,实现了M-V的代码分离

  • 在View部分有新增显示时,也就是UI层中新增了游戏物体,比如本次案例中,新增点击道具图标或鼠标悬停显示道具的描述信息,那么就需要我们首先在层级图中搭建新的UI组件,并且去View部分的代码中新增这部分的显示,再根据上边的步骤来进行Model部分和Controller部分的改进或新增

所以可以总结,在MVC设计模式下的UI背包系统,具有:

  • 结构清晰,耦合性低
  • 如果MVC三部分由不同的开发人员完成,View部分人员在拿到需求后对UI框架和View部分代码进行新增,Model部分和Controller部分的人员只需根据新增的游戏物体去代码中实现对应的新增即可,使得开发人员分工明确,提高开发的效率
  • 后期维护更加方便,降低了维护成本,如果出现BUG,只需到对应游戏物体的MVC部分分别进行检查。

Unity MVC设计模式与UI背包界面制作相关推荐

  1. 【Unity】励志成为最强UI仔—BeaverJoe项目之UI界面制作【上】代码优化-动态加载UI对象

    跟BeaverJoe老师学习UI界面制作中,在原先的项目中,角色的solt是静态的五个角色.所以我想把他改成一个动态通过PlayerGM来控制的一个动态加载UI 效果展示 /// <summar ...

  2. Unity NGUI 网络斗地主 -界面制作

    Unity NGUI 网络斗地主 -界面制作 源文件在群(63438968群共享!) @灰太龙 这一节说一下NGUI的界面摆放,并且教会大家使用NGUI的自适应功能! 在这里感谢@Gamer,是他给我 ...

  3. Unity游戏积分/计分UI系统制作方法

    Unity游戏积分/计分UI系统制作方法 本篇博客将讲解游戏开发中常见的积分/计分系统的制作,看过论坛有很多这种帖子,但基本都是从一个大型系统里进行讲解,今天我把这个方法独立挖出来做个小案例,来分析这 ...

  4. Ruby‘s Adventrue游戏制作笔记(十六)Unity子弹数量及其UI

    Ruby's Adventrue游戏制作笔记(十六)Unity子弹数量及其UI 前言 一.创建新的UI 二.编辑脚本 三.创建获得子弹的道具 系列链接 前言 本文章是我学习Unity官方项目项目所做笔 ...

  5. 学习笔记 --- 工厂、单体、适配器、策略、观察者、MVC设计模式及ASP.NET MVC开发模式、关闭缓存的方法...

    关于工厂.单体.适配器.策略.观察者没啥好说的, 代码中有说明 //DesignPattern.cs View Code using System; using System.Collections. ...

  6. iOS中MVC设计模式

    在组织大型项目的代码文件时,我们常用MVC的思想.MVC的概念讲起来非常简单,就和对象(object)一样.但是理解和应用起来却非常困难.今天我们就简单总结一下MVC设计理念. MVC(Model V ...

  7. ASP.NET下MVC设计模式的实现

    1 MVC设计模式简介 MVC由Trygve Reenskaug提出,首先被应用在SmallTalk-80环境中,是许多交互和界面系统的构成基础.MVC结构是为那些需要为同样的数据提供多个视图的应用程 ...

  8. Javaweb MVC设计模式、Modle发展史、项目分层和三层架构

    文章目录 MVC设计模式 MVC的目的 MVC举例 jsp+servlet+javabean模式 MVC的优点 MVC的缺点 Modle 发展史 项目分层 三层架构 MVC设计模式 MVC模式(Mod ...

  9. javacript中的mvc设计模式

    以下内容为原创翻译,翻译不对的地方还请原谅,凑合着看吧. 原文网址是: 来源:http://www.alexatnet.com/articles/model-view-controller-mvc-j ...

最新文章

  1. mysql教程左右链接_mysql的左右内连接用法实例
  2. 【基础】CSS实现多重边框的5种方式
  3. ACTIVEX DLL时,如何在回调函数中改变DLL的一些私有变量的值,并触发用户事件?...
  4. H5新增的标签以及属性 2
  5. 【今晚7点半】:华为云视频直播在各细分场景的体验指标优化实践
  6. 什么是依赖注入?(听来的一个笑话)
  7. 苹果手机耗电快_iPhone12用5G耗电快,苹果回应
  8. python PyQt5初级教程hello world
  9. UI_UISlider控件
  10. 1.恶意软件中的防双开
  11. 使用Go实现Socket服务端和客户端通信
  12. oracle客户端sqlplus镜像(可通过ssh客户端远程连接操作sqlplus命令)
  13. 多选框取值和默认勾选
  14. 大厂的区块链之路|蚂蚁金服怎么玩?
  15. 成为oracle白金会员,华为成为Linux基金会白金会员
  16. transact sql mysql_Sql Server数据库常用Transact-SQL脚本(推荐)
  17. ASP.NET建筑工程管理系统
  18. PS 逆时针绕圈文字
  19. 程序员的520表白代码,你给你对象整过几个?
  20. DID分布式身份标识技术调研

热门文章

  1. 如何在Keil官网里面下载固件库(以STM32系列主芯片固件库为例)【最新版】
  2. 00012.02设计模式(模板设计模式的了解)
  3. 创建 Laravel 项目
  4. python操作pdf做文档的分割、合并,内容提取
  5. 使用第三方短信服务商云片发送短信(php样例)
  6. 电脑“IP地址冲突”,上不了网,该怎么办
  7. oracle关键字pivot行转列
  8. 什么是真正的 3D CAD 模型(2)
  9. vue项目中使用腾讯美颜SDK
  10. 英文版ebook网站