【Godot4.2】2D导航02 - AstarGrid2D及其使用方法

2024-03-20 08:36

本文主要是介绍【Godot4.2】2D导航02 - AstarGrid2D及其使用方法,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

概述

AstarGrid2D是Godot4.0新增的A*寻路辅助类型。可以看做是Astar2D的加强版。它允许你通过设置其sizecell_size属性来创建一个虚拟的网格。

并使用set_point_solid()这样的方法来在指定位置创建障碍物。

AstarGrid2D的好处是你不再需要手动的添加点以及点与点之间的连接,而是直接用get_point_path()这样的方法来获取最短路径(也就是一个包含了最短路径经过的点的数组)。

通过遍历这个数组,就可以实现路径移动了。

extends Controlvar astar_grid = AStarGrid2D.new()var cell_size = Vector2.ONE * 100func _ready():astar_grid.size = Vector2i.ONE * 32astar_grid.cell_size = Vector2i.ONE * 32astar_grid.update()func _draw():# 绘制网格var grid_width = astar_grid.size.x * astar_grid.cell_size.xvar cell_width = astar_grid.cell_size.xvar cell_height = astar_grid.cell_size.yfor i in range(astar_grid.size.x):draw_line(i * Vector2i(0,cell_height),i * Vector2i(grid_width,cell_height),Color.DARK_OLIVE_GREEN,2)for j in range(astar_grid.size.y):draw_line(j * Vector2i(cell_height,0),j * Vector2i(cell_height,grid_width),Color.DARK_OLIVE_GREEN,2)# 绘制路径和其上的点var path  = astar_grid.get_point_path(Vector2i(0,0),Vector2i(10,10))for pot in path:draw_circle(pot,5,Color.YELLOW)draw_polyline(path,Color.YELLOW,2)

image.png

extends Controlvar astar_grid = AStarGrid2D.new()var path:PackedVector2Arrayvar solids = []func _ready():randomize()astar_grid.size = Vector2i.ONE * 32astar_grid.cell_size = Vector2i.ONE * 32astar_grid.offset = astar_grid.cell_size/2astar_grid.update()# 随机生成障碍for i in range(50):var solid_point = Vector2i(randi_range(0,astar_grid.size.x),randi_range(0,astar_grid.size.y))astar_grid.set_point_solid(solid_point,true)solids.append(solid_point)func _draw():var grid_width = astar_grid.size.x * astar_grid.cell_size.xvar cell_width = astar_grid.cell_size.xvar cell_height = astar_grid.cell_size.y# 绘制网格for i in range(astar_grid.size.x):draw_line(i * Vector2i(0,cell_height),i * Vector2i(grid_width,cell_height),Color.DARK_OLIVE_GREEN,2)for j in range(astar_grid.size.y):draw_line(j * Vector2i(cell_height,0),j * Vector2i(cell_height,grid_width),Color.DARK_OLIVE_GREEN,2)# 绘制路径和其上的点if path.size() > 0:for pot in path:draw_circle(pot,5,Color.YELLOW)draw_polyline(path,Color.YELLOW,2)for p in solids:draw_rect(Rect2(p * Vector2i(astar_grid.cell_size),astar_grid.cell_size),Color.GRAY)func _on_gui_input(event):if event is InputEventMouseButton:if event.button_index == MOUSE_BUTTON_LEFT:if event.is_pressed():path = astar_grid.get_point_path(Vector2i(0,0),floor(get_global_mouse_position()/astar_grid.cell_size))queue_redraw()
  • 13行:将AstarGridoffset 设为 astar_grid.cell_size/2,也就实现了整体的坐标偏移。
  • 48行:floor(get_global_mouse_position()/astar_grid.cell_size)获得的就是鼠标点击所在的单元格的坐标。

AstarGrid寻路2.gif
目前为止,AstarGrid显示对角线形式的连接。

如果我们需要设置其只显示横平竖直的连接,则可以设置AstarGriddiagonal_modeDIAGONAL_MODE_NEVER

astar_grid.diagonal_mode = AStarGrid2D.DIAGONAL_MODE_NEVER

AstarGrid寻路3.gif

DiagonalMode说明
DIAGONAL_MODE_ALWAYS0该寻路算法将忽略目标单元格周围的实体邻居,并允许沿对角线通过。
DIAGONAL_MODE_NEVER1该寻路算法将忽略所有对角线,并且路径始终是正交的。
DIAGONAL_MODE_AT_LEAST_ONE_WALKABLE2如果在特定路径段的相邻单元格周围放置了至少两个障碍物,则该寻路算法将避免使用对角线。
DIAGONAL_MODE_ONLY_IF_NO_OBSTACLES3如果在特定路径段的相邻单元格周围放置了任意障碍物,则该寻路算法将避免使用对角线。
DIAGONAL_MODE_MAX4代表 DiagonalMode 枚举的大小。

实现玩家基于AstarGrid2D的移动

用一个简单Sprite2D作为玩家。将其缩放为原始尺寸128×128的四分之一,也就是32×32。刚好可以填入AstarGrid2D的单元格中。
image.pngimage.png

extends Controlvar astar_grid = AStarGrid2D.new()
var path:PackedVector2Array
var solids = [] # 障碍物列表@onready var icon = $Icon
var speed = 200.0
var can_walk = false
var target_pos:Vector2func _process(delta):if can_walk:if path.size()>0:target_pos = path[0]if icon.position.distance_to(target_pos)>0:icon.position = icon.position.move_toward(target_pos,speed * delta)else:path.remove_at(0)queue_redraw()else:can_walk = falsefunc _ready():randomize()astar_grid.size = Vector2i.ONE * 32astar_grid.cell_size = Vector2i.ONE * 32astar_grid.offset = astar_grid.cell_size/2icon.position = astar_grid.cell_size/2astar_grid.diagonal_mode = AStarGrid2D.DIAGONAL_MODE_NEVERastar_grid.default_compute_heuristic  = AStarGrid2D.HEURISTIC_MANHATTANastar_grid.update()# 随机生成障碍for i in range(150):var solid_point = Vector2i(randi_range(0,astar_grid.size.x),randi_range(0,astar_grid.size.y))astar_grid.set_point_solid(solid_point,true)solids.append(solid_point)func _draw():var grid_width = astar_grid.size.x * astar_grid.cell_size.xvar cell_width = astar_grid.cell_size.xvar cell_height = astar_grid.cell_size.y# 绘制网格for i in range(astar_grid.size.x):draw_line(i * Vector2i(0,cell_height),i * Vector2i(grid_width,cell_height),Color.DARK_OLIVE_GREEN,2)for j in range(astar_grid.size.y):draw_line(j * Vector2i(cell_height,0),j * Vector2i(cell_height,grid_width),Color.DARK_OLIVE_GREEN,2)# 绘制路径和其上的点if path.size() > 0:for pot in path:draw_circle(pot,5,Color.YELLOW)draw_polyline(path,Color.YELLOW,2)for p in solids:draw_rect(Rect2(p * Vector2i(astar_grid.cell_size),astar_grid.cell_size),Color.GRAY)func _on_gui_input(event):if event is InputEventMouseButton:if event.button_index == MOUSE_BUTTON_LEFT:if event.is_pressed():can_walk = truepath = astar_grid.get_point_path(floor(icon.position/astar_grid.cell_size),floor(get_global_mouse_position()/astar_grid.cell_size))queue_redraw()

每次获取路径的第一个点,利用简单的距离判断和移动,到达后,从路径中删除该点。继续获取路径第一个,如此循环,知道路径中的点删除完毕。

基于AstarGrid2D的简单玩家移动

可行动范围的获取

矩形范围查找法:
先不考虑对角线移动的问题,我们假设玩家可行动点数是4,那么我们只需要遍历一个8×8矩阵的每个点到玩家的是否有可行走的路径,如果有且路径的点数<=4,则标记为可到达的点,否则,标记为不能到达的点。
image.png

extends Controlvar astar_grid = AStarGrid2D.new()
var path:PackedVector2Array
var solids = [] # 障碍物列表
var max_step:int = 4 # 玩家单次的最大行动点数
var can_walk_rect:Rect2i
var can_walk_points:PackedVector2Array @onready var icon = $Icon
var speed = 200.0
var can_walk = false
var target_pos:Vector2func _process(delta):if can_walk:if path.size()>0:target_pos = path[0]if icon.position.distance_to(target_pos)>0:icon.position = icon.position.move_toward(target_pos,speed * delta)else:path.remove_at(0)queue_redraw()else:can_walk = falsequeue_redraw()func _ready():randomize()astar_grid.size = Vector2i.ONE * 32astar_grid.cell_size = Vector2i.ONE * 32astar_grid.offset = astar_grid.cell_size/2icon.position = astar_grid.cell_size/2astar_grid.diagonal_mode = AStarGrid2D.DIAGONAL_MODE_NEVERastar_grid.update()# 随机生成障碍for i in range(150):var solid_point = Vector2i(randi_range(0,astar_grid.size.x),randi_range(0,astar_grid.size.y))astar_grid.set_point_solid(solid_point,true)solids.append(solid_point)func _draw():var grid_width = astar_grid.size.x * astar_grid.cell_size.xvar cell_width = astar_grid.cell_size.xvar cell_height = astar_grid.cell_size.y# 绘制网格for i in range(astar_grid.size.x):draw_line(i * Vector2i(0,cell_height),i * Vector2i(grid_width,cell_height),Color.DARK_OLIVE_GREEN,2)for j in range(astar_grid.size.y):draw_line(j * Vector2i(cell_height,0),j * Vector2i(cell_height,grid_width),Color.DARK_OLIVE_GREEN,2)# 绘制路径和其上的点if path.size() > 0:for pot in path:draw_circle(pot,5,Color.YELLOW)draw_polyline(path,Color.YELLOW,2)# 绘制障碍物for p in solids:draw_rect(Rect2(p * Vector2i(astar_grid.cell_size),astar_grid.cell_size),Color.GRAY)# 绘制可行走范围# 遍历矩形if !can_walk:var player_pos = floor(icon.position/astar_grid.cell_size)var top_left = clamp(player_pos - Vector2.ONE * max_step,Vector2.ZERO,player_pos)var end =  clamp(player_pos + Vector2.ONE * max_step,player_pos,Vector2(astar_grid.size))can_walk_rect = Rect2(top_left,end-top_left) # 获取矩形for i in range(can_walk_rect.position.x,can_walk_rect.end.x + 1):for j in range(can_walk_rect.position.y,can_walk_rect.end.y + 1):var v = Vector2(i,j)if astar_grid.get_point_path(player_pos,v).size() <= max_step+1:if !astar_grid.is_point_solid(v):can_walk_points.append(v)draw_rect(Rect2(v * astar_grid.cell_size,astar_grid.cell_size),Color.YELLOW_GREEN,false,2)func _on_gui_input(event):if event is InputEventMouseButton:if event.button_index == MOUSE_BUTTON_LEFT:if event.is_pressed():can_walk = truevar player_pos = floor(icon.position/astar_grid.cell_size)var targ_pos = floor(get_global_mouse_position()/astar_grid.cell_size)if targ_pos in can_walk_points: # 如果在可行走的范围内path = astar_grid.get_point_path(player_pos,targ_pos)can_walk_points.clear() # 清空原来的可行走范围queue_redraw()

AstarGrid寻路-显示和限定行走范围.gif
AstarGrid寻路-显示和限定行走范围2.gif

这篇关于【Godot4.2】2D导航02 - AstarGrid2D及其使用方法的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

PHP轻松处理千万行数据的方法详解

《PHP轻松处理千万行数据的方法详解》说到处理大数据集,PHP通常不是第一个想到的语言,但如果你曾经需要处理数百万行数据而不让服务器崩溃或内存耗尽,你就会知道PHP用对了工具有多强大,下面小编就... 目录问题的本质php 中的数据流处理:为什么必不可少生成器:内存高效的迭代方式流量控制:避免系统过载一次性

Python使用FastAPI实现大文件分片上传与断点续传功能

《Python使用FastAPI实现大文件分片上传与断点续传功能》大文件直传常遇到超时、网络抖动失败、失败后只能重传的问题,分片上传+断点续传可以把大文件拆成若干小块逐个上传,并在中断后从已完成分片继... 目录一、接口设计二、服务端实现(FastAPI)2.1 运行环境2.2 目录结构建议2.3 serv

Spring Security简介、使用与最佳实践

《SpringSecurity简介、使用与最佳实践》SpringSecurity是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架,本文给大家介绍SpringSec... 目录一、如何理解 Spring Security?—— 核心思想二、如何在 Java 项目中使用?——

springboot中使用okhttp3的小结

《springboot中使用okhttp3的小结》OkHttp3是一个JavaHTTP客户端,可以处理各种请求类型,比如GET、POST、PUT等,并且支持高效的HTTP连接池、请求和响应缓存、以及异... 在 Spring Boot 项目中使用 OkHttp3 进行 HTTP 请求是一个高效且流行的方式。

python获取指定名字的程序的文件路径的两种方法

《python获取指定名字的程序的文件路径的两种方法》本文主要介绍了python获取指定名字的程序的文件路径的两种方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要... 最近在做项目,需要用到给定一个程序名字就可以自动获取到这个程序在Windows系统下的绝对路径,以下

Java使用Javassist动态生成HelloWorld类

《Java使用Javassist动态生成HelloWorld类》Javassist是一个非常强大的字节码操作和定义库,它允许开发者在运行时创建新的类或者修改现有的类,本文将简单介绍如何使用Javass... 目录1. Javassist简介2. 环境准备3. 动态生成HelloWorld类3.1 创建CtC

JavaScript中的高级调试方法全攻略指南

《JavaScript中的高级调试方法全攻略指南》什么是高级JavaScript调试技巧,它比console.log有何优势,如何使用断点调试定位问题,通过本文,我们将深入解答这些问题,带您从理论到实... 目录观点与案例结合观点1观点2观点3观点4观点5高级调试技巧详解实战案例断点调试:定位变量错误性能分

使用Python批量将.ncm格式的音频文件转换为.mp3格式的实战详解

《使用Python批量将.ncm格式的音频文件转换为.mp3格式的实战详解》本文详细介绍了如何使用Python通过ncmdump工具批量将.ncm音频转换为.mp3的步骤,包括安装、配置ffmpeg环... 目录1. 前言2. 安装 ncmdump3. 实现 .ncm 转 .mp34. 执行过程5. 执行结

Python中 try / except / else / finally 异常处理方法详解

《Python中try/except/else/finally异常处理方法详解》:本文主要介绍Python中try/except/else/finally异常处理方法的相关资料,涵... 目录1. 基本结构2. 各部分的作用tryexceptelsefinally3. 执行流程总结4. 常见用法(1)多个e

Java使用jar命令配置服务器端口的完整指南

《Java使用jar命令配置服务器端口的完整指南》本文将详细介绍如何使用java-jar命令启动应用,并重点讲解如何配置服务器端口,同时提供一个实用的Web工具来简化这一过程,希望对大家有所帮助... 目录1. Java Jar文件简介1.1 什么是Jar文件1.2 创建可执行Jar文件2. 使用java