基于NavigationRegion2D
我们基于NavigationRegion2D的逻辑一文的场景结构,但是将NavigationRegion2D
删除,更改为TileMap节点。
为TileMap创建Tileset,并创建一个导航层。在TileSet面板中,为草地和黄色泥土地面图块绘制可通行区域(整个矩形)。这样做的含义是只有这连个图块允许通行,其余图块全是障碍物。
切换至TileMap面板,利用矩形和直线绘制方式绘制一个简单的有出口的地图。
运行场景,会发现基于NavigationAgent2D
的导航在TileMap上依旧是可行的。
## 基于AStarGrid2D
可以看出基于NavigationRegion2D
的导航总还是有些不尽如人意的地方,偶尔就卡在了半路上。
AStarGrid2D
本身基于网格,那么它与TileMap
组合可以说是“天作之合”。
删除Player的NavigationRegion2D
以及脚本。
查看icon.svg可以发现其图片大小为128×128,而我们的tileset的cell_size为16×16,所以为了将玩家显示在一个网格里,我们需要将icon.svg缩放为16×16。
128/16=8,所以我们需要将其设为原来的1/8,也就是缩小到原来的0.125倍。相应调整碰撞形状的大小。
移动玩家到某个单元格内。
将world2的脚本改成如下:
# world2.gd
extends Node2D
@onready var player = $Player
@onready var tile_map = $TileMap
var astar_grid = AStarGrid2D.new()
var move_speed = 200.0
var path:PackedVector2Array
var solids:PackedVector2Array
func _ready():
# 获取tile_map包含所有已绘制图块的矩形
var rect = tile_map.get_used_rect()
# 构建AStarGrid2D
astar_grid.size = rect.size
astar_grid.cell_size = tile_map.tile_set.tile_size
astar_grid.offset = astar_grid.cell_size/2
astar_grid.update()
# 获取所有已经绘制的图块的数组
var cells = tile_map.get_used_cells(0)
for cell in cells:
# TileData包含图块的信息
var data:TileData = tile_map.get_cell_tile_data(0,cell)
if !data.get_navigation_polygon(0): # 如果图块没有设置导航
solids.append(cell)
astar_grid.set_point_solid(cell,true) # 设为障碍物
# 左键点击,获取导航目标位置
func _input(event):
if event is InputEventMouseButton:
if event.button_index == MOUSE_BUTTON_LEFT and event.is_pressed():
# TileMap的local_to_map()用于将屏幕坐标转化为TileMap的网格坐标
var p1 = tile_map.local_to_map(player.position)
var p2 = tile_map.local_to_map(get_global_mouse_position())
path = astar_grid.get_point_path(p1,p2)
queue_redraw()
# 实现沿着路径行走
func _process(delta):
if path.size() > 0:
var target_pos = path[0]
if target_pos.distance_to(player.position)>2:
player.velocity = player.position.direction_to(target_pos) * move_speed
player.move_and_slide()
else:
path.remove_at(0)
queue_redraw()
# 绘制路径信息
func _draw():
var grid_width = astar_grid.size.x * astar_grid.cell_size.x
var cell_width = astar_grid.cell_size.x
var cell_height = astar_grid.cell_size.y
# 绘制路径和其上的点
if path.size() > 0:
for pot in path:
draw_circle(pot,5,Color.YELLOW)
draw_polyline(path,Color.YELLOW,2)
开启显示碰撞区域和导航。
运行后的效果如下:
基本的导航还是可以的。
:::success
提示
默认由于TileMap是world2的子节点,所以world2上用_draw绘制的内容会被TileMap绘制的地图覆盖而无法显示。解决方法是将TileMap的z_index设为-1。
:::