查看: 1968|回复: 1

[已经解决] 怪物在地图上移动的AI

[复制链接]
梦石
0
星屑
50
在线时间
53 小时
注册时间
2013-2-16
回帖
13
发表于 2013-10-22 13:53:13 | 显示全部楼层 |阅读模式

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

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

×
把怪物放在地图上设置接近玩家

但是怪物和玩家中间只要隔着一个障碍物

怪物就被卡住了

请问该如何做才能让怪物撞到墙壁时

会自动寻找四周有没有通路可以走

而不是直接卡在那里不动呢

评分

参与人数 1星屑 +7 收起 理由
熊喵酱 + 7 主動認可獎勵

查看全部评分

梦石
0
星屑
22879
在线时间
8598 小时
注册时间
2011-12-31
回帖
3270
发表于 2013-10-22 14:08:54 | 显示全部楼层
本帖最后由 tseyik 于 2013-10-22 14:35 编辑

経路探索VXAce
http://canarialt.blog.fc2.com/blog-entry-43.html
1.gif
範本(含有其他脚本)
https://skydrive.live.com/redir. ... ey=!ACU2FoKV2gdVl-w
注釈取得スクリプト (VX Ace用)
[pre lang="ruby" line="1"]#==============================================================================
# ■ 注釈取得スクリプト (VX Ace用)
#------------------------------------------------------------------------------
# 製作者     : CanariAlternate
# サイト名   : カルトの鳥篭
# サイトURL  : http://canarialt.blog.fc2.com
#------------------------------------------------------------------------------
# ■ 概要 : 注釈を取得する処理を定義する。
#
# ■ 必須 : なし
#
# ■ 位置 : 「Game_Event」より下
#------------------------------------------------------------------------------
# ■ 注釈での設定方法
#   以下の例のように実行内容の1行目から注釈以外のコマンドまでの注釈のどこかに
#   キーワードを記述すると有効(結果を反転してる場合には無効)になります。
#
#             例 : 実行内容
#                   ◆注釈:キーワードA キーワードC
#                   :    :キーワードG キーワードF
#                   :    :キーワードE
#                   ◆注釈:キーワードD
#                   :    :キーワードH キーワードB
#                   ◆変数の操作などの注釈以外のイベントコマンド
#                   ◆注釈:ここ以下に書いても無視される。
#                   ◆
#
# ※キーワードとなる文字列の設定は正規表現です。詳しいことはヘルプを「正規表現」で検索すると出てきます。
#   簡単に言うと / で囲まれた文字列があるかないかで判定します。
#   \ とか ! みたいな記号を使いたい時はヘルプを参照して下さい。
#   設定例、/\[キーワード\]/  #=>  [キーワード]
#
# ※キーワードを記述すると有効と無効が切り替わります。初期状態は無効です。
#   例 : 初期状態を反転, 記述したイベントの全ての頁を反転, 記述した頁のみを反転
#   まず  「初期状態を反転」                  で有効に
#   次に  「記述したイベントの全ての頁を反転」で無効に
#   最後に「記述した頁のみを反転」            で有効に
#------------------------------------------------------------------------------
# 更新履歴 : 2012/10/05 Ver1.00 コードを共通処理スクリプトに統一
#            2012/10/06 Ver1.01 コードを共通処理スクリプトから分離
#            2012/10/08 Ver1.02 コードの整理
#            2013/02/19 Ver1.03 共通処理スクリプトの廃止による変更を施した。
#==============================================================================

$imported ||= {}
$imported[:CanariAlternate_EventNote] = true

#==============================================================================
# ■ Calt
#------------------------------------------------------------------------------
#  CanariAlternateが製作したスクリプトの管理用モジュール
#==============================================================================
module Calt
  #--------------------------------------------------------------------------
  # ● イベントコマンドから注釈を取得 [新規]
  #--------------------------------------------------------------------------
  def self.Read_EventNote(list)
    return String.new unless list && list[i = 0].code == 108
    note = list.parameters[0]
    note += "\n#{list.parameters[0]}" while [108, 408].include?(list[i += 1].code)
    return note
  end
end

#==============================================================================
# ■ Game_Event
#------------------------------------------------------------------------------
#  イベントを扱うクラスです。条件判定によるイベントページ切り替えや、並列処理
# イベント実行などの機能を持っており、Game_Map クラスの内部で使用されます。
#==============================================================================
class Game_Event < Game_Character
  #--------------------------------------------------------------------------
  # ● イベントページの設定をセットアップ [追加]
  #--------------------------------------------------------------------------
  alias setup_page_settings_EventNote setup_page_settings
  def setup_page_settings
    setup_page_settings_EventNote
    setup_note_settings
  end
  #--------------------------------------------------------------------------
  # ● イベントページ更新時に注釈から設定を取得 [新規]
  #--------------------------------------------------------------------------
  def setup_note_settings
    # 注釈の取得処理はここに追加
  end
  #--------------------------------------------------------------------------
  # ● 1頁目の注釈にキーワードが含まれているか判定 [新規]
  #--------------------------------------------------------------------------
  def whole_note_include?(keyword)
    return @event.pages[0].event_note =~ keyword ? true : false
  end
  #--------------------------------------------------------------------------
  # ● この頁の注釈にキーワードが含まれているか判定 [新規]
  #--------------------------------------------------------------------------
  def event_note_include?(keyword)
    return @page.event_note =~ keyword ? true : false
  end
end

#==============================================================================
# ■ RPG::Event::Page
#------------------------------------------------------------------------------
#  イベントページのデータクラス。
#==============================================================================
class RPG::Event::Page
  #--------------------------------------------------------------------------
  # ● 取得する注釈内容を変更 [新規]
  #--------------------------------------------------------------------------
  attr_writer   :event_note
  #--------------------------------------------------------------------------
  # ● この頁の注釈を取得 [新規]
  #--------------------------------------------------------------------------
  def event_note
    return @event_note ||= Calt::Read_EventNote(@list)
  end
end[/pre]

経路探索VXAce (VX Ace用)
[pre lang="ruby" line="1"]#==============================================================================
# ■ 経路探索VXAce (VX Ace用)
#------------------------------------------------------------------------------
# 製作者     : CanariAlternate
# サイト名   : カルトの鳥篭
# サイトURL  : http://canarialt.blog.fc2.com
#------------------------------------------------------------------------------
# ■ 概要 : 指定した座標(またはキャラクター)まで経路探索をしながら移動する。
#           この経路探索では一定時間動いていないイベントは障害物として認識する。
#
# ■ 必須 : 「注釈取得スクリプト」
#
# ■ 位置 : 「注釈取得スクリプト」より下
#           「マルチレイヤーシステム」より下
#------------------------------------------------------------------------------
# ■ 機能 : 1. 指定した座標(またはキャラクター)まで移動する。
#
#           2. 経路探索の移動で進入禁止の領域をリージョンIDで指定する。
#
#           3. 注釈にキーワードを記述すると経路探索において障害物と見なさない。
#
# ■ スクリプト(移動ルートの設定) :
#           1. route_search(引数)
#              目標に到達するまで移動を繰り返す。
#              移動ルートの設定の「移動できない場合は飛ばす」は route_search には無効である。
#              引数はハッシュで設定する。省略するとあらかじめ設定されている初期値が採用される。
#              a. 目標の設定(※初期値はプレイヤー)
#                 :x => x座標
#                 :y => y座標
#                 :z => z座標(通常は使用しないので省略可能)
#                 :char_id => イベントID
#                 :character => オブジェクト($game_playerなど)
#
#                 指定する方法は以下の3通りである。
#                 :x, :y, :z は座標で指定する。:x と :y の両方設定しないと無効になる。
#                 :char_id はイベントのIDで指定する。プレイヤーを指定したい場合は -1 である。
#                 :character はオブジェクトを直接指定する。
#                 例1 route_search(:x => 5, :y => 7)             座標(5, 7)が目標
#                 例2 route_search(:char_id => 5)                イベントIDが5番のイベントが目標
#                 例3 route_search(:character => $game_player)   プレイヤーが目標
#
#              b. 斜め移動の使用(※初期値はfalse)
#                 :diagonal => true / false
#                 true  は斜め移動を使用する。
#                 false は斜め移動を使用しない。
#
#              c. 探索失敗時の行動(※初期値はtrue)
#                 :fail => true / false
#                 true  は失敗時はランダムに移動する。
#                 false は失敗時はウェイトする。
#
#              d. 経路探索を実行する目標との距離(※初期値は25)
#                 :length => 数値
#                 設定した数値より目標が遠い場合はウェイトする。
#
#              e. ウェイトの時間(※初期値は6)
#                 :wait => フレーム数
#                 false を設定するとウェイトせずに経路探索を中止する。
#
#              f. 目標の何歩前まで移動するか(※初期値は0)
#                 :near => 数値
#                 例えば、1 の場合は目標の1歩手前で探索終了する。
#                 0 であれば目標と重なるまで探索を終了しない。
#
#              g. 探索回数の上限(※初期値はfalse)
#                 :give_up => 数値
#                 false を設定すると自動モードになる。
#                 大きな数値(およそ100以上)を設定すると遠くまで探索するがPCへの負荷も大きくなる。
#
#              h. 1回移動したら次の命令を実行(※初期値はfalse)
#                 :manual => true / false
#                 true  は1回移動すると次の命令を実行します。
#                 false は目標への移動が完了するまで移動を続けます。
#
#              i. 進入禁止のリージョンIDを設定(※初期値はこの下で設定)
#                 :region => [5, 6, 7]
#                 リージョンIDは配列で設定する必要がある。
#
#              これらの引数は全て省略可能で route_search と記述するだけでもプレイヤーを追尾する。
#------------------------------------------------------------------------------
# 更新履歴 : 2013/01/05 Ver2.05 スクリプトを大幅に変更した。スクリプト名を変更した。
#            2013/01/09 Ver3.05 内部処理を大幅に変更した。
#            2013/02/19 Ver3.06 共通処理スクリプトの廃止による変更を施した。
#            2013/06/28 Ver3.07 失敗時のランダム移動のバグを修正した。
#            2013/06/28 Ver3.08 斜め移動では[経路探索で無視]を無効にするように変更した。
#            2013/06/29 Ver3.09 ある条件を満たすと経路情報を捨てる機能を廃止した。
#            2013/06/29 Ver3.10 1回移動したら次の命令を実行するオプションを追加した。
#            2013/06/29 Ver3.11 ループしたマップで正常に移動しないバグを修正した。
#            2013/08/18 Ver3.12 進入禁止のリージョンIDを個別に設定できる機能を追加した。
#==============================================================================

$imported ||= {}
$imported[:CanariAlternate_RouteSearch] = true

#==============================================================================
# ■ Calt
#------------------------------------------------------------------------------
#  CanariAlternateが製作したスクリプト用のモジュールです。
#==============================================================================
module Calt
  #-----------------------------------------------------------------------------
  # ◆経路探索で進入禁止にするリージョンIDの配列(引数を省略した際に適用される設定)
  NOT_ENTER_REGION_ID = [4]
  #
  # ◆イベントを障害物として扱わないことを設定するキーワード
  SCAN_THROUGH_EVENT_NOTE = /\[経路探索で無視\]/ # 記述した頁に適用
  SCAN_THROUGH_WHOLE_NOTE = /\<経路探索で無視\>/ # 全ての頁に適用(1頁目に記述)
  #-----------------------------------------------------------------------------
end

#==============================================================================
# ■ 探索処理 ■
#==============================================================================
#==============================================================================
# ■ Game_Character
#------------------------------------------------------------------------------
#  主に移動ルートなどの処理を追加したキャラクターのクラスです。Game_Player、
# Game_Follower、GameVehicle、Game_Event のスーパークラスとして使用されます。
#==============================================================================
class Game_Character < Game_CharacterBase
  #--------------------------------------------------------------------------
  # ● 定数
  #--------------------------------------------------------------------------
  LongSearchRate = 0.1                    # 探索回数の自動設定で長距離探索する確率
  #--------------------------------------------------------------------------
  # ● クラス変数
  #--------------------------------------------------------------------------
  @@route_search_frame = 0                # 経路探索を実行したフレームを記憶
  @@give_up_rate       = LongSearchRate   # 長距離探索を実行する確率
  #--------------------------------------------------------------------------
  # ● 経路探索で移動 [新規]
  #--------------------------------------------------------------------------
  def route_search(argument={})
    # パラメータの取得
    gx, gy, gz = get_goal_coordinate(argument)                          # 目的地(対象)の座標を取得
    diagonal = argument[:diagonal] != nil ? argument[:diagonal] : false # 斜め移動の使用の有無
    length   = argument[:length]   != nil ? argument[:length]   : 25    # 探索を実行する対象との距離
    wait     = argument[:wait]     != nil ? argument[:wait]     : 6     # ウェイト時間(false は待機せず探索中止)
    near     = argument[:near]     != nil ? argument[:near]     : 0     # 対象の何歩手前で停止するか
    fail     = argument[:fail]     != nil ? argument[:fail]     : true  # 探索失敗時に適当な移動を行うか
    give_up  = argument[:give_up]  != nil ? argument[:give_up]  : false # 探索回数の上限(false は自動)
    manual   = argument[:manual]   != nil ? argument[:manual]   : false # 1回移動したら次の命令を実行
    region   = argument[:region]   != nil ? argument[:region]   : Calt::NOT_ENTER_REGION_ID # 進入禁止のリージョンIDの配列

    # 経路探索の設定
    @routing_object ||= Route_Search.new(self)                          # 経路探索のクラスのオブジェクトを生成
    @routing_object.not_enter_region_id     = region                    # 進入禁止のリージョンIDの配列を設定
    @routing_object.limit_search_count      = get_give_up(give_up)      # 探索回数の上限を設定
    @routing_object.always_collide_through  = false                     # 常に衝突判定無視を無効に設定
    @routing_object.ending_collide_distance = near != 0 ? near - 1 : 0  # 終点付近で衝突判定無視の距離を設定
    @routing_object.ending_through_distance = near - 1                  # 終点付近で通行判定無視の距離を設定
    @routing_object.diagonal                = diagonal                  # 斜め移動の使用の有無を設定
    @routing_object.active_collide_through  = 60                        # 指定フレーム以内に移動してるキャラクターは衝突判定無視を設定

    # 探索実行範囲内か判定
    distance = @routing_object.distance_xy(@x, @y, gx, gy)              # 終点までの最短距離を取得
    return wait_search(wait, manual) if distance > length               # 探索範囲外の場合はウェイト

    # 探索成功範囲内か判定
    if distance <= near && !(gx == @x && gy == @y && gz != z)           # 探索成功の場合
      return end_route_search                                           # 経路探索終了の手続きを実行
    end

    last_x, last_y, last_z = @x, @y, z                                  # 現在の座標を記憶
    last_route_list = @route_list                                       # 現在の経路情報を記憶
   
    # 通常の経路探索
    @route_list = @routing_object.a_star_search(gx, gy, gz)             # 経路情報の取得
    if !@route_list || @route_list.empty?                               # 経路情報を取得できなかった場合
      @route_list = nil                                                 # 経路情報を消去
      return wait_search(wait, manual) unless fail                      # 失敗時にランダム移動しない場合はウェイト
      fail_move_random(diagonal, region)                                # 失敗した場合の移動
      set_move_succeed(wait, last_x, last_y, last_z)                    # 移動成功を判定
      continue_route_search unless manual                               # 経路探索継続の手続きを実行
      return
    end
    if @route_list[0] == [gx, gy, gz]                                   # 終点までの経路情報の取得に成功した場合
      route_list_to_move(@route_list)                                   # 経路に従って移動
      set_move_succeed(wait, last_x, last_y, last_z)                    # 移動成功を判定
      continue_route_search unless manual                               # 経路探索継続の手続きを実行
      return
    end

    # 以前の経路情報を使用して経路探索
    new_route_list = @route_list                                        # 最新の推定経路情報を記憶
    @routing_object.limit_search_count = get_give_up(give_up)           # 探索回数の上限を設定
    @route_list = @routing_object.reuse_route_list(last_route_list, 5, gx, gy, gz)  # 以前の経路情報を使って探索
    if !@route_list || @route_list.empty?                               # 経路情報を取得できなかった場合
      @route_list = nil                                                 # 経路情報を消去
      return wait_search(wait, manual) unless fail                      # 失敗時に移動しない場合はウェイト
      @route_list = new_route_list                                      # 最新の推定経路情報を復帰
      route_list_to_move(@route_list)                                   # 経路に従って移動
      set_move_succeed(wait, last_x, last_y, last_z)                    # 移動成功を判定
      continue_route_search unless manual                               # 経路探索継続の手続きを実行
      return
    end
    if @route_list[0] == [gx, gy, gz]                                   # 終点までの経路情報の取得に成功した場合
      route_list_to_move(@route_list)                                   # 経路に従って移動
      set_move_succeed(wait, last_x, last_y, last_z)                    # 移動成功を判定
      continue_route_search unless manual                               # 経路探索継続の手続きを実行
      return
    end
    unless fail                                                         # 失敗時に移動しない場合
      @route_list = nil                                                 # 経路情報を消去
      wait_search(wait, manual)                                         # ウェイト
      return
    end
    old_x = @route_list[0][0]
    old_y = @route_list[0][1]
    old_distance = @routing_object.distance_xy(old_x, old_y, gx, gy)    # 更新推定経路の終点までの最短距離を取得
    new_x = new_route_list[0][0]
    new_y = new_route_list[0][1]
    new_distance = @routing_object.distance_xy(new_x, new_y, gx, gy)    # 最新推定経路の終点までの最短距離を取得
    @route_list = new_route_list if old_distance >= new_distance        # 最新推定経路の方が優れている場合は経路情報を復帰
    route_list_to_move(@route_list)                                     # 経路に従って移動
    set_move_succeed(wait, last_x, last_y, last_z)                      # 移動成功を判定
    continue_route_search unless manual                                 # 経路探索継続の手続きを実行
    return
  end
  #--------------------------------------------------------------------------
  # ● 対象の座標を取得 [新規]
  #--------------------------------------------------------------------------
  def get_goal_coordinate(argument)
    if argument[:x] && argument[:y]           # 座標で直接指定の場合
      return [argument[:x], argument[:y], argument[:z] || 0]
    end
    character = argument[:char]               # キャラクターのオブジェクトを直接指定
    if !character && id = argument[:char_id]  # キャラクターのIDで指定の場合
      character = case id
      when 0 ; self                           # id : 0  はこのキャラクター自体
      when -1; $game_player                   # id : -1 はプレイヤー
      else   ; $game_map.events[id] || self   # それ以外はイベント
      end
    else
      character = $game_player                # 指定が無い場合はプレイヤー
    end
    return [character.x, character.y, character.z]
  end
  #--------------------------------------------------------------------------
  # ● 経路探索を待機 [新規]
  #--------------------------------------------------------------------------
  def wait_search(wait, manual)
    if wait
      @wait_count = wait      # ウェイトを設定
      manual ? end_route_search : continue_route_search
    else
      @wait_count = 0         # ウェイトはなし
      end_route_search
    end
  end
  #--------------------------------------------------------------------------
  # ● 経路探索による移動を終了の手続き [新規]
  #--------------------------------------------------------------------------
  def end_route_search
    @move_succeed = true
  end
  #--------------------------------------------------------------------------
  # ● 経路探索による移動を継続の手続き [新規]
  #--------------------------------------------------------------------------
  def continue_route_search
    @move_succeed = true
    @move_route_index -= 1  # もう1度実行されるように実行位置を1つ戻す
  end
  #--------------------------------------------------------------------------
  # ● 経路リストに従って移動 [新規]
  #--------------------------------------------------------------------------
  def route_list_to_move(route_list)
    dx = distance_x_from(route_list[-1][0])
    dy = distance_y_from(route_list[-1][1])
    if dx == 0
      dy == 0 ? nil              : (dy < 0 ? move_straight(2)    : move_straight(8)   )
    elsif dx < 0
      dy == 0 ? move_straight(6) : (dy < 0 ? move_diagonal(6, 2) : move_diagonal(6, 8))
    else
      dy == 0 ? move_straight(4) : (dy < 0 ? move_diagonal(4, 2) : move_diagonal(4, 8))
    end
  end
  #--------------------------------------------------------------------------
  # ● 移動成功を判定 [新規]
  #--------------------------------------------------------------------------
  def set_move_succeed(wait, last_x, last_y, last_z)
    if last_x != @x || last_y != @y || last_z != z
      @move_succeed = true                                      # 移動成功
    else
      @move_succeed = false                                     # 移動失敗
      @wait_count = (wait || 6)                                 # 過負荷を回避する為にウェイト
      @route_list.push [@x, @y, z] if @route_list               # 経路情報を修正
    end
  end
  #--------------------------------------------------------------------------
  # ● 負荷を考慮した探索回数の上限値を取得 [新規]
  #--------------------------------------------------------------------------
  def get_give_up(give_up)
    return give_up if give_up
    if @@route_search_frame != Graphics.frame_count
      @@give_up_rate = LongSearchRate                           # 確率を初期化
      @@route_search_frame = Graphics.frame_count               # フレームの記憶を更新
    end
    if rand(10000) < 10000 * @@give_up_rate
      @@give_up_rate = 0                                        # 確率を更新
      result = 10 + rand(140)                                   # 高い上限を設定
    else
      @@give_up_rate *= @@give_up_rate / (1.0 - @@give_up_rate) # 確率を更新
      result = 10                                               # 低い上限を設定
    end
    return result
  end
  #--------------------------------------------------------------------------
  # ● 失敗した場合の移動 [新規]
  #--------------------------------------------------------------------------
  def fail_move_random(diagonal, region)
    # 斜め移動の場合
    if diagonal && rand(2) == 0
      horz = rand(2) == 0 ? 4 : 6
      vert = rand(2) == 0 ? 2 : 8
      nx = $game_map.round_x_with_direction(@x, horz)
      ny = $game_map.round_y_with_direction(@y, vert)
      return if region.include?($game_map.region_id(nx, ny))
      move_diagonal(horz, vert)
      return
    end
    # 4方向移動の場合
    if rand(2) == 0
      horz = rand(2) == 0 ? 4 : 6
      nx = $game_map.round_x_with_direction(@x, horz)
      return if region.include?($game_map.region_id(nx, @y))
      move_straight(horz)
    else
      vert = rand(2) == 0 ? 2 : 8
      ny = $game_map.round_y_with_direction(@y, vert)
      return if region.include?($game_map.region_id(@x, ny))
      move_straight(vert)
    end
  end
end

#==============================================================================
# ■ 基礎定義 ■
#==============================================================================
#==============================================================================
# ■ Game_CharacterBase
#------------------------------------------------------------------------------
#  キャラクターを扱う基本のクラスです。全てのキャラクターに共通する、座標やグ
# ラフィックなどの基本的な情報を保持します。
#==============================================================================
class Game_CharacterBase
  #--------------------------------------------------------------------------
  # ● クラス変数
  #--------------------------------------------------------------------------
  @@active_collide_through = false        # 座標一致判定の無効化
  @@collide_through        = false        # 衝突判定無視
  @@search_diagonal        = false        # 斜めの移動判定か否か
  def active_collide_through=(arg) ; @@active_collide_through = arg ; end
  def collide_through=(arg)        ; @@collide_through        = arg ; end
  def search_diagonal=(arg)        ; @@search_diagonal        = arg ; end
  #--------------------------------------------------------------------------
  # ● 公開インスタンス変数
  #--------------------------------------------------------------------------
  attr_reader   :stopwatch                # 連続停止時間
  #--------------------------------------------------------------------------
  # ● z 座標の取得 [新規]
  #--------------------------------------------------------------------------
  def z
    return 0                              # 座標が3次元の場合は再定義する
  end
  #--------------------------------------------------------------------------
  # ● 公開メンバ変数の初期化 [追加]
  #--------------------------------------------------------------------------
  alias init_public_members_RouteSearch init_public_members
  def init_public_members
    init_public_members_RouteSearch
    @stopwatch = 1.0/0                    # 1.0/0 #=> Infinity
    @movement_record = [[nil, nil, nil]]  # 移動履歴
  end
  #--------------------------------------------------------------------------
  # ● 非公開メンバ変数の初期化 [追加]
  #--------------------------------------------------------------------------
  alias init_private_members_RouteSearch init_private_members
  def init_private_members
    init_private_members_RouteSearch
    @scan_through_event = false           # 経路探索において障害物として扱わないフラグ
  end
  #--------------------------------------------------------------------------
  # ● フレーム更新 [追加]
  #--------------------------------------------------------------------------
  alias update_RouteSearch update
  def update
    update_RouteSearch
    update_stopwatch                      # 連続停止時間を更新
  end
  #--------------------------------------------------------------------------
  # ● 連続停止時間を更新 [新規]
  #--------------------------------------------------------------------------
  def update_stopwatch
    if @movement_record[-1] != [x, y, z]
      @stopwatch = 0 if @movement_record.size > 1
      @movement_record.push [x, y, z]
      @movement_record.shift if @movement_record.size > 10
    elsif !moving?
      @stopwatch += 1
    end
  end
  #--------------------------------------------------------------------------
  # ● 座標一致判定 [追加]
  #--------------------------------------------------------------------------
  alias pos_RouteSearch? pos?
  def pos?(*args)
    return false if @@collide_through and !tile? || @scan_through_event && !@@search_diagonal
    # 連続停止時間が指定値以下の場合は座標を不一致にすることで衝突判定を回避する。
    return false if @@active_collide_through and @stopwatch <= @@active_collide_through && !tile? || @scan_through_event && !@@search_diagonal
    pos_RouteSearch?(*args)
  end
end
#==============================================================================
# ■ Game_Event
#------------------------------------------------------------------------------
#  イベントを扱うクラスです。条件判定によるイベントページ切り替えや、並列処理
# イベント実行などの機能を持っており、Game_Map クラスの内部で使用されます。
#==============================================================================
class Game_Event < Game_Character
  #--------------------------------------------------------------------------
  # ● 衝突判定無視の設定を取得 [新規]
  #--------------------------------------------------------------------------
  def get_scan_through_event_note
    event_result = event_note_include?(Calt::SCAN_THROUGH_EVENT_NOTE)
    whole_result = whole_note_include?(Calt::SCAN_THROUGH_WHOLE_NOTE)
    return event_result ^ whole_result
  end
  #--------------------------------------------------------------------------
  # ● イベントページ更新時に注釈から設定を取得 [追加]
  #--------------------------------------------------------------------------
  alias setup_note_settings_RouteSearch setup_note_settings
  def setup_note_settings
    setup_note_settings_RouteSearch
    @scan_through_event = get_scan_through_event_note
  end
end
#==============================================================================
# ■ Route_Search
#------------------------------------------------------------------------------
#  経路探索の処理を定義したクラスです。
#==============================================================================
class Route_Search
  #--------------------------------------------------------------------------
  # ● 基礎変数の初期化
  #--------------------------------------------------------------------------
  def init_base_instance(character)
    @character = character                # キャラクターへの参照
    @x , @y , @z  = 0, 0, 0               # 今のノードの座標
    @sx, @sy, @sz = 0, 0, 0               # 始点の座標
    @gx, @gy, @gz = 0, 0, 0               # 終点の座標
    @diagonal = false                     # 斜め移動の使用の有無
    @collide_through = false              # 衝突判定を無視するフラグ
    @active_collide_through = 60          # 指定フレーム以内に移動してるキャラクターは衝突判定無視
  end
  #--------------------------------------------------------------------------
  # ● 2点間の x 方向の距離
  #--------------------------------------------------------------------------
  def distance_x(start_x, end_x)
    result = end_x - start_x
    return result unless $game_map.loop_horizontal? && result.abs > $game_map.width / 2
    return result + (result < 0 ? $game_map.width : -$game_map.width)
  end
  #--------------------------------------------------------------------------
  # ● 2点間の y 方向の距離
  #--------------------------------------------------------------------------
  def distance_y(start_y, end_y)
    result = end_y - start_y
    return result unless $game_map.loop_vertical? && result.abs > $game_map.height / 2
    return result + (result < 0 ? $game_map.height : -$game_map.height)
  end
  #--------------------------------------------------------------------------
  # ● 2点間の z 方向の距離
  #--------------------------------------------------------------------------
  def distance_z(start_z, end_z)
    return end_z - start_z
  end
  #--------------------------------------------------------------------------
  # ● 2点の距離を取得
  #--------------------------------------------------------------------------
  def distance_xy(start_x, start_y, end_x, end_y)
    if @diagonal
      return [distance_x(start_x, end_x).abs, distance_y(start_y, end_y).abs].max # 8方向の場合
    else
      return distance_x(start_x, end_x).abs + distance_y(start_y, end_y).abs      # 4方向の場合
    end
  end
  #--------------------------------------------------------------------------
  # ● x 座標の取得
  #--------------------------------------------------------------------------
  def character_x
    return @character.x
  end
  #--------------------------------------------------------------------------
  # ● y 座標の取得
  #--------------------------------------------------------------------------
  def character_y
    return @character.y
  end
  #--------------------------------------------------------------------------
  # ● z 座標の取得
  #--------------------------------------------------------------------------
  def character_z
    return @character.z
  end
  #--------------------------------------------------------------------------
  # ● z 座標の変換
  #--------------------------------------------------------------------------
  def xyz_to_z(x, y, z)
    return z
  end
  #--------------------------------------------------------------------------
  # ● 進入禁止のリージョンIDの座標か判定
  #--------------------------------------------------------------------------
  def not_enter_coordinate?(x, y, z)
    return not_enter_region_id.include?($game_map.region_id(x, y))
  end
  #--------------------------------------------------------------------------
  # ● 終点か判定
  #--------------------------------------------------------------------------
  def end_coordinate?(x, y, z)
    return x == @gx && y == @gy && z == @gz
  end
  #--------------------------------------------------------------------------
  # ● 終点から範囲内か判定
  #--------------------------------------------------------------------------
  def ending_distance?(x, y, z, distance)
    return false if x == @gx && y == @gy && z != @gz
    return false if distance_xy(x, y, @gx, @gy) > distance
    return true
  end
  # #--------------------------------------------------------------------------
  # # ● 始点から範囲内か判定
  # #--------------------------------------------------------------------------
  # def starting_distance?(x, y, z, distance)
  #   return false if x == @sx && y == @sy && z != @sz
  #   return false if distance_xy(x, y, @sx, @sy) > distance
  #   return true
  # end
  # #--------------------------------------------------------------------------
  # # ● 始点の隣接座標か判定(始点を含む)
  # #--------------------------------------------------------------------------
  # def adjoin_coordinate?(x, y, z)
  #   return false unless starting_distance?(x, y, z, 1)
  #   return xyz_to_z(x, y, @sz) == z
  # end
  #--------------------------------------------------------------------------
  # ● 通行可能判定
  #--------------------------------------------------------------------------
  def passable?(d)
    @character.active_collide_through = @active_collide_through
    @character.collide_through = @collide_through
    @character.search_diagonal = false
    result = base_passable?(:passable?, d)
    @character.collide_through = false
    @character.active_collide_through = false
    return result
  end
  #--------------------------------------------------------------------------
  # ● 斜めの通行可能判定
  #--------------------------------------------------------------------------
  def diagonal_passable?(horz, vert)
    @character.active_collide_through = @active_collide_through
    @character.collide_through = @collide_through
    @character.search_diagonal = true
    result = base_passable?(:diagonal_passable?, horz, vert)
    @character.collide_through = false
    @character.active_collide_through = false
    return result
  end
  #--------------------------------------------------------------------------
  # ● 通行可能判定の呼び出し
  #--------------------------------------------------------------------------
  def base_passable?(name, *d)
    return @character.send(name, @x, @y, *d)
  end
end

#==============================================================================
# ■ 内部処理 ■
#==============================================================================
#==============================================================================
# ■ Route_Search
#------------------------------------------------------------------------------
#  経路探索の処理を定義したクラスです。
#==============================================================================
class Route_Search
  #--------------------------------------------------------------------------
  # ● 座標の配列のインデックス
  #--------------------------------------------------------------------------
  X = 0                                   # x座標
  Y = 1                                   # y座標
  Z = 2                                   # z座標
  #--------------------------------------------------------------------------
  # ● ノードの配列のインデックス
  #--------------------------------------------------------------------------
  RANK_A = 0                              # 評価値A
  RANK_B = 1                              # 評価値B
  RANK_C = 2                              # 評価値C
  PARENT = 3                              # 親の座標
  LENGTH = 4                              # 移動距離
  #--------------------------------------------------------------------------
  # ● 公開インスタンス変数
  #--------------------------------------------------------------------------
  attr_accessor :not_enter_region_id      # 進入禁止のリージョンIDの配列
  attr_accessor :limit_search_count       # 探索回数の上限値
  attr_accessor :always_collide_through   # 常に衝突判定を無視のフラグ
  attr_accessor :ending_collide_distance  # 終点付近で衝突判定を無視の距離
  attr_accessor :ending_through_distance  # 終点付近で通行判定を無視の距離
  attr_accessor :diagonal                 # 斜め移動の使用の有無
  attr_accessor :active_collide_through   # 指定フレーム以内に移動してるキャラクターは衝突判定無視
  #--------------------------------------------------------------------------
  # ● オブジェクト初期化
  #--------------------------------------------------------------------------
  def initialize(character)
    init_base_instance(character)
    @parent = [nil, nil, nil]             # 親の座標
    @past_distance = 0                    # 探索点までの移動距離
    @open_list = {}                       # 探索候補のノードのリスト
    @close_list = {}                      # 探索済みのノードのリスト
    @limit_search_count = 100
    @always_collide_through = false
    @ending_collide_distance = 0
    @ending_through_distance = 0
  end
  #--------------------------------------------------------------------------
  # ● A*経路探索の通常実行
  #--------------------------------------------------------------------------
  def a_star_search(gx, gy, gz=0)
    init_process(gx, gy, gz)              # A*経路探索の初期化
    return main_process                   # A*経路探索の主要処理
  end
  #--------------------------------------------------------------------------
  # ● A*経路探索の初期化
  #--------------------------------------------------------------------------
  def init_process(gx, gy, gz)
    @x, @y, @z = character_x, character_y, character_z # 現在の座標
    @sx, @sy, @sz = @x, @y, @z            # 始点の座標
    @gx, @gy, @gz = gx, gy, gz            # 終点の座標
    @open_list  = {[@sx, @sy, @sz]=>create_node(@sx, @sy, @sz, [nil, nil, nil], 0)}
    @close_list = {}
  end
  #--------------------------------------------------------------------------
  # ● A*経路探索の主要処理
  #--------------------------------------------------------------------------
  def main_process
    count = 0
    until @open_list.empty?
      node, coordinate = get_best_node(@open_list)  # 最も高評価のノードを探索候補のリストから取得
      
      # 探索成功の場合
      return create_route_list(node, coordinate) if end_coordinate?(*coordinate)

      # 探索回数の上限オーバーの場合
      return create_estimate_list if (count += 1) > @limit_search_count

      # 隣接点を探索候補に追加する処理
      @close_list[coordinate] = @open_list.delete(coordinate) # 探索候補から探索済みに移動
      @x, @y, @z     = *coordinate
      @parent        = node[PARENT]
      @past_distance = node[LENGTH] + 1
      down  = $game_map.round_y(@y + 1)
      up    = $game_map.round_y(@y - 1)
      right = $game_map.round_x(@x + 1)
      left  = $game_map.round_x(@x - 1)
      open_list_add(@x, down , 2) # 下
      open_list_add(@x, up   , 8) # 上
      open_list_add(right, @y, 6) # 右
      open_list_add(left , @y, 4) # 左
      if @diagonal
        open_list_add_diagonal(right, down, 6, 2) # 右下
        open_list_add_diagonal(left , down, 4, 2) # 左下
        open_list_add_diagonal(right, up  , 6, 8) # 右上
        open_list_add_diagonal(left , up  , 4, 8) # 左上
      end
    end
    return false  # 到達する経路は存在しない。
  end
  #--------------------------------------------------------------------------
  # ● ノードのデータを生成
  #--------------------------------------------------------------------------
  def create_node(x, y, z, parent, length)
    dx = distance_x(x, @gx).abs
    dy = distance_y(y, @gy).abs
    rank_a = length + dx + dy
    rank_b = Math.sqrt(dx ** 2 + dy ** 2)
    rank_c = distance_z(z, @gz).abs
    return [rank_a, rank_b, rank_c, parent, length]
    # [評価値A, 評価値B, 評価値C, 親の座標, 移動距離]
    # 評価値A : 始点から終点までの推定最短経路の距離
    # 評価値B : 終点との直線距離
    # 評価値C : z 座標の距離
    # 親の座標 : 移動元の座標
    # 移動距離 : 始点からこの点までの移動距離
  end
  #--------------------------------------------------------------------------
  # ● 最も高評価のノードを取得
  #--------------------------------------------------------------------------
  def get_best_node(hash_list, compare=:node_a_better?)
    best_node = [1.0/0, 1.0/0, 1.0/0] # 1.0/0 #=> Infinity
    best_coordinate = nil
    hash_list.each do |coordinate, node|
      next if send(compare, best_node, node)  # node よりも best_node が高評価なら next
      best_node = node
      best_coordinate = coordinate
    end
    return best_node, best_coordinate
  end
  #--------------------------------------------------------------------------
  # ● ノードの優劣判定
  #--------------------------------------------------------------------------
  def node_a_better?(node_a, node_b)
    return true  if node_a[RANK_A] < node_b[RANK_A]
    return false if node_a[RANK_A] > node_b[RANK_A]
    return true  if node_a[RANK_B] < node_b[RANK_B]
    return false if node_a[RANK_B] > node_b[RANK_B]
    return true  if node_a[RANK_C] < node_b[RANK_C]
    return false if node_a[RANK_C] > node_b[RANK_C]
    return true
  end
  #--------------------------------------------------------------------------
  # ● 探索候補リストに隣接点を追加
  #--------------------------------------------------------------------------
  def open_list_add(nx, ny, d)
    return if @parent[X] == nx && @parent[Y] == ny
    nz = xyz_to_z(nx, ny, @z)                         # z 座標の変換
    result = exception_decision?(nx, ny, nz)
    return if result == false                         # 通行禁止の場合
    if result == nil                                  # 通行可能判定に委ねる場合
      @collide_through = collide_through?(nx, ny, nz)
      return if !passable?(d)                         # 通行可能判定
    end
    open_list_add_node(nx, ny, nz)                    # 探索候補リストに追加
  end
  #--------------------------------------------------------------------------
  # ● 探索候補リストに隣接点を追加(斜めの判定版)
  #--------------------------------------------------------------------------
  def open_list_add_diagonal(nx, ny, horz, vert)
    return if @parent[X] == nx && @parent[Y] == ny
    nz = xyz_to_z(nx, ny, @z)                         # z 座標の変換
    result = exception_decision?(nx, ny, nz)
    return if result == false                         # 通行禁止の場合
    if result == nil                                  # 通行可能判定に委ねる場合
      @collide_through = collide_through?(nx, ny, nz)
      return if !diagonal_passable?(horz, vert)       # 斜めの通行可能判定
    end
    open_list_add_node(nx, ny, nz)                    # 探索候補リストに追加
  end
  #--------------------------------------------------------------------------
  # ● 例外通行判定
  #--------------------------------------------------------------------------
  def exception_decision?(x, y, z)
    return false if not_enter_coordinate?(x, y, z)
    return true if ending_distance?(x, y, z, @ending_through_distance)
    return nil
  end
  #--------------------------------------------------------------------------
  # ● 衝突判定を無視するか判定
  #--------------------------------------------------------------------------
  def collide_through?(x, y, z)
    return true if @always_collide_through
    return true if ending_distance?(x, y, z, @ending_collide_distance)
    return false
  end
  #--------------------------------------------------------------------------
  # ● 探索候補リストに隣接点を追加する処理
  #--------------------------------------------------------------------------
  def open_list_add_node(nx, ny, nz)
    next_coordinate = [nx, ny, nz]
    if old_node = @close_list[next_coordinate]                            # 探索済みリストに既にある場合
      if old_node[LENGTH] > @past_distance                                # 移動距離が優れている場合
        @open_list[next_coordinate] = @close_list.delete(next_coordinate) # 探索候補リストに移動
        update_node(old_node)
      end
      return
    end
    if old_node = @open_list[next_coordinate]                             # 探索候補リストに既にある場合
      update_node(old_node) if old_node[LENGTH] > @past_distance          # 移動距離が優れている場合
      return
    end
    @open_list[next_coordinate] = create_node(*next_coordinate, [@x, @y, @z], @past_distance)
  end
  #--------------------------------------------------------------------------
  # ● ノードのデータを更新
  #--------------------------------------------------------------------------
  def update_node(old_node)
    old_node[RANK_A] += @past_distance - old_node[LENGTH] # 移動距離の差を減算
    old_node[PARENT]  = [@x, @y, @z]                      # 親の座標を更新
    old_node[LENGTH]  = @past_distance                    # 移動距離を更新
  end
  #--------------------------------------------------------------------------
  # ● 経路の配列を生成
  #--------------------------------------------------------------------------
  def create_route_list(node, coordinate)
    parent = node[PARENT]
    route_list = []
    while parent != [nil, nil, nil]
      route_list.push coordinate
      parent = @close_list[coordinate = parent][PARENT]
    end
    return route_list # 先頭が終点で末尾が始点の次の座標(推定経路の場合は先頭は終点ではない)
  end
  #--------------------------------------------------------------------------
  # ● 推定経路の配列を生成
  #--------------------------------------------------------------------------
  def create_estimate_list
    open_node, open_coordinate = get_best_node(@open_list, :estimate_node_a_better?)
    close_node, close_coordinate = get_best_node(@close_list, :estimate_node_a_better?)
    if estimate_node_a_better?(open_node, close_node)
      return create_route_list(open_node, open_coordinate)
    else
      return create_route_list(close_node, close_coordinate)
    end
  end
  #--------------------------------------------------------------------------
  # ● ノードの優劣判定(推定経路用)
  #--------------------------------------------------------------------------
  def estimate_node_a_better?(node_a, node_b)
    return true  if node_a[RANK_B] < node_b[RANK_B]
    return false if node_a[RANK_B] > node_b[RANK_B]
    return true  if node_a[RANK_C] < node_b[RANK_C]
    return false if node_a[RANK_C] > node_b[RANK_C]
    return true
  end
  #--------------------------------------------------------------------------
  # ● 探索結果を再利用
  #--------------------------------------------------------------------------
  def reuse_route_list(route_list, reuse_distance, gx, gy, gz=0)
    return false unless route_list
    @sx, @sy, @sz = character_x, character_y, character_z
    return false unless index = route_list.index([@sx, @sy, @sz]) # 始点を含んでいるか判定
    (route_list.size - index).times { route_list.pop }
    return false if route_list.empty?
    @gx, @gy, @gz = gx, gy, gz
    if index = route_list.index([@gx, @gy, @gz])                  # 終点を含んでいるか判定
      index.times { route_list.shift }
      return false unless route_list_valid?(route_list)           # 経路を使用可能か判定
      return route_list
    else
      return false unless ending_distance?(*route_list[0], reuse_distance)
      return false unless route_list_valid?(route_list)           # 経路を使用可能か判定
      reuse_search(route_list)                                    # 以前の探索結果を使用して経路探索
    end
  end
  #--------------------------------------------------------------------------
  # ● 経路を使用可能か判定
  #--------------------------------------------------------------------------
  def route_list_valid?(route_list)
    @x, @y, @z = @sx, @sy, @sz
    route_list.reverse.each do |coordinate|
      nx, ny, nz = *coordinate
      result = exception_decision?(nx, ny, nz)
      return false if result == false                             # 通行禁止の場合
      if result == nil                                            # 通行可能判定に委ねる場合
        @collide_through = collide_through?(nx, ny, nz)
        if nx == @x
          return false unless ny == @y ? nil          : (ny > @y ? passable?(2)             : passable?(8)            )
        elsif nx > @x
          return false unless ny == @y ? passable?(6) : (ny > @y ? diagonal_passable?(6, 2) : diagonal_passable?(6, 8))
        else
          return false unless ny == @y ? passable?(4) : (ny > @y ? diagonal_passable?(4, 2) : diagonal_passable?(4, 8))
        end
      end
      @x , @y , @z  = nx, ny, nz
    end
    return true
  end
  #--------------------------------------------------------------------------
  # ● 以前の探索結果を使用して経路探索
  #--------------------------------------------------------------------------
  def reuse_search(route_list)
    init_process(@gx, @gy, @gz)
    parent = [@sx, @sy, @sz]
    route_list.reverse.each do |coordinate|
      @open_list[coordinate] = create_node(*coordinate, parent, 0)  # 移動距離に細工をして追加
      parent = coordinate
    end
    @close_list = @open_list.dup
    return main_process
  end
end

#==============================================================================
# ■ マルチレイヤーシステムとの競合回避 ■
#==============================================================================
if $imported[:CanariAlternate_Multilayer]
  #==============================================================================
  # ■ Game_CharacterBase
  #------------------------------------------------------------------------------
  #  キャラクターを扱う基本のクラスです。全てのキャラクターに共通する、座標やグ
  # ラフィックなどの基本的な情報を保持します。
  #==============================================================================
  class Game_CharacterBase
    #--------------------------------------------------------------------------
    # ● z 座標の取得 [◆再定義]
    #--------------------------------------------------------------------------
    def z
      return current_floor
    end
  end
  #==============================================================================
  # ■ Route_Search
  #------------------------------------------------------------------------------
  #  経路探索の処理を定義したクラスです。
  #==============================================================================
  class Route_Search
    #--------------------------------------------------------------------------
    # ● 定数
    #--------------------------------------------------------------------------
    DummyEvent = Game_CharacterBase.new     # ダミーのイベント
    #--------------------------------------------------------------------------
    # ● z 座標の変換 [◆再定義]
    #--------------------------------------------------------------------------
    def xyz_to_z(x, y, z)
      return $game_map.floor_xy(x, y, z)
    end
    #--------------------------------------------------------------------------
    # ● 進入禁止のリージョンIDの座標か判定 [◆再定義]
    #--------------------------------------------------------------------------
    def not_enter_coordinate?(x, y, z)
      return not_enter_region_id.include?($game_map.region_id(x, y, z))
    end
    #--------------------------------------------------------------------------
    # ● 通行可能判定の呼び出し [◆再定義]
    #--------------------------------------------------------------------------
    def base_passable?(name, *d)
      DummyEvent.current_floor = @z
      return $game_map.temp_execute(DummyEvent) { @character.send(name, @x, @y, *d) }
    end
  end
end[/pre]

点评

非常感激! ! 成功用出效果了! !  发表于 2013-10-22 17:09

评分

参与人数 2星屑 +70 收起 理由
3106345123 + 4 精品文章
熊喵酱 + 66 精品文章

查看全部评分

回复

使用道具 举报

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

本版积分规则

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

在本版发帖返回顶部