通用游戏地图解决方案设计解析

news2024/11/21 0:31:45

前言:

在软件开发过程中,我们都希望能设计出一个稳健的,可维护的系统,为了实现这个目的,人们总结出了很多相关的设计原则,比如SOLID原则, KISS原则等等。SOLID每个字母代表了一种设计原则,具体大家可以去看《架构整洁之道》这本书或者在网上找相关的博客学习。在本系统中将应用这些原则,力求得到一个可用好用可理解可改动的通用地图模块。SOLID原则中的D,指的是Dependence Inversion Principle,中文可以叫依赖倒置原则或者依赖反转原则。其强调的是在处理依赖关系时,应当尽可能依赖抽象类型,而不是具体实现。 在绝大多数情况下,抽象的东西是稳定的,在设计这个抽象类或者接口时,我们往往会带着前瞻性的思维去考虑,这个接口设计得是否合理?是否方便使用者去实现去拓展?如果一个抽象的对象发生变化了,那对应的所有实现一定会发生改变,而如果具体实现发生变更,不会影响到抽象接口。因此我们有理由判定,接口是不太容易变更的,如果一个接口被反复修改,那一定是设计这个接口的时候有地方不合理:接口承担的内容过多;或者是接口方法不够通用,带进来了一些业务相关的东西。依赖反转原则也是本文主要讨论的原则。

正文

结构组成

本系统主要由三部分内容组成,一个是基础的数据结构模块,一个是寻路模块,一个是地图模块。三个模块功能内聚,但也存在耦合,为了防止一个模块修改导致其他模块发生变更,所以在耦合时,尽量使其仅依赖其他模块中的接口,因此模块具体实现修改并不会导致其他模块受到影响。下面将简单介绍这三个模块的内容与设计思路。

数据结构模块

基础的数据结构模块提供了一个最小堆和最大堆,这是具体的实现,其目的是为寻路模块提供服务的,寻路模块直接依赖了数据结构模块,如果从设计的角度出发,这其实是不应该的,因为前面说了我们要依赖抽象类或接口,而不是具体实现,不过我们也可以发现,最小堆和最大堆相对来说是一个较稳定的东西,其设计完成一旦通过验证,就不太会有理由再去修改它们,因此可以认为这个虽然是具体实现,但是是一个不容易发生变更的具体实现,所以直接依赖这个具体实现也是没问题的。如果非常希望解除耦合将这个直接依赖给取消掉,我们也可以考虑引入一个IHeap的接口,让寻路模块依赖这个接口,同时原来的最小堆最大堆实现该接口,因此寻路依赖堆实现类,变成了寻路和堆实现类均依赖IHeap接口。

原依赖关系

image-20230303132941756

新依赖关系

image-20230303133036132

寻路模块

寻路模块中定义了一个寻路的接口IPathSearch,并实现了三种A星算法,分别是原始的A星算法和两种经过一定优化后的A星算法,实现这个接口的好处在于,如果后期我们需要针对特定的场景比如横冲技能,使用额外的寻路规则,仅需额外再实现一种新的寻路算法,并在原本实例化寻路算法的地方改成实例化新的寻路算法即可,而使用的地方由于依赖的是IPathSearch接口,因此完全不需要做任何修改。
PathFinding类图.drawio

地图模块

地图模块主要分成地图抽象层、地图具体实现和地图生成三个部分,具体可查看文章后面的类图或项目代码。

地图抽象层定义了整个地图模块的基本要素和必要方法,比如IMapGrid接口定义了地图的基本数据,对外的方法等等。该接口依赖INode接口,INode接口定义了一个最基础的地图节点是啥样的,而IPathNode则在INode的基础上,增加了寻路相关的属性。另外定义了一个IMapShow的接口,负责处理地图节点的实际显示,比如绑定地图数据层和实际节点对象等功能。INodeEntity定义了节点的基本操作,比如设置高亮等等。在这个层中定义的接口,均不依赖某个具体实现,但定义了地图的几乎所有行为,在实现功能的前提下保证了该层的稳定性。

地图具体实现则实现了两种常见的游戏地图:网格地图和六边形地图。这两种地图实现了IMapGrid的方法,并在实例化的地方注入了创建节点的方法,使得地图类型和节点可以摆脱依赖,便于后面实现同种类的地图不同的节点。

地图生成部分定义了一个生成配置类和生成类,配置类负责配置要生成的地图种类,地图所处的平面,以及各种类地图的一些独有参数。生成类依赖配置来创建具体的地图,并为其绑定对应的显示。

系统完整类图

MapGridInUnity类图.drawio
看不清可以去这里查看。

额外的实现细节

系统的整体结构与设计如上文所示,为了实现地图的通用和寻路通用,还有一些比较巧妙的设计,这里进行补充阐述。

  • 在寻路的时候,经常会需要从一个节点访问到该节点的一圈邻居,但是不同的地图,获取某个节点邻居的方式不同,为了通用我们需要定义一个方法,能直接获取到某节点的所有邻居。本系统定义了一个IDir接口,用来获取一个节点四周邻居相对于该节点的索引偏移,这样在寻路时可以通过该节点和索引偏移计算出周围所有邻居的节点。在网格地图中,分为了两种,一种是四方向的,一种是八方向的,对应的索引偏移列表不同。而对于六边形地图,则是另一种索引偏移,所以在本系统中,分别实现了三个具体实现,并通过配置来决定使用哪组偏移列表。
  • 为了高效在六边形地图中实现范围高亮,六边形使用了Cube coordinates,这个名词来自于Amit Patel的《Hexagonal Grids 》中,其使用了三个轴来表示每个节点,使得在访问邻居时,可以在三个轴上做索引偏移,大大减少了查找邻居的消耗。这里我最开始是使用了偏移坐标,但是在实际去高亮大范围节点时发现并没有那么轻松,尤其是高亮可能发生在地图边缘,后面忍痛对此做了重构改用立方体坐标了。
  • 在处理扇形高亮时,实际上就是从圆形高亮中抠出一部分,因此其内部是判断一个朝向,并根据扇形宽度算出扇形左右最大偏移角,最后调用圆形高亮的方法并添加一个角度的Filter对节点做筛选。
  • 地图配置类中,增加了编辑器类,来对配置类Inspector界面做个性化显示,根据选取的地图类型,来展示该类型所需要的独有参数,并隐藏无关的参数。

总结

以上便是整个地图系统的全部内容和设计思路了,在类图中,箭头代表了依赖关系走向,A指向B表示A依赖B。从图中不难看出,箭头指向的对象基本都是接口,抽象类,或者是一些不太会发生变化的东西。如数据结构小节中的图示,即便依赖的对象是具体实现,我们也可以引入更抽象的接口对象,实现依赖反转,使得整个依赖关系走向更合理,系统更稳定可靠。当然这个系统并不完善,也可能不如料想的那般好用,但我希望通过开源出来给更多的人看见,给更多的人使用,并提供反馈来完善这个东西,使得其可以代代相传(doge。

项目地址:

https://github.com/tang-xiaolong/MapGridInUnity

引用:

  1. Amit Patel.《Hexagonal Grids 》https://www.redblobgames.com/grids/hexagons/
  2. Catlike Coding.《Hex Map》https://catlikecoding.com/unity/tutorials/hex-map/
  3. Robert C.Martin. 《架构整洁之道》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/387891.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据是如何在计算机中存储的

我们普通人对于数据存储的认识恐怕大多数都是从自己使用的电脑来的。现在几乎人手一台电脑,而我们的电脑存储着各种各样的文件,比如视频文件、音频文件和Word文档等。这些文件从计算机术语的角度都可以称为数据。 如图1-1所示是Windows 10 “我的电脑”的截图。通过该截图我…

202302读书笔记|《长安的荔枝》——只要肯努力,办法总比困难多

202302读书笔记|《长安的荔枝》——只要肯努力,办法总比困难多 《长安的荔枝》这本书真是酣畅淋漓啊,读起来一气呵成,以讲故事的口吻叙述,上林署九品小官员——李善德,兢兢业业工作多年,终于借贷买了房&…

mac M1 nvm安装教程,避坑

mac M1 nvm 安装问题 新款的mac搭载了苹果自研的芯片,放弃了intel的x86芯片,那之前的软件难免会存在兼容性问题。 鄙人有幸踩了第一个坑。 在通过nvm 安装不同版本的node 时,出现了问题。 问题一:先说一下 nvm的安装问题&#…

Python02 基础语法

Python02 基础语法 二、基础语法 2.1 注释 概念:对某一部分代码的解释说明,在程序执行的时候不会去进行执行。比如:产品的说明书、电影的旁白。 程序员最讨厌的两件事: 一件是自己写代码被要求加注释 另一件是接手别人代码&…

Cesium三维数据格式以及生产流程详解(glb,osgb,obj,bim,ifc)等

最近收到私信问我在cesium上展示的一些三维数据是如何生产和处理的,这篇文章就给大家一次性讲个透彻。 首先我们来做做分类。市面上能接触到的,常见的,cesium上支持展示的三维数据大致分为以下几种: 1.倾斜摄影(osgb,obj) 2.点云数据(las,pts) 3.手工模型(gltf,…

win10安装oracle

文件放到最后。我的电脑是win11的,因为老师让写下安装笔记,在11上安装的时候没有截屏,所以在虚拟机上重新安装下吧。室友说要把文件夹放到c盘才能打开。我试了下,具体的是要把Oracle11g文件夹放到c盘根目录下。如果解压后不是这个…

修正一些formdesigner的一些bug与操作

之前集成了formdesigner表单设计器,但还是有些问题,所以进行一些bug修复与功能修正 一、之前组件布局的图标不见了 在main.js里增加下面一行 import /components/formdesigner/assets/iconfont/iconfont.js 效果如下: 二、选择列表没有数…

react-draggable实现拖拽详解

react-draggable属性常用属性属性列表事件列表举例首先安装 react-draggable实现移动希望小编写的能够帮助到你😘属性 常用属性 属性默认值介绍axisxhandle拖动的方向,可选值 x ,y,bothhandle无指定拖动handle的classposition无handle的位置&#xff0…

习题30 if elif else 语句

people 30#变量people赋值30 cars 40#变量cars赋值40 buses 15#变量buses赋值 if cars > people:#如果出租车比人多print("We should take the cars")#我们坐出租车 elif cars < people:#elif后面必须跟条件&#xff0c;print("We should not take the…

MyBatis框架的入门案例

MyBatis框架的入门案例 资源地址&#xff1a;https://download.csdn.net/download/weixin_41957626/87531373 1.MyBatis的配置 环境&#xff1a;基于maven的结构 1.1目录结构 1.2依赖包 <dependencies><!--mybatis--><dependency><groupId>org.mybatis…

【Vue3 第二十二章】过渡动画

一、基本用法 <Transition> 是一个内置组件&#xff0c;这意味着它在任意别的组件中都可以被使用&#xff0c;无需注册。它可以将进入和离开动画应用到通过默认插槽传递给它的元素或组件上。进入或离开可以由以下的条件之一触发&#xff1a; 由 v-if 所触发的切换由 v-…

函数栈帧的创建和销毁(详解)

函数栈帧的创建和销毁&#x1f996;函数栈帧是什么&#xff1f;&#x1f996;函数栈帧的创建和销毁解析&#x1f40b;栈是什么&#xff1f;&#x1f40b;认识相关寄存器和汇编指令&#x1f40b;解析函数栈帧的创建和销毁&#x1f433;预备知识&#x1f433;函数的调用堆栈&…

SpringBoot入门 - 配置热部署devtools工具

在SpringBoot开发调试中&#xff0c;如果我每行代码的修改都需要重启启动再调试&#xff0c;可能比较费时间&#xff1b;SpringBoot团队针对此问题提供了spring-boot-devtools&#xff08;简称devtools&#xff09;插件&#xff0c;它试图提升开发调试的效率。准备知识点什么是…

汇编系列02-借助操作系统输出Hello World

说明:本节的程序使用的是x86_64指令集的。 汇编语言是可以编译成机器指令的&#xff0c;机器指令是可以直接在CPU上面执行的。我们编写的汇编程序既可以直接在操作系统的帮助下执行&#xff0c;也可以绕过操作系统&#xff0c;直接在硬件上执行。 如果你打算编写的汇编程序在…

Buuctf Younger-drive 题解

目录 一.查壳 二.运行缺少dll 三.主函数 四.hObject线程 五.Thread线程 六.judge函数 七.解题脚本 这题的关键在于了解一定的线程相关知识 一.查壳 32位带壳,用upx脱壳 二.运行缺少dll 后续尝试了各种方法修复dll但是还是运行不了 值得一提的是脱壳后的程序不能动态调试…

分布式事务概述

什么是分布式事务&#xff1f;和本地事务的区别 分布式事务是指会涉及到操作多个数据库的事务。其实就是将对同一库事务的概念扩大到了对多个库的事务。目的是为了保证分布式系统中的数据一致性。分布式事务处理的关键是必须有一种方法可以知道事务在任何地方所做的所有动作&a…

1.机器学习笔记第一周

机器学习利用领域&#xff1a; 1&#xff1a;随着网络数据增大&#xff0c;需要搜集用户的数据&#xff0c;做喜好性偏向判断等。 2&#xff1a;只要有数据的&#xff0c;无论是医疗领域&#xff0c;还是基因领域都是需要机器学习来发现数据密码。 3&#xff1a;机器自我学习…

JUC包:CyclicBarrier源码+实例讲解

1 缘起 上篇文章讲到了CountDownLatch&#xff1a;https://blog.csdn.net/Xin_101/article/details/129116170 作为同系的佼佼者&#xff0c;不得不提CyclicBarrier&#xff0c; 设计理念相似&#xff0c;都是多线程等待&#xff0c;但是&#xff0c;应用的技术以及功能不同&a…

Halcon——关于halcon中的一些语法

Halcon——关于halcon中的一些语法前言一、变量的创建与赋值二、if语句三、for语句四、while语句五、中断语句六、switch语句总结前言 在HDevelep环境下编程时&#xff0c;所用的一些语法与C#有些差异&#xff0c;在此做下记录。 一、变量的创建与赋值 Hdevelep中调用函数时&…

北大青鸟天府校区IT学习大揭秘

口罩已放开&#xff0c;一切都要重新出发。 开年才一个多月&#xff0c;已经有很多小伙伴想培训转行IT行业或者已经在咨询、报名培训IT技术。作为老牌培训机构&#xff0c;也有很多小伙伴相信我们&#xff0c;选择了我们。很感谢大家的信任&#xff0c;作为老牌培训机构&#…