通过过滤器模式实现功能:根据不同选择对数据进行针对性排序。

思路
1.获得玩家的排序选择(如类型、数量、价格等,可使用简单的按钮button组合或者下拉框等方式)。
2.使用过滤器模式进行设计。
3.根据玩家的选择,选取不同的过滤器进行数据过滤、排序并返回。

过滤器模式
    帮助开发者用不同的标准来过滤一组对象,通过逻辑运算以解耦的方式把它们连接起来。这种类型的设计模式属于结构型模式,它结合多个标准来获得单一标准。
    1.创建一个类
    2.创建一个接口-接口实现方法:过滤方法(参数为该类的列表,返回也为该类的列表)
    3.创建该接口的实体类来过滤对象类的列表

通过在实体类中的过滤方法进行过滤(实体类的过滤方法中新建列表,然后遍历传入的参数列表,过滤符合条件的加入到新建列表中。最后返回这个新建列表)
    过滤器的核心有两点:1.通过接口将其实体类的过滤方法联系起来。2.针对具体过滤条件进行编程。
    应用场景:需要对一系列的对象进行不同条件筛选的场景。
        如排序、筛选符合条件的对象集等。

相关部分代码:

本例在排序部分使用了快排。

接口的实体类

public interface IGoodsCriteria
{/// <summary>/// 排序标准/// </summary>/// <param name="listGoods">排序的列表</param>/// <param name="order">>=0表示顺序,0<表示逆序</param>/// <returns></returns>List<BaseGoods> sortCriteria(List<BaseGoods> listGoods,int order = 1);
}public class SortGoodsAmount : IGoodsCriteria
{public List<BaseGoods> sortCriteria(List<BaseGoods> listGoods,int order =1){List<BaseGoods> amountGoods = new List<BaseGoods>();//快排·从小到大ToolMgr.Instance.QuickSortList(listGoods, GlobalPropertyMgr.SortGoodsType.Amount, 0, listGoods.Count - 1);//顺序if (order >= 0){//将排序结果顺序加入for (int i = 0; i < listGoods.Count; i++){amountGoods.Add(listGoods[i]);}}//逆序else{//将排序结果逆序加入for (int i = listGoods.Count - 1; i >= 0; i--){amountGoods.Add(listGoods[i]);}}return amountGoods;}
}public class SortGoodsQuality : IGoodsCriteria
{public List<BaseGoods> sortCriteria(List<BaseGoods> listGoods, int order = 1){List<BaseGoods> qualityGoods = new List<BaseGoods>();//快排·从小到大ToolMgr.Instance.QuickSortList(listGoods, GlobalPropertyMgr.SortGoodsType.Quality, 0, listGoods.Count - 1);//顺序if (order >= 0){//将排序结果顺序加入for (int i = 0; i < listGoods.Count; i++){qualityGoods.Add(listGoods[i]);}}//逆序else{//将排序结果逆序加入for (int i = listGoods.Count-1; i >=0; i--){qualityGoods.Add(listGoods[i]);}}return qualityGoods;}
}

流程代码:

    private void OnClickDPOption(int index){//根据下拉选项框进行排序方式的选择sortType = (SortGoodsType)dpSortOrder.value;}//修改排序次序private void OnClickChangeOrder(){//修改排序顺序intSortOrder *= -1;//重置dicBagItem//点击事件foreach (BagItem item in dicBagItem.Values){item.ClickBtn.onClick.RemoveListener(delegate { OnClickBagItem(item.Go); });}//字典数据清除dicBagItem.Clear();//每次点击事件时根据点击的选项生成,默认生成第一个选项(“全部”)//清除原有的选项(优化:修改原有的选项,多余的删除,不足的新建)//清除所有选项for (int i = 0; i < goBagContent.transform.childCount; i++){Destroy(goBagContent.transform.GetChild(i).gameObject);}//按照该排序方式进行排序更新OnSort();AddBagItemBySortList();}//排序private void OnSort(){//根据选择的排序方式进行过滤器排序//默认为品质从高到低排序。采用快排算法switch (sortType){case SortGoodsType.Quality:listBagItem = listSortQuality.sortCriteria(listBagItem, intSortOrder);break;case SortGoodsType.Amount:listBagItem = listSortAmount.sortCriteria(listBagItem, intSortOrder);break;default:Debug.LogError("Wrong sortType:"+ sortType);break;}}//OnClickSort_end/// <summary>/// 根据listBagItem(排序列表)进行重新生成物品选项集/// </summary>private void AddBagItemBySortList(){foreach (BaseGoods bagItem in listBagItem){//根据需要,初始化BagItem选项BagItem newBagItem = new BagItem();//Go,new预设体并设置父节点。需要调整SceneMgrnewBagItem.Go = GameObject.Instantiate(SceneMgr.Instance.UIGoBagItem, goBagContent.transform);//根据具体的预设体,调整BagItem类与以下数据获取的语句newBagItem.Name = newBagItem.Go.GetChildControl<Text>("Name");newBagItem.Icon = newBagItem.Go.GetChildControl<Image>("Icon");newBagItem.ClickBtn = newBagItem.Go.GetComponent<Button>();newBagItem.Amount = newBagItem.Go.GetChildControl<Text>("Amount");newBagItem.Quality = newBagItem.Go.GetChildControl<Text>("Quality");//修改Text文本if (String.IsNullOrEmpty(bagItem.Name)){Debug.LogError("item.refObjs.Name:" + bagItem.Name);return;}newBagItem.Name.text = bagItem.Name;//如果数量<=1,则不显示数量if (bagItem.Amount<=1){newBagItem.Amount.gameObject.SetActive(false);}else{newBagItem.Amount.text = bagItem.Amount.ToString();}newBagItem.Quality.text = bagItem.ValueType;//更新图标iconif (String.IsNullOrEmpty(bagItem.IconPath)){Debug.LogError("item.refObjs.Name:" + bagItem.IconPath);return;}newBagItem.Icon.sprite = Resources.Load<Sprite>(bagItem.IconPath);//添加点击事件newBagItem.ClickBtn.onClick.AddListener(delegate { OnClickBagItem(newBagItem.Go); });newBagItem.Id = bagItem.Id;dicBagItem.Add(bagItem.Id, newBagItem);}}

快速排序算法

    private int Paritition<T>(List<T> list, SortGoodsType sortKind, int low, int high){//判定是否为BaseGoodsvar tryList = list as List<BaseGoods>;if (tryList != null){BaseGoods pivotObj = tryList[low];//自定义数据类型,前置操作符要比后置操作符性能好点switch (sortKind){case SortGoodsType.Quality://品质:int(数值越大,品质越好)while (low < high){//从高位一直往后寻找第一个比pivot小或等于的数while (low < high && pivotObj.Quality <= tryList[high].Quality){--high;}//将array[low]即pivot,赋给这个比pivot小的数list[low] = list[high];//从低位一直往前到当前high的位置寻找第一个比pivot大或等于的数while (low < high && pivotObj.Quality >= tryList[low].Quality){++low;}//将array[high]赋值给array[low]。tryList[high] = tryList[low];}//将保存的pivot(初始值)赋值给low,完成交换tryList[low] = pivotObj;//将当前的位置返回return low;case SortGoodsType.Amount://数量:intwhile (low < high){//从高位一直往后寻找第一个比pivot小或等于的数while (low < high && pivotObj.Amount <= tryList[high].Amount){--high;}//将array[low]即pivot,赋给这个比pivot小的数list[low] = list[high];//从低位一直往前到当前high的位置寻找第一个比pivot大或等于的数while (low < high && pivotObj.Amount >= tryList[low].Amount){++low;}//将array[high]赋值给array[low]。tryList[high] = tryList[low];}//将保存的pivot(初始值)赋值给low,完成交换tryList[low] = pivotObj;//将当前的位置返回return low;default:break;}}Debug.LogError("No find List<obj> or wrong sortKind!");return low;}/// <summary>/// List快排/// 备注:与其做成泛型增加复杂度(N*M),不如拆解为各自类的List的方法/// </summary>/// <typeparam name="T"></typeparam>/// <param name="list"></param>/// <param name="sortType">枚举:排序类型</param>/// <param name="low">起始点</param>/// <param name="high">终止点</param>public void QuickSortList<T>(List<T> list,SortGoodsType sortType, int low, int high){//判定是否为BaseGoodsvar tryList = list as List<BaseGoods>;if (tryList != null){if (low < high){int pivot = Paritition(list, sortType, low, high);QuickSortList(list, sortType, low, pivot - 1);QuickSortList(list, sortType, pivot + 1, high);}}//根据需要添加判定是否为其他类List//...}

实现效果:

排序(过滤器模式+快速排序算法)


查找(通过最相似字符串匹配对目标字符串相似数据查找)

1.获取玩家需要查找的数据(如使用UI获取玩家输入,并对数据进行整理)
2.对数据集遍历寻找玩家需要的数据(简单的if语句进行匹配即可)。对于如名字等这类字符串使用简单的最相似字符串匹配算法进行匹配检测。
3.将符合查找条件的数据整合并返回。

最相似字符串匹配算法:
    设置一个匹配度(int,0~1)。制定匹配规则(大小写错误惩罚、字符缺失惩罚、字符匹配加权等)。将字符串从头到尾遍历匹配字符,每找到一个符合匹配规则的字符,根据匹配规则对匹配度进行累加,并开始下一个字符匹配。直到到达字符串尾部。最终得到一个0~1的匹配度,0表示毫无相关,1表示一模一样。匹配度越接近1,表示2个字符串越匹配。
代码:

   //BagPanel//查找private void OnClickSearch(){//关闭详细信息UIcurGoods = null;//打开条件查找窗口SearchPanel//位置为具体信息UI的地方出现,方便玩家查找。WindowMgr.Instance.OpenWindow<SearchPanel>(true);//窗口的确定/查找按钮使用消息播报传回Send.SendMsg(SendType.SearchSendInfo, this.name);RefreshUI();}
private void EventSearchGoods(object[] _objs){if (_objs.Length < 5){Debug.LogError("Wrong args");return;}string name = (string)_objs[0];int mainType = (int)_objs[1];int kind = (int)_objs[2];int quality = (int)_objs[3];int priceBegin = (int)_objs[4];int priceEnd = (int)_objs[5];ClearDicBagItem();//将性能消耗最多的名字匹配放在最里层foreach (BaseGoods bagItem in PlayerMgr.Instance.ItemView.Bag.DetailData.Values){//寻找匹配MainType的背包数据或curMainType为"全部"if (bagItem.MainType == mainType || mainType == GlobalPropertyMgr.MAINTYPE_ALL){//寻找匹配Kind的背包数据或curKind为"全部"if (bagItem.Kind == kind || kind == GlobalPropertyMgr.KIND_ALL){if (bagItem.Quality==quality || quality==GlobalPropertyMgr.QUALITY_ALL){if (bagItem.PriceSpace <= priceEnd && bagItem.PriceSpace>=priceBegin){//如果名字有输入,则进行匹配if (!string.IsNullOrEmpty(name)){//名字匹配·最相似字符串算法:6-55,写在GameTool中,传入string与string,返回相似匹配的数值//待优化:将匹配度按高到底再排序一次float similar = GameToolMgr.MaxSimilarMatchString(name,bagItem.Name);if (similar >= GlobalPropertyMgr.FLO_SIMILAR_NAME){listBagItem.Add(bagItem);}}else{listBagItem.Add(bagItem);}}}//if_Quality}//if_Kind}//if_MainType}//foreach_Bag.DetailData_end//根据当前排序选项进行list排序OnSort();//根据listBagItem进行生成UI物品选项AddBagItemBySortList();}
//SearchPanel
private void OnClickSearch(){//将搜集到的查找信息反馈给对应的Panelstring name = inputName.text;int mainType = dpMainType.value;int kind = dpKind.value;int quality = dpQuality.value;int priceBegin = int.Parse(inputPriceBegin.text);int priceEnd = int.Parse(inputPriceEnd.text);switch (sendName){case "Win_BagPanel":Send.SendMsg(SendType.SearchBagGoods,name,mainType,kind,quality,priceBegin,priceEnd);break;case "Win_TradePanel":Send.SendMsg(SendType.SearchTradeGoods, name, mainType, kind, quality, priceBegin, priceEnd);break;default:Debug.LogError("Wrong Name Switch:"+sendName);break;}}
   //GameToolMgr/// <summary>/// 最相似字符串匹配/// 说明:该算法不是最优化算法,最佳性能不是本算法的首要考虑方向。游戏正常运行是不会调用该算法。/// 本算法匹配规则未实现:位置错误惩罚/// 后续游戏需要则根据具体需求优化该算法的新版本或使用经典字符串匹配算法技术/// 优化方向:创建索引的哈希表、动态规划思想、额外的存储等。/// </summary>/// <param name="str1"></param>/// <param name="str2"></param>/// <returns>匹配度</returns>public static float MaxSimilarMatchString(string str1, string str2){//匹配度//每找到一个匹配字符,则给matchVal增加比重数值float matchVal = 0;//左右字符串的长度int leftSize = str1.Length;int rightSize = str2.Length;//取大的字符串的长度int largerSize = leftSize > rightSize ? leftSize : rightSize;//起始int left = 0;int right = 0;//记录匹配的字符数量int count = 0;while (left != leftSize && right != rightSize){//最简单的左右匹配检测if (str1[left] == str2[right]){//增加权值matchVal += 1.0f / largerSize;++count;//若字符串未到其尾部,则+1if (left != leftSize){++left;}if (right != rightSize){++right;}}//大小写无关的匹配检测else if (char.ToLower(str1[left]) == char.ToLower(str2[right])){//增加权值,需乘以 大小写敏感惩罚系数matchVal += 1.0f / largerSize * CaseSensitive;++count;//若字符串未到其尾部,则+1if (left != leftSize){++left;}if (right != rightSize){++right;}}else{//核心:在限定的距离内(bestCount)遍历寻找目标int lsBest = leftSize;int rsBest = rightSize;int totalCount = 0;//StrBestCount为默认遍历长度,数值越小效率越高(如果字符串过长,而该值过小,可能导致与预期有相差)//如"超级无敌霹雷火云邪神求和信"与"求和信"。按理“求和信”应该有匹配度,如果StrBestCount设置为5,其访问不到,导致最终匹配值为0//如果StrBestCount设置为15,就可以访问到,最终得到匹配值为:0.2307692int bestCount = StrBestCount;int leftCount = 0;int rightCount = 0;//在外层循环遍历整个左字符串,并确保不会超过最佳数量(bestCount)for (int ls = left; ls != (leftSize) && (leftCount + rightCount) < bestCount; ++ls){for (int rs = right; rs != (rightSize) && (leftCount + rightCount) < bestCount; ++rs){//大小写无关的匹配检测if (char.ToLower(str1[ls]) == char.ToLower(str2[rs])){//如果当前遍历总数量(长度)>=最佳数量,则跳过并停止循环(局部剪枝优化)totalCount = ls + rs;if (totalCount < bestCount){//更新最佳数量bestCount = totalCount;lsBest = ls;rsBest = rs;}}++rightCount;}++leftCount;rightCount = 0;}//从匹配的左右位置开始下一个比较。在下一轮的比较中进行matchVal值的计算left = lsBest;right = rsBest;}}//匹配数量缺失惩罚//matchVal需要乘以 其匹配字符数量占最长字符串长度的比重。matchVal *= count * 1.0f / largerSize;//避免浮点错误:如果需要返回这个matchValif (matchVal > 0.99f){matchVal = 1.0f;}else if (matchVal < 0.01f){matchVal = 0.0f;}//打印相关信息Debug.Log(str1 + "|" + str2 + "|匹配度:" + matchVal);return matchVal;}

实现效果:

最大相似字符串匹配


记忆回退Esc

创建一个栈。当手动打开一个窗口时入栈这个窗口,关闭这个窗口时出栈一个窗口进行关闭。Esc则是一个手动出栈窗口,当栈的数量>0时关闭最近一个窗口。但数量为0时,打开Esc窗口。

代码:

//EscPanel/// <summary>/// 当玩家点击Esc按键时,出现或隐藏EscUI/// 如果是在最外层,则显示或隐藏Esc。负责回退上一层。类似原神打开其他窗口,按Esc回退上一层/// </summary>/// <param name="_objs"></param>private void EventEscBtn(object[] _objs) {//如果Esc栈中没有保存的窗口,则显示/隐藏Esc窗口//需要设计一个关闭CD//...if (WindowMgr.Instance.StackWindow.Count<=0){if (goEscContent.activeSelf){goEscContent.SetActive(false);}else{goEscContent.SetActive(true);}}else{//移除栈顶,并关闭该窗口string closeWin = WindowMgr.Instance.StackWindow.Peek();WindowMgr.Instance.CloseWindow(closeWin,true);}}
/// <summary>
/// 窗口管理
/// </summary>
public class WindowMgr : Singleton<WindowMgr> {public Dictionary<string, BaseWindow> allList = new Dictionary<string, BaseWindow>();public List<string> openList = new List<string>();public Stack<string> StackWindow = new Stack<string>();                         //窗口栈(用于玩家返回上一级使用)public void OpenWindow<T>(bool addStack=false) where T : BaseWindow {string winName = typeof(T).Name;BaseWindow window = GetWindow(winName);if (window == null) {Debug.LogError("open window fail window is null " + winName);return;}if (window.hasOpen)return;if (window.windowInfo.group != 0) {CloseGroupWindow(window.windowInfo.group);}if (addStack){StackWindow.Push(winName);}openList.Add(winName);window.transform.SetAsLastSibling();window.DoOpen();}public void CloseWindow<T>(bool popStack = false) {string winName = typeof(T).Name;CloseWindow(winName,popStack);}/// <summary>/// 关闭窗口/// </summary>/// <param name="winName">需要关闭的窗口名</param>/// <param name="isPop">是否需要出栈</param>public void CloseWindow(string winName,bool isPop = false) {BaseWindow window = GetWindow(winName);if (window == null){Debug.LogError("open window fail window is null " + winName);return;}if (!window.hasOpen)return;//如果需要出栈,且出栈的窗口名与需要关闭的窗口名一致if (isPop && winName.CompareTo(StackWindow.Peek())==0){//出栈StackWindow.Pop();openList.Remove(winName);window.DoClose();}else{//只关闭窗口openList.Remove(winName);window.DoClose();}}}

演示效果

记忆回退Esc功能

【游戏面包屑】功能:排序(过滤器)、查找(最相似)、记忆回退Esc相关推荐

  1. Vue动态面包屑功能的实现方法

    面包屑应该是我们在项目中经常使用的一个功能,一般情况下它用来表示我们当前所处的站点位置,也可以帮助我们能够更快的回到上个层级. 今天我们就来聊聊如何在 Vue 的项目中实现面包屑功能.以下案例都是使用 ...

  2. 【游戏面包屑】时间回溯·逆流吧

    学习链接 [Creator3]实现时间倒放效果,游戏开发时间倒流的技术实现方案,时间回溯在游戏开发中如何实现,来个tenet信条玩玩_哔哩哔哩_bilibili 早上看了这个学习视频,心里挺痒的,正好 ...

  3. 【游戏面包屑】简单的导航栏设计

    前言 为了便于UI的开发,设计了一个简单的导航栏结构.目前版本的导航栏抽象类简化了导航栏选项的事件监听.移除.后续根据需求再丰富功能.结构. 本次实例用于设计角色·技能UI窗口的左侧导航栏以及右下角的 ...

  4. 【游戏面包屑】图片自定义裁剪

    栏目介绍 本栏目主要是介绍一些游戏开发的逻辑思路以及记录可能的游戏心得.不会对排版进行追求(懒). 思路 通过给图片设定定点,并将定点按顺序连接进行裁剪,然后将裁剪获得的图片替代原图即可. 详细过程 ...

  5. 【游戏面包屑】开发类职业攻略1.0

    简单的做了一个技术类的职业规划,以及一些提升能力的方式.仅供参考,具体职位的介绍可以自行查阅资料. 游戏开发工程师职业路线 职业要求(偏独立开发者) 初级游戏客户端工程师     定位:掌握基础专业能 ...

  6. Vue+Element项目实战系列-动态面包屑

    面包屑应该是我们在项目中经常使用的一个功能,一般情况下它用来表示我们当前所处的站点位置,也可以帮助我们能够更快的回到上个层级. 今天我们就来聊聊如何在 Vue 的项目中实现面包屑功能.以下案例都是使用 ...

  7. 后台——侧边和面包屑导航栏设计与实现方案

    一.功能描述 后台管理系统最重要的功能之一无疑是导航的设计,不管是侧边导航栏还是头部面包屑导航栏,导航与路由如何去匹配是开发人员需要去考虑的问题.点击侧边导航栏的某一项可以跳转到当前页面的路由,头部面 ...

  8. android 面包屑控件,重新认识控件(一)【面包屑导航】

    最近的项目中,纠结了一下面包屑的使用.于是研究了一下这个"古老"的控件 什么是面包屑导航 网页上让用户感知,当前页面所在的层级位置,或者是产品的属性之间的关系的控件 面包屑的一般样 ...

  9. java面包屑实现_在Java中实现过滤器和面包店锁

    java面包屑实现 为了了解锁的工作原理,实现自定义锁是一种好方法. 这篇文章将展示如何在Java上实现Filter和Bakery锁(自旋锁),并将它们的性能与Java的ReentrantLock进行 ...

最新文章

  1. mysql 4 基础教程_MySQL基础教程(四):MySQL 管理
  2. 【算法漫画】什么是红黑树?
  3. 现在的网盘对开发都极度不友好
  4. Qt之QStackedWidget
  5. Java学习小代码(1)编写三个数的排序程序
  6. 【转】1:C#的三种异步的详细介绍及实现
  7. python离线录音转文字_Python将文字转成语音并读出来的实例详解
  8. java 内置web服务器_Webstorm 2016内置web服务器配置
  9. ajax前台转换json数据库,基于jQuery的ajax功能实现web service的json转化
  10. 洛谷OJ:P3811 【模板】乘法逆元(线性递推求逆元)
  11. 如何使用RDP报表工具预览页面可以出现完整数据而不是只显示一条
  12. abaqus单位问题
  13. 在windows 2003系统上安装诺基亚pc套件的方法
  14. Android 免root 备份数据,真正免root的完美备份详细使用教程
  15. PostgreSQL 各种查询
  16. Codeforces 1144G(dp)
  17. 292. Nim 游戏(简单题)
  18. 新浪随机图片壁纸API接口,刷新网页换背景接口
  19. Java Date Time 教程-java.sql.Date
  20. 提问的智慧(smart questions)

热门文章

  1. coreldraw凹槽_一种用于建筑绘图的坐标纸的制作方法
  2. 国际快递查询代码,网站的online track
  3. 泛播云服务器,什么是泛播技术?
  4. CC1310有关GPIO外设的配置过程
  5. ajax异步请求——form表单提交 及 表单序列化提交
  6. 电脑城国庆中秋装机版V16.0.iso
  7. 硬件在环仿真(HiL)测试介绍
  8. cacti监控linux网卡流量,Linux下使用Cacti监控流量状况
  9. 光纤收发器的原理及应用_光纤收发器的工作原理及使用方法介绍
  10. 思维导图制作软件/网站