Project1

标题: [rgss]XP、VX、VXAce通用的四方向 A* 尋路 [打印本页]

作者: 禾西    时间: 2012-4-11 18:06
标题: [rgss]XP、VX、VXAce通用的四方向 A* 尋路
本帖最后由 禾西 于 2013-1-26 20:27 编辑

調用方法
    astr = AStar.new($game_map)
    astr.set_origin($game_player.x, $game_player.y)
    astr.set_target(0, 0)
    path = astr.do_search

path 攜帶的就是方向信息,看小鍵盤自己翻譯。

RUBY 代码复制
  1. #==================================================================#
  2. # AStar Core v1.01 by 禾西 on 2012.01.02
  3. #------------------------------------------------------------------#
  4. # (此算法基于 4 方向)
  5. # public methods
  6. # * new(Game_Map map)
  7. #     生成關聯地圖的 A* 實例
  8. # * set_origin(int ox, int oy)
  9. #     設置起始點(超過地圖范圍會引起 Runtime Error )
  10. # * set_target(int tx, int ty)
  11. #     設置終止點(超過地圖范圍會引起 Runtime Error )
  12. # * Array do_search(bool print_rst = false)
  13. #     進行尋路,返回包含移動信息的數組
  14. #
  15. # private methods
  16. # * int _f(x, y)
  17. #     f 值算法
  18. # * print_data
  19. #     數據打印算法(此方法僅在 do_search(true) 時被使用)
  20. #==================================================================#
  21. #
  22. #調用方法
  23. #    astr = AStar.new($game_map)
  24. #    astr.set_origin($game_player.x, $game_player.y)
  25. #    astr.set_target(0, 0)
  26. #    path = astr.do_search
  27. #
  28. #==================================================================#
  29. class AStar
  30.   Point = Struct.new(:x, :y)
  31.   public
  32.   # ============================================================== #
  33.   # 初始化數據
  34.   # ============================================================== #
  35.   def initialize(map)
  36.     @map_width  = map.width
  37.     @map_height = map.height
  38.     @g_data = Table.new(@map_width, @map_height)
  39.     @f_data = Table.new(@map_width, @map_height)
  40.     @p_data = Table.new(@map_width, @map_height)
  41.     @ox = 0;@oy = 0
  42.     @tx = 0;@ty = 0
  43.     @openList = []
  44.     @g = 0
  45.     @search_done = false
  46.   end
  47.   # ============================================================== #
  48.   # 設置 起始點
  49.   # ============================================================== #
  50.   def set_origin(ox, oy)
  51.     @ox = ox;@oy = oy
  52.     if is_Overmap(ox, oy)
  53.       raise RuntimeError, "Origin location is overmap!"
  54.     end
  55.   end
  56.   # ============================================================== #
  57.   # 設置 目標點
  58.   # ============================================================== #
  59.   def set_target(tx, ty)
  60.     @tx = tx;@ty = ty
  61.     if is_Overmap(tx, ty)
  62.       raise RuntimeError, "Target location is overmap!"
  63.     end
  64.   end
  65.   # ============================================================== #
  66.   # 開始尋路
  67.   # ============================================================== #
  68.   # 主邏輯
  69.   def do_search(print_rst = false)
  70.     x = @ox;y = @oy
  71.     @g_data[x, y] = 2;@f_data[x, y] = 1
  72.     @openList << [x, y]
  73.     t0 = Time.now
  74.     t = 0
  75.     begin
  76.       t += 1
  77.       point = @openList.shift
  78.       return [] if point == nil
  79.       check_4dir(point[0], point[1]) # ->檢查 4 方向
  80.     end until @search_done
  81.     if @g_data[@tx,@ty] == 1
  82.       @tx = point[0];@ty = point[1];
  83.     end
  84.     t1 = Time.now
  85.     make_path # ->生成路徑
  86.     if print_rst
  87.       print "#{t1 - t0}, #{t}\n"
  88.       print_data # ->打印數據
  89.     end
  90.     return @path
  91.   end
  92.  
  93.   private
  94.   # ============================================================== #
  95.   # 檢查 4 方向
  96.   def check_4dir(x, y)
  97.     @g = @g_data[x, y] + 1
  98.     mark_point(x, y - 1, 8) # ->檢查單點
  99.     mark_point(x, y + 1, 2) # ->檢查單點
  100.     mark_point(x - 1, y, 4) # ->檢查單點
  101.     mark_point(x + 1, y, 6) # ->檢查單點
  102.   end
  103.   # ============================================================== #
  104.   # 檢查單點
  105.   def mark_point(x, y, dir)
  106.     if is_Overmap(x, y) # ->檢查地圖是否超界
  107.       return
  108.     end
  109.     if @g_data[x, y] > 1
  110.       return
  111.     end
  112.     if check_passibility(x, y, dir) # ->檢查通行度
  113.       f = _f(x, y)
  114.       @g_data[x, y] = @g
  115.       @f_data[x, y] = f
  116.       point = @openList[0]
  117.       if point.nil?
  118.         @openList.push [x, y]
  119.       elsif (f <= @f_data[point[0], point[1]])
  120.         @openList.unshift [x, y]
  121.       else
  122.         @openList.push [x, y]
  123.       end
  124.     else
  125.       @g_data[x, y] = 1
  126.       @f_data[x, y] = _f(x, y)
  127.     end
  128.     if x == @tx && y == @ty
  129.       @search_done = true
  130.     end
  131.   end
  132.   # ============================================================== #
  133.   # 生成路徑
  134.   def make_path
  135.     x = @tx;y = @ty
  136.     @path = []
  137.     while !(x == @ox && y == @oy) #TODO:有過多障礙時死轉……
  138.       @g = @g_data[x, y]
  139.       @best_f = 0
  140.       dir = 0
  141.       dir = make_step(x, y - 1, 2)||dir # ->生成單步
  142.       dir = make_step(x, y + 1, 8)||dir # ->生成單步
  143.       dir = make_step(x - 1, y, 6)||dir # ->生成單步
  144.       dir = make_step(x + 1, y, 4)||dir # ->生成單步
  145.       @path.unshift(dir)
  146.       case dir
  147.       when 2 then y -= 1;
  148.       when 8 then y += 1;
  149.       when 6 then x -= 1;
  150.       when 4 then x += 1;
  151.       end
  152.       @p_data[x, y] = 1
  153.     end
  154.   end
  155.   # ============================================================== #
  156.   # 生成單步
  157.   def make_step(x, y, dir)
  158.     if @g_data[x, y].nil? || @p_data[x, y] == 1
  159.       return nil
  160.     end
  161.     if (@g - @g_data[x, y]) == 1 || @g == 1
  162.       f = @f_data[x, y]
  163.       if f > 0 && (@best_f == 0 || f < @best_f)
  164.         @best_f = f
  165.         return dir
  166.       end
  167.     end
  168.     return nil
  169.   end
  170.   # ============================================================== #
  171.   # 檢查地圖通行度
  172.   def check_passibility(x, y, dir)
  173.     case dir
  174.     when 2 then y -= 1;
  175.     when 8 then y += 1;
  176.     when 4 then x += 1;
  177.     when 6 then x -= 1;
  178.     end
  179.     return $game_player.passable?(x, y, dir)
  180.   end
  181.   # ============================================================== #
  182.   # 檢查地圖是否超界
  183.   def is_Overmap(x, y)
  184.     return (x|y|(@map_width - x - 1)|(@map_height - y - 1)) < 0
  185.   end
  186.   # ============================================================== #
  187.   # f 值算法
  188.   def _f(x, y)
  189.     return ((x - @tx).abs + (y - @ty).abs) + @g
  190.   end
  191.   # ============================================================== #
  192.   # 打印數據
  193.   def print_data
  194.     strf = ""
  195.     strg = ""
  196.     for y in [email]0...@f_data.ysize[/email]
  197.       for x in [email]0...@f_data.xsize[/email]
  198.         if @f_data[x, y] == 0
  199.           strf += "  "
  200.         else
  201.           strf += sprintf("%02d", @f_data[x, y])
  202.         end
  203.         strf += ","
  204.         if @g_data[x, y] < 2
  205.           strg += sprintf("%02d", @g_data[x, y])#"  "
  206.         else
  207.           strg += sprintf("%02d", @g_data[x, y])
  208.         end
  209.         strg += ","
  210.       end
  211.       strf += "\n"
  212.       strg += "\n"
  213.     end
  214.     print "f:\n"
  215.     print strf + "\n"
  216.     print "g:\n"
  217.     print strg + "\n"
  218.     strp = ""
  219.     for y in [email]0...@p_data.ysize[/email]
  220.       for x in [email]0...@p_data.xsize[/email]
  221.         if @p_data[x, y] == 0
  222.           strp += "  "
  223.         else
  224.           strp += sprintf("%02d", @p_data[x, y])
  225.         end
  226.         strp += ","
  227.       end
  228.       strp += "\n"
  229.     end
  230.  
  231.     print "p:\n"
  232.     print strp
  233.     print @path
  234.   end
  235. end





欢迎光临 Project1 (https://rpg.blue/) Powered by Discuz! X3.1