当前位置: 首页 > news >正文

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

前言:

在软件开发过程中,我们都希望能设计出一个稳健的,可维护的系统,为了实现这个目的,人们总结出了很多相关的设计原则,比如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. 《架构整洁之道》

相关文章:

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

前言: 在软件开发过程中,我们都希望能设计出一个稳健的,可维护的系统,为了实现这个目的,人们总结出了很多相关的设计原则,比如SOLID原则, KISS原则等等。SOLID每个字母代表了一种设计原则&…...

java @Autowired @Resource @Inject 三个注解的区别

javax.annotation.Resourcejdk 内置的,JSR-250 中的注解。依赖注入通过 org.springframework.context.annotation.CommonAnnotationBeanPostProcessor 来处理。org.springframework.beans.factory.annotation.Autowired org.springframework.beans.factory.annotati…...

「媒体分流直播」媒体直播和传统直播的区别,以及媒体直播的特点

传媒如春雨,润物细无声,大家好直播毋庸置疑已经融入到了我们生活的方方面面,小到才艺,游戏,大到政策的发布,许多企业和机构也越来越重视直播,那么一场活动怎么最大化的进行传播,一是…...

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

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

Day907.分区表 -MySQL实战

分区表 Hi,我是阿昌,今天学习记录的是关于分区表的内容。 经常被问到这样一个问题: 分区表有什么问题,为什么公司规范不让使用分区表呢? 一、分区表是什么? 为了说明分区表的组织形式,先创建…...

C++概览:工具链、基础知识、进阶及总结

本篇写给C初学者,作为概览,文中仅包含各方面基础知识,无深入分析。 C基础概念简介 C编译过程示意图 关键词:源文件、预编译、编译、汇编、链接 C工具链总结 cmake项目工程文件是一种中介工程文件,可以转化成其他…...

目标检测中回归损失函数(L1Loss,L2Loss,Smooth L1Loss,IOU,GIOU,DIOU,CIOU,EIOU,αIOU ,SIOU)

文章目录L-norm Loss 系列L1 LossL2 LossSmooth L1 LossIOU系列IOU (2016)GIOU (2019)DIOU (2020)CIOU (2020)EIOU (2022)αIOU (2021)SIOU (2022…...

JOSN数据转换和解析

文章目录JOSN数据转换和解析内容回顾Map 集合转成 JSON 字符串List 集合转换成 JSON 字符串Ajax 异步和同步异步概念同步概念异步和同步区别异步请求案例同步请求时间格式化旧时间 api 格式化格式化和解析的工具类JSTL 时间格式化JSTL 使用JOSN数据转换和解析 内容回顾 ajax …...

浅析Linux内核中进程完全公平CFS调度

一、前序 目前Linux支持三种进程调度策略,分别是SCHED_FIFO 、 SCHED_RR和SCHED_NORMAL;而Linux支持两种类型的进程,实时进程和普通进程。实时进程可以采用SCHED_FIFO 和SCHED_RR调度策略;普通进程则采用SCHED_NORMAL调度策略。从…...

安装 RustDesk 服务器 (适用 Rocky Linux, CentOS, RHEL 系列发行版)

环境:Rocky Linux 9.1 1. 安装 Docker Engine 可以参考 [[linux-docker-rocky-install]] https://cc01cc.com/2023/03/02/linux-docker-rocky-install/英文可以参考官方文档 Install Docker Engine on RHEL https://docs.docker.com/engine/install/rhel/ 2. 安装…...

23种设计模式-策略模式

策略模式是一种设计模式,它允许在运行时选择算法的行为。它定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户端。在本文中,我们将深入探讨策略模式的概念和实际应用&#…...

C#开发的OpenRA的游戏主界面怎么样创建

通过前面加载界面布局数据,可以把整个界面逻辑的数据加载到内存, 但是这些数据怎么显示出来,又是没有定义的。比如前面定义了多个界面的布局, 又是怎么样知道需要显示哪一个界面? 现在就来解决这个问题,其实整个游戏都是可以通过yaml文件进行配置的, 所以我们需要从yaml…...

考研还是工作?两战失败老道有话说

老道入职第一周自我介绍谈谈考研谈谈工作新的启程自我介绍 大家好!在下是一枚考研失败两次的自认为聪明能干的有点小帅的实则超级垃圾的三非名校毕业的自动化渣男。大一下就加入实验室,在实验室焊板子、画板子、培训、打比赛外加摸鱼;参加过…...

引用是否有地址的讨论的

说在前头,纯属个人理解,关于引用是否有地址,实际上并没有一个很统一的说法, C标准没有规定一个引用是否需要占用一块内存。 这里引用知乎“C 中引用是一块内存的标记,那引用本身有地址吗_百度知道 (baidu.com)”里面的…...

1、JAVA 开发环境搭建 - JDK 的安装配置

文章目录一、下载 JDK81、官网地址:[**https://www.oracle.com**](https://www.oracle.com)二、安装 JDK1、鼠标右键安装包,以管理员身份运行(无脑下一步即可)2、细节说明三、配置环境变量1、为啥要配置环境变量呢?2、原因分析3、配置环境变量…...

【Storm】【六】Storm 集成 Redis 详解

Storm 集成 Redis 详解 一、简介二、集成案例三、storm-redis 实现原理四、自定义RedisBolt实现词频统计一、简介 Storm-Redis 提供了 Storm 与 Redis 的集成支持&#xff0c;你只需要引入对应的依赖即可使用&#xff1a; <dependency><groupId>org.apache.storm…...

算法代码题——模板

文章目录1. 双指针: 只有一个输入, 从两端开始遍历2. 双指针: 有两个输入, 两个都需要遍历完3. 滑动窗口4. 构建前缀和5. 高效的字符串构建6. 链表: 快慢指针7. 反转链表8. 找到符合确切条件的子数组数9. 单调递增栈10. 二叉树: DFS (递归)]11. 二叉树: DFS (迭代)12. 二叉树: …...

CentOS 7.9汇编语言版Hello World

先下载、编译nasm汇编器。NASM汇编器官网如下图所示&#xff1a; 可以点图中的List进入历史版本下载网址&#xff1a; 我这里下载的是nasm-2.15.05.tar.bz2 在CentOS 7中&#xff0c;使用 wget http://www.nasm.us/pub/nasm/releasebuilds/2.15.05/nasm-2.15.05.tar.bz2下载…...

CoreData数据库探索

CoreData是什么 Core Data 是苹果公司提供的一个对象-关系映射框架&#xff08;Object-Relational Mapping&#xff0c;ORM&#xff09;&#xff0c;用于管理应用程序的数据模型。Core Data 提供了一个抽象层&#xff0c;使开发人员能够使用面向对象的方式访问和操作数据&…...

FreeRTOS入门

目录 一、简介 二、堆的概念 三、栈的概念 四、从官方源码中精简出第一个FreeRTOS程序 五、修改官方源码增加串口打印 一、简介 FreeRTOS是一个迷你的实时操作系统内核。作为一个轻量级的操作系统&#xff0c;功能包括&#xff1a;任务管理、时间管理、信号量、消息队列、…...

JVM运行时数据区划分

Java内存空间 内存是非常重要的系统资源&#xff0c;是硬盘和cpu的中间仓库及桥梁&#xff0c;承载着操作系统和应用程序的实时运行。JVM内存布局规定了JAVA在运行过程中内存申请、分配、管理的策略&#xff0c;保证了JVM的高效稳定运行。不同的jvm对于内存的划分方式和管理机…...

重装系统一半电脑蓝屏如何解决

小编相信大家在使用电脑或者给电脑重装系统的时候都遇到过电脑蓝屏等等故障问题。最近有用户就遇到了这样一个问题&#xff0c;问小编重装系统一半电脑蓝屏怎么办&#xff0c;那么今天小编就告诉大家重装系统一半电脑蓝屏的解决方法。 工具/原料&#xff1a; 系统版本&#x…...

SpringBoot(tedu)——day01——环境搭建

SpringBoot(tedu)——day01——环境搭建 目录SpringBoot(tedu)——day01——环境搭建零、今日目标一、IDEA2021项目环境搭建1.1 通过 ctrl鼠标滚轮 实现字体大小缩放1.2 自动提示设置 去除大小写匹配1.3 设置参数方法自动提示1.4 设定字符集 要求都使用UTF-8编码1.5 设置自动编…...

springboot整合redis

1.redis的数据类型,一共有5种.后面结合Jedis和redistemplate,以及单元测试junit一起验证  1)字符串  2)hash  3)列表  4)set(无序集合)  5)zset(有序集合) 2.Jedis的使用  a)引入依赖 <!--加入springboot的starter的起步依赖--><dependency><groupId>…...

【Java】Spring Boot下的MVC

文章目录Spring MVC程序开发1. 什么是Spring MVC&#xff1f;1.1 MVC定义1.2 MVC 和 Spring MVC 的关系2. 为什么学习Spring MVC&#xff1f;3. 怎么学习Spring MVC&#xff1f;3.1 Spring MVC的创建和连接3.1.1 创建Spring MVC项目3.1.2 RequestMapping 注解介绍3.1.3 Request…...

【项目精选】 塞北村镇旅游网站设计(视频+论文+源码)

点击下载源码 摘要 城市旅游产业的日新月异影响着村镇旅游产业的发展变化。网络、电子科技的迅猛前进同样牵动着旅游产业的快速成长。随着人们消费理念的不断发展变化&#xff0c;越来越多的人开始注意精神文明的追求&#xff0c;而不仅仅只是在意物质消费的提高。塞北村镇旅游…...

十、Spring IoC注解式开发

1 声明Bean的注解 负责声明Bean的注解&#xff0c;常见的包括四个&#xff1a; ComponentControllerServiceRepository Controller、Service、Repository这三个注解都是Component注解的别名。 也就是说&#xff1a;这四个注解的功能都一样。用哪个都可以。 只是为了增强程序…...

Linux系统GPIO应用编程

目录应用层如何操控GPIOGPIO 应用编程之输出GPIO 应用编程之输入GPIO 应用编程之中断在开发板上测试GPIO 输出测试GPIO 输入测试GPIO 中断测试本章介绍应用层如何控制GPIO&#xff0c;譬如控制GPIO 输出高电平、或输出低电平。应用层如何操控GPIO 与LED 设备一样&#xff0c;G…...

手敲Mybatis-反射工具天花板

历时漫长的岁月&#xff0c;终于鼓起勇气继续研究Mybatis的反射工具类们&#xff0c;简直就是把反射玩出花&#xff0c;但是理解起来还是很有难度的&#xff0c;涉及的内容代码也颇多&#xff0c;所以花费时间也比较浩大&#xff0c;不过当了解套路每个类的功能也好&#xff0c…...

Java -数据结构,【优先级队列 / 堆】

一、二叉树的顺序存储 在前面我们已经讲了二叉树的链式存储&#xff0c;就是一棵树的左孩子和右孩子 而现在讲的是&#xff1a;顺序存储一棵二叉树。 1.1、存储方式 使用数组保存二叉树结构&#xff0c;方式即将二叉树用层序遍历方式放入数组中。 一般只适合表示完全二叉树&a…...