查看: 9420|回复: 8

[原创发布] 【原创】事件寻路脚本(基于广度优先搜索和RGSS1)

[复制链接]
梦石
0
星屑
2826
在线时间
2634 小时
注册时间
2013-1-16
回帖
5453

贵宾

发表于 2014-8-4 21:20:50 | 显示全部楼层 |阅读模式

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

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

×
本帖最后由 myownroc 于 2014-8-5 17:01 编辑

[fold=旧版本]用法:在事件页中使用脚本,输入以下内容。
[pre lang="ruby" line="1"]f = BFS.new
f.find_path(character, tx, ty)[/pre]
说明:
如果想要移动主角,character为$game_player;若想移动事件,character为$game_map.events[n] (注:n为相应事件的ID)
注意:该脚本适用于较小的地图(推荐不超过50*50)且工程的系统应基于默认的RGSS。
[pre lang="ruby" line="1" file="BFS"]# This algorithm is based on Breadth-First-Search.
# Due to BFS algorithm, we suggest your map's area be below 50*50.
# In a word, this algorithm is suitable for small map.
# If you want to use this method, please write following codes
# in the events pages:
#
# f = BFS.new
# f.find_way(character, target_x, target_y)
#
# If you want to move your own player, 'character' would be '$game_player'
# If you want to move a event(ID :n), 'character' would be '$game_map.events[n]'
# Once you finish what we had said above, the selected character would move to
# the target point.
class BFS
  def find_path(character, target_x, target_y)
    origin_x, origin_y = character.x, character.y
    # exit when it's out of map
    return if ((target_x > $game_map.width - 1) ||
    (target_y > $game_map.height - 1))
    # exit when it is not accessable
    return if (!$game_map.passable?(target_x, target_y, 0, character))
    # exit when target is where character is.
    return if (target_x == character.x and target_y == character.y)
    # find the path
    openlist = []
    closelist = []
    father = []
    for i in 0...$game_map.width
      father << []
      for j in 0...$game_map.height
          father[-1] << nil
      end
    end
    path = []
    found = false
    openlist << [character.x, character.y]
    while !openlist.empty? and !found
      openlist.each{|e|
        # delete & move
        openlist.delete(e)
        closelist << e
        # down
        if $game_map.passable?(e[0], e[1] + 1, 2, character) &&
          !closelist.include?([e[0], e[1] + 1]) &&
          !openlist.include?([e[0], e[1] + 1])
          openlist << [e[0], e[1] + 1] if !openlist.include?([e[0], e[1] + 1])
          father[e[0]][e[1] + 1] = [e[0], e[1]] if !closelist.include?([e[0], e[1] + 1])
        end
        # left
        if $game_map.passable?(e[0] - 1, e[1], 4, character) &&
          !closelist.include?([e[0] - 1, e[1]]) &&
          !openlist.include?([e[0] - 1, e[1]])
          openlist << [e[0] - 1, e[1]] if !openlist.include?([e[0] - 1, e[1]])
          father[e[0] - 1][e[1]] = [e[0], e[1]] if !closelist.include?([e[0] - 1, e[1]])
        end
        # up
        if $game_map.passable?(e[0], e[1] - 1, 8, character) &&
          !closelist.include?([e[0], e[1] - 1]) &&
          !openlist.include?([e[0], e[1] - 1])
          openlist << [e[0], e[1] - 1] if !openlist.include?([e[0], e[1] - 1])
          father[e[0]][e[1] - 1] = [e[0], e[1]] if !closelist.include?([e[0], e[1] - 1])
        end
        # right
        if $game_map.passable?(e[0] + 1, e[1], 6, character) &&
          !closelist.include?([e[0] + 1, e[1]]) &&
          !openlist.include?([e[0] + 1, e[1]])
          openlist << [e[0] + 1, e[1]] if !openlist.include?([e[0] + 1, e[1]])
          father[e[0] + 1][e[1]] = [e[0], e[1]] if !closelist.include?([e[0] + 1, e[1]])
        end
        # get target?
        if openlist.include?([target_x, target_y])
          found = true
          break
        end
      }
    end
    # exit when we can't find a path
    return if !found
    # create move command & carry out
    if found
      x, y = target_x, target_y
      while [x, y] != [origin_x, origin_y]
        dx, dy = father[x][y][0] - x, father[x][y][1] - y
        step = 2 if dy == -1
        step = 4 if dx == 1
        step = 8 if dy == 1
        step = 6 if dx == -1
        path << step
        x, y = father[x][y][0], father[x][y][1]
      end
      path.reverse!
    end
    list = []
    path.each{|e|
      code = 1 if e == 2
      code = 2 if e == 4
      code = 4 if e == 8
      code = 3 if e == 6
      list << RPG::MoveCommand.new(code)
    }
    list << RPG::MoveCommand.new(0)
    skippable = false
    repeat = false
    commands = RPG::MoveRoute.new
    commands.skippable = skippable
    commands.repeat = repeat
    commands.list = list
    # move
    if character == nil
      # continue
      return true
    end
    # force_move
    character.force_move_route(commands)
    # continue
    return true
  end
end[/pre]
[pre lang="ruby" line="1" file="BFS"]class BFS
  def find_path(character, tx, ty)
    ox, oy = character.x, character.y
    return if (tx >$game_map.width - 1) || (ty > $game_map.height - 1)
    return if !($game_map.passable?(tx, ty, 0, character))
    return if (tx == ox) && (ty == oy)
    checked = []
    father = []
    for i in 0...$game_map.width
      checked << []
      father << []
      for j in 0...$game_map.height
        checked[-1] << 0
        father[-1] << nil
      end
    end
    path = []
    found = false
    checked[ox][oy] = 1
    empty = false
    loop do
      empty = true
      for i in 0...$game_map.width
        for j in 0...$game_map.height
          if checked[j] == 1
            checked[j] = 2
            if $game_map.passable?(i, j + 1, 2, character) &&
              checked[j + 1] == 0
              checked[j + 1] = 1
              father[j + 1] = [i, j]
            end
            if $game_map.passable?(i - 1, j, 4, character) &&
              checked[i - 1][j] == 0
              checked[i - 1][j] = 1
              father[i - 1][j] = [i, j]
            end
            if $game_map.passable?(i, j - 1, 8, character) &&
              checked[j - 1] == 0
              checked[j - 1] = 1
              father[j - 1] = [i, j]
            end
            if $game_map.passable?(i + 1, j, 6, character) &&
              checked[i + 1][j] == 0
              checked[i + 1][j] = 1
              father[i + 1][j] = [i, j]
            end
          end
          if checked[tx][ty] == 1
            found = true
            break
          end
        end
      end
      for i in 0...$game_map.width
        empty = false if checked.include?(1)
      end
      if found || empty
        break
      end
    end
    return if !found
    if found
      x, y = tx, ty
      while [x, y] != [ox, oy]
        dx, dy = father[x][y][0] - x, father[x][y][1] - y
        step = 2 if dy == -1
        step = 4 if dx == 1
        step = 8 if dy == 1
        step = 6 if dx == -1
        path << step
        x, y = father[x][y][0], father[x][y][1]
      end
      path.reverse!
    end
    list = []
    path.each{|e|
      code = 1 if e == 2
      code = 2 if e == 4
      code = 4 if e == 8
      code = 3 if e == 6
      list << RPG::MoveCommand.new(code)
    }
    list << RPG::MoveCommand.new(0)
    skippable = false
    repeat = false
    commands = RPG::MoveRoute.new
    commands.skippable = skippable
    commands.repeat = repeat
    commands.list = list
    if character == nil
      return true
    end
    character.force_move_route(commands)
    return true
  end
end[/pre][/fold]
以下为最新版本
用法:在事件页中使用脚本,输入以下内容。
[pre lang="ruby" line="1"]BFS.find_path(character, target_x, target_y)[/pre]
说明:
如果想要移动主角,character为$game_player;若想移动事件,character为$game_map.events[n] (注:n为相应事件的ID)
注意:该脚本适用于较小的地图(推荐不超过100*100)且工程的系统应基于默认的RGSS。
[pre lang="ruby" line="1" file="BFS"]module BFS
  def self.find_path(character, tx, ty)
    ox, oy = character.x, character.y
    return if (tx > $game_map.width - 1) || (ty > $game_map.height - 1)
    return if !($game_map.passable?(tx, ty, 0, character))
    return if (tx == ox) && (ty == oy)
    checked = []
    father = []
    for i in 0...$game_map.width
      checked << []
      father << []
      for j in 0...$game_map.height
        checked[-1] << 0
        father[-1] << nil
      end
    end
    path = []
    found = false
    checked[ox][oy] = 1
    empty = false
    loop do
      empty = true
      for i in 0...$game_map.width
        for j in 0...$game_map.height
          if checked[j] == 1
            checked[j] = 2
            if $game_map.passable?(i, j + 1, 2, character) &&
              checked[j + 1] == 0
              checked[j + 1] = 1
              father[j + 1] = [i, j]
            end
            if $game_map.passable?(i - 1, j, 4, character) &&
              checked[i - 1][j] == 0
              checked[i - 1][j] = 1
              father[i - 1][j] = [i, j]
            end
            if $game_map.passable?(i, j - 1, 8, character) &&
              checked[j - 1] == 0
              checked[j - 1] = 1
              father[j - 1] = [i, j]
            end
            if $game_map.passable?(i + 1, j, 6, character) &&
              checked[i + 1][j] == 0
              checked[i + 1][j] = 1
              father[i + 1][j] = [i, j]
            end
          end
          if checked[tx][ty] == 1
            found = true
            break
          end
        end
      end
      for i in 0...$game_map.width
        empty = false if checked.include?(1)
      end
      if found || empty
        break
      end
    end
    return if !found
    if found
      x, y = tx, ty
      while [x, y] != [ox, oy]
        dx, dy = father[x][y][0] - x, father[x][y][1] - y
        step = 2 if dy == -1
        step = 4 if dx == 1
        step = 8 if dy == 1
        step = 6 if dx == -1
        path << step
        x, y = father[x][y][0], father[x][y][1]
      end
      path.reverse!
    end
    list = []
    path.each{|e|
      code = 1 if e == 2
      code = 2 if e == 4
      code = 4 if e == 8
      code = 3 if e == 6
      list << RPG::MoveCommand.new(code)
    }
    list << RPG::MoveCommand.new(0)
    skippable = false
    repeat = false
    commands = RPG::MoveRoute.new
    commands.skippable = skippable
    commands.repeat = repeat
    commands.list = list
    if character == nil
      return true
    end
    character.force_move_route(commands)
    return true
  end
end[/pre]
如果还不能理解以上所述,可以下载附件进行考察。 FindPath.rar (192.79 KB, 下载次数: 389)
冲突评估(欢迎讨论):
双远景/三远景系统(预测,没有实验证据)
鼠标系统(预测,没有实验证据)
八方行走系统(预测,没有实验证据)
更新记录:
当前新版本比之前一个版本修正了绕路的bug。
提高了算法的效率。

点评

我弄错了没有问题。。。好像我改到哪里了。  发表于 2014-8-15 19:42
↓如果能提供工程就更感谢了  发表于 2014-8-15 16:53
↓如果能提供工程就更感谢了  发表于 2014-8-15 16:53
↓能否截图说明?“墙上”是什么样的情况?  发表于 2014-8-15 16:53
有个问题能否解决:自己在墙上陷入死循环  发表于 2014-8-15 15:58

评分

参与人数 1星屑 +750 梦石 +1 收起 理由
怪蜀黍 + 750 + 1 发布奖励

查看全部评分

(Created by @喵kano)


施工现场:hotege.github.io
梦石
0
星屑
167
在线时间
673 小时
注册时间
2013-6-15
回帖
663
发表于 2014-8-5 14:26:17 | 显示全部楼层
还不错~帮了大忙了~

点评

内容更新,敬请关注。  发表于 2014-8-5 15:34

评分

参与人数 1星屑 +20 收起 理由
myownroc + 20 感谢支持

查看全部评分

回复

使用道具 举报

…あたしは天

梦石
0
星屑
2308
在线时间
4033 小时
注册时间
2010-10-4
回帖
10548

开拓者贵宾

发表于 2014-8-5 14:44:40 | 显示全部楼层
不直接整合进事件的移动路线-接近吗?

其实我挺喜欢禾西的A*脚本的,只给一个寻路的算法核心,整合这种东西交给用户。

点评

翻禾西的空间……  发表于 2014-8-14 11:05
我也忘了原版是什么样子的了= =  发表于 2014-8-14 11:05
好吧……  发表于 2014-8-14 11:04
反正我修改过,用的 Game_Character#passable? 判断通行度。  发表于 2014-8-14 11:02
我测试的时候就是有bug…还是说XP、VA不一样?  发表于 2014-8-14 09:49

评分

参与人数 1星屑 +20 收起 理由
myownroc + 20 欢迎捧场

查看全部评分

回复

使用道具 举报

梦石
0
星屑
49
在线时间
200 小时
注册时间
2014-7-17
回帖
372
发表于 2014-8-12 19:24:03 | 显示全部楼层
哇哇,广度优先算法出来啦!
我准备写一个深度优先算法~

点评

深度优先在数据结构上适合树,而不适合图。RM的地图一般情况下都是图,很少是树。  发表于 2014-9-15 01:42

知其然,而不欲知其所以然,耻也!
回复

使用道具 举报

梦石
0
星屑
752
在线时间
800 小时
注册时间
2011-10-20
回帖
2257

开拓者

发表于 2014-8-12 22:29:22 | 显示全部楼层
效率能说一下提高的量吗(貌似0.01s一张小地图)
,记得原来有个是障碍越多效率越快,不知LZ您的是不是这样(不清楚算法)

点评

只是对大地图的对角线进行了效率测试,提高只是从直观上感觉的,并没有确切的值  发表于 2014-8-12 22:32
欢迎点此进入我的egames.wink.ws,有RMQQ堂等

[url=http://rpg.blue/thread-317273-1-1.html]短篇八-赶选

http://yun.baidu.com/share/link?shareid=2158225779&uk=169642147&third=0


历险?ARPG?赢回你的疆域?新的战斗模式?…………点击这里:[宋乱贼狂 for QQ堂]
http://rpg.blue/group-368-1.html
programing ....?
[url=http://rpg.blue/thrd-234658-1-1.html]
回复

使用道具 举报

梦石
0
星屑
752
在线时间
800 小时
注册时间
2011-10-20
回帖
2257

开拓者

发表于 2014-8-15 21:36:57 | 显示全部楼层
不太懂算法。。理解错误请见谅:
刚刚想了想这个算法,能否考虑
把不可通行标记为3或其他,交换
PASSABLE和标记==0的位置,在判断过的障碍那里就不必2次PASSABLE判断了
最主要,交换之后对于之前搜索过已经变为2标记的位置,将减少一次passable判断的多余
虽然到后面标记为2的已经减少了,但加起来的多余passble就很多了

另外初始化标记为0非要用循环吗- -b

点评

呃…这个问题我考虑过…不知道有没有彻底解决  发表于 2014-8-16 11:50
好像寻路都有这个瑕疵没注意到  发表于 2014-8-16 10:19
有一个小bug- -设置通行四方向的图块寻路会失败,原因应该是判断通行里没有判断从:移动者的元件无法来到指定方向的情况下,不过应该无大碍  发表于 2014-8-16 10:18
感觉不错,有空我试试看。循环是为了保证每一个点都被标记为0,否则是nil  发表于 2014-8-15 22:39

评分

参与人数 1星屑 +200 收起 理由
myownroc + 200 塞糖

查看全部评分

欢迎点此进入我的egames.wink.ws,有RMQQ堂等

[url=http://rpg.blue/thread-317273-1-1.html]短篇八-赶选

http://yun.baidu.com/share/link?shareid=2158225779&uk=169642147&third=0


历险?ARPG?赢回你的疆域?新的战斗模式?…………点击这里:[宋乱贼狂 for QQ堂]
http://rpg.blue/group-368-1.html
programing ....?
[url=http://rpg.blue/thrd-234658-1-1.html]
回复

使用道具 举报

梦石
0
星屑
1556
在线时间
626 小时
注册时间
2010-8-5
回帖
317
发表于 2014-9-7 12:07:06 | 显示全部楼层
LZ继续加油,争取把八方向的写好
回复

使用道具 举报

匿·蹤

梦石
0
星屑
65
在线时间
99 小时
注册时间
2006-3-19
回帖
404
发表于 2014-11-10 09:28:00 | 显示全部楼层
寻路算法也,楼主好牛!
记得原来有个whbm出的寻路算法,用了下也感觉好牛X。
卐忍 → 解忍 → 元忍 → 隐忍 → 卍忍 → 匿踪(最终)
完全退步到了卐忍阶段
回复

使用道具 举报

梦石
0
星屑
12161
在线时间
4435 小时
注册时间
2014-4-11
回帖
5616

开拓者

发表于 2015-12-14 01:03:45 | 显示全部楼层
用条件分歧来设置多个目标点轮流走动时。
每当经过目标点,都会变卡

点评

好吧我在做范例的时候并没有这样的情况  发表于 2015-12-14 20:29
应该不是地图大吧。在范例里面设置也是这个情况。我用其他的事件自寻路脚本也是有这个情况,走的时候都不卡,就是在目标点转折时卡。  发表于 2015-12-14 12:40
地图有点大?这个算法效率不是很高。另外Ruby的效率本身就不高。  发表于 2015-12-14 12:36
回复

使用道具 举报

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

本版积分规则

Powered by Discuz! X5.0 © 2001-2026 Discuz! Team.

在本版发帖返回顶部