vxで経路探索スクリプト

ダイトウ
記事: 12
登録日時: 2016年8月19日(金) 21:15

vxで経路探索スクリプト

投稿記事by ダイトウ » 2018年4月02日(月) 18:44

質問トピックでも書かせて頂いていますが、
Vxで経路探索スクリプトを作っていらっしゃる方はおられるでしょうか?
Vxaceではどうも動作が重たいようなので
止めておいた方が良いなら諦めます。。

faida
記事: 203
登録日時: 2015年12月17日(木) 16:44

Re: vxで経路探索スクリプト

投稿記事by faida » 2018年4月06日(金) 21:53

以前作ったこれから必要な機能だけ引き出してきました。
VXBeteranだとどうも重たいなぁ……何か原因があるんだろうか?

コード: 全て選択

=begin
◆概要
経路探索のような動きができます。

◆機能
・移動ルートの設定のスクリプトで「move_toward_target($game_player)」とやると
プレイヤーに近づくかもしれません。
・移動ルートの設定のスクリプトで「move_toward_target($game_map.events[n])」
(nはID)とやると、そのイベントに近づくかもしれません。
・地点指定は「move_toward_point([x, y])」

◆仕様
・VXBeteranで作成したのでVXで動くかが分からない。
・必ず繰り返してください。

◆使用上の注意
・特になし。

=end

class Game_Map
  #--------------------------------------------------------------------------
  # ○ 地点からの距離計算
  #--------------------------------------------------------------------------
  def distance_points(point1, point2)
    sx = point1[0] - point2[0]
    sy = point1[1] - point2[1]
    if $game_map.loop_horizontal?         # 横にループしているとき
      if sx.abs > $game_map.width / 2     # 絶対値がマップの半分より大きい?
        sx -= $game_map.width             # マップの幅を引く
      end
    end
    if $game_map.loop_vertical?           # 縦にループしているとき
      if sy.abs > $game_map.height / 2    # 絶対値がマップの半分より大きい?
        sy -= $game_map.height            # マップの高さを引く
      end
    end
    return sx.abs + sy.abs
  end
  #--------------------------------------------------------------------------
  # ○ イベント含む通行可能?
  #     x : X 座標
  #     y : Y 座標
  #--------------------------------------------------------------------------
  def passable_pef?(x, y)
    return false if !passable?(x, y)
    ([$game_player] + events.values).all?{|c|!c.pos_nt?(x, y)}
  end
  #--------------------------------------------------------------------------
  # ○ ある地点の周りでの通行可能な地点の取得
  #     x : X 座標
  #     y : Y 座標
  #--------------------------------------------------------------------------
  def movable_points(x, y)
    points = []
    [[x,y+1],[x-1,y],[x+1,y],[x,y-1]].each{|point|
    points.push(point) if passable_pef?(point[0], point[1])}
    points
  end
  #--------------------------------------------------------------------------
  # ○ ある地点の周りでのマップ上通行可能な地点の取得
  #     x : X 座標
  #     y : Y 座標
  #--------------------------------------------------------------------------
  def passable_points(x, y)
    points = []
    [[x,y+1],[x-1,y],[x+1,y],[x,y-1]].each{|point|
    points.push(point) if passable?(point[0], point[1])}
    points
  end
end

class Game_Character
  #--------------------------------------------------------------------------
  # ○ 地点からの X 距離計算
  #--------------------------------------------------------------------------
  def distance_x_from_point(x)
    sx = @x - x
    if $game_map.loop_horizontal?         # 横にループしているとき
      if sx.abs > $game_map.width / 2     # 絶対値がマップの半分より大きい?
        sx -= $game_map.width             # マップの幅を引く
      end
    end
    return sx
  end
  #--------------------------------------------------------------------------
  # ○ 地点からの Y 距離計算
  #--------------------------------------------------------------------------
  def distance_y_from_point(y)
    sy = @y - y
    if $game_map.loop_vertical?           # 縦にループしているとき
      if sy.abs > $game_map.height / 2    # 絶対値がマップの半分より大きい?
        sy -= $game_map.height            # マップの高さを引く
      end
    end
    return sy
  end
  #--------------------------------------------------------------------------
  # ○ 動ける箇所の取得
  #--------------------------------------------------------------------------
  def movable_points
    points = [[x,y+1],[x-1,y],[x+1,y],[x,y-1]].select{|point|
      $game_map.passable_pef?(point[0], point[1])
    }
    case @direction
    when 2; points.sort{|a, b|a[1] <=> b[1]}
    when 4; points.sort{|a, b|b[0] <=> a[0]}
    when 6; points.sort{|a, b|a[0] <=> b[0]}
    when 8; points.sort{|a, b|b[1] <=> a[1]}
    end
  end
  #--------------------------------------------------------------------------
  # ○ 地点に向かう
  #--------------------------------------------------------------------------
  def move_point(x, y)
    sx = distance_x_from_point(x); sy = distance_y_from_point(y)
    if sx != 0 or sy != 0
      if sx.abs > sy.abs
        sx > 0 ? move_left : move_right
        sy > 0 ? move_up : move_down if @move_failed && sy != 0
      elsif sx.abs < sy.abs
        sy > 0 ? move_up : move_down
        sx > 0 ? move_left : move_right if @move_failed && sx != 0
      else
        if rand(2).zero?
          sx > 0 ? move_left : move_right
          sy > 0 ? move_up : move_down if @move_failed && sy != 0
        else
          sy > 0 ? move_up : move_down
          sx > 0 ? move_left : move_right if @move_failed && sx != 0
        end
      end
    end
  end
  #--------------------------------------------------------------------------
  # ○ 地点に近づく
  #--------------------------------------------------------------------------
  def move_toward_point(point)
    return if moving?
    if @route && !@route.empty?
      if @target
        d1 = $game_map.distance_points([self.x, self.y], [@target.x, @target.y])
        d2 = $game_map.distance_points(@route[-1], [@target.x, @target.y])
        if d1 < d2
          route_set([@target.x, @target.y])
        else
          po = @route.shift
          move_point(po[0], po[1])
        end
      else
        po = @route.shift
        move_point(po[0], po[1])
      end
    else
      route_set(point)
    end
  end
  #--------------------------------------------------------------------------
  # ○ ルートの設定
  #--------------------------------------------------------------------------
  def route_set(point)
    return if (self.x - point[0]).abs + (self.y - point[1]).abs <= 1
    return if !map_passable?(point[0], point[1])
    return if moving?
    def_point = !movable_points.compact.empty? ? movable_points[0] : [0, 0]
    mx = self.x; my = self.y; minip = nil; min = 32; route = []
    fail_route = []; @loop_count = [0, 0]
    loop do
      min = 32
      points = $game_map.movable_points(mx, my) - route - fail_route
      points=$game_map.passable_points(mx,my)-route-fail_route if points.empty?
      if points.empty?
        fail_route.push([mx, my])
        while ($game_map.passable_points(mx, my) - route - fail_route).empty?
          route.delete_at(-1); return @route.clear if route.empty?
          fp = route[-1]; mx = fp[0]; my = fp[1]
          fail_route.push([mx, my])
          @loop_count[1] += 1; return if @loop_count[1] > 10
        end
        @loop_count[1] = 0
        mx = def_point[0]; my = def_point[1]; route = [def_point]; next
      end
      points.each{|point_a|minip = point_a if minip.nil?
      if min >= (point[0] - point_a[0]).abs + (point[1] - point_a[1]).abs
        minip = point_a
        min = (point[0] - point_a[0]).abs + (point[1] - point_a[1]).abs
      end}
      mx, my = minip[0], minip[1]; route.push(minip); break if min <= 1
      @loop_count[0] += 1; return if @loop_count[0] > 32
    end
    @route = route
  end
  #--------------------------------------------------------------------------
  # ○ 目標に近づく(壁周り)
  #--------------------------------------------------------------------------
  def move_toward_target(target)
    return if !target
    @target = target
    move_toward_point([target.x, target.y])
  end
end
---------------------------------------------
自作の(改造でない)スクリプト素材に
関しては、リードミーもしくは作中に
「faida」と記名していただければ
利用可能です。
ダイトウ
記事: 12
登録日時: 2016年8月19日(金) 21:15

Re: vxで経路探索スクリプト

投稿記事by ダイトウ » 2018年4月08日(日) 20:49

度々、ありがとうございます!

何度か試させて頂いたのですが
障害物のないマップなら成功したのですが、
目標地点xへの直線上に通行不可のタイルがあると
停止してしまうのですが、
どのように命令を組めば良いのでしょうか…?

質問ばかりで申し訳ありません。
faida
記事: 203
登録日時: 2015年12月17日(木) 16:44

Re: vxで経路探索スクリプト

投稿記事by faida » 2018年4月08日(日) 22:36

状況がよくわかりません。添付画像のA~Cならどれに当たりますでしょうか。
ちなみにこちらは自律移動の「カスタム」でのみ調査しています。
とりあえずちょっと改造してみましたがこんなんでどうでしょう。

コード: 全て選択

=begin
◆概要
経路探索のような動きができます。

◆機能
・移動ルートの設定のスクリプトで「move_toward_target($game_player)」とやると
プレイヤーに近づくかもしれません。
・移動ルートの設定のスクリプトで「move_toward_target($game_map.events[n])」
(nはID)とやると、そのイベントに近づくかもしれません。
・地点指定は「move_toward_point([x, y])」

◆仕様
・VXBeteranで作成したのでVXで動くかが分からない。
・必ず繰り返してください。

◆使用上の注意
・特になし。

=end

class Game_Map
  #--------------------------------------------------------------------------
  # ○ 地点からの距離計算
  #--------------------------------------------------------------------------
  def distance_points(point1, point2)
    sx = point1[0] - point2[0]
    sy = point1[1] - point2[1]
    if $game_map.loop_horizontal?         # 横にループしているとき
      if sx.abs > $game_map.width / 2     # 絶対値がマップの半分より大きい?
        sx -= $game_map.width             # マップの幅を引く
      end
    end
    if $game_map.loop_vertical?           # 縦にループしているとき
      if sy.abs > $game_map.height / 2    # 絶対値がマップの半分より大きい?
        sy -= $game_map.height            # マップの高さを引く
      end
    end
    return sx.abs + sy.abs
  end
  #--------------------------------------------------------------------------
  # ○ イベント含む通行可能?
  #     x : X 座標
  #     y : Y 座標
  #--------------------------------------------------------------------------
  def passable_pef?(x, y)
    return false if !passable?(x, y)
    ([$game_player] + events.values).all?{|c|!c.pos_nt?(x, y)}
  end
  #--------------------------------------------------------------------------
  # ○ ある地点の周りでの通行可能な地点の取得
  #     x : X 座標
  #     y : Y 座標
  #--------------------------------------------------------------------------
  def movable_points(x, y)
    points = []
    [[x,y+1],[x-1,y],[x+1,y],[x,y-1]].each{|point|
    points.push(point) if passable_pef?(point[0], point[1])}
    points
  end
  #--------------------------------------------------------------------------
  # ○ ある地点の周りでのマップ上通行可能な地点の取得
  #     x : X 座標
  #     y : Y 座標
  #--------------------------------------------------------------------------
  def passable_points(x, y)
    points = []
    [[x,y+1],[x-1,y],[x+1,y],[x,y-1]].each{|point|
    points.push(point) if passable?(point[0], point[1])}
    points
  end
end

class Game_Character
  #--------------------------------------------------------------------------
  # ○ 地点からの X 距離計算
  #--------------------------------------------------------------------------
  def distance_x_from_point(x)
    sx = @x - x
    if $game_map.loop_horizontal?         # 横にループしているとき
      if sx.abs > $game_map.width / 2     # 絶対値がマップの半分より大きい?
        sx -= $game_map.width             # マップの幅を引く
      end
    end
    return sx
  end
  #--------------------------------------------------------------------------
  # ○ 地点からの Y 距離計算
  #--------------------------------------------------------------------------
  def distance_y_from_point(y)
    sy = @y - y
    if $game_map.loop_vertical?           # 縦にループしているとき
      if sy.abs > $game_map.height / 2    # 絶対値がマップの半分より大きい?
        sy -= $game_map.height            # マップの高さを引く
      end
    end
    return sy
  end
  #--------------------------------------------------------------------------
  # ○ 動ける箇所の取得
  #--------------------------------------------------------------------------
  def movable_points
    points = [[x,y+1],[x-1,y],[x+1,y],[x,y-1]].select{|point|
      $game_map.passable_pef?(point[0], point[1])
    }
    case @direction
    when 2; points.sort{|a, b|a[1] <=> b[1]}
    when 4; points.sort{|a, b|b[0] <=> a[0]}
    when 6; points.sort{|a, b|a[0] <=> b[0]}
    when 8; points.sort{|a, b|b[1] <=> a[1]}
    end
  end
  #--------------------------------------------------------------------------
  # ○ 地点に向かう
  #--------------------------------------------------------------------------
  def move_point(x, y)
    sx = distance_x_from_point(x); sy = distance_y_from_point(y)
    if sx != 0 or sy != 0
      if sx.abs > sy.abs
        sx > 0 ? move_left : move_right
        sy > 0 ? move_up : move_down if @move_failed && sy != 0
      elsif sx.abs < sy.abs
        sy > 0 ? move_up : move_down
        sx > 0 ? move_left : move_right if @move_failed && sx != 0
      else
        if rand(2).zero?
          sx > 0 ? move_left : move_right
          sy > 0 ? move_up : move_down if @move_failed && sy != 0
        else
          sy > 0 ? move_up : move_down
          sx > 0 ? move_left : move_right if @move_failed && sx != 0
        end
      end
    end
  end
  #--------------------------------------------------------------------------
  # ○ 地点に近づく
  #--------------------------------------------------------------------------
  def move_toward_point(point)
    return if moving?
    if @route && !@route.empty?
#~       if @target
#~         d1 = $game_map.distance_points([self.x, self.y], [@target.x, @target.y])
#~         d2 = $game_map.distance_points(@route[-1], [@target.x, @target.y])
#~         if d1 < d2
#~           route_set([@target.x, @target.y])
#~         else
          po = @route.shift
          move_point(po[0], po[1])
#~         end
#~       else
#~         po = @route.shift
#~         move_point(po[0], po[1])
#~       end
    else
      route_set(point)
    end
  end
  #--------------------------------------------------------------------------
  # ○ ルートの設定
  #--------------------------------------------------------------------------
  def route_set(point)
    return if (self.x - point[0]).abs + (self.y - point[1]).abs <= 1
    return if !map_passable?(point[0], point[1])
    return if moving?
    def_point = !movable_points.compact.empty? ? movable_points[0] : [0, 0]
    mx = self.x; my = self.y; minip = nil; min = 32; route = [[mx, my]]
    fail_route = []; @loop_count = [0, 0]
    loop do
      min = 32
      points = $game_map.movable_points(mx, my) - route - fail_route
      points=$game_map.passable_points(mx,my)-route-fail_route if points.empty?
      if points.empty?
        begin
          fail_route.push([mx, my])
          route.delete_at(-1); return (@route || []).clear if route.empty?
          fp = route[-1]; mx = fp[0]; my = fp[1]
          @loop_count[1] += 1; return if @loop_count[1] > 16
        end while ($game_map.passable_points(mx, my) - route - fail_route).empty?
        @loop_count = [0, 0]
        mx = def_point[0]; my = def_point[1]; route = [def_point]; next
      end
      points.each{|point_a|minip = point_a if minip.nil?
      if min >= (point[0] - point_a[0]).abs + (point[1] - point_a[1]).abs
        minip = point_a
        min = (point[0] - point_a[0]).abs + (point[1] - point_a[1]).abs
      end}
      mx, my = minip[0], minip[1]; route.push(minip); break if min <= 1
      @loop_count[0] += 1; return if @loop_count[0] > 32
    end
    @route = route
  end
  #--------------------------------------------------------------------------
  # ○ 目標に近づく(壁周り)
  #--------------------------------------------------------------------------
  def move_toward_target(target)
    return if !target
    @target = target
    move_toward_point([target.x, target.y])
  end
end
ちなみに、「def route_set(point)」の中にある「min = 32」「@loop_count[1] > 16」「@loop_count[0] > 32」の数値は
ループ限界値で、この数値を大きくすると遠くまで経路探索してくれるようになります。
当然、負荷も大きいです。
添付ファイル
between.png
---------------------------------------------
自作の(改造でない)スクリプト素材に
関しては、リードミーもしくは作中に
「faida」と記名していただければ
利用可能です。
ダイトウ
記事: 12
登録日時: 2016年8月19日(金) 21:15

Re: vxで経路探索スクリプト

投稿記事by ダイトウ » 2018年4月10日(火) 14:01

ご丁寧にありがとうございます!

今回御作り頂いたスクリプトも試したのですが
画像Bの様に2,3歩上や下に迂回するだけで目的地に着ける場合は動くのですが
それ以上歩数があったり、ルートが複雑になると停止してしまいます。
目的地が近ければ迷路みたいな道でも動くようです。
そうするとやはり距離の問題でしょうか?
詳しくは分かりませんがループ限界値を大きくすれば出来そうですが不可も大きいのですよね?
それを複数設置するとなると…無理そうですよね。。
faida
記事: 203
登録日時: 2015年12月17日(木) 16:44

Re: vxで経路探索スクリプト

投稿記事by faida » 2018年4月10日(火) 19:04

以前のものから大幅に変更した新作の試作版が出来上がりました。
・A*と呼ばれる方法のマネをしています。完全に再現できてるかは怪しい。
・負荷が大きくならないよう、経路探索を途中でやめて、近そうだなと思ったところに行きます。
・なのでよくわからないところに行ったりする。
・以前の経路探索情報を利用します。大した効果はない。
・(2つ前のヤツにもあったけど)目的地が動いたらルート途中で曲がったりします。
※このスクリプトは試作版なので正常かつ負荷なく動くかは知りません。

コード: 全て選択

# ◆作者:faida @faida3983

class A_star_Node
  attr_reader   :x
  attr_reader   :y
  attr_accessor :real_cost
  attr_reader   :predict_cost
  def initialize(x, y, parent = [])
    @x, @y = x, y
    @parent = parent
    @real_cost = 0
    @predict_cost = 0
    @state = :none
  end
  def calc_predict_cost(goal)
    @predict_cost = (goal[0] - x).abs + (goal[1] - y).abs
  end
  def score
    @real_cost + @predict_cost
  end
  def open?
    @state == :open
  end
  def closed?
    @state == :closed
  end
  def open
    @state = :open
  end
  def close
    @state = :closed
  end
  def get_route
    @parent + [[@x, @y]]
  end
  def pos?(x, y)
    @x == x && @y == y
  end
  def movable_points
    $game_map.movable_points(@x, @y)
  end
  def passable_points
    $game_map.passable_points(@x, @y)
  end
  def check_node
    [@x, @y, real_cost, score]
  end
end

class A_star_Route
  attr_reader :ended_route_search
  def initialize(start, goal)
    @start, @goal = start, goal
    @x, @y = @start[0], @start[1]
    @route = []
    @open_node = [A_star_Node.new(@x, @y, [])]
    @closed_node = []
    @loop_count = 0
    @ended_route_search = false
  end
  def route_search
    start_route_search
    loop do
      s_node = get_standard_node
      return end_route_search(s_node) if s_node.pos?(@goal[0], @goal[1])
      return temp_end_route_search if @loop_count > 128
      points = s_node.movable_points
      points = s_node.passable_points if points.empty?
      points.each{|point|
        next if (@open_node + @closed_node).any?{|n|n.pos?(point[0], point[1])}
        @open_node << A_star_Node.new(point[0], point[1], s_node.get_route)
        @open_node[-1].real_cost = s_node.real_cost + 1
        @open_node[-1].calc_predict_cost(@goal)
      }
      @open_node.delete(s_node)
      @closed_node << s_node
      @loop_count += 1
    end
  end
  def start_route_search
  end
  def get_standard_node
    score = @open_node.collect{|n|n.score}.min
    arr = @open_node.select{|n|n.score == score}
    real_cost = arr.collect{|n|n.real_cost}.min
    arr.find{|n|n.real_cost}
  end
  def end_route_search(node)
    route = node.get_route
    $game_system.route_list.add_route_search(route)
    @ended_route_search = true
    route
  end
  def temp_end_route_search
    get_standard_node.get_route
  end
end

class A_star_RouteResult
  attr_reader :start
  attr_reader :route
  attr_reader :goal
  def initialize(route)
    @start, @route, @goal = route[0], route, route[-1]
  end
  def concat(result)
    if result.start == self.goal
      @route.concat(result.route); return true
    elsif self.start == result.goal
      @route = result.route.concat(@route); return true
    end
    false
  end
  def include?(start, goal)
    @route.include?(start) && @route.include?(goal)
  end
  def reuse_route(start, goal)
    return nil if !include?(start, goal)
    if @route.index(start) < @route.index(goal)
      @route[@route.index(start), @route.index(goal) - @route.index(start) + 1]
    else
      @route[@route.index(goal), @route.index(start) - @route.index(goal) + 1]
    end
  end
end

class A_star_RouteList
  def initialize(map_id)
    @map_id = map_id
    @list = []
  end
  def add_route_search(route)
    @list.push(A_star_RouteResult.new(route))
  end
  def include?(start, goal)
    @list.find{|result|result.include?(start, goal)}
  end
end

class Game_System
  attr_reader :route_list
  alias fai_routesearch_initialize initialize
  def initialize
    fai_routesearch_initialize
    @route_list = {}
  end
  def setup_route_list(map_id)
    @route_list = {} if !@route_list
    @route_list[map_id] = A_star_RouteList.new(map_id)
  end
  def add_route_list(map_id, route)
    setup_route_list(map_id) if !@route_list || !@route_list[map_id]
    @route_list[map_id].add_route_search(route)
  end
  def route_list
    setup_route_list($game_map.map_id) if !@route_list || !@route_list[$game_map.map_id]
    @route_list[$game_map.map_id]
  end
end

class Game_Map
  #--------------------------------------------------------------------------
  # ○ 地点からの距離計算
  #--------------------------------------------------------------------------
  def distance_points(point1, point2)
    sx = point1[0] - point2[0]
    sy = point1[1] - point2[1]
    if $game_map.loop_horizontal?         # 横にループしているとき
      if sx.abs > $game_map.width / 2     # 絶対値がマップの半分より大きい?
        sx -= $game_map.width             # マップの幅を引く
      end
    end
    if $game_map.loop_vertical?           # 縦にループしているとき
      if sy.abs > $game_map.height / 2    # 絶対値がマップの半分より大きい?
        sy -= $game_map.height            # マップの高さを引く
      end
    end
    return sx.abs + sy.abs
  end
  #--------------------------------------------------------------------------
  # ○ イベント含む通行可能?
  #     x : X 座標
  #     y : Y 座標
  #--------------------------------------------------------------------------
  def passable_pef?(x, y)
    return false if !passable?(x, y)
    (events.values).all?{|c|!c.pos_nt?(x, y)}
  end
  #--------------------------------------------------------------------------
  # ○ ある地点の周りでの通行可能な地点の取得
  #     x : X 座標
  #     y : Y 座標
  #--------------------------------------------------------------------------
  def movable_points(x, y)
    points = []
    [[x,y+1],[x-1,y],[x+1,y],[x,y-1]].each{|point|
    points.push(point) if passable_pef?(point[0], point[1])}
    points
  end
  #--------------------------------------------------------------------------
  # ○ ある地点の周りでのマップ上通行可能な地点の取得
  #     x : X 座標
  #     y : Y 座標
  #--------------------------------------------------------------------------
  def passable_points(x, y)
    points = []
    [[x,y+1],[x-1,y],[x+1,y],[x,y-1]].each{|point|
    points.push(point) if passable?(point[0], point[1])}
    points
  end
end


class Game_Character
  #--------------------------------------------------------------------------
  # ○ 地点からの X 距離計算
  #--------------------------------------------------------------------------
  def distance_x_from_point(x)
    sx = @x - x
    if $game_map.loop_horizontal?         # 横にループしているとき
      if sx.abs > $game_map.width / 2     # 絶対値がマップの半分より大きい?
        sx -= $game_map.width             # マップの幅を引く
      end
    end
    return sx
  end
  #--------------------------------------------------------------------------
  # ○ 地点からの Y 距離計算
  #--------------------------------------------------------------------------
  def distance_y_from_point(y)
    sy = @y - y
    if $game_map.loop_vertical?           # 縦にループしているとき
      if sy.abs > $game_map.height / 2    # 絶対値がマップの半分より大きい?
        sy -= $game_map.height            # マップの高さを引く
      end
    end
    return sy
  end
  #--------------------------------------------------------------------------
  # ○ 地点に向かう
  #--------------------------------------------------------------------------
  def move_point(x, y)
    sx = distance_x_from_point(x); sy = distance_y_from_point(y)
    if sx != 0 or sy != 0
      if sx.abs > sy.abs
        sx > 0 ? move_left : move_right
        sy > 0 ? move_up : move_down if @move_failed && sy != 0
      elsif sx.abs < sy.abs
        sy > 0 ? move_up : move_down
        sx > 0 ? move_left : move_right if @move_failed && sx != 0
      else
        if rand(2).zero?
          sx > 0 ? move_left : move_right
          sy > 0 ? move_up : move_down if @move_failed && sy != 0
        else
          sy > 0 ? move_up : move_down
          sx > 0 ? move_left : move_right if @move_failed && sx != 0
        end
      end
    end
  end
  #--------------------------------------------------------------------------
  # ○ 地点に近づく
  #--------------------------------------------------------------------------
  def move_toward_point(point)
    return if moving?
    if @route && !@route.empty?
      if @target && @ended_route_search
        d1 = $game_map.distance_points([self.x, self.y], [@target.x, @target.y])
        d2 = $game_map.distance_points(@route[-1], [@target.x, @target.y])
        if d1 < d2
          route_set([@target.x, @target.y])
        else
          po = @route.shift
          move_point(po[0], po[1])
        end
      else
        po = @route.shift
        move_point(po[0], po[1])
      end
    else
      route_set(point)
    end
  end
  #--------------------------------------------------------------------------
  # ○ ルートの設定
  #--------------------------------------------------------------------------
  def route_set(point)
    result = $game_system.route_list.include?([self.x, self.y], point)
    if $game_system.route_list.include?([self.x, self.y], point)
      @route = result.reuse_route([self.x, self.y], point)
    else
      a_star_route = A_star_Route.new([self.x, self.y], point)
      @ended_route_search = a_star_route.ended_route_search
      @route = a_star_route.route_search
    end
  end
  #--------------------------------------------------------------------------
  # ○ 目標に近づく(壁周り)
  #--------------------------------------------------------------------------
  def move_toward_target(target)
    return if !target
    @target = target
    x_d = distance_x_from_point(target.x).abs
    y_d = distance_y_from_point(target.y).abs
    return if x_d + y_d <= 1
    move_toward_point([target.x, target.y])
  end
end
負荷に関しては実際に確かめてみて、妥協するところは妥協したほうがよろしいかと。

※19:49 色々不備があったので流石に修正 Rubyのバージョンが古くて使えないメソッドは失念してた
---------------------------------------------
自作の(改造でない)スクリプト素材に
関しては、リードミーもしくは作中に
「faida」と記名していただければ
利用可能です。
ダイトウ
記事: 12
登録日時: 2016年8月19日(金) 21:15

Re: vxで経路探索スクリプト

投稿記事by ダイトウ » 2018年4月10日(火) 20:48

度々、本当にありがとうございます!
ルートに壁や障害物があっても停止しなくなりました!
何とか目的地へ進もうとしてくれているのが愛おしいくらいです(笑)
ただ、やはり目的地が遠すぎると途中で諦めてスタート地点とそこまでの地点を行ったり来たりしてしまいますが。
こればかりは仕様ですよね?
あと複数設置して作動させたら、たまに画像の様な警告が出てシャットダウンされますが、
何が原因なのでしょうか?
わざわざスクリプトを組んで頂いて、更に質問ばかり重ねて本当に申し訳ありません。。
添付ファイル
キャプチャs.PNG
キャプチャs.PNG (27.72 KiB) 閲覧数: 581 回
faida
記事: 203
登録日時: 2015年12月17日(木) 16:44

Re: vxで経路探索スクリプト

投稿記事by faida » 2018年4月13日(金) 08:27

とりあえずの修正版です。

コード: 全て選択

# ◆作者:faida @faida3983

module FAI_RouteSearch
  # 経路探索するマスの個数。
  # 小さいほど精度が下がるが、大きいと負荷が上がる。
  SEARCH_AREA = 64
end

class A_star_Node
  attr_reader   :x
  attr_reader   :y
  attr_accessor :real_cost
  attr_reader   :predict_cost
  def initialize(x, y, parent = [])
    @x, @y = x, y
    @parent = parent
    @real_cost = 0
    @predict_cost = 0
    @state = :none
  end
  def calc_predict_cost(goal)
    @predict_cost = (goal[0] - x).abs + (goal[1] - y).abs
  end
  def score
    @real_cost + @predict_cost
  end
  def open?
    @state == :open
  end
  def closed?
    @state == :closed
  end
  def open
    @state = :open
  end
  def close
    @state = :closed
  end
  def get_route
    @parent + [[@x, @y]]
  end
  def pos?(x, y)
    @x == x && @y == y
  end
  def movable_points
    $game_map.movable_points(@x, @y)
  end
  def passable_points
    $game_map.passable_points(@x, @y)
  end
  def check_node
    [@x, @y, real_cost, score]
  end
end

class A_star_Route
  attr_reader :ended_route_search
  def initialize(start, goal)
    @start, @goal = start, goal
    @x, @y = @start[0], @start[1]
    @route = []
    @open_node = [A_star_Node.new(@x, @y, [])]
    @open_node[0].calc_predict_cost(goal)
    @closed_node = []
    @loop_count = 0
    @ended_route_search = false
  end
  def route_search
    start_route_search
    loop do
      s_node = get_standard_node
      return stop_route_search if !s_node
      return end_route_search(s_node) if s_node.pos?(@goal[0], @goal[1])
      return temp_end_route_search if @loop_count > FAI_RouteSearch::SEARCH_AREA
      points = s_node.movable_points
      points = s_node.passable_points if points.empty?
      points.each{|point|
        next if (@open_node + @closed_node).any?{|n|n.pos?(point[0], point[1])}
        @open_node << A_star_Node.new(point[0], point[1], s_node.get_route)
        @open_node[-1].real_cost = s_node.real_cost + 1
        @open_node[-1].calc_predict_cost(@goal)
      }
      @open_node.delete(s_node)
      @closed_node << s_node
      @loop_count += 1
    end
  end
  def start_route_search
  end
  def get_standard_node
    score = @open_node.collect{|n|n.score}.min
    arr = @open_node.select{|n|n.score == score}
    real_cost = arr.collect{|n|n.real_cost}.min
    arr.find{|n|n.real_cost}
  end
  def end_route_search(node)
    route = node.get_route
    $game_system.route_list.add_route_search(route)
    @ended_route_search = true
    route
  end
  def temp_end_route_search
    get_standard_node.get_route
  end
  def stop_route_search
    @closed_node.delete_at(0)
    if !@closed_node.empty?
      score = @closed_node.collect{|n|n.score}.min
      arr = @closed_node.select{|n|n.score == score}
      real_cost = arr.collect{|n|n.real_cost}.min
      arr.find{|n|n.real_cost}.get_route
    elsif !$game_map.movable_points(@x, @y).empty?
      index = rand($game_map.movable_points(@x, @y).size)
      [$game_map.movable_points(@x, @y)[index]]
    else
      []
    end
  end
end

class A_star_RouteResult
  attr_reader :start
  attr_reader :route
  attr_reader :goal
  def initialize(route)
    @start, @route, @goal = route[0], route, route[-1]
  end
  def concat(result)
    if result.start == self.goal
      @route.concat(result.route); return true
    elsif self.start == result.goal
      @route = result.route.concat(@route); return true
    end
    false
  end
  def include?(start, goal)
    @route.include?(start) && @route.include?(goal)
  end
  def reuse_route(start, goal)
    return nil if !include?(start, goal)
    if @route.index(start) < @route.index(goal)
      @route[@route.index(start), @route.index(goal) - @route.index(start) + 1]
    else
      @route[@route.index(goal), @route.index(start) - @route.index(goal) + 1]
    end
  end
end

class A_star_RouteList
  def initialize(map_id)
    @map_id = map_id
    @list = []
  end
  def add_route_search(route)
    @list.push(A_star_RouteResult.new(route))
  end
  def include?(start, goal)
    @list.find{|result|result.include?(start, goal)}
  end
end

class Game_System
  attr_reader :route_list
  alias fai_routesearch_initialize initialize
  def initialize
    fai_routesearch_initialize
    @route_list = {}
  end
  def setup_route_list(map_id)
    @route_list = {} if !@route_list
    @route_list[map_id] = A_star_RouteList.new(map_id)
  end
  def add_route_list(map_id, route)
    setup_route_list(map_id) if !@route_list || !@route_list[map_id]
    @route_list[map_id].add_route_search(route)
  end
  def route_list
    setup_route_list($game_map.map_id) if !@route_list || !@route_list[$game_map.map_id]
    @route_list[$game_map.map_id]
  end
end

class Game_Map
  #--------------------------------------------------------------------------
  # ○ 地点からの距離計算
  #--------------------------------------------------------------------------
  def distance_points(point1, point2)
    sx = point1[0] - point2[0]
    sy = point1[1] - point2[1]
    if $game_map.loop_horizontal?         # 横にループしているとき
      if sx.abs > $game_map.width / 2     # 絶対値がマップの半分より大きい?
        sx -= $game_map.width             # マップの幅を引く
      end
    end
    if $game_map.loop_vertical?           # 縦にループしているとき
      if sy.abs > $game_map.height / 2    # 絶対値がマップの半分より大きい?
        sy -= $game_map.height            # マップの高さを引く
      end
    end
    return sx.abs + sy.abs
  end
  #--------------------------------------------------------------------------
  # ○ イベント含む通行可能?
  #     x : X 座標
  #     y : Y 座標
  #--------------------------------------------------------------------------
  def passable_pef?(x, y)
    return false if !passable?(x, y)
    (events.values).all?{|c|!c.pos_nt?(x, y)}
  end
  #--------------------------------------------------------------------------
  # ○ ある地点の周りでの通行可能な地点の取得
  #     x : X 座標
  #     y : Y 座標
  #--------------------------------------------------------------------------
  def movable_points(x, y)
    points = []
    [[x,y+1],[x-1,y],[x+1,y],[x,y-1]].each{|point|
    points.push(point) if passable_pef?(point[0], point[1])}
    points
  end
  #--------------------------------------------------------------------------
  # ○ ある地点の周りでのマップ上通行可能な地点の取得
  #     x : X 座標
  #     y : Y 座標
  #--------------------------------------------------------------------------
  def passable_points(x, y)
    points = []
    [[x,y+1],[x-1,y],[x+1,y],[x,y-1]].each{|point|
    points.push(point) if passable?(point[0], point[1])}
    points
  end
end


class Game_Character
  #--------------------------------------------------------------------------
  # ○ 地点からの X 距離計算
  #--------------------------------------------------------------------------
  def distance_x_from_point(x)
    sx = @x - x
    if $game_map.loop_horizontal?         # 横にループしているとき
      if sx.abs > $game_map.width / 2     # 絶対値がマップの半分より大きい?
        sx -= $game_map.width             # マップの幅を引く
      end
    end
    return sx
  end
  #--------------------------------------------------------------------------
  # ○ 地点からの Y 距離計算
  #--------------------------------------------------------------------------
  def distance_y_from_point(y)
    sy = @y - y
    if $game_map.loop_vertical?           # 縦にループしているとき
      if sy.abs > $game_map.height / 2    # 絶対値がマップの半分より大きい?
        sy -= $game_map.height            # マップの高さを引く
      end
    end
    return sy
  end
  #--------------------------------------------------------------------------
  # ○ 地点に向かう
  #--------------------------------------------------------------------------
  def move_point(x, y)
    sx = distance_x_from_point(x); sy = distance_y_from_point(y)
    if sx != 0 or sy != 0
      if sx.abs > sy.abs
        sx > 0 ? move_left : move_right
        sy > 0 ? move_up : move_down if @move_failed && sy != 0
      elsif sx.abs < sy.abs
        sy > 0 ? move_up : move_down
        sx > 0 ? move_left : move_right if @move_failed && sx != 0
      else
        if rand(2).zero?
          sx > 0 ? move_left : move_right
          sy > 0 ? move_up : move_down if @move_failed && sy != 0
        else
          sy > 0 ? move_up : move_down
          sx > 0 ? move_left : move_right if @move_failed && sx != 0
        end
      end
    end
  end
  #--------------------------------------------------------------------------
  # ○ 地点に近づく
  #--------------------------------------------------------------------------
  def move_toward_point(point)
    return if moving?
    if @route && !@route.empty?
      if @target && @ended_route_search
        d1 = $game_map.distance_points([self.x, self.y], [@target.x, @target.y])
        d2 = $game_map.distance_points(@route[-1], [@target.x, @target.y])
        if d1 < d2
          route_set([@target.x, @target.y])
        else
          po = @route.shift
          move_point(po[0], po[1])
        end
      else
        po = @route.shift
        move_point(po[0], po[1])
      end
    else
      route_set(point)
    end
  end
  #--------------------------------------------------------------------------
  # ○ ルートの設定
  #--------------------------------------------------------------------------
  def route_set(point)
    result = $game_system.route_list.include?([self.x, self.y], point)
    if $game_system.route_list.include?([self.x, self.y], point)
      @route = result.reuse_route([self.x, self.y], point)
    else
      a_star_route = A_star_Route.new([self.x, self.y], point)
      @ended_route_search = a_star_route.ended_route_search
      @route = a_star_route.route_search
    end
  end
  #--------------------------------------------------------------------------
  # ○ 目標に近づく(壁周り)
  #--------------------------------------------------------------------------
  def move_toward_target(target)
    return if !target
    @target = target
    x_d = distance_x_from_point(target.x).abs
    y_d = distance_y_from_point(target.y).abs
    return if x_d + y_d <= 1
    move_toward_point([target.x, target.y])
  end
end
ただ、やはり目的地が遠すぎると途中で諦めてスタート地点とそこまでの地点を行ったり来たりしてしまいますが。
こればかりは仕様ですよね?
添付ファイルのようなマップを組むと特に顕著ですが、赤い線をたどったとしてもそこで止まると
次のルートが黒い線になったりします。回り込みすぎる場合は負荷を上げるしかないです。
一応設定項目で探査マス(距離や道のりではない)を設定できます。
あと、実は一度探査したルートを再使用していたりするので、(一度探査すれば)実際そこまで負荷が高い感じは
ありません。
添付ファイル
route_search_mapping.png
route_search_mapping.png (79.09 KiB) 閲覧数: 507 回
---------------------------------------------
自作の(改造でない)スクリプト素材に
関しては、リードミーもしくは作中に
「faida」と記名していただければ
利用可能です。
ダイトウ
記事: 12
登録日時: 2016年8月19日(金) 21:15

Re: vxで経路探索スクリプト

投稿記事by ダイトウ » 2018年4月15日(日) 13:24

何度も修正してくださり、本当にありがとうございます!
大事に使わせて頂きます。。

現在、画像の様なマップで1~6を経路探索させて
目的地の☆の位置まで移動させていますが、たどり着くには1・2・4で
他の番号は初期位置と近辺をいったりきたりしています。
これはやはりマップが広く、目的地が遠くて複雑だからですよね?
もう少しマップを簡略化させるしかないか…
添付ファイル
bbb.PNG
bbb.PNG (8.01 KiB) 閲覧数: 460 回
faida
記事: 203
登録日時: 2015年12月17日(木) 16:44

Re: vxで経路探索スクリプト

投稿記事by faida » 2018年4月15日(日) 20:30

少しだけ評価方法を変えた修正版です。

コード: 全て選択

# ◆作者:faida @faida3983

module FAI_RouteSearch
  # 経路探索するマスの個数。
  # 小さいほど精度が下がるが、大きいと負荷が上がる。
  SEARCH_AREA = 150
end

class A_star_Node
  attr_reader   :x
  attr_reader   :y
  attr_accessor :real_cost
  attr_reader   :predict_cost
  def initialize(x, y, parent = [])
    @x, @y = x, y
    @parent = parent
    @real_cost = 0
    @predict_cost = 0
    @state = :none
  end
  def calc_predict_cost(goal)
    @predict_cost = (goal[0] - x).abs + (goal[1] - y).abs
  end
  def score
    @real_cost + @predict_cost
  end
  def open?
    @state == :open
  end
  def closed?
    @state == :closed
  end
  def open
    @state = :open
  end
  def close
    @state = :closed
  end
  def get_route
    @parent + [[@x, @y]]
  end
  def pos?(x, y)
    @x == x && @y == y
  end
  def movable_points
    $game_map.movable_points(@x, @y)
  end
  def passable_points
    $game_map.passable_points(@x, @y)
  end
  def check_node
    [@x, @y, real_cost, score]
  end
end

class A_star_Route
  attr_reader :ended_route_search
  def initialize(start, goal)
    @start, @goal = start, goal
    @x, @y = @start[0], @start[1]
    @route = []
    @open_node = [A_star_Node.new(@x, @y, [])]
    @open_node[0].calc_predict_cost(goal)
    @closed_node = []
    @loop_count = 0
    @ended_route_search = false
  end
  def route_search
    start_route_search
    loop do
      s_node = get_standard_node
      return stop_route_search if !s_node
      return end_route_search(s_node) if s_node.pos?(@goal[0], @goal[1])
      return temp_end_route_search if @loop_count > FAI_RouteSearch::SEARCH_AREA
      points = s_node.movable_points
      points = s_node.passable_points if points.empty?
      points.each{|point|
        next if (@open_node + @closed_node).any?{|n|n.pos?(point[0], point[1])}
        @open_node << A_star_Node.new(point[0], point[1], s_node.get_route)
        @open_node[-1].real_cost = s_node.real_cost + 1
        @open_node[-1].calc_predict_cost(@goal)
      }
      @open_node.delete(s_node)
      @closed_node << s_node
      @loop_count += 1
    end
  end
  def start_route_search
  end
  def get_standard_node
    score = @open_node.collect{|n|n.score}.min
    arr = @open_node.select{|n|n.score == score}
    real_cost = arr.collect{|n|n.real_cost}.min
    arr.find{|n|n.real_cost}
  end
  def get_closed_standard_node
    score = @closed_node.collect{|n|n.score}.min
    arr = @closed_node.select{|n|n.score == score}
    real_cost = arr.collect{|n|n.real_cost}.min
    arr.find{|n|n.real_cost}
  end
  def end_route_search(node)
    route = node.get_route
    $game_system.route_list.add_route_search(route)
    @ended_route_search = true
    route
  end
  def temp_end_route_search
    o_node = get_standard_node
    c_node = get_closed_standard_node
    if o_node.score < c_node.score
      get_closed_standard_node.get_route
    else
      get_standard_node.get_route
    end
  end
  def stop_route_search
    @closed_node.delete_at(0)
    if !@closed_node.empty?
      score = @closed_node.collect{|n|n.score}.min
      arr = @closed_node.select{|n|n.score == score}
      real_cost = arr.collect{|n|n.real_cost}.min
      arr.find{|n|n.real_cost}.get_route
    elsif !$game_map.movable_points(@x, @y).empty?
      index = rand($game_map.movable_points(@x, @y).size)
      [$game_map.movable_points(@x, @y)[index]]
    else
      []
    end
  end
end

class A_star_RouteResult
  attr_reader :start
  attr_reader :route
  attr_reader :goal
  def initialize(route)
    @start, @route, @goal = route[0], route, route[-1]
  end
  def concat(result)
    if result.start == self.goal
      @route.concat(result.route); return true
    elsif self.start == result.goal
      @route = result.route.concat(@route); return true
    end
    false
  end
  def include?(start, goal)
    @route.include?(start) && @route.include?(goal)
  end
  def reuse_route(start, goal)
    return nil if !include?(start, goal)
    if @route.index(start) < @route.index(goal)
      @route[@route.index(start), @route.index(goal) - @route.index(start) + 1]
    else
      @route[@route.index(goal), @route.index(start) - @route.index(goal) + 1]
    end
  end
end

class A_star_RouteList
  def initialize(map_id)
    @map_id = map_id
    @list = []
  end
  def add_route_search(route)
    @list.push(A_star_RouteResult.new(route))
  end
  def include?(start, goal)
    @list.find{|result|result.include?(start, goal)}
  end
end

class Game_System
  attr_reader :route_list
  alias fai_routesearch_initialize initialize
  def initialize
    fai_routesearch_initialize
    @route_list = {}
  end
  def setup_route_list(map_id)
    @route_list = {} if !@route_list
    @route_list[map_id] = A_star_RouteList.new(map_id)
  end
  def add_route_list(map_id, route)
    setup_route_list(map_id) if !@route_list || !@route_list[map_id]
    @route_list[map_id].add_route_search(route)
  end
  def route_list
    setup_route_list($game_map.map_id) if !@route_list || !@route_list[$game_map.map_id]
    @route_list[$game_map.map_id]
  end
end

class Game_Map
  #--------------------------------------------------------------------------
  # ○ 地点からの距離計算
  #--------------------------------------------------------------------------
  def distance_points(point1, point2)
    sx = point1[0] - point2[0]
    sy = point1[1] - point2[1]
    if $game_map.loop_horizontal?         # 横にループしているとき
      if sx.abs > $game_map.width / 2     # 絶対値がマップの半分より大きい?
        sx -= $game_map.width             # マップの幅を引く
      end
    end
    if $game_map.loop_vertical?           # 縦にループしているとき
      if sy.abs > $game_map.height / 2    # 絶対値がマップの半分より大きい?
        sy -= $game_map.height            # マップの高さを引く
      end
    end
    return sx.abs + sy.abs
  end
  #--------------------------------------------------------------------------
  # ○ イベント含む通行可能?
  #     x : X 座標
  #     y : Y 座標
  #--------------------------------------------------------------------------
  def passable_pef?(x, y)
    return false if !passable?(x, y)
    (events.values).all?{|c|!c.pos_nt?(x, y)}
  end
  #--------------------------------------------------------------------------
  # ○ ある地点の周りでの通行可能な地点の取得
  #     x : X 座標
  #     y : Y 座標
  #--------------------------------------------------------------------------
  def movable_points(x, y)
    points = []
    [[x,y+1],[x-1,y],[x+1,y],[x,y-1]].each{|point|
    points.push(point) if passable_pef?(point[0], point[1])}
    points
  end
  #--------------------------------------------------------------------------
  # ○ ある地点の周りでのマップ上通行可能な地点の取得
  #     x : X 座標
  #     y : Y 座標
  #--------------------------------------------------------------------------
  def passable_points(x, y)
    points = []
    [[x,y+1],[x-1,y],[x+1,y],[x,y-1]].each{|point|
    points.push(point) if passable?(point[0], point[1])}
    points
  end
end


class Game_Character
  #--------------------------------------------------------------------------
  # ○ 地点からの X 距離計算
  #--------------------------------------------------------------------------
  def distance_x_from_point(x)
    sx = @x - x
    if $game_map.loop_horizontal?         # 横にループしているとき
      if sx.abs > $game_map.width / 2     # 絶対値がマップの半分より大きい?
        sx -= $game_map.width             # マップの幅を引く
      end
    end
    return sx
  end
  #--------------------------------------------------------------------------
  # ○ 地点からの Y 距離計算
  #--------------------------------------------------------------------------
  def distance_y_from_point(y)
    sy = @y - y
    if $game_map.loop_vertical?           # 縦にループしているとき
      if sy.abs > $game_map.height / 2    # 絶対値がマップの半分より大きい?
        sy -= $game_map.height            # マップの高さを引く
      end
    end
    return sy
  end
  #--------------------------------------------------------------------------
  # ○ 地点に向かう
  #--------------------------------------------------------------------------
  def move_point(x, y)
    sx = distance_x_from_point(x); sy = distance_y_from_point(y)
    if sx != 0 or sy != 0
      if sx.abs > sy.abs
        sx > 0 ? move_left : move_right
        sy > 0 ? move_up : move_down if @move_failed && sy != 0
      elsif sx.abs < sy.abs
        sy > 0 ? move_up : move_down
        sx > 0 ? move_left : move_right if @move_failed && sx != 0
      else
        if rand(2).zero?
          sx > 0 ? move_left : move_right
          sy > 0 ? move_up : move_down if @move_failed && sy != 0
        else
          sy > 0 ? move_up : move_down
          sx > 0 ? move_left : move_right if @move_failed && sx != 0
        end
      end
    end
  end
  #--------------------------------------------------------------------------
  # ○ 地点に近づく
  #--------------------------------------------------------------------------
  def move_toward_point(point)
    return if moving?
    if @route && !@route.empty?
      if @target && @ended_route_search
        d1 = $game_map.distance_points([self.x, self.y], [@target.x, @target.y])
        d2 = $game_map.distance_points(@route[-1], [@target.x, @target.y])
        if d1 < d2
          route_set([@target.x, @target.y])
        else
          po = @route.shift
          move_point(po[0], po[1])
        end
      else
        po = @route.shift
        move_point(po[0], po[1])
      end
    else
      route_set(point)
    end
  end
  #--------------------------------------------------------------------------
  # ○ ルートの設定
  #--------------------------------------------------------------------------
  def route_set(point)
    result = $game_system.route_list.include?([self.x, self.y], point)
    if $game_system.route_list.include?([self.x, self.y], point)
      @route = result.reuse_route([self.x, self.y], point)
    else
      a_star_route = A_star_Route.new([self.x, self.y], point)
      @ended_route_search = a_star_route.ended_route_search
      @route = a_star_route.route_search
    end
  end
  #--------------------------------------------------------------------------
  # ○ 目標に近づく(壁周り)
  #--------------------------------------------------------------------------
  def move_toward_target(target)
    return if !target
    @target = target
    x_d = distance_x_from_point(target.x).abs
    y_d = distance_y_from_point(target.y).abs
    return if x_d + y_d <= 1
    move_toward_point([target.x, target.y])
  end
end
ダイトウさんの提示したマップを実際に組んで確かめたところ、修正版ではSEARCH_AREAを150にすれば
全員が目標地点までたどり着けることが分かりました。
これ以外にイベントを置かなければあまり負荷がかからず達成できると思います(実際はあるんでしょうけれど)。

ちなみに経路探索の最大の敵はマップの幅です。
幅を1本にしたところ、SEARCH_AREAが96で足りました(ただし全員すり抜け)。
SEARCH_AREAの意味は「探索するマスの数」なので、探索するマスの数が多ければ多いほど精度が下がります。
---------------------------------------------
自作の(改造でない)スクリプト素材に
関しては、リードミーもしくは作中に
「faida」と記名していただければ
利用可能です。

“VX / Ace:スクリプト素材のリクエスト” へ戻る