设为首页收藏本站|繁體中文

Project1

 找回密码
 注册会员
搜索
查看: 4416|回复: 10
打印 上一主题 下一主题

[RMVA发布] spfa四方向自动寻路系统,交通工具,设定速度等 更新v1.1

[复制链接]

Lv3.寻梦者

梦石
0
星屑
1774
在线时间
950 小时
注册时间
2012-7-5
帖子
245
跳转到指定楼层
1
发表于 2016-2-20 00:42:49 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

加入我们,或者,欢迎回来。

您需要 登录 才可以下载或查看,没有帐号?注册会员

x
本帖最后由 浮云半仙 于 2016-2-20 17:48 编辑

RMVA上使用的四方向自动寻路
2016.02.20更新至v1.1版,兼容v1.0功能。新增可设置寻路时移动速度,可以判断不可通行的地形标记(下面有解释)
范例工程,演示了如何使用,以及对寻路功能进行了测试 findpath.zip (1.5 MB, 下载次数: 227)
截图:
在样例工程里面可以使用道具触发自动寻路。如何在事件->脚本中调用,请仔细看脚本开始的说明

npc的自动寻路

地形标记的判断,防止出bug

迷宫寻路

水上寻路

飞艇寻路 飞艇全部可通行


寻路过程中可用任意方向键盘或使用鼠标左键点击停止寻路(鼠标点击停止寻路可以关闭)
执行效率不错,算法本身比较优秀,并且常数优化做得也很到位。大地图不怂
为什么没有用A*而用了spfa,见脚本前面的说明

以上,祝玩嗨
以下,代码 (spfa四方向寻路系统v1.1) 使用时一定要仔细看前面的用法。
RUBY 代码复制
  1. =begin
  2. * SPFA 自动寻路系统v1.0 by 浮云半仙  2016.02.19
  3. * 支持小船大船飞行船等交通工具的自动寻路
  4. * 支持寻路过程中,按任意方向键打断寻路。
  5. * 支持寻路过程中,鼠标左键点击游戏窗口中任一点,打断寻路。(往下翻有可选开关)
  6. * 支持特定的地形标记(往下翻,可更改标记的编号)的图块强行判定为不可通行(仅在步行状态下)
  7.     (用途:防止将房顶认为可通行而试图穿越房顶,但实际上又穿越不过去,导致寻路中断)
  8. * 玩家(注意,只是玩家)使用本脚本寻路时,会打开对应的全局开关 (往下翻,有设置开关编号)
  9. * 用法 事件->脚本 FYBX::FindPath.find_path(参数)
  10.   参数以哈希表的形式传入,下列是识别的参数
  11.   start_x,start_y         (可选参数) 起点的x,y坐标,缺省为玩家坐标
  12.   map                      (可选参数) 地图(Game_Map),缺省为 $game_map
  13.   character                (可选参数) 要移动的对象(Game_Character)
  14.   speed                    (可选参数) 移动速度,注意移动速度的代号如下(与事件 设置移动速度代号相同):
  15.     代号1:1/8倍速度 代号2:1/4倍速度 代号3:1/2倍速度 代号4:1倍速 代号5:2倍速 代号6:4倍速
  16.                         缺省为移动速度与原来相同,寻路完毕后恢复之前的速度
  17.   target_x, target_y       (必要参数) 目标位置的坐标
  18.   若无法到达,相应的全局变量(注意往下翻,会找到相关设置)会被设置为对应的flag
  19.     flag: 0                  无法到达
  20.           -1                 无需寻路(即起点与终点重合)
  21.           除了0 -1以外的数    起点到终点的最短路径的长度
  22.   *常用用法示例
  23.     玩家以2倍速自动寻路到(20, 15)
  24.       FYBX::FindPath.find_path(:target_x => 20, :target_y => 15, :speed => 5)
  25.     某事件自动寻路到(x,y) (事件->脚本)                  (↑注意这是 => ,不要写成 = )
  26.       FYBX::FindPath(:character => $game_map.events[@event_id], :target_x => x, :target_y => y)
  27. * 若有bug,可以联系qq 1441157749 (作者本人)
  28. * 版权声明:自由使用。使用和转载时请保留声明信息以及作者信息。修改请注明修改自本脚本。
  29. * 一定要注意往下翻,进行设置!
  30. * 常数优化已经做到丧心病狂的地步了!数组避免多次分配空间,大量地实行以空间换时间
  31. * 应该是跑得飞快的
  32. * 题外话:为什么不使用A* (启发式搜索)
  33.     因为种种玄学的原因,事实上,多数情况下A*的实际运行效率低于spfa,why?这是ruby代码
  34.     实现的问题。比如A*算法中,从open_list取出权值最小的元素,用Array做open_list,取
  35.     的时候open_list.min,但是这个min方法,会遍历整个数组,时间复杂度达到O(元素个数)。
  36.     以及,移除权值最小的元素时,很可能导致后面的元素都要向前移动,时间复杂度又高了。
  37.     意思就是说,这些操作在A*算法是常常需要频繁使用的,但是他们实际的工作效率十分低下,
  38.     大大拖慢了A*算法的运行速度!如果想要快速完成取最小元素以及删除元素,需要优先队列
  39.     和链表的数据结构,但是ruby标准库里面并没有。。。我也不想自己手写(懒人+1),因此
  40.     最后决定不使用A*。目前使用的是spfa算法(队列优化的Bellman ford算法),跑得飞快~
  41.     不过我csdn博客里面有我写的一份A*搜索的c++代码实现。感兴趣的童鞋可以到
  42.     [url=http://blog.csdn.net/u013632138/article/details/50672596]http://blog.csdn.net/u013632138/article/details/50672596[/url] 观赏一二
  43.     (博主就是我)
  44. * 扯淡:离开学就剩3天,作业还剩4本,然而我还在淡定地写代码。
  45. * SPFA 自动寻路系统v1.1更新 by 浮云半仙  2016.02.20
  46.   更新内容上面已经写过,增加speed选项,增加对地形标记的判断
  47. =end
  48.  
  49. module FYBX
  50.   #注意一下这里 寻路结果 全局变量编号。
  51.   FYBX_FINDPATH_NUM_FIND_PATH_RET = 3
  52.   #正在使用本脚本进行寻路的标识的全局开关编号。防止跟事件->设置移动路线的强制移动冲突。
  53.   FYBX_FINDPATH_NUM_FIND_PATH_SWITCH = 10
  54.   #鼠标点击游戏窗口内任一一点,停止寻路的开关。默认打开(=true),如果需要关闭
  55.   FYBX_FINDPATH_MOUSE_CLICK_STOP = true                        #请赋值false
  56.   #寻路时当作不可通行的图块的地形标记(数据库->图块,右边地形标记)编号,默认为7,
  57.   FYBX_FINDPATH_NOT_PASSABLE_TAG = 7
  58.  
  59.   #这个是内部使用的。防止被全局开关冲突。。。。
  60.   $fybx_findpath_switch = false
  61.   GetKeyState = Win32API.new("user32", "GetKeyState", "i", "i")
  62.  
  63.   class SPFAFindPath
  64.     private
  65.     attr_reader :map
  66.     attr_reader :path
  67.     attr_reader :changed_speed
  68.     attr_reader :move_speed
  69.  
  70.     DIR_TABLE = {:UP => 8, :DOWN => 2, :LEFT => 4, :RIGHT => 6}
  71.  
  72.     #这里的顺序不要乱改
  73.     DIR = [6, 4, 8, 2]
  74.     DX = [1, -1, 0, 0]
  75.     DY = [0, 0, -1, 1]
  76.     ANTI_DIR = [4, 6, 2, 8]
  77.     ANTI_DIR_BYDIR = [0, 0, 8, 0, 6, 0, 4, 0, 2]
  78.  
  79.  
  80.     INF = 0x23333333   #挑个喜庆的数当哨兵
  81.     public
  82.     attr_reader :target_x   #允许修改目标
  83.     attr_reader :target_y
  84.     attr_reader :start_x
  85.     attr_reader :start_y
  86.     attr_reader :character
  87.     attr_reader :is_finding_path
  88.     attr_reader :old_speed
  89.  
  90.     def method_missing(name)
  91.       raise ::NoMethodError, "出错啦! SPFAFindPath里面可没有叫 #{name.to_s} 的成员方法,请检查拼写"
  92.     end
  93.  
  94.     def initialize(arg)
  95.       @is_finding_path = false
  96.       @changed_speed = false
  97.       @map = arg[:map]?arg[:map] : $game_map
  98.       if arg[:speed]
  99.         @changed_speed = true
  100.         @move_speed = arg[:speed]
  101.       end
  102.       raise ::ArgumentError, "出错啦!map要求是一个Game_map的实例!" if !@map.is_a?(Game_Map)
  103.       reset_character(arg[:character]?arg[:character]:$game_player)
  104.       raise ::ArgumentError, "出错啦!SPFAFindPath里面的目标的坐标是必填的参数!请补全正确的参数!" if !arg[:target_x] || !arg[:target_y]
  105.       @target_x = arg[:target_x]
  106.       @target_y = arg[:target_y]
  107.       @path = Array.new(@map.width){Array.new(@map.height, nil)}
  108.       @move_speed = (@changed_speed?@move_speed: @character.move_speed)
  109.       @old_speed = @character.move_speed
  110.       #not public
  111.       @in_q = Array.new(@map.width) {Array.new(@map.height) {false}}
  112.       @cost = Array.new(@map.width) {Array.new(@map.height) {INF}}
  113.       @q = Array.new(@map.height * @map.width * 2, 0)
  114.       yield(self) if block_given?
  115.     end
  116.  
  117.     def reset_target(x, y)
  118.       @target_x = x
  119.       @target_y = y
  120.     end
  121.  
  122.     def reset_character(c)
  123.       raise ::ArgumentError, "出错啦,character参数要求是Game_CharacterBase的实例" if !c.is_a?(Game_Character)
  124.       @start_x = c.x
  125.       @start_y = c.y
  126.       @character = c
  127.     end
  128.  
  129.     def reset_speed(s)
  130.       @old_speed = @character.move_speed
  131.       @move_speed = s
  132.       @changed_speed = true
  133.     end
  134.  
  135.     #失败返回false
  136.     def find_path
  137.       @is_finding_path = true
  138.       return 0 if @target_x < 0 || @target_y < 0 || @target_x >= @map.width || @target_y >= @map.height
  139.       return -1 if @start_x == @target_x || @start_y == @target_y
  140.       @in_q.each_with_index {|e, i| @in_q[i].fill(false)}
  141.       @cost.each_with_index {|e, i| @cost[i].fill(INF)}
  142.       @path.each_with_index {|e, i| @path[i].fill(nil)}
  143.  
  144.       head = 0        #按照左闭右开的惯例
  145.       @q[0] = @start_x
  146.       @q[1] = @start_y
  147.       tail = 2
  148.       @in_q[@start_x][@start_y] = true
  149.       @cost[@start_x][@start_y] = 0
  150.       while head != tail
  151.         cx = @q[head]
  152.         cy = @q[head+1]
  153.         #puts "#{cx} #{cy}"
  154.         head += 2
  155.         for i in 0...4
  156.           dir = DIR[i]
  157.           nx = cx+DX[i]
  158.           ny = cy+DY[i]
  159.           next if !block_passable?(nx, ny, dir)
  160.           #puts "#{nx} #{ny} #{dir}"
  161.           if(@cost[nx][ny] > @cost[cx][cy] + 1)
  162.             @cost[nx][ny] = @cost[cx][cy] + 1
  163.             @path[nx][ny] = ANTI_DIR[i]
  164.  
  165.             if !@in_q[nx][ny]
  166.               @q[tail] = nx
  167.               @q[tail+1] = ny
  168.               tail += 2
  169.               @in_q[nx][ny] = true
  170.             end
  171.           end
  172.         end
  173.         @in_q[cx][cy] = false
  174.       end
  175.  
  176.       return 0 if @cost[@target_x][@target_y] == INF  
  177.  
  178.       note = Array.new(@cost[@target_x][@target_y], 0)
  179.       j = 0
  180.       x = @target_x
  181.       y = @target_y
  182.  
  183.       #puts cost[x][y]
  184.  
  185.       #print @path
  186.       while true
  187.         break if !@path[x][y]
  188.         note[j] = ANTI_DIR_BYDIR[@path[x][y]]
  189.         j += 1
  190.         break if x == @start_x && y == @start_y
  191.         case @path[x][y]
  192.           when 2 then
  193.             y += 1
  194.           when 4 then
  195.             x -= 1
  196.           when 6 then
  197.             x += 1
  198.           when 8 then
  199.             y -= 1
  200.         end
  201.       end
  202.  
  203.       $fybx_findpath_switch = true
  204.       $game_switches[FYBX_FINDPATH_NUM_FIND_PATH_SWITCH] = true
  205.       @old_speed = @character.move_speed
  206.       cmds = RPG::MoveRoute.new
  207.       #puts @changed_speed, @move_speed
  208.       cmds.list = (@changed_speed?[RPG::MoveCommand.new(29, [@move_speed])]: [])+note.reverse.map!{|e| RPG::MoveCommand.new(e/2)}+(@changed_speed?[RPG::MoveCommand.new(29, [@old_speed])]: [])
  209.       cmds.list << RPG::MoveCommand.new(0)
  210.       cmds.list << RPG::MoveCommand.new(45, "$fybx_findpath_switch = $game_switches[FYBX_FINDPATH_NUM_FIND_PATH_SWITCH] = false")
  211.       cmds.repeat = false
  212.       @character.force_move_route(cmds)
  213.  
  214.       @is_finding_path = false
  215.       @changed_speed = false
  216.       return @cost[@target_x][@target_y]
  217.     end
  218.  
  219.     private
  220.     def block_passable?(x, y, dir)
  221.       return false if x < 0 || y < 0 || x >= @map.width || y >= @map.height
  222.       if @character == $game_player
  223.         case $game_player.instance_eval{@vehicle_type}
  224.           when :walk then
  225.             return false if @map.terrain_tag(x,y) ==  FYBX_FINDPATH_NOT_PASSABLE_TAG
  226.             return @map.passable?(x, y, dir) || (@map.ladder?(x, y)&&(dir == 2 || dir == 8))
  227.           when :boat then
  228.             return @map.boat_passable?(x, y)
  229.           when :ship then
  230.             return @map.ship_passable?(x, y)
  231.           when :airship then
  232.             return true
  233.         end
  234.       else
  235.         return @map.passable?(x, y, dir)
  236.       end
  237.     end
  238.   end
  239.  
  240.  
  241.   module FindPath
  242.     FINDPATH_ALGORITHM = SPFAFindPath
  243.  
  244.     def self.find_path(arg)
  245.       @cache ||= {}
  246.       map = arg[:map]? arg[:map]:$game_map
  247.       if @cache[map] == nil
  248.         @cache[map] = FINDPATH_ALGORITHM.new(arg)
  249.       else
  250.         m = @cache[map]
  251.         m.reset_character(arg[:character]? arg[:character]: $game_player)
  252.         m.reset_target(arg[:target_x], arg[:target_y])
  253.         m.reset_speed(arg[:speed]) if arg[:speed]
  254.       end
  255.       return $game_variables[FYBX_FINDPATH_NUM_FIND_PATH_RET] = @cache[map].find_path
  256.     end
  257.  
  258.     def self.read_cache_bymap(map)
  259.       return nil if !@cache
  260.       return @cache[map] if @cache[map]
  261.       return @cache[map] = FINDPATH_ALGORITHM.new(:target_x => 0, :target_y => 0)
  262.     end
  263.  
  264.     def self.clear_cache
  265.       @cache = {}
  266.     end
  267.   end
  268.  
  269. end
  270.  
  271. class Scene_Map
  272.   alias :fybx_update_20160219 :update
  273.   #强制停止角色(player)的自动寻路
  274.   def fybx_force_player_stop_findpath
  275.     $game_player.instance_eval{@move_route_forcing = false}
  276.     #修正速度
  277.     x = FYBX::FindPath.read_cache_bymap($game_map)
  278.     $game_player.instance_eval{@move_speed = x.old_speed}
  279.     $fybx_findpath_switch = $game_switches[FYBX::FYBX_FINDPATH_NUM_FIND_PATH_SWITCH] = false
  280.   end
  281.   def update
  282.     fybx_update_20160219
  283.     if $fybx_findpath_switch && $game_player.move_route_forcing
  284.       if Input.trigger?(:DOWN) || Input.trigger?(:UP) || Input.trigger?(:LEFT) || Input.trigger?(:RIGHT)
  285.         fybx_force_player_stop_findpath
  286.       end
  287.       if FYBX::FYBX_FINDPATH_MOUSE_CLICK_STOP == true
  288.         if FYBX::GetKeyState.call(1) < 0
  289.           fybx_force_player_stop_findpath
  290.         end
  291.       end
  292.     end
  293.  
  294.   end
  295. end


脚本冲突风险预估:
几率有点大,因为改了Scene_Map的update,很多脚本都会改这里......泥萌懂点脚本的话可以自己协调一下(逃~ )      
高一狗要开学了Orz....{:2_286:}    

评分

参与人数 3星屑 +102 梦石 +1 收起 理由
zaiy2863 + 2 SPFA好评
晴兰 + 100 塞糖
taroxd + 1

查看全部评分

tan(pi/2)

Lv1.梦旅人

梦石
0
星屑
50
在线时间
49 小时
注册时间
2015-10-17
帖子
38
2
发表于 2016-3-11 23:36:40 | 只看该作者
先收藏一下  有时间下载下来试试~~~不知道对于武器强化脚本会不会冲突····
回复 支持 反对

使用道具 举报

Lv1.梦旅人

梦石
0
星屑
140
在线时间
49 小时
注册时间
2016-3-6
帖子
49
3
发表于 2016-4-9 23:29:03 | 只看该作者
%%%%%%%神犇
居然用到了SPFA,收下来研究一下,O(∩_∩)O谢谢分享【话说居然才高一被吓到了,果然是人外有人。。
回复 支持 反对

使用道具 举报

Lv5.捕梦者

梦石
0
星屑
24277
在线时间
5046 小时
注册时间
2016-3-8
帖子
1618
4
发表于 2020-8-13 04:08:35 | 只看该作者
本帖最后由 alexncf125 于 2020-8-17 21:43 编辑
以下,代码 (spfa四方向寻路系统v1.1) 使用时一定要仔细看前面的用法。

请问v1.1在那里???
脚本和范例都是v1.0的说...

啊这...原来是写到下方去了...最新的更新內容不是写在最上方...有点奇怪呢...
回复 支持 反对

使用道具 举报

Lv5.捕梦者

梦石
0
星屑
24277
在线时间
5046 小时
注册时间
2016-3-8
帖子
1618
5
发表于 2020-9-20 13:27:47 | 只看该作者
本帖最后由 alexncf125 于 2020-10-30 16:04 编辑

Pathfinder.rar (1.42 MB, 下载次数: 67)
附件內的工程有三个问题,
1.玩家的寻路路徑不是最短的
2.女NPC寻路时不会绕过路徑上的障礙物(如图中,要玩家向右移开一步, 女NPC才会移动)
3.女NPC寻路到关口时不知为何会停下
还望LZ有空时能修复完善
回复 支持 反对

使用道具 举报

Lv4.逐梦者

梦石
2
星屑
6639
在线时间
501 小时
注册时间
2018-3-23
帖子
533

R考场第七期银奖

6
发表于 2020-9-20 14:07:18 | 只看该作者
本帖最后由 MCCF 于 2020-9-20 14:10 编辑

无权图用A*不香吗

(此图出自NOI2018 D1T1)
感觉SPFA算法还是适合带权图吧,无权图A*因为有启发式函数所以会快很多
另外如果使用二分查找或者直接使用二叉堆,可以将单次加入时间复杂度降为O(logn),单次取出操作降为O(1),时间几乎可以忽略不计。
顺便安利一下我的TyPath
祝好。
回复 支持 反对

使用道具 举报

Lv3.寻梦者

梦石
0
星屑
1774
在线时间
950 小时
注册时间
2012-7-5
帖子
245
7
 楼主| 发表于 2020-9-22 16:38:06 | 只看该作者
本帖最后由 浮云半仙 于 2020-9-22 16:41 编辑
alexncf125 发表于 2020-9-20 13:27
好像有兩个BUG喔及后发现原来第一个和第二个的成因是一样的
1.从黑衣人那领了卷轴后, 馬上使用卷 ...


Wow,感谢fix bug
当初太过naive,这个脚本的架构和算法都不是很优,着实有很多不足,能被选用或者被帮助fix bug实在是荣幸。
---
我已经退坑RM好久了,不过最近搞了一个RMVA实时光线追踪渲染 https://rpg.blue/forum.php?mod=v ... d=482613&page=1 (强行打广告)

点评

大佬误会了, 我不是来fix bug的...  发表于 2020-9-22 17:58
为什么会被加上删除线...  发表于 2020-9-22 16:38
tan(pi/2)
回复 支持 反对

使用道具 举报

Lv3.寻梦者

梦石
0
星屑
1774
在线时间
950 小时
注册时间
2012-7-5
帖子
245
8
 楼主| 发表于 2020-9-22 16:40:28 | 只看该作者
MCCF 发表于 2020-9-20 14:07
无权图用A*不香吗

(此图出自NOI2018 D1T1)

NOI2017退役选手瑟瑟发抖
SPFA是信仰!!!
RM的地图可以使用几何距离作为启发, A*一波确实香
tan(pi/2)
回复 支持 反对

使用道具 举报

Lv3.寻梦者

梦石
0
星屑
1723
在线时间
395 小时
注册时间
2017-11-24
帖子
108
9
发表于 2021-11-28 21:57:09 | 只看该作者
66666666666666
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册会员

本版积分规则

拿上你的纸笔,建造一个属于你的梦想世界,加入吧。
 注册会员
找回密码

站长信箱:[email protected]|手机版|小黑屋|无图版|Project1游戏制作

GMT+8, 2024-4-25 20:01

Powered by Discuz! X3.1

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表