查看: 4100|回复: 4

[已经解决] RMXP用寻路脚本(使NPC接近主角)

[复制链接]

影

梦石
10
星屑
324
在线时间
1406 小时
注册时间
2010-12-8
回帖
2508

贵宾

发表于 2015-2-15 20:43:03 | 显示全部楼层 |阅读模式
悬赏25星屑已解决
本帖最后由 lianran123456 于 2015-2-16 09:45 编辑

众所周知默认的NPC寻路能力有待提高,经常被一点小障碍给卡住。
我需要一个能使NPC绕开障碍接近主角的寻路脚本。
只要不引起卡顿或者是可以感知的掉帧就行。
理想情况(移动设置直接点上接近就能用):https://rpg.blue/forum.php?mod=viewthread&tid=369852
是RMXP用的
@余烬之中
@RyanBern
@myownroc
三位曾经发布过寻路系统的大大有没有兴趣帮我一下,感激不尽。

最佳答案

查看完整内容

set_origin里的是起始点,set_target才是终点,应该写成:然后后面调用句$game_map.events[@event_id].move_dr(path[0]) 关于move_dr的定义:[hr] 随便做了个范例,参考下吧

虱子

梦石
0
星屑
121
在线时间
1782 小时
注册时间
2010-6-19
回帖
3515
发表于 2015-2-15 20:43:04 | 显示全部楼层
本帖最后由 Wind2010 于 2015-2-16 23:31 编辑
lianran123456 发表于 2015-2-16 17:01
可是我按照他所说的
    astr = AStar.new($game_map)
    astr.set_origin($game_player.x, $game_playe ...


set_origin里的是起始点,set_target才是终点,应该写成:
  1.     astr = AStar.new($game_map)
  2.     astr.set_origin($game_map.events[@event_id].x,$game_map.events[@event_id].y)
  3.     astr.set_target($game_player.x,$game_player.y)
  4.     path = astr.do_search
复制代码
然后后面调用句$game_map.events[@event_id].move_dr(path[0])
关于move_dr的定义:
  1. class Game_Character
  2.   def move_dr(dr)
  3.     case dr
  4.     when 1
  5.       move_lower_left
  6.     when 2
  7.       move_down
  8.     when 3
  9.       move_lower_right
  10.     when 4
  11.       move_left
  12.     when 6
  13.       move_right
  14.     when 7
  15.       move_upper_left
  16.     when 8
  17.       move_up
  18.     when 9
  19.       move_upper_right
  20.     else
  21.       move_toward_game_player
  22.     end
  23.   end
  24. end
复制代码


随便做了个范例,参考下吧

Project3.rar

188.99 KB, 下载次数: 124

点评

请问$game_map.events[@id].move_dr(path[0])是加在path = astr.do_search后面下一行吗?  发表于 2015-2-16 22:48
"关于move_dr的定义"这一段应该放在哪里?  发表于 2015-2-16 22:47

评分

参与人数 1星屑 +66 收起 理由
lianran123456 + 66 感激不尽!

查看全部评分


http://rpg.blue/thread-175056-1-2.html
PVZ型塔防物一个
http://rpg.blue/thread-155199-1-2.html
RMXP技术讨论区手动认可帖,得到答案请认可
回复

使用道具 举报

梦石
0
星屑
2826
在线时间
2634 小时
注册时间
2013-1-16
回帖
5453

贵宾

发表于 2015-2-16 11:19:56 | 显示全部楼层
接近?如果NPC和主角已经贴在一起了怎么办?

点评

就像RM默认那样啊= =继续贴着........(我的游戏里设置与事件接触后主角会随机移动一步远离事件...)  发表于 2015-2-16 11:45
(Created by @喵kano)


施工现场:hotege.github.io
回复

使用道具 举报

…あたしは天

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

开拓者贵宾

发表于 2015-2-16 15:56:12 | 显示全部楼层
本帖最后由 taroxd 于 2015-2-16 16:36 编辑

寻路这东西基本上都是通用的
发个我自己用的吧:https://rpg.blue/thread-228259-1-1.html

贴出来了
#==================================================================#
# AStar Core v1.01 by 禾西 on 2012.01.02
#------------------------------------------------------------------#
# (此算法基于 4 方向)
# public methods
# * new(Game_Map map)
#     生成關聯地圖的 A* 實例
# * set_origin(int ox, int oy)
#     設置起始點(超過地圖范圍會引起 Runtime Error )
# * set_target(int tx, int ty)
#     設置終止點(超過地圖范圍會引起 Runtime Error )
# * Array do_search(bool print_rst = false)
#     進行尋路,返回包含移動信息的數組
#
# private methods
# * int _f(x, y)
#     f 值算法
# * print_data
#     數據打印算法(此方法僅在 do_search(true) 時被使用)
#==================================================================#
#
#調用方法
#    astr = AStar.new($game_map)
#    astr.set_origin($game_player.x, $game_player.y)
#    astr.set_target(0, 0)
#    path = astr.do_search
#
#==================================================================#
class AStar
  Point = Struct.new(:x, :y)
  public
  # ============================================================== #
  # 初始化數據
  # ============================================================== #
  def initialize(map)
    @map_width  = map.width
    @map_height = map.height
    @g_data = Table.new(@map_width, @map_height)
    @f_data = Table.new(@map_width, @map_height)
    @p_data = Table.new(@map_width, @map_height)
    @ox = 0;@oy = 0
    @tx = 0;@ty = 0
    @openList = []
    @g = 0
    @search_done = false
  end
  # ============================================================== #
  # 設置 起始點
  # ============================================================== #
  def set_origin(ox, oy)
    @ox = ox;@oy = oy
    if is_Overmap(ox, oy)
      raise RuntimeError, "Origin location is overmap!"
    end
  end
  # ============================================================== #
  # 設置 目標點
  # ============================================================== #
  def set_target(tx, ty)
    @tx = tx;@ty = ty
    if is_Overmap(tx, ty)
      raise RuntimeError, "Target location is overmap!"
    end
  end
  # ============================================================== #
  # 開始尋路
  # ============================================================== #
  # 主邏輯
  def do_search(print_rst = false)
    x = @ox;y = @oy
    @g_data[x, y] = 2;@f_data[x, y] = 1
    @openList << [x, y]
    t0 = Time.now
    t = 0
    begin
      t += 1
      point = @openList.shift
      return [] if point == nil
      check_4dir(point[0], point[1]) # ->檢查 4 方向
    end until @search_done
    if @g_data[@tx,@ty] == 1
      @tx = point[0];@ty = point[1];
    end
    t1 = Time.now
    make_path # ->生成路徑
    if print_rst
      print "#{t1 - t0}, #{t}\n"
      print_data # ->打印數據
    end
    return @path
  end

  private
  # ============================================================== #
  # 檢查 4 方向
  def check_4dir(x, y)
    @g = @g_data[x, y] + 1
    mark_point(x, y - 1, 8) # ->檢查單點
    mark_point(x, y + 1, 2) # ->檢查單點
    mark_point(x - 1, y, 4) # ->檢查單點
    mark_point(x + 1, y, 6) # ->檢查單點
  end
  # ============================================================== #
  # 檢查單點
  def mark_point(x, y, dir)
    if is_Overmap(x, y) # ->檢查地圖是否超界
      return
    end
    if @g_data[x, y] > 1
      return
    end
    if check_passibility(x, y, dir) # ->檢查通行度
      f = _f(x, y)
      @g_data[x, y] = @g
      @f_data[x, y] = f
      point = @openList[0]
      if point.nil?
        @openList.push [x, y]
      elsif (f <= @f_data[point[0], point[1]])
        @openList.unshift [x, y]
      else
        @openList.push [x, y]
      end
    else
      @g_data[x, y] = 1
      @f_data[x, y] = _f(x, y)
    end
    if x == @tx && y == @ty
      @search_done = true
    end
  end
  # ============================================================== #
  # 生成路徑
  def make_path
    x = @tx;y = @ty
    @path = []
    while !(x == @ox && y == @oy) #TODO:有過多障礙時死轉……
      @g = @g_data[x, y]
      @best_f = 0
      dir = 0
      dir = make_step(x, y - 1, 2)||dir # ->生成單步
      dir = make_step(x, y + 1, 8)||dir # ->生成單步
      dir = make_step(x - 1, y, 6)||dir # ->生成單步
      dir = make_step(x + 1, y, 4)||dir # ->生成單步
      @path.unshift(dir)
      case dir
      when 2 then y -= 1;
      when 8 then y += 1;
      when 6 then x -= 1;
      when 4 then x += 1;
      end
      @p_data[x, y] = 1
    end
  end
  # ============================================================== #
  # 生成單步
  def make_step(x, y, dir)
    if @g_data[x, y].nil? || @p_data[x, y] == 1
      return nil
    end
    if (@g - @g_data[x, y]) == 1 || @g == 1
      f = @f_data[x, y]
      if f > 0 && (@best_f == 0 || f < @best_f)
        @best_f = f
        return dir
      end
    end
    return nil
  end
  # ============================================================== #
  # 檢查地圖通行度
  def check_passibility(x, y, dir)
    case dir
    when 2 then y -= 1;
    when 8 then y += 1;
    when 4 then x += 1;
    when 6 then x -= 1;
    end
    return $game_player.passable?(x, y, dir)
  end
  # ============================================================== #
  # 檢查地圖是否超界
  def is_Overmap(x, y)
    return (x|y|(@map_width - x - 1)|(@map_height - y - 1)) < 0
  end
  # ============================================================== #
  # f 值算法
  def _f(x, y)
    return ((x - @tx).abs + (y - @ty).abs) + @g
  end
  # ============================================================== #
  # 打印數據
  def print_data
    strf = ""
    strg = ""
    for y in 0...@f_data.ysize
      for x in 0...@f_data.xsize
        if @f_data[x, y] == 0
          strf += "  "
        else
          strf += sprintf("%02d", @f_data[x, y])
        end
        strf += ","
        if @g_data[x, y] < 2
          strg += sprintf("%02d", @g_data[x, y])#"  "
        else
          strg += sprintf("%02d", @g_data[x, y])
        end
        strg += ","
      end
      strf += "\n"
      strg += "\n"
    end
    print "f:\n"
    print strf + "\n"
    print "g:\n"
    print strg + "\n"
    strp = ""
    for y in 0...@p_data.ysize
      for x in 0...@p_data.xsize
        if @p_data[x, y] == 0
          strp += "  "
        else
          strp += sprintf("%02d", @p_data[x, y])
        end
        strp += ","
      end
      strp += "\n"
    end

    print "p:\n"
    print strp
    print @path
  end
end
  

点评

不,他这个是完全通用的,移动啥都行。用法写在注释里了  发表于 2015-2-16 16:52
用不了- -什么反应也没有.....另外他的这个好像是移动主角的吧。。  发表于 2015-2-16 16:45
那个复制不了......你能不能帮忙贴一下= =  发表于 2015-2-16 16:20
一直都有。。。  发表于 2015-2-16 16:18
我靠(╯‵□′)╯︵┻━┻...................论坛什么时候插脚本有滚动条了.................................................我一直以为那个不全....  发表于 2015-2-16 16:15
回复

使用道具 举报

影

梦石
10
星屑
324
在线时间
1406 小时
注册时间
2010-12-8
回帖
2508

贵宾

 楼主| 发表于 2015-2-16 17:01:41 | 显示全部楼层
taroxd 发表于 2015-2-16 15:56
寻路这东西基本上都是通用的
发个我自己用的吧:https://rpg.blue/thread-228259-1-1.html

可是我按照他所说的
    astr = AStar.new($game_map)
    astr.set_origin($game_player.x, $game_player.y)
    astr.set_target(0, 0)
    path = astr.do_search
什么都没有发生

点评

①move_dr随便开个新脚本插入就行。②:是跟在后面的  发表于 2015-2-16 22:54
(╯‵□′)╯︵┻━┻  发表于 2015-2-16 19:32
如果我知道,早就告诉你了= = XP 脚本没试过  发表于 2015-2-16 18:37
脚本渣表示看不懂QAQ直接告诉我path哪一行该怎么用吧  发表于 2015-2-16 18:31
path 是路线的数组,元素为 2,4,6,8.照着方向走就是了  发表于 2015-2-16 17:09
回复

使用道具 举报

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

本版积分规则

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

在本版发帖返回顶部