我如何使用 A* (Astart) 寻路系统连接点?在戈多
How I connect dots using A* (Astart) pathfinding system? in GODOT
我正在尝试做一些与往常不同的事情。
我有一个 3D 网格图节点设置,我正在尝试使用 A* 自动生成点和连接
我没有创建障碍物瓦片,而是在瓦片之间创建墙,所以瓦片仍然可以行走,你只是不能穿过墙。我已经想通了
但我不知道如何编码如何以简单的方式连接点,而不是连接中间有墙的点...
我正在使用 RaycastCast 节点检测墙,以及它穿过每个网格时的位置
但我想不出一个嵌套循环来找到要连接的邻居点
这就是我尝试做的(显然 get_closest_point() 没有按照我想要的方式工作)。
如果我可以只使用 Vector3 坐标得到一个点,我想我可以让它工作。
额外:如果你们能告诉我一种清理代码的方法,尤其是在“FORs”语法上,因为我有点不知道自己在做什么
任何其他干净的代码建议都会很棒并且非常受欢迎
最后有一个想法逻辑的视觉图(图像)。
onready var rc = $RayCast
onready var aS = AStar.new()
var floor_size = Vector3(12,0,12)
var origin = Vector3(-5.5, 0.5, -2.5)
var FRONT = Vector3(1,0,0)
var RIGHT = Vector3(0,0,1)
var BACK = Vector3(-1,0,0)
var LEFT = Vector3(-1,0,0)
func set_walkable():
var value = 0
var value2 = 0
var i = 0
for _length in range (origin.x, origin.x + floor_size.x + 1):
value += 1
value2 = 0
for _width in range(origin.z, origin.z + floor_size.z):
i += 1
value2 += 1
aS.add_point(i, Vector3(origin.x + value -1 , 0.5, origin.z + value2 -1) , 1.0)
value = 0
for _u in range(origin.x, origin.x + floor_size.x + 1):
value += 1
value2 = 0
for _v in range(origin.z, origin.z + floor_size.z):
value2 += 1
var from = aS.get_closest_point(Vector3(origin.x + value ,0.5, origin.z + value2) ) # Current
rc.translation = Vector3(origin.x + value -1 ,0.5, origin.z + value2 -1)
draw_points()
print(rc.translation)
rc.cast_to = FRONT
var to = aS.get_closest_point(rc.translation) # Front
if from != -1 and !rc.is_colliding():
aS.connect_points(from, to)
draw_connections(Vector3(rc.translation.x + 0.5,rc.translation.y,rc.translation.z))
rc.cast_to = BACK
to = aS.get_closest_point(rc.translation) # back
if from != -1 and !rc.is_colliding():
aS.connect_points(from, to)
draw_connections(Vector3(rc.translation.x + -0.5,rc.translation.y,rc.translation.z))
rc.cast_to = RIGHT
to = aS.get_closest_point(rc.translation) # right
if from != -1 and !rc.is_colliding():
aS.connect_points(from, to)
draw_connections(Vector3(rc.translation.x,rc.translation.y,rc.translation.z + 0.5))
rc.cast_to = LEFT
to = aS.get_closest_point(rc.translation) # left
if from != -1 and !rc.is_colliding():
aS.connect_points(from, to)
draw_connections(Vector3(rc.translation.x + 0.5,rc.translation.y,rc.translation.z + -0.5))
func draw_points(): # Make points visible
var cube = MeshInstance.new()
cube.mesh = CubeMesh.new()
cube.translation = rc.translation
cube.scale = Vector3(0.25,0.25,0.25)
add_child(cube)
print("Cubo adicionado")
func draw_connections(position): # Make connections visible
var line = MeshInstance.new()
line.mesh = PlaneMesh.new()
line.scale = Vector3(0.03,0.03,0.03)
line.translation = position
add_child(line)
print("Cubo adicionado")
坐标和点 id 之间的转换
让我们在坐标和点id之间建立一个映射。鉴于我们有 floor_size
,这很容易:
func vector_to_id(vector:Vector3, size:Vector3) -> int:
return int(int3(vector).dot(dimension_size(size)))
func id_to_vector(id:int, size:Vector3) -> Vector3:
var s:Vector3 = dimension_size(size)
var z:int = int(id / s.z)
var y:int = int((id % int(s.z)) / s.y)
var x:int = id % int(s.y)
return Vector3(x, y, z)
func int3(vector:Vector3) -> Vector3:
return Vector3(int(vector.x), int(vector.y), int(vector.z))
func dimension_size(size:Vector3) -> Vector3:
return Vector3(1, int(size.x + 1), int(size.x + 1) * int(size.y + 1))
可能的优化:
- 存储
dimension_size(floor_size)
并直接使用。
- 跳过调用
int3
,前提是您传递给 vector_to_id
的值保证为整数。
我们需要一个函数来获取总点数:
func total_size(size:Vector3) -> int:
return int(size.x + 1) * int(size.y + 1) * int(size.z + 1)
说明
让我们从 1D(一维)开始。我们将只有一个坐标。所以我们有一个假设的 Vector1
有一个 x
属性。我们只是把东西排成一行。
那么映射就很简单了:要从坐标转换为 id,我们采用 id = int(vector.x)
,如果我们想要坐标,我们只需 vector = Vector1(id)
.
现在,让我们转到二维。我们有 Vector2
,x
和 y
。值得庆幸的是我们有一个大小(当不知道大小时有一些方法可以进行映射,但是有一个大小很方便)。
因此,我们将制作一个具有一定宽度和高度的二维网格。 y
坐标告诉我们所在的行,x
告诉我们在行中的位置。
然后如果我们有一些id,我们需要弄清楚我们需要多少行才能到达那里,然后我们在该行中的什么位置。找出行很容易,我们除以网格的宽度。而在行中的位置就是提醒。一个警告:我们从 0 开始测量(因此宽度 0 实际上意味着每行 1 个元素)。
我们有:
func id_to_vector(id:int, size:Vector2) -> Vector2:
var y:int = int(id / (size.x + 1))
var x:int = id % int(size.x + 1)
return Vector2(x, y)
反过来怎么样?好吧,我们用 y
乘以一行的长度(宽度),然后加上 x
:
func vector_to_id(vector:Vector2, size:Vector2) -> int:
return int(vector.x) + int(vector.y) * int(size.x + 1)
通知:
- 我们不需要
size.y
。
- 两个函数都需要
size.x + 1
。
vector_to_id
看起来非常类似于点积。
因此,让我们创建一个新函数,returns 我们将用来制作点积的向量:
func dimension_size(size:Vector2) -> Vector2:
return Vector2(1, int(size.x + 1))
并使用它:
func vector_to_id(vector:Vector2, size:Vector2) -> int:
return int(vector.dot(dimensional_size(size)))
func id_to_vector(id:int, size:Vector2) -> Vector2:
var s = dimensional_size(size)
var y:int = int(id / int(s.y))
var x:int = id % int(s.y)
return Vector2(x, y)
注意如果不能保证vector
在vector_to_id
中只有整数,点积make中的小数部分会导致错误的结果.这就是为什么我有一个函数让它只有整数。
3D 时间到了。我们有 Vector3
和 x
、y
和 z
。我们正在制作一个 3D 网格。现在 z
会告诉我们图层,每个图层都是一个二维网格。
让我们回顾一下 dimensional_size
,我们有:
func dimension_size(size:Vector2) -> Vector2:
return Vector2(1, int(size.x + 1))
也就是一个元素的大小(1
),一行的大小(size.x + 1
),我们需要加上一层的大小。即二维网格的大小,就是宽度乘以高度。
func dimension_size(size:Vector3) -> Vector3:
return Vector3(1, int(size.x + 1), int(size.x + 1) * int(size.y + 1))
我们如何从 id 中获取 z
?我们除以网格的大小(所以我们知道我们在什么网格上)。然后从那个分区的提示中我们可以找到y
:
func vector_to_id(vector:Vector3, size:Vector3) -> int:
return int(vector.dot(dimensional_size(size)))
func id_to_vector(id:int, size:Vector3) -> Vector3:
var s = dimensional_size(size)
var z:int = int(id / int(s.z))
var y:int = int(int(id % int(s.z)) / int(s.y))
var x:int = id % int(s.y)
return Vector2(x, y, z)
事实上,从技术上讲,所有这些坐标都是以相同的形式计算的:
func id_to_vector(id:int, size:Vector3) -> Vector3:
var s = dimensional_size(size)
var tot = total_size(size)
var z:int = int(int(id % int(tot)) / int(s.z))
var y:int = int(int(id % int(s.z)) / int(s.y))
var x:int = int(int(id % int(s.y)) / int(s.x))
return Vector2(x, y, z)
除此之外,不需要带总大小的提醒,因为 id
应该总是小于那个。而且不需要除以s.x
,因为单个元素的大小总是1
。 而且我还删除了一些多余的 int 转换。
什么是total_size
? dimensional_size
的下一个元素,当然是:
func dimension_size(size:Vector3) -> Vector3:
return Vector3(1, int(size.x + 1), int(size.x + 1) * int(size.y + 1))
func total_size(size:Vector3) -> int:
return int(size.x + 1) * int(size.y + 1) * int(size.z + 1)
正在检查连接
以及检查连通性的方法:
func check_connected(start:Vector3, end:Vector3) -> bool:
rc.transform.origin = start
rc.cast_to = end
rc.force_update_transform()
rc.force_raycast_update()
return !raycast.is_colliding()
你对 FRONT
、RIGHT
、BACK
和 LEFT
的想法是正确的,但将它们放在一个数组中:
var offsets = [Vector3(1,0,0), Vector3(0,0,1), Vector3(-1,0,0), Vector3(-1,0,0)]
注意 我正在调用 force_update_transform
和 force_raycast_update
因为在同一帧上进行多次光线投射检查。
填充AStar
好了,足够的设置,我们现在可以迭代:
for id in total_size(floor_size):
pass
在每次迭代中,我们需要得到向量:
for id in total_size(floor_size):
var vector = id_to_vector(id, floor_size)
可能的优化:直接迭代向量坐标以避免调用id_to_vector
.
我们可以将向量添加到AStar
:
for id in total_size(floor_size):
var vector = id_to_vector(id, floor_size)
aS.add_point(id, vector)
接下来我们需要相邻向量:
for id in total_size(floor_size):
var vector = id_to_vector(id, floor_size)
aS.add_point(id, vector)
for offset in offsets:
var adjacent = vector + offset
让我们也将它们添加到 AStar
:
for id in total_size(floor_size):
var vector = id_to_vector(id, floor_size)
aS.add_point(id, vector)
for offset in offsets:
var adjacent = vector + offset
var adjacent_id = vector_to_id(adjacent, floor_size)
aS.add_point(adjacent_id, adjacent)
可能的优化:
- 不添加如果
has_point
returns true.
- 如果相邻向量的id较低,则不处理。
- 修改
offsets
以便您只检查尚未添加的相邻位置(从而防止前两种情况)。
让我们检查连接:
for id in total_size(floor_size):
var vector = id_to_vector(id, floor_size)
aS.add_point(id, vector)
for offset in offsets:
var adjacent = vector + offset
var adjacent_id = vector_to_id(adjacent, floor_size)
aS.add_point(adjacent_id, adjacent)
if check_connected(vector, adjacent):
pass
并告诉 AStar
连接情况:
for id in total_size(floor_size):
var vector = id_to_vector(id, floor_size)
aS.add_point(id, vector)
for offset in offsets:
var adjacent = vector + offset
var adjacent_id = vector_to_id(adjacent, floor_size)
aS.add_point(adjacent_id, adjacent)
if check_connected(vector, adjacent):
connect_points(id, adjacent_id)
我正在尝试做一些与往常不同的事情。
我有一个 3D 网格图节点设置,我正在尝试使用 A* 自动生成点和连接 我没有创建障碍物瓦片,而是在瓦片之间创建墙,所以瓦片仍然可以行走,你只是不能穿过墙。我已经想通了
但我不知道如何编码如何以简单的方式连接点,而不是连接中间有墙的点...
我正在使用 RaycastCast 节点检测墙,以及它穿过每个网格时的位置
但我想不出一个嵌套循环来找到要连接的邻居点
这就是我尝试做的(显然 get_closest_point() 没有按照我想要的方式工作)。 如果我可以只使用 Vector3 坐标得到一个点,我想我可以让它工作。
额外:如果你们能告诉我一种清理代码的方法,尤其是在“FORs”语法上,因为我有点不知道自己在做什么
任何其他干净的代码建议都会很棒并且非常受欢迎
最后有一个想法逻辑的视觉图(图像)。
onready var rc = $RayCast
onready var aS = AStar.new()
var floor_size = Vector3(12,0,12)
var origin = Vector3(-5.5, 0.5, -2.5)
var FRONT = Vector3(1,0,0)
var RIGHT = Vector3(0,0,1)
var BACK = Vector3(-1,0,0)
var LEFT = Vector3(-1,0,0)
func set_walkable():
var value = 0
var value2 = 0
var i = 0
for _length in range (origin.x, origin.x + floor_size.x + 1):
value += 1
value2 = 0
for _width in range(origin.z, origin.z + floor_size.z):
i += 1
value2 += 1
aS.add_point(i, Vector3(origin.x + value -1 , 0.5, origin.z + value2 -1) , 1.0)
value = 0
for _u in range(origin.x, origin.x + floor_size.x + 1):
value += 1
value2 = 0
for _v in range(origin.z, origin.z + floor_size.z):
value2 += 1
var from = aS.get_closest_point(Vector3(origin.x + value ,0.5, origin.z + value2) ) # Current
rc.translation = Vector3(origin.x + value -1 ,0.5, origin.z + value2 -1)
draw_points()
print(rc.translation)
rc.cast_to = FRONT
var to = aS.get_closest_point(rc.translation) # Front
if from != -1 and !rc.is_colliding():
aS.connect_points(from, to)
draw_connections(Vector3(rc.translation.x + 0.5,rc.translation.y,rc.translation.z))
rc.cast_to = BACK
to = aS.get_closest_point(rc.translation) # back
if from != -1 and !rc.is_colliding():
aS.connect_points(from, to)
draw_connections(Vector3(rc.translation.x + -0.5,rc.translation.y,rc.translation.z))
rc.cast_to = RIGHT
to = aS.get_closest_point(rc.translation) # right
if from != -1 and !rc.is_colliding():
aS.connect_points(from, to)
draw_connections(Vector3(rc.translation.x,rc.translation.y,rc.translation.z + 0.5))
rc.cast_to = LEFT
to = aS.get_closest_point(rc.translation) # left
if from != -1 and !rc.is_colliding():
aS.connect_points(from, to)
draw_connections(Vector3(rc.translation.x + 0.5,rc.translation.y,rc.translation.z + -0.5))
func draw_points(): # Make points visible
var cube = MeshInstance.new()
cube.mesh = CubeMesh.new()
cube.translation = rc.translation
cube.scale = Vector3(0.25,0.25,0.25)
add_child(cube)
print("Cubo adicionado")
func draw_connections(position): # Make connections visible
var line = MeshInstance.new()
line.mesh = PlaneMesh.new()
line.scale = Vector3(0.03,0.03,0.03)
line.translation = position
add_child(line)
print("Cubo adicionado")
坐标和点 id 之间的转换
让我们在坐标和点id之间建立一个映射。鉴于我们有 floor_size
,这很容易:
func vector_to_id(vector:Vector3, size:Vector3) -> int:
return int(int3(vector).dot(dimension_size(size)))
func id_to_vector(id:int, size:Vector3) -> Vector3:
var s:Vector3 = dimension_size(size)
var z:int = int(id / s.z)
var y:int = int((id % int(s.z)) / s.y)
var x:int = id % int(s.y)
return Vector3(x, y, z)
func int3(vector:Vector3) -> Vector3:
return Vector3(int(vector.x), int(vector.y), int(vector.z))
func dimension_size(size:Vector3) -> Vector3:
return Vector3(1, int(size.x + 1), int(size.x + 1) * int(size.y + 1))
可能的优化:
- 存储
dimension_size(floor_size)
并直接使用。 - 跳过调用
int3
,前提是您传递给vector_to_id
的值保证为整数。
我们需要一个函数来获取总点数:
func total_size(size:Vector3) -> int:
return int(size.x + 1) * int(size.y + 1) * int(size.z + 1)
说明
让我们从 1D(一维)开始。我们将只有一个坐标。所以我们有一个假设的 Vector1
有一个 x
属性。我们只是把东西排成一行。
那么映射就很简单了:要从坐标转换为 id,我们采用 id = int(vector.x)
,如果我们想要坐标,我们只需 vector = Vector1(id)
.
现在,让我们转到二维。我们有 Vector2
,x
和 y
。值得庆幸的是我们有一个大小(当不知道大小时有一些方法可以进行映射,但是有一个大小很方便)。
因此,我们将制作一个具有一定宽度和高度的二维网格。 y
坐标告诉我们所在的行,x
告诉我们在行中的位置。
然后如果我们有一些id,我们需要弄清楚我们需要多少行才能到达那里,然后我们在该行中的什么位置。找出行很容易,我们除以网格的宽度。而在行中的位置就是提醒。一个警告:我们从 0 开始测量(因此宽度 0 实际上意味着每行 1 个元素)。
我们有:
func id_to_vector(id:int, size:Vector2) -> Vector2:
var y:int = int(id / (size.x + 1))
var x:int = id % int(size.x + 1)
return Vector2(x, y)
反过来怎么样?好吧,我们用 y
乘以一行的长度(宽度),然后加上 x
:
func vector_to_id(vector:Vector2, size:Vector2) -> int:
return int(vector.x) + int(vector.y) * int(size.x + 1)
通知:
- 我们不需要
size.y
。 - 两个函数都需要
size.x + 1
。 vector_to_id
看起来非常类似于点积。
因此,让我们创建一个新函数,returns 我们将用来制作点积的向量:
func dimension_size(size:Vector2) -> Vector2:
return Vector2(1, int(size.x + 1))
并使用它:
func vector_to_id(vector:Vector2, size:Vector2) -> int:
return int(vector.dot(dimensional_size(size)))
func id_to_vector(id:int, size:Vector2) -> Vector2:
var s = dimensional_size(size)
var y:int = int(id / int(s.y))
var x:int = id % int(s.y)
return Vector2(x, y)
注意如果不能保证vector
在vector_to_id
中只有整数,点积make中的小数部分会导致错误的结果.这就是为什么我有一个函数让它只有整数。
3D 时间到了。我们有 Vector3
和 x
、y
和 z
。我们正在制作一个 3D 网格。现在 z
会告诉我们图层,每个图层都是一个二维网格。
让我们回顾一下 dimensional_size
,我们有:
func dimension_size(size:Vector2) -> Vector2:
return Vector2(1, int(size.x + 1))
也就是一个元素的大小(1
),一行的大小(size.x + 1
),我们需要加上一层的大小。即二维网格的大小,就是宽度乘以高度。
func dimension_size(size:Vector3) -> Vector3:
return Vector3(1, int(size.x + 1), int(size.x + 1) * int(size.y + 1))
我们如何从 id 中获取 z
?我们除以网格的大小(所以我们知道我们在什么网格上)。然后从那个分区的提示中我们可以找到y
:
func vector_to_id(vector:Vector3, size:Vector3) -> int:
return int(vector.dot(dimensional_size(size)))
func id_to_vector(id:int, size:Vector3) -> Vector3:
var s = dimensional_size(size)
var z:int = int(id / int(s.z))
var y:int = int(int(id % int(s.z)) / int(s.y))
var x:int = id % int(s.y)
return Vector2(x, y, z)
事实上,从技术上讲,所有这些坐标都是以相同的形式计算的:
func id_to_vector(id:int, size:Vector3) -> Vector3:
var s = dimensional_size(size)
var tot = total_size(size)
var z:int = int(int(id % int(tot)) / int(s.z))
var y:int = int(int(id % int(s.z)) / int(s.y))
var x:int = int(int(id % int(s.y)) / int(s.x))
return Vector2(x, y, z)
除此之外,不需要带总大小的提醒,因为 id
应该总是小于那个。而且不需要除以s.x
,因为单个元素的大小总是1
。 而且我还删除了一些多余的 int 转换。
什么是total_size
? dimensional_size
的下一个元素,当然是:
func dimension_size(size:Vector3) -> Vector3:
return Vector3(1, int(size.x + 1), int(size.x + 1) * int(size.y + 1))
func total_size(size:Vector3) -> int:
return int(size.x + 1) * int(size.y + 1) * int(size.z + 1)
正在检查连接
以及检查连通性的方法:
func check_connected(start:Vector3, end:Vector3) -> bool:
rc.transform.origin = start
rc.cast_to = end
rc.force_update_transform()
rc.force_raycast_update()
return !raycast.is_colliding()
你对 FRONT
、RIGHT
、BACK
和 LEFT
的想法是正确的,但将它们放在一个数组中:
var offsets = [Vector3(1,0,0), Vector3(0,0,1), Vector3(-1,0,0), Vector3(-1,0,0)]
注意 我正在调用 force_update_transform
和 force_raycast_update
因为在同一帧上进行多次光线投射检查。
填充AStar
好了,足够的设置,我们现在可以迭代:
for id in total_size(floor_size):
pass
在每次迭代中,我们需要得到向量:
for id in total_size(floor_size):
var vector = id_to_vector(id, floor_size)
可能的优化:直接迭代向量坐标以避免调用id_to_vector
.
我们可以将向量添加到AStar
:
for id in total_size(floor_size):
var vector = id_to_vector(id, floor_size)
aS.add_point(id, vector)
接下来我们需要相邻向量:
for id in total_size(floor_size):
var vector = id_to_vector(id, floor_size)
aS.add_point(id, vector)
for offset in offsets:
var adjacent = vector + offset
让我们也将它们添加到 AStar
:
for id in total_size(floor_size):
var vector = id_to_vector(id, floor_size)
aS.add_point(id, vector)
for offset in offsets:
var adjacent = vector + offset
var adjacent_id = vector_to_id(adjacent, floor_size)
aS.add_point(adjacent_id, adjacent)
可能的优化:
- 不添加如果
has_point
returns true. - 如果相邻向量的id较低,则不处理。
- 修改
offsets
以便您只检查尚未添加的相邻位置(从而防止前两种情况)。
让我们检查连接:
for id in total_size(floor_size):
var vector = id_to_vector(id, floor_size)
aS.add_point(id, vector)
for offset in offsets:
var adjacent = vector + offset
var adjacent_id = vector_to_id(adjacent, floor_size)
aS.add_point(adjacent_id, adjacent)
if check_connected(vector, adjacent):
pass
并告诉 AStar
连接情况:
for id in total_size(floor_size):
var vector = id_to_vector(id, floor_size)
aS.add_point(id, vector)
for offset in offsets:
var adjacent = vector + offset
var adjacent_id = vector_to_id(adjacent, floor_size)
aS.add_point(adjacent_id, adjacent)
if check_connected(vector, adjacent):
connect_points(id, adjacent_id)