路径查找器AI
首先放出测试程序和path库源码。
介绍
问题源于我想建立一个游戏AI,它要能够定义一条从起点到终点的路径,同时避开路上的墙壁障碍物。为此,我写了一个C#库(path.dll),它允许定义一个二维空间(MAXX,MAXY),并为这个空间设立一些矩形的“墙“。在添加完所有的墙后,path类将计算能够绕过墙的AI所有“可见”的AI节点(可见指节点之间没有墙)之间是连接的。这个类实现了一个路径查找算法,使用C#的Delegates(委托)与AI节点实例进行通信。最后,使用这个O_O算法(扩展欧几里得算法)将会得到一个子类,它是所节点的下一个目的AI节点的集合。在示例图中,可以看到墙(橙色),AI NODES(红色),起点(蓝色)和终点(蓝色)。
上面还给了一个path库的测试程序。
想法
这个想法是通过初始化Cartesio
类定义一个2D空间。这个类允许在二维空间中添加矩形的墙。
创建墙后,Cartesio类会创建(Create_ai_nodes()方法)一些“围绕”在墙壁四角的AI节点。在这个例子中,我们可以看到2个墙(红色)和相关的AI节点。
创建AI节点时,Cartesio
类会自动创建“可视弧”,可视弧,也就是把相邻的节点连接到一起,同时避开所有的墙壁的线段。(看图)
然后,Cartesio为每个节点创建一个区域以及相邻节点(我称之为AI_star)列表,通过它来到达目的地。所以,当我们在一个节点(或者我们可以看到它)时,我们就可以做到遍历每一个节点。
最后,通过传递一个Cartesio
对象,起点P1
和终点P2
来初始化Super_path
类。Super_path.Next()
可以一步一步地从起点移动到终点。
While (!(supe_path.Next())) {
//渲染2D图像
}
使用path库
使用Cartesio的 Super_path类非常简单,如示例。
//初始化 Cartesio ,2D空间大小:300*300
cart = new Cartesio(300,300);
// 添加一些墙到空间中
cart.AddWall(56,56,100,10);
cart.AddWall(156,15,11,231);
cart.AddWall(10,135,114,26);
// 生成AI节点 和对应的ai_stars
cart.Create_ai_nodes();
可能需要一段时间,具体取决于节点的数量(这一步只需进行一次)。可见弧的数量也影响生成时间。
// 初始化Super_path ,参数:起点P1,终点P2 ,Cartesio对象cart
Np = new super_path(P1,P2,cart);
// Next() 从起点一步步移动到终点
while (!Np.Next())
{
P = Np.Pa;
// 渲染场景..
优化
当两个墙壁重叠时,那么Create_ai_nodes
方法会忽略定位在墙上的无用节点。看例子:
委托和路径查找算法
假设读者了解C#中的委托(delegate)和事件(event)。
解释一下如何从节点S的相邻节点中找出最佳选择以到达节点E.
首先,在创建AI节点的过程中,我们为每个节点创建一个委托,并且添加到由该委托所代表的监听器列表中的所有相邻节点。
从起点S到终点E,我们从终点E开始往回看。E抛出以下信息
- 对E(目的)的引用
- 对S(来源)的引用
- 一个指向它的节点的引用(即上一个节点,在本例中为E);
- 距离D(终点到E的距离,在这种情况下为0)。
当中间某个节点A收到消息M:
- 如果它是第一个消息,则保存距离D=D + dist(this,M.Throu)(从这个节点到抛出消息节点的距离,这里就是dist(A,E)),并且更新D + dist(this,M.Throu)和M.Throu,生成新的消息。
- 如果不是第一个消息,则如果新的总距离D + dist(this,M.Throu)小于存储在该节点中的距离,则存储距离取新的总距离D + dist(this,M.Throu),并抛出一个新的消息去更新D与D + dist(this,M.Throu)和M.Throu本身。文字描述晦涩难懂,后面会有简单明了示意图。
对于起点S接收到的每个消息M,S考虑具有最小距离D的那个消息,然后,S就可以知道应该怎么走,才能尽快到达E了。
举一个从E到S的消息传播的例子(蓝色箭头为传播方向),如下图(图中只列出部分信息)。如图所示,每一个节点T都挑出从T到节点E的最短路径,再抛出信息给其他节点,最后,S将会收到信息9和10,再考虑消息中附带的距离,分析哪条路径最好。
如何使用测试程序
测试程序的界面非常简单。你可以绘制墙(选中Draw walls后鼠标左键拖动即可绘制矩形墙)。之后,取消选中Draw walls,然后,就可以设置开始点(右键单击)和结束点(左键单击)。也可以点击Simulation进行距离为2000的自动测试来验证程序。最后,如果想重新绘制墙壁,可以点击Clear清除墙壁,再重新绘图。
嗨,我的老伙计,希望你能喜欢它,并看在上帝的面子上给我一些好的建议。
- CentOS6 安装并破解Jira 7
- Martin Odersky访谈录所思
- 解决Boost库链接出错问题
- 引入Option优雅地保证健壮性
- java正则校验,密码必须由字母和数字组成
- Spring Boot集成JasperReports生成PDF文档
- Redux框架reducer对状态的处理
- 使用Spring Cloud Security OAuth2搭建授权服务
- Nginx性能优化
- linux 如何正确的关闭mongodb
- 运用Aggregator模式实现MapReduce
- vue 2 使用Bus.js进行兄弟(非父子)组件通信 简单案例
- spring boot项目在外部tomcat环境下部署
- 利用Actor实现管道过滤器模式
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- php获取微信openid方法总结
- Laravel 关联模型-关联新增和关联更新的方法
- 如何解决PHP获取不到SESSION信息之一般情况
- PHP实现通过二维数组键值获取一维键名操作示例
- laravel 判断查询数据库返回值的例子
- laravel框架数据库配置及操作数据库示例
- laravel 输出最后执行sql 附:whereIn的使用方法
- laravel框架模型、视图与控制器简单操作示例
- Laravel关系模型指定条件查询方法
- 在laravel中使用with实现动态添加where条件
- laravel Model 执行事务的实现
- 在laravel中实现事务回滚的方法
- thinkphp5框架实现数据库读取的数据转换成json格式示例
- phpfpm的作用和用法
- 浅谈PHP5.6 与 PHP7.0 区别