Threejs路径规划_基于A*算法案例V2

news/2024/11/15 6:17:04/

        路径规划算法中有两种算法使用最普遍,第一个是Dijkstr算法,第二个是A*算法,两个算法各有千秋,Dijkstra算法可以保证最优解,但是复杂度较高,尤其当点数量较多时,A*算法是一种启发式搜索算法,它不保证最优解,但成本很低,也是很多机器人移动或者游戏中人物自动寻路场景中常用的算法

        下面来说下大概得逻辑,假设有30*30的方格,从1,1点要到25,20点,会先从1,1出发,查询1,1能到达的点,假设1,1能够到达(0,1),(1,0),(1,2),(2,1)这四个点,那么求出四个点中哪个点距离目标点(25,20)的曼哈顿距离最近,得到这个这里距离最近的点之后,再找出这个点能够到达的点,从它能够到达的点中再求出四个点中哪个点距离目标点(25,20)的曼哈顿距离最近。依此类推,直到到达目标点。

        ps:曼哈顿距离也就是横向距离加上纵向距离,而不是勾股定理求出的横向距离的平方和纵向距离的平方再开根,因为一般使用A*算法的更多的是街道或者网格状的场景,用曼哈顿距离更加符合实际场景。

        下面可以通过threejs来演示,首先在threejs场景中绘制一个30*30的网格,在绘制网格的时候就正好存储每个网格的点,并用x.y为点的名字,再存储每个点能到达点的路线,另外为了演示更真实可以防止一些障碍物,并在绘制场景的时候去掉能连接到障碍物的路线,

    initTable() {const cylinderMaterial = new THREE.MeshLambertMaterial({ color: '#d3d3d3' })const table = []for (let i = 0; i < 30; i++) {for (let j = 0; j < 30; j++) {const boxGeometry = new THREE.BoxGeometry(9, 9, 9)const box1 = new THREE.Mesh(boxGeometry, cylinderMaterial)box1.position.set(i * 10, j * 10, 5)// this.scene.add(box1)box1.updateMatrix()// 更新模型变换矩阵const boxMesh = box1.geometry.applyMatrix4(box1.matrix)// 启动并应用变换矩阵table.push(boxMesh)const params = { name: i + '.' + j, x: (i * 10), y: (j * 10) }this.pointList.push(params)if (i > 1) {this.roadList.push({ begin: i + '.' + j, end: (i - 1) + '.' + j })}if (j > 1) {this.roadList.push({ begin: i + '.' + j, end: i + '.' + (j - 1) })}this.roadList.push({ begin: i + '.' + j, end: (i + 1) + '.' + j })this.roadList.push({ begin: i + '.' + j, end: i + '.' + (j + 1) })}}const bayGeometry = mergeGeometries(table)// 合并模型数组const boxList = new THREE.Mesh(bayGeometry, cylinderMaterial)// 生成一整个新的模型this.scene.add(boxList)for (let i = 0; i < this.roadList.length; i++) {for (let j = 0; j < this.obstacle.length; j++) {if (this.roadList[i].begin === (this.obstacle[j].x + '.' + this.obstacle[j].y) ||this.roadList[i].end === (this.obstacle[j].x + '.' + this.obstacle[j].y)) {console.log(this.obstacle[j].x + '.' + this.obstacle[j].y)this.roadList.splice(i, 1)j--}}}},

        这样一个网格布局就绘制完成,然后可以开始按照刚才的步骤实现具体的寻路过程,在寻路时每次获取到的点都存放到集合中,并用绘制方格的方法绘制路线方格,与网格用不同的颜色来区分。

buildPath() {let begin = (parseInt(this.beginPoint.x) - 1) + '.' + (parseInt(this.beginPoint.y) - 1)const end = (parseInt(this.endPoint.x) - 1) + '.' + (parseInt(this.endPoint.y) - 1)this.removeOnce()this.drawPoint(begin)while (begin !== end) {const result = this.calcDistance(begin, end)this.drawPoint(result.point)begin = result.point}},calcDistance(begin, end) {const tempRoads = []for (let i = 0; i < this.roadList.length; i++) {if (this.roadList[i].begin === begin) {tempRoads.push(this.roadList[i].end)}}let result = { distance: Infinity, point: '' }for (let i = 0; i < tempRoads.length; i++) {const beginX = tempRoads[i].split('.')[0]const beginY = tempRoads[i].split('.')[1]const endX = end.split('.')[0]const endY = end.split('.')[1]const distance = Math.abs(parseInt(endX - beginX)) + Math.abs(parseInt(endY - beginY))if (distance < result.distance) {result = { distance: distance, point: tempRoads[i] }}}return result},drawPoint(point) {const pointX = point.split('.')[0]const pointY = point.split('.')[1]const boxGeometry = new THREE.BoxGeometry(10, 10, 10)const material = new THREE.MeshPhongMaterial({color: '#00FF00', // 设置颜色shininess: 20 // 设置高光大小,范围为0到128,默认值为30})const pointMesh = new THREE.Mesh(boxGeometry, material, 0)pointMesh.position.set(parseInt(pointX) * 10, parseInt(pointY) * 10, 6)pointMesh.name = 'path'this.scene.add(pointMesh)},

我们可以先设置默认的起点和结束点看下效果,设置起始点为(1,1),结束点为(20.16),规划后看下效果:

可以同样用上个章节的加入element的输入框来实现动态规划,并在每次规划后清除上一次规划的路线。

我们可以规划试试,效果如下,

A*初級

还可以把距离比较换成两个点之间的直线距离,效果如下:因为两个数相加的和相等的情况下,两个数越接近,直线距离最短,简单的说,周长相等的情况下,正方形的对角线最短。但是在实际地图中,转弯是需要花费成本的,所以还是使用曼哈顿距离更合适。

不过此方式不太适合有障碍物的情况,因为在有复杂障碍物时,并不是距离越接近目标点花费成本越低,直到到达目标点,下面的章节再探讨复杂障碍物的处理方式。


http://www.ppmy.cn/news/1462674.html

相关文章

【Vue】Vue2与Vue3的区别

目录 响应式系统组合式API更小的体积编译优化新的生命周期钩子更好的性能组件结构与模板TeleportFragments 静态节点标记异步组件Slots的改进更好的TypeScript支持Composition API的引入 响应式系统 Vue2使用Object.defineProperty来实现响应式系统&#xff0c;这意味着只有预…

初识C语言——第二十八天

代码练习1&#xff1a; 用函数的方式实现9*9乘法表 void print_table(int n) {int i 0;int j 0;for (i 1; i< n; i){for (j 1; j< i; j){printf("%d*%d%-3d ", i, j, i * j);}printf("\n");}}int main() {int n 0;scanf("%d", &a…

IO端口编址

统一编址 特点 独立编址 特点 内存地址分配 区别 应用 IO端口地址译码 硬件上的实现 示例1&#xff1a; 示例2&#xff1a; IO指令 软件上的实现 示例

WEB转Flutter基础学习笔记(内含vue和flutter对比)

一、Widget简要概括 如果说Vue的UI是template包裹的一个个组件 那么Flutter的UI就是baseBuild中return出来的嵌套罗列的widget StatelessWidget 用于不需要维护状态的场景&#xff0c;它通常在build方法中通过嵌套其他 widget 来构建UI&#xff0c;在构建过程中会递归的构建其…

html+css 驾考首页设计

效果 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><title>17sucai - A Pen by Mark Boots</title><!-- <link rel"stylesheet" href"./style.css"> --></head>…

代码随想录——平衡二叉树(Leetcode110)

题目链接 后序遍历高度&#xff0c;高度判断是否平衡 前序遍历深度 递归 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val val; }* …

2024-5-24 石群电路-15

2024-5-24&#xff0c;星期五&#xff0c;22:15&#xff0c;天气&#xff1a;晴&#xff0c;心情&#xff1a;晴。今天最后一天上班&#xff0c;终于要放返校假啦&#xff0c;开心&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;不过放假也不能耽误…

Python 魂斗罗的音效和动漫效果

一、实现游戏音效 音效是游戏中不可或缺的一部分&#xff0c;它可以为游戏增添氛围和趣味性。在 Pygame 中&#xff0c;我们可以使用 pygame.mixer 模块来播放音效。下面是一个简单的示例代码&#xff0c;演示如何在游戏中播放音效&#xff1a; import pygamepygame.mixer.init…