Unity中实现合理塔防寻路机制

前言

在一款TD游戏中,最重要的单位就两大类:防御塔(Tower)和敌人单位(Enemy)。在处理敌人单位的AI行为时,最基本也是最重要的就是自动寻路。在各式TD游戏中,防御塔的攻击方式以及敌人单位的Buff机制往往是能做出差异化的地方;而在寻路问题上,几乎是没有差异的,面对的都是同一套问题模型。

以魔兽争霸中的TD地图、KingdomRush为代表的这一类”固定路径,固定塔位“的寻路模型是最为常见的。本文对于寻路问题所参照实现的,则是久负盛名的Defense Grid(中文译名防御阵型);作为最经典的TD游戏之一,不仅是因为其在早年发布的第一部作品中就表现出了非常优秀3D画面,更重要的是在前述的寻路模式下引入了新的机制:允许在敌人单位的前进路线上放置防御塔或障碍物,从而在运行时动态地改变路径。这一创新极大的提高了可玩性,在此之后许多流行的TD游戏都有使用这种寻路方式 例如Infinitode、X-Morph、国产游戏重构Refactor等。

寻路问题的建模

我们首先要做的是根据游戏中的地图特征,将所有能够行走的地面抽象为数据结构,然后再决定合适的寻路算法。

对于路径建模有三种主流方式,我们以下图作为原版地图来说明:

image

格子(Grid)

image

格子模型将地图分割为许多大小相等的格子,利用二维数组存储起来。使用各种值表示数组中对应的位置类型 如0用来表示障碍物,1表示可行走的区域。

优点:

  • 实现简单
  • 易于在运行时动态修改

缺点:

  • 如果想更为细致地表达路径,则需要将格子分割地更小,从而导致内存占用更大且大范围寻路时计算代价更大

对于2D画面的小地图,是该模型最适合的场景。笔者在刚上大学时写过一个2D的塔防游戏,当时就是以这种方式来处理地图的(https://www.cnblogs.com/geek1116/p/5953456.html)

路径点(WayPoint)

image

这种方式需要在手动标注出所有可以行走的点,这些点互相连接的直线就是单位行走的路径。通常在这些连接边上还需要带上权重,用邻接表的数据结构存储。

优点:

  • 内存占用小
  • 在此基础上做寻路算法的计算代价小

缺点:

  • 需要人工添加路径点,且在地图变动后都需要再次调整路径点,工作量大
  • 由于单位只能沿着路径点行走,行为方式在观感上会表现的较为生硬,不够灵活;在大地图范围寻路以及需要考虑单位碰撞的场景下,该问题更为明显

对于传统的固定线路的塔防中,WayPoint是非常适合的建模方式

导航网格(NavMesh)

image

通过一系列的算法将可行走地形转换为由若干凸多边形组成的网格,并维护多边形之间的邻接关系。在导航网格中求两点之间的移动路径,需要根据两点所在的多边形以及多边形之间的邻接关系,按照某种寻路算法找出需要经过的所有多边形;再根据拐点算法计算途中的各个拐点来得出”平滑“的移动路径。

优点:

  • 相比于前两种模型,NavMesh更能表达出地形的特征
  • 适用于3D地形

缺点:

  • 建模过程复杂,自己从零造轮子的话实现成本很大
  • 不便于动态修改;在修改地形后再重新烘焙NavMesh的过程比较耗时,想要运行时执行这一过程的话性能代价较大

Unreal Engine、Unity等游戏引擎中的导航系统内部原理都是基于NavMesh的

寻路算法

三种经典的路径规划算法:

  • 广度优先搜索(BFS)
  • Dijkstra最短路算法
  • A*算法(A star)

在游戏开发中实现寻路问题时,A*算法通常是不二之选。这也是Unity中采用的寻路算法。

如果是以WayPoint的方式作地图建模的话,那么数据结构存储的是一个无向加权图,在此之上进行路径规划,Dijkstra算法通常会是一个合适的选择。

在塔防游戏中选择合适的寻路方案

回到我们的主题,要以Defense Grid为蓝本在Unity中实现敌人单位的寻路。

最简单粗暴是使用游戏引擎自带的导航系统,给定一个终点后剩下的都交给导航系统去处理了。这种方式下单位在地图上寻路时所走的都是近似最短路的路线。这种方式不能说是错的,但从游戏设计的角度讲 这种表现方式显得”很蠢“:在游戏实际运行中,敌人单位是一波一波出现的,但同一波次中的若干敌人在前进时会显得完全没有”集体意识“,大家都各自为战,最终会挤到同一条直线上。如下所示:

image

以右上方红点设为目的地,三个单位都只从自身位置考虑,算出当前到终点的最短路线;当经过第一个拐点后,它们仨都会位于同一直线上前行。

笔者曾关注过的塔防作品中确实见到过极个别真是这么实现的......

我们在前文提到过,对于固定路线的这类型地图寻路中可以采用WayPoint的方式建模,单位的前进路线则按着路径点之间的走,但这仍然会存在上述的问题。在Unity中做个demo,尝试下在两个拐

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Kettle-Docker部署+Sqlserver数据同步Mysql+Start定时任务

一. 背景介绍 1. ETL是什么 ETL(Extract-Transform-Load),即数据抽取、转换、装载的过程。它是一种思想,主要是说,从不同的数据源获取数据,并通过对数据进行处理(格式,协议等转换&a…

unity学习笔记----游戏练习07

一、僵尸攻击和植物的掉血和销毁 当僵尸接触到植物开始攻击时会持续削减植物的血量,当植物血量为零时就销毁当前植物。 在plantManager中, 为植物添加一个血量HP 100, public int HP 100; 在写一个减少血量的方法,来减少血…

NC65中间件能启动,前端客户端启动失败,加载异常,卡住(org.owasp.esapi)

控制台输出错误 ESAPI.properties could not be loaded by any means. Fail.SecurityConfiguration class(org.owasp.esapi.reference.DefaultSecurityConfiguration) CTOR threw exception.效果图: 解决方案 添加如下参数: -Dorg.owasp.esapi.resou…

cocos creator 碰撞系统

设置碰撞组件 3种组件类型,矩形碰撞,圆形碰撞, 多边形碰撞 开启碰撞检测 start() {//开启碰撞管理器let cm cc.director.getCollisionManager()cm.enabled true//绘制碰撞检测边界线。用于调试cm.enabledDebugDraw true//绘制精灵的边界c…

docker 修改默认存储位置

✨✨✨✨✨✨✨ 🎀前言🎁查看前面docker储存位置🎁移动文件位置🎁修改配置文件docker.service🎁修改daemon.json🎁加载配置并重启 🎀前言 最近服务出现系统盘满了,发现其中docker存储占用很大一…

nginx使用stream模块代理端口

今天原本准备将本地金仓的54321端口进行代理的,但是发现代理后总是无法访问。后来突然发现之前使用nginx代理的是一个地址而不是端口。因此做以下笔记用来做记录。 一、stream安装与配置 假设nginx在usr/local/nginx中 1. 测试当前nginx是否有stream模块 ./usr/…

实用工具合集(持续更新...)

一、搜索引擎 1.1、小白盘 网站:https://www.xiaobaipan.com 度盘资源搜索的网站,能够搜索电影、电视剧、小说、音乐等资源(注意:评论区很多小伙伴说小白盘有毒,我用谷歌浏览器搜索过几次并无大碍,请慎用…

wpf控件Expander集合下的像素滚动

项目场景:Expander集合滚动 如下图,有一个Expander集合,且设置 ScrollViewer.VerticalScrollBarVisibility "Auto" 每个Expaner下包含有若干元素,当打开Expader(即IsExpanded "true")时&#…

五分钟教会你如何制作宠物服务小程序

随着宠物市场的不断扩大和人们对宠物健康和幸福的关注度越来越高,宠物服务行业也迅速崛起。作为一名有志于创业的宠物爱好者,搭建一款宠物小程序是一个很好的切入点。本文将为大家介绍使用第三方制作平台乔拓云网搭建宠物小程序的全攻略。 第一步&#x…

1. MySQL 数据库

本章内容 关系型数据库基础 安装 MySQL 管理数据库和表 用户和权限管理 函数,存储过程,触发器和事件 MySQL 架构 存储引擎 服务器选项,系统和状态变量 优化查询和索引管理 锁和事务管理 日志管理 备份还原 MySQL 集群 压力测试…

23111 C++ day3

思维导图 设计一个Per类&#xff0c;类中包含私有成员:姓名、年龄、指针成员身高、体重&#xff0c;再设计一个Stu类&#xff0c;类中包含私有成员:成绩、Per类对象p1&#xff0c;设计这两个类的构造函数、析构函数和拷贝构造函数。 #include <iostream>using namespac…

自学java马上入职怕干不了活怎么办?

自学java马上入职怕干不了活怎么办&#xff1f; 在开始前我分享下我的经历&#xff0c;刚入行时遇到一个好公司和师父&#xff0c;给了我机会&#xff0c;两年时间从3k薪资涨到18k的&#xff0c; 我师父给了一些【java 】学习方法和资料&#xff0c;让我不断提升自己&#…

arcgis实现截图/截屏功能

arcgis实现截图/截屏功能 文章目录 arcgis实现截图/截屏功能前言效果展示相关代码 前言 本篇将使用arcgis实现截图/截屏功能&#xff0c;类似于qq截图 效果展示 相关代码 <!DOCTYPE html> <html> <head><meta charset"utf-8"><meta nam…

OpenCV图像的基本操作

图像的基本操作&#xff08;Python&#xff09; 素材图 P1&#xff1a;die.jpg P2&#xff1a;cool.jpg V&#xff1a;rabbit.mp4&#xff0c; 下载地址 读取展示-图像 import cv2img_1 cv2.imread(./die.jpg) # default cv2.IMREAD_COLOR print("die.jpg shape(imre…

【论文笔记】Learning Deconvolution Network for Semantic Segmentation

重要说明&#xff1a;严格来说&#xff0c;论文所指的反卷积并不是真正的 deconvolution network 。 关于 deconvolution network 的详细介绍&#xff0c;请参考另一篇博客&#xff1a;什么是Deconvolutional Network&#xff1f; 一、参考资料 Learning Deconvolution Netwo…

uniapp组件库Line 线条 的适用方法

目录 #平台差异说明 #基本使用 #线条类型 1.3.7 #兼容性 #API #Props 此组件一般用于显示一根线条&#xff0c;用于分隔内容块&#xff0c;有横向和竖向两种模式&#xff0c;且能设置0.5px线条&#xff0c;使用也很简单。 #平台差异说明 AppH5微信小程序支付宝小程序百…

算法:日志采集系统

一、算法描述 题目 日志采集是运维系统的的核心组件。日志是按行生成&#xff0c;每行记做一条&#xff0c;由采集系统分批上报。如果上报太频繁&#xff0c; 会对服务端造成压力&#xff1b;如果上报太晚&#xff0c;会降低用户的体验&#xff1b;如果一次上报的条数太多&…

Java和Redis实现一个简单的热搜功能

1. 前言 我们有一个简单的需求&#xff1a; 搜索栏展示当前登陆的个人用户的搜索历史记录&#xff0c;删除个人历史记录。用户在搜索栏输入某字符&#xff0c;则将该字符记录下来 以zset格式存储的redis中&#xff0c;记录该字符被搜索的个数以及当前的时间戳 &#xff08;用…

使用DBSyncer同步Oracle11g数据到Mysql5.7中_实现全量数据同步和增量数据实时同步_操作过程---数据同步之DBSyncer工作笔记007

之前都是用mysql和Postgresql之间进行同步的,已经实现了数据的实时同步,现在要实现Oracle数据库到Mysql数据库的全量,以及增量同步. 因为之前配置的不对,这里架构名写成了orcl,所以导致,虽然能连接上,但是,在进行数据同步的时候,看不到表,所以这里说一下如何进行连接 这里,首先…

代码随想录算法训练营第30天 | 回溯总结 + 3道Hard题目

今日任务 332.重新安排行程 51. N皇后 37. 解数独 总结 总结 回溯总结&#xff1a;代码随想录 回溯是递归的副产品&#xff0c;只要有递归就会有回溯&#xff0c;所以回溯法也经常和二叉树遍历&#xff0c;深度优先搜索混在一起&#xff0c;因为这两种方式都是用了递归。 …