我一直在努力寻找一种路径寻找脚本,该脚本在每次调用事件时只触发一次移动。经过大量的试错,以及不幸的是,在聊天机器人的帮助下,我成功地将这个 2011 年的经典寻路器改造成我想要的样子。
感谢原始脚本作者 cozziekuns(以及 Modern Albegra)。此外,大部分描述由 cozziekuns 编写,并在适用处进行了少量编辑。
特别感谢 johantnjl,他在 2012 年 1 月请求了此功能/脚本修改。希望这能对您的项目有所帮助 ![]()
版本历史
- <版本 1.0> 2011.12.31 - 原始发布 (myhgis 于 2026.07.19 进行了少量修改)
描述
此脚本允许您使用最短路径将任何角色移动到另一个图块。在大型地图上,这需要 significantly 更长的时间。
功能
-
极快且准确的路径寻找
-
简单的脚本调用
-
几乎无需配置
说明
要按原意使用脚本而不是一步一步地移动,请创建一个移动路线事件,并作为脚本命令使用:
find_path(target_x, target_y)
此外,可以通过以下调用强制通过脚本进行路径规划,这是经过编辑以在每次调用脚本时仅移动一次的版本:
force_path(target_x, target_y)
*Modern Algebra 的脚本中存在一个已知错误,涉及找到路径后的重新计算。由于此脚本的某些部分仅仅是从他 VX 版本的转换(仅算法不同,稍慢且更粗糙),因此此脚本中也存在该错误。
致谢
- cozziekuns
- Modern Algebra,感谢他的 VX 版本脚本
感谢
- Patrick Lester,以及他著名的 A* 教程,这对我们非常有帮助。
#===============================================================================
# [VXA] 路径寻找
#-------------------------------------------------------------------------------
# 版本:1.0
# 作者:cozziekuns (rmrk)
# 最后更新日期:2011/12/31 (MM/DD/YYYY)
#===============================================================================
# 描述:
#-------------------------------------------------------------------------------
# 此脚本允许您使用最短路径将任何角色移动到另一个图块。在大型地图上,
# 这需要 significantly 更长的时间。
#===============================================================================
# 更新
# ------------------------------------------------------------------------------
# o 2011/12/31 - 开始编写脚本
#===============================================================================
# 待办事项
#-------------------------------------------------------------------------------
# o 允许动态重新计算,如果出于任何原因想重复路径寻找过程。
#===============================================================================
# 说明
#-------------------------------------------------------------------------------
# 要使用,请创建一个移动路线事件,并作为脚本命令使用:
#
# find_path(target_x, target_y)
#
# 此外,可以通过以下调用强制通过脚本进行路径规划:
#
# force_path(target_x, target_y)
#
# Modern Algebra 的脚本中存在一个已知错误,涉及找到路径后的重新计算。由于
# 此脚本的某些部分仅仅是从他 VX 版本的转换(仅算法不同,稍慢且更粗糙 :P),
# 因此此脚本中也存在该错误。
#===============================================================================
#==============================================================================
# ** Game_Map
#==============================================================================
class Game_Map
def find_path(target_x, target_y, sx, sy, passable, char)
path = []
max_elements = width * height + 2
checked_items = 0
@open_list_items = 0
@open_list = Table.new(max_elements)
@nodes = Table.new(max_elements, 2)
open = Table.new(width, height)
closed = Table.new(width, height)
parent = Table.new(width, height, 3)
@f_cost = Table.new(width, height)
@open_list[0] = 0
@nodes[0, 0] = sx
@nodes[0, 1] = sy
next_point = [sx, sy]
closed[sx, sy] = 1
loop do
next_point = delete_from_heap if not next_point == [sx, sy]
# 目标不可达 — 取消搜索并保持空闲
return [] if next_point.nil?
open[next_point[0], next_point[1]] = 0
closed[next_point[0], next_point[1]] = 2 if not next_point == [sx, sy]
parent_x, parent_y = next_point[0], next_point[1]
for i in 1..4
dir = i * 2
x, y = case dir
when 2; [parent_x, parent_y + 1]
when 4; [parent_x - 1, parent_y]
when 6; [parent_x + 1, parent_y]
when 8; [parent_x, parent_y - 1]
end
next unless $game_map.valid?(x, y)
next if closed[x, y] == 2
next unless custom_passable?(char, parent_x, parent_y, dir, x, y, target_x, target_y)
if not open[x, y] == 1
open[x, y] = 1
parent[x, y, 0] = parent_x
parent[x, y, 1] = parent_y
parent[x, y, 2] = parent[parent_x, parent_y, 2] + 10
g = parent[x, y, 2] + 10
h = ((target_x - x).abs + (target_y - y).abs) * 10
@f_cost[x, y] = g
checked_items += 1
@open_list_items += 1
@nodes[checked_items, 0] = x
@nodes[checked_items, 1] = y
add_to_heap(checked_items)
else
old_g = parent[x, y, 2] + 10
new_g = parent[parent_x, parent_y, 2] + 20
next if old_g < new_g
parent[x, y, 0] = parent_x
parent[x, y, 1] = parent_y
parent[x, y, 2] = new_g
g = parent[x, y, 2] + 10
h = ((target_x - x).abs + (target_y - y).abs) * 10
@f_cost[x, y] = g
end
end
next_point = nil
break if closed[target_x, target_y] == 2
end
# 再次检查是否到达目的地
return [] unless closed[target_x, target_y] == 2
path_x, path_y = target_x, target_y
loop do
parent_x = parent[path_x, path_y, 0]
parent_y = parent[path_x, path_y, 1]
dx = path_x - parent_x
dy = path_y - parent_y
if dx == 1
code = 6 # 右
elsif dx == -1
code = 4 # 左
elsif dy == 1
code = 2 # 下
elsif dy == -1
code = 8 # 上
else
code = 0
end
path.push(RPG::MoveCommand.new(code))
path_x, path_y = parent_x, parent_y
break if path_x == sx and path_y == sy
end
return path
end
def custom_passable?(char, x, y, d, nx, ny, target_x, target_y)
return false unless char.map_passable?(x, y, d)
return true if nx == target_x && ny == target_y
return !char.collide_with_characters?(nx, ny)
end
def add_to_heap(value)
m = @open_list_items
@open_list[m] = value
while m != 1
if fcost(@open_list[m]) < fcost(@open_list[m / 2])
temp = @open_list[m / 2]
@open_list[m / 2] = @open_list[m]
@open_list[m] = temp
m /= 2
else
break
end
end
end
def delete_from_heap
return nil if @open_list_items <= 0
next_point = @open_list[0]
@open_list[0] = @open_list[@open_list_items]
@open_list_items -= 1
v = 1
loop do
u = v
w = 2 * u
if w + 1 <= @open_list_items
v = w if fcost(@open_list[u - 1]) >= fcost(@open_list[w - 1])
v = w + 1 if fcost(@open_list[v - 1]) >= fcost(@open_list[w])
elsif w <= @open_list_items
v = w if fcost(@open_list[u - 1]) >= fcost(@open_list[w - 1])
end
if u != v
temp = @open_list[u - 1]
@open_list[u - 1] = @open_list[v - 1]
@open_list[v - 1] = temp
else
break
end
end
return nil if next_point.nil?
return @nodes[next_point, 0], @nodes[next_point, 1]
end
def fcost(point)
x = @nodes[point, 0]
y = @nodes[point, 1]
return @f_cost[x, y]
end
end
#==============================================================================
# ** Game_CharacterBase
#==============================================================================
class Game_CharacterBase
def find_path(target_x, target_y)
path = $game_map.find_path(target_x, target_y, @x, @y, false, self)
path = path[0, 1] || []
@move_route.list.delete_at(@move_route_index)
path.each { |cmd| @move_route.list.insert(@move_route_index, cmd) }
@move_route_index -= 1
end
def force_path(target_x, target_y)
return if @x == target_x && @y == target_y
path = $game_map.find_path(target_x, target_y, @x, @y, false, self)
return if path.nil? || path.empty?
next_step = path.last
move_straight(next_step.code)
end
def count_iterations(target_x, target_y)
path = $game_map.find_path(target_x, target_y, @x, @y, true, self)
return path.size
end
end