【Godot4.0】自定义A*寻路拓展类TileMapAStar2D及其使用

2024-03-17 22:28

本文主要是介绍【Godot4.0】自定义A*寻路拓展类TileMapAStar2D及其使用,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

概述

Godot提供的AStar2DAStarGrid2D基本可以解决所有2D的A*寻路问题:

  • 前者提供了基础的A*寻路支持,但是需要手动处理很多内容
  • 后者针对基于方形图块的A*寻路,进行了很多自动化的工作,用起来十分简便。但是不使用于六边形、isometric之类的图块

所以针对AStar2DAStarGrid2D各自特性和现况,好的办法就是自己基于AStar2D扩展一个类似于AStarGrid2D自动化完成添加点和连线,又可以不限于仅在方形图块下使用的类型。


说明:原文写于2023年7月,Godot版本4.0,为了B友要求先发到CSDN中,有欠缺的部分后续再改。


源代码

# =====================================================
# TileMapAStar2D
# AStar2D扩展类型,用于为TileMap自动化的创建A*导航网格
# 作者:巽星石
# Godot版本:v4.0.3.stable.official [5222a99f5]
# 创建时间:202361718:33:28
# 最后修改时间:202361721:13:44
# ====================================================extends AStar2D
class_name TileMapAStar2Dvar _tile_map:TileMap
var _layer_id:intvar _pos_labs:Array[Label] = []
var _id_labs:Array[Label] = []# 是否用Label显示所有可通行位置的单元格坐标
@export var show_navigation_cells_pos:bool = false:set(val):show_navigation_cells_pos = valif val:for cell in get_has_navigation_cells():show_cell_pos(cell)else:for lab in _pos_labs:lab.queue_free()_pos_labs.clear()# 是否用Label显示所有可通行位置的单元格坐标
@export var show_navigation_cells_id:bool = false:set(val):show_navigation_cells_id = valif val:for point_id in get_point_ids():var cell = Vector2i(get_point_position(point_id))if cell in get_has_navigation_cells():show_cell_id(cell,point_id)else:for lab in _id_labs:lab.queue_free()_id_labs.clear()# 基于TileMap,添加可通行点和连接线,创建Astar网格,并返回一个AStar2D实例
func _init(tile_map:TileMap,layer_id:int):# 存储TileMap对象和layer_id_tile_map = tile_map_layer_id = layer_id# 添加可通行点for cell in get_has_navigation_cells():var id = get_available_point_id()add_point(id,cell)# 遍历已经添加了的可通行点,进行连线var points_ids = get_point_ids()for point_id in points_ids:var point_cell_pos = get_point_position(point_id) # id转为单元格坐标var surround_cell_pos_arr = tile_map.get_surrounding_cells(point_cell_pos)# 获取周边6个位置for cel in surround_cell_pos_arr:var cel_id = get_closest_point(cel)# 尝试获取最接近的点的idif Vector2i(get_point_position(cel_id)) in surround_cell_pos_arr: # 验证获取的点的ID在周围6个位置内if cel_id in points_ids: # 验证点在已经添加的位置内if !are_points_connected(cel_id,point_id): # 验证两个点之间不存在连接connect_points(cel_id,point_id)# 判断单元格是否存在导航多边形
func is_cell_has_navigation_polygon(cell:Vector2i):var bol = falsevar data = _tile_map.get_cell_tile_data(_layer_id,cell)if data:if data.get_navigation_polygon(_layer_id): # 有导航区域bol = truereturn bol# 获取TileMap所有拥有导航网格的单元格
# 查找是在get_used_cells基础上进行的
func get_has_navigation_cells() -> Array[Vector2i]:var cells:Array[Vector2i] = []# 获取所有已经绘制的单元格var used_cells:Array[Vector2i] = _tile_map.get_used_cells(_layer_id)# 遍历所有网格,将带有导航区域的单元格位置添加为AStar的可通行点for cell in used_cells:if is_cell_has_navigation_polygon(cell): # 格子有导航多边形cells.append(cell)return cells# =================================== 底层辅助函数 ===================================func create_lab(content:String,font_color:Color = Color("#ffffff")):var lab = Label.new()lab.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTERlab.vertical_alignment = VERTICAL_ALIGNMENT_CENTER# 构造LabelSettingsvar lab_setting = LabelSettings.new()lab_setting.font_color = font_colorlab_setting.font_size = _tile_map.tile_set.tile_size.x /8lab_setting.outline_color = Color("#444444")lab_setting.outline_size = lab_setting.font_size/3lab_setting.shadow_color = Color("#3333339e")lab_setting.shadow_size = 1lab_setting.shadow_offset = Vector2(2,2)# 其他配置lab.label_settings = lab_settinglab.text = contentreturn lab# 显示TileMap对应单元格的位置信息
func show_cell_pos(cell:Vector2i):var lab = create_lab(str(cell))lab.position = _tile_map.map_to_local(cell)_tile_map.add_child(lab)_pos_labs.append(lab)# 显示单元格对应的AStar2D的点ID
func show_cell_id(cell:Vector2i,id:int):var lab = create_lab(str(id),Color.ORANGE)lab.position = _tile_map.map_to_local(cell) - Vector2(_tile_map.tile_set.tile_size /5.5)_tile_map.add_child(lab)_id_labs.append(lab)

使用方法

在一个使用TileMap的场景中,比如这里直接有一个以TileMap为根节点的场景。
image.png
创建TileSet,并为可以通行的图块绘制导航多边形(默认形状即可)。
image.png
然后用可通行和不可通行的图块在TileMap上任意绘制一块地图区域。
image.png
为TileMap添加如下脚本:

extends TileMapvar astar = TileMapAStar2D.new(self,0)func _ready():astar.show_navigation_cells_pos = true # 显示单元格的坐标astar.show_navigation_cells_id = true  # 显示TileMapAStar2D为单元个创建的位置ID

运行后就可以看到如下效果:
显示所有可通行位置的AStar2D的ID和对应TileMap的单元格坐标

显示导航网格

为TileMap添加一个Control类型的节点,起名叫debug。用来实现TileMapAStar2D自动生成的导航网格。
image.png

extends Controlvar astar:AStar2D
@export var path_color:Color = Color.YELLOW_GREEN
@export var point_color:Color = Color.YELLOW_GREENfunc _ready():astar = get_parent().astarfunc _draw():if astar:var map:TileMap = get_parent()var points_ids = astar.get_point_ids()# 先画线for point_id in points_ids:var cel_pos = astar.get_point_position(point_id) # 转为单元格位置坐标var cel_screen_pos = map.map_to_local(cel_pos)   # 转为屏幕坐标# 获取连接信息var connects = astar.get_point_connections(point_id)for cnt_point_id in connects:var cnt_pos = astar.get_point_position(cnt_point_id) # 转为单元格位置坐标var cnt_screen_pos = map.map_to_local(cnt_pos)   # 转为屏幕坐标draw_line(cel_screen_pos,cnt_screen_pos,path_color,1)# 后画点for point_id in points_ids:var cel_pos = astar.get_point_position(point_id) # 转为单元格位置坐标draw_circle(map.map_to_local(cel_pos),5,point_color)

将TileMap的脚本改写为:

extends TileMapvar astar = TileMapAStar2D.new(self,0)@onready var debug = $debugfunc _ready():debug.astar = astarastar.show_navigation_cells_pos = trueastar.show_navigation_cells_id = truedebug.queue_redraw()

运行后就可以看到绘制出的A导航网格。
利用debug层显示AStar网格
有了 TileMapAStar2DTileMap自动化的生成导航网格,以及利用其show_navigation_cells_posshow_navigation_cells_id属性,还有用debug层绘制导航网格,则基于TileMap进行A
导航就有了非常好的视觉基础。无论是学习还是使用都更直观了。
当然对于一般四边形TileSet应该也是适用的,只不过Godot4已经提供了AStarGrid2D专用于四边形TileSet,但是它只适用于四边形网格,而TileMapAStar2D则更具有宽泛的适用性。
TileMapAStar2D用于方形TileSet的效果
在Half-Offset下的验证
在isometric图块下的验证

孤岛验证

验证算法在任何孤立的导航区域都能正确生成A*导航网格。
六边形图块的孤岛验证

这篇关于【Godot4.0】自定义A*寻路拓展类TileMapAStar2D及其使用的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/820371

相关文章

mysql递归查询语法WITH RECURSIVE的使用

《mysql递归查询语法WITHRECURSIVE的使用》本文主要介绍了mysql递归查询语法WITHRECURSIVE的使用,WITHRECURSIVE用于执行递归查询,特别适合处理层级结构或递归... 目录基本语法结构:关键部分解析:递归查询的工作流程:示例:员工与经理的层级关系解释:示例:树形结构的数

Redis中RedisSearch使用及应用场景

《Redis中RedisSearch使用及应用场景》RedisSearch是一个强大的全文搜索和索引模块,可以为Redis添加高效的搜索功能,下面就来介绍一下RedisSearch使用及应用场景,感兴... 目录1. RedisSearch的基本概念2. RedisSearch的核心功能(1) 创建索引(2

Redis中HyperLogLog的使用小结

《Redis中HyperLogLog的使用小结》Redis的HyperLogLog是一种概率性数据结构,用于统计唯一元素的数量(基数),本文主要介绍了Redis中HyperLogLog的使用小结,感兴... 目录 一、HyperlogLog 是什么?️ 二、使用方法1. 添加数据2. 查询基数China编程3.

Linux系统调试之ltrace工具使用与调试过程

《Linux系统调试之ltrace工具使用与调试过程》:本文主要介绍Linux系统调试之ltrace工具使用与调试过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录一、ltrace 定义与作用二、ltrace 工作原理1. 劫持进程的 PLT/GOT 表2. 重定

POI从入门到实战轻松完成EasyExcel使用及Excel导入导出功能

《POI从入门到实战轻松完成EasyExcel使用及Excel导入导出功能》ApachePOI是一个流行的Java库,用于处理MicrosoftOffice格式文件,提供丰富API来创建、读取和修改O... 目录前言:Apache POIEasyPoiEasyExcel一、EasyExcel1.1、核心特性

Java 如何创建和使用ExecutorService

《Java如何创建和使用ExecutorService》ExecutorService是Java中用来管理和执行多线程任务的一种高级工具,可以有效地管理线程的生命周期和任务的执行过程,特别是在需要处... 目录一、什么是ExecutorService?二、ExecutorService的核心功能三、如何创建

使用FileChannel实现文件的复制和移动方式

《使用FileChannel实现文件的复制和移动方式》:本文主要介绍使用FileChannel实现文件的复制和移动方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录使用 FileChannel 实现文件复制代码解释使用 FileChannel 实现文件移动代码解释

Java中的getBytes()方法使用详解

《Java中的getBytes()方法使用详解》:本文主要介绍Java中getBytes()方法使用的相关资料,getBytes()方法有多个重载形式,可以根据需要指定字符集来进行转换,文中通过代... 目录前言一、常见重载形式二、示例代码三、getBytes(Charset charset)和getByt

Java使用Stream流的Lambda语法进行List转Map的操作方式

《Java使用Stream流的Lambda语法进行List转Map的操作方式》:本文主要介绍Java使用Stream流的Lambda语法进行List转Map的操作方式,具有很好的参考价值,希望对大... 目录背景Stream流的Lambda语法应用实例1、定义要操作的UserDto2、ListChina编程转成M

Spring框架中@Lazy延迟加载原理和使用详解

《Spring框架中@Lazy延迟加载原理和使用详解》:本文主要介绍Spring框架中@Lazy延迟加载原理和使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录一、@Lazy延迟加载原理1.延迟加载原理1.1 @Lazy三种配置方法1.2 @Component