#============================================================================== # ■ 簡略移動設定 (VX Ace用) #------------------------------------------------------------------------------ # バージョン : 1.04 # 最終更新日 : 2012/05/10 # 製作者 : CanariAlternate # サイト名 : カルトの鳥篭 # サイトURL : http://canarialt.blog.fc2.com #------------------------------------------------------------------------------ # ■機能 : 「指定した座標まで移動」を移動ルートの設定に追加 # : 「指定した座標まで移動(最短経路探索)」を移動ルートの設定に追加 # : 「複数のキャラを移動」をイベントコマンドに追加 # : 「複数のキャラの向きを変更」をイベントコマンドに追加 # # ★指定した座標まで移動 : 移動ルート設定のスクリプトから実行 # attain4(x, y) : 座標x, 座標y まで移動します。 # # attain8(x, y) : 座標x, 座標y まで移動します。斜め移動も使用します。 # # # ★指定した座標まで移動(最短経路探索) : 移動ルート設定のスクリプトから実行 # attain4s(x, y, event, out) : 最初に経路探索を行いルートを設定します。 # 経路が見つからなかった場合は移動しません。 # # attain8s(x, y, event, out) : 最初に経路探索を行いルートを設定します。 # 経路が見つからなかった場合は移動しません。 # 斜め移動も使用します。 # # ☆ attain4s 及び attain8s の引数について # x : 目的地のX座標 # y : 目的地のY座標 # # event : false 又は true 。false にするとイベントを無視して経路探索を行う。 # 省略すると true になる。 # # out : 経路探索を打ち切る基準を数値で指定します。 # 最短経路の推定距離(マス数)が与えた数値より大きくなったら経路探索を # 中断します。省略すると 35 になる。 # 処理が重くてもいいから長距離の経路探索がしたい時はこれに大きな数値を与えて下さい。 # # # ★複数のキャラを移動 : イベントコマンドのスクリプトから実行 # easy_move(char, xys, diago, wait, skip) # char = [index, index, index, … , index] # index = 0 : このイベント # index = -1 : プレイヤー(隊列1人目) # index = -2 : 隊列2人目 # index = -3 : 隊列3人目 # index = -4 : 隊列4人目 # index = イベントID : IDに対応するイベント # xys = [ [x, y], [x, y], … ,[x, y] ] # daigo = 4 か 8 省略時:4 # wait = true か false 省略時:true # skip = true か false 省略時:false # # 引数の説明 # char : 移動させる対象の指定(配列) # 最初に指定したキャラクターが基準になります。 # 対象は基準との位置関係を維持した座標に移動します。 # 例: [3, 5, 7, 8, 15, 2] # 基準はIDが3番のイベントです。 # # xys = [ 経由A, 経由B, … ,目的地 ] : 移動先の指定(2次元配列) # 経由A⇒経由B⇒ … ⇒目的地 と移動します。 # 例: [ [4, 5], [4, 8], [11, 8] ] # # daigo : 斜め移動の使用の可否です。4は使用しない。8は使用する。 # 省略した場合は 4 になります。 # # wait : 全員の移動終了まで待機の可否です。trueは待機する。falseは待機しない。 # 省略した場合は true になります。 # # skip : 移動が出来ない場合は飛ばすの可否です。trueは飛ばす。falseは飛ばさない。 # 省略した場合は false になります。 # # 設定例1 ◆スクリプト : easy_move([3,11], [ [5, 10], [13, 11] ]) # 省略できる引数は全て省略した例 # # 設定例2 ◆スクリプト : easy_move([-1,0], [ [3, 2], [7, 5] ], 8) # 斜め移動を許可した場合の例 # # 設定例3 ◆スクリプト : easy_move([5], [ [8, 9] ], 4, false) # ウェイトさせない場合の例 # # # ★複数のキャラの向きを変更 : イベントコマンドのスクリプトで実行 # easy_turn(char, d, wait) # char = [index, index, index, … , index] # index = 0 : このイベント # index = -1 : プレイヤー(隊列1人目) # index = -2 : 隊列2人目 # index = -3 : 隊列3人目 # index = -4 : 隊列4人目 # index = イベントID : IDに対応するイベント # d = 2か4か6か8 # wait = true か false 省略時:true # # 引数の説明 # char : 上記の easy_move と同じなので説明は省略 # d : 方向を変える向き。 # 2(下),4(左),6(右),8(上) # wait : 上記の easy_move と同じなので説明は省略 # 省略すると true になります。 # #------------------------------------------------------------------------------ # 更新履歴 : 2012/05/06 Ver1.00 当スクリプトを作成 # : 2012/05/08 Ver1.01 A*経路探索を追加 # : 2012/05/09 Ver1.02 実行位置のループ処理を座標が一致したら # 確実に離脱できるように修正 # : 2012/05/09 Ver1.03 A*経路探索の評価値の計算式を変更 # : 2012/05/10 Ver1.04 A*経路探索の処理を微妙に軽量化 # 軽量化の過程でコードが整理された。 #============================================================================== $imported ||= {} $imported["CanariAlternate_Easy_Move"] = true if $imported["CanariAlternate_Easy_Move"] #============================================================================== # ■ Game_Character #------------------------------------------------------------------------------ #  主に移動ルートなどの処理を追加したキャラクターのクラスです。Game_Player、 # Game_Follower、GameVehicle、Game_Event のスーパークラスとして使用されます。 #============================================================================== class Game_Character < Game_CharacterBase #-------------------------------------------------------------------------- # ● 非公開メンバ変数の初期化 [alias] #-------------------------------------------------------------------------- alias _init_private_members_calt init_private_members def init_private_members _init_private_members_calt @repeat = false # 繰り返し実行 @searchlist = [] # 経路探索リスト end #-------------------------------------------------------------------------- # ● 移動ルートの実行位置を進める [alias] #-------------------------------------------------------------------------- alias _advance_move_route_index_calt advance_move_route_index def advance_move_route_index _advance_move_route_index_calt if advance_judge @repeat = false if @repeat end #-------------------------------------------------------------------------- # ● 移動ルートの実行位置を進めるか判定 [new] #-------------------------------------------------------------------------- def advance_judge attain_pos = false attain_pos = true if @repeat[0] == @x && @repeat[1] == @y if @repeat @move_succeed = true if attain_pos !@repeat || !@move_succeed && @move_route.skippable || attain_pos end #-------------------------------------------------------------------------- # ● 指定した座標まで移動 [new] #-------------------------------------------------------------------------- def attain4(x, y) move_toward_coordinate(x, y) @repeat = [x, y] end #-------------------------------------------------------------------------- # ● 指定した座標まで移動(A*) [new] #-------------------------------------------------------------------------- def attain4s(x, y, event=true, out=35) @searchlist = a_star_search(x, y, event, out) if @searchlist.size == 0 if @searchlist.size > 0 coord = @searchlist.pop move_toward_coordinate(coord[0], coord[1]) @searchlist.push(coord) unless @move_succeed @repeat = [x, y] return end @repeat = [@x, @y] end #-------------------------------------------------------------------------- # ● 指定した座標まで移動(斜め移動可) [new] #-------------------------------------------------------------------------- def attain8(x, y) move_toward_coordinate_diagonal(x, y) @repeat = [x, y] end #-------------------------------------------------------------------------- # ● 指定した座標まで移動(A*斜め移動可) [new] #-------------------------------------------------------------------------- def attain8s(x, y, event=true, out=35) @searchlist = a_star_search(x, y, event, out) if @searchlist.size == 0 if @searchlist.size > 0 if @searchlist.size == 1 coord = @searchlist.pop move_toward_coordinate(coord[0], coord[1]) @searchlist.push(coord) unless @move_succeed else coord = @searchlist.pop coord2 = @searchlist.pop sx = distance_x_from(coord2[0]) sy = distance_y_from(coord2[1]) if sx.abs == 1 && sy.abs == 1 if diagonal_passable?(@x, @y, sx > 0 ? 4 : 6, sy > 0 ? 8 : 2) move_toward_coordinate_diagonal(coord2[0], coord2[1]) @repeat = [x, y] return end end @searchlist.push(coord2) move_toward_coordinate(coord[0], coord[1]) @searchlist.push(coord) unless @move_succeed end @repeat = [x, y] return end @repeat = [@x, @y] end #-------------------------------------------------------------------------- # ● 指定した座標に近づく [new] #-------------------------------------------------------------------------- def move_toward_coordinate(x, y) sx = distance_x_from(x) sy = distance_y_from(y) if sx.abs > sy.abs move_straight(sx > 0 ? 4 : 6) move_straight(sy > 0 ? 8 : 2) if !@move_succeed && sy != 0 elsif sy != 0 move_straight(sy > 0 ? 8 : 2) move_straight(sx > 0 ? 4 : 6) if !@move_succeed && sx != 0 end end #-------------------------------------------------------------------------- # ● 指定した座標に近づく(斜め移動可) [new] #-------------------------------------------------------------------------- def move_toward_coordinate_diagonal(x, y) sx = distance_x_from(x) sy = distance_y_from(y) if sx == 0 || sy == 0 if sx.abs > sy.abs move_straight(sx > 0 ? 4 : 6) move_straight(sy > 0 ? 8 : 2) if !@move_succeed && sy != 0 elsif sy != 0 move_straight(sy > 0 ? 8 : 2) move_straight(sx > 0 ? 4 : 6) if !@move_succeed && sx != 0 end else move_diagonal(sx > 0 ? 4 : 6, sy > 0 ? 8 : 2) if !@move_succeed if sx.abs > sy.abs move_straight(sx > 0 ? 4 : 6) move_straight(sy > 0 ? 8 : 2) if !@move_succeed && sy != 0 elsif sy != 0 move_straight(sy > 0 ? 8 : 2) move_straight(sx > 0 ? 4 : 6) if !@move_succeed && sx != 0 end end end end #-------------------------------------------------------------------------- # ● A*経路探索 [new] # gx : 目的地X座標, gy : 目的地Y座標, # event : イベントとの衝突判定, out : 探索を中止する最短経路距離 #-------------------------------------------------------------------------- def a_star_search(gx, gy, event, out) goal = Array.new([gx, gy]) # 終了節点の座標 now = Array.new([@x, @y]) # 探索節点の座標 # 移動元リスト @parentlist = Array.new($game_map.width) { Array.new($game_map.height) {nil} } # 探索予定リスト @openlist = [ a_star_node(goal, now) ] # 探索終了リスト @closelist = [] out += 1 # 探索を中止する推定最短経路のマス数 while @openlist.size > 0 # 評価値が最小(優先順位が最も高い)の要素を抽出 @openlist.sort! { |a, b| b[0] <=> a[0] } node = @openlist.pop # 探索中止の処理 return [] if node[0] > out now = node[1] # 探索節点の座標 parent = node[2] # 探索節点の移動元 # 探索成功の処理 if now == goal resultlist = [now] # 移動ルートのリスト while parent resultlist.push(parent) parent = @parentlist[ parent[0] ][ parent[1] ] end resultlist.pop # 出発節点の座標を消去 return resultlist end # 隣接節点を探索予定リストに追加する処理 @closelist.push(node) # 探索終了リストに追加 next_route = node[3] + 1 # 隣接節点までのマス数 # 隣接節点の座標を計算 down = [now[0], $game_map.round_y(now[1] + 1)] # 下 up = [now[0], $game_map.round_y(now[1] - 1)] # 上 right = [$game_map.round_x(now[0] + 1), now[1]] # 右 left = [$game_map.round_x(now[0] - 1), now[1]] # 左 # 隣接節点(下)をリストに追加 openlist_add_border(goal, now, parent, next_route, down, 2, event) # 隣接節点(上)をリストに追加 openlist_add_border(goal, now, parent, next_route, up, 8, event) # 隣接節点(右)をリストに追加 openlist_add_border(goal, now, parent, next_route, right, 6, event) # 隣接節点(左)をリストに追加 openlist_add_border(goal, now, parent, next_route, left, 4, event) end return [] # 探索失敗 end #-------------------------------------------------------------------------- # ● 節点データの生成 [new] #-------------------------------------------------------------------------- def a_star_node(goal, now, parent=false, route=0) @parentlist[ now[0] ][ now[1] ] = parent # 移動元リストに移動元をセット # 探索優先順位の生成 sx = distance_x_from_calt(now[0], goal[0]) sy = distance_y_from_calt(now[1], goal[1]) sl = sx.abs + sy.abs # 目的地までのマス数 # 推定最短経路のマス数(route + sl) + 優先順位(1以下になるように10000で除算) est = route + sl + Math.sqrt(sx * sx + sy * sy) / 10000 return [est, now, parent, route] end #-------------------------------------------------------------------------- # ● 探索予定リストに隣接節点を追加する処理 [new] #-------------------------------------------------------------------------- def openlist_add_border(goal, now, parent, next_route, next_coord, d, event) return false if next_coord == parent # 移動元と同座標 return false unless passable_check?(now, next_coord, d, event) # 移動可否の判定 return false unless closelist_check?(next_coord, now, next_route) # 探索終了リストに同座標が既にないか判定 return false unless openlist_check?(next_coord, now, next_route) # 探索予定リストに同座標が既にないか判定 @openlist.push(a_star_node(goal, next_coord, now, next_route)) # 探索予定リストに追加 end #-------------------------------------------------------------------------- # ● 2点間の X 方向の距離計算 [new] #-------------------------------------------------------------------------- def distance_x_from_calt(now_x, goal_x) result = goal_x - now_x if $game_map.loop_horizontal? && result.abs > $game_map.width / 2 if result < 0 result += $game_map.width else result -= $game_map.width end end result end #-------------------------------------------------------------------------- # ● 2点間の Y 方向の距離計算 [new] #-------------------------------------------------------------------------- def distance_y_from_calt(now_y, goal_y) result = goal_y - now_y if $game_map.loop_vertical? && result.abs > $game_map.height / 2 if result < 0 result += $game_map.height else result -= $game_map.height end end result end #-------------------------------------------------------------------------- # ● 移動可否の判定 [new] #-------------------------------------------------------------------------- def passable_check?(now, next_coord, d, event) # プリセットで行われていた座標計算を消去し、引数から取得 x = now[0] y = now[1] x2 = next_coord[0] y2 = next_coord[1] return false unless $game_map.valid?(x2, y2) # 有効座標判定 return true if @through || debug_through? # すり抜け判定 return false unless map_passable?(x, y, d) # 往路判定 return false unless map_passable?(x2, y2, reverse_dir(d)) # 復路判定 # イベントを無視するかの判定を追加 return false if collide_with_characters?(x2, y2) if event # イベントとの衝突判定 return true end #-------------------------------------------------------------------------- # ● 探索終了リストを探索 [new] #-------------------------------------------------------------------------- def closelist_check?(coord, now, next_route) @closelist.size.times do |i| if @closelist[i][1] == coord if @closelist[i][3] > next_route # データを更新し、探索予定リストへ移動 tmp = @closelist.delete_at(i) @parentlist[ tmp[1][0] ][ tmp[1][1] ] = now # 移動元リストを更新 tmp[0] -= tmp[3] - next_route # 評価値の更新 tmp[2] = now # 移動元を更新 tmp[3] = next_route # 節点までの経路のマス数を更新 @openlist.push(tmp) end return false end end return true end #-------------------------------------------------------------------------- # ● 探索予定リストを探索 [new] #-------------------------------------------------------------------------- def openlist_check?(coord, now, next_route) @openlist.size.times do |i| if @openlist[i][1] == coord if @openlist[i][3] > next_route # データを更新 @parentlist[ @openlist[i][1][0] ][ @openlist[i][1][1] ] = now # 移動元リストを更新 @openlist[i][0] -= @openlist[i][3] - next_route # 評価値の更新 @openlist[i][2] = now # 移動元を更新 @openlist[i][3] = next_route # 節点までの経路のマス数を更新 end return false end end return true end end #============================================================================== # ■ Game_Interpreter #------------------------------------------------------------------------------ #  イベントコマンドを実行するインタプリタです。このクラスは Game_Map クラス、 # Game_Troop クラス、Game_Event クラスの内部で使用されます。 #============================================================================== class Game_Interpreter #-------------------------------------------------------------------------- # ● 移動ルートの簡易設定 # char : 対象キャラ(配列) , coordinate : 座標(2次元配列), # diago : 斜め移動(初期値:4) # wait : ウェイト(初期値:T) , skip : スキップ(初期値:F) #-------------------------------------------------------------------------- def easy_move(char, xys, diago=4, wait=true, skip=false) characters = [] # 対象を格納する配列(ウェイト用) char.size.times do |i| $game_map.refresh if $game_map.need_refresh if char[i] < -1 # 隊列の場合 char_id = -char[i] - 2 # 変換 character = $game_player.followers[char_id] # 隊列キャラ情報 else character = get_character(char[i]) end if character characters.push(character) # 対象を格納 # 移動ルートの作成 route = RPG::MoveRoute.new route.repeat = false route.skippable = skip route.wait = false route.list = [] xys.size.times do |j| cx = xys[j][0] # 目標X座標 cy = xys[j][1] # 目標Y座標 # 基準との位置関係を補正 if characters.size >= 2 cx += character.x - characters[0].x cy += character.y - characters[0].y end # 目標座標に移動の設定 route.list.push( RPG::MoveCommand.new(Game_Character::ROUTE_SCRIPT, make_parame(cx, cy, diago)) ) end route.list.push( RPG::MoveCommand.new(Game_Character::ROUTE_END, []) ) character.force_move_route(route) # 移動ルートを設定 end end # 全員の移動が終了するまで待機 if wait characters.size.times do |i| Fiber.yield while characters[i].move_route_forcing end end end #-------------------------------------------------------------------------- # ● 複数キャラに向きの設定 # char : 対象キャラ(配列), d : 向き, wait : ウェイト(初期値:T) #-------------------------------------------------------------------------- def easy_turn(char, d, wait=true) characters = [] # 対象を格納する配列(ウェイト用) char.size.times do |i| $game_map.refresh if $game_map.need_refresh if char[i] < -1 # 隊列の場合 char_id = -char[i] - 2 # 変換 character = $game_player.followers[char_id] # 隊列キャラ情報 else character = get_character(char[i]) end if character characters.push(character) # 対象を格納 # 移動ルートの作成 route = RPG::MoveRoute.new route.repeat = false route.skippable = false route.wait = false route.list = [] # 向き指定の設定 case d when 2;route.list.push( RPG::MoveCommand.new(Game_Character::ROUTE_TURN_DOWN, []) ) when 4;route.list.push( RPG::MoveCommand.new(Game_Character::ROUTE_TURN_LEFT, []) ) when 6;route.list.push( RPG::MoveCommand.new(Game_Character::ROUTE_TURN_RIGHT, []) ) when 8;route.list.push( RPG::MoveCommand.new(Game_Character::ROUTE_TURN_UP, []) ) end route.list.push( RPG::MoveCommand.new(Game_Character::ROUTE_END, []) ) character.force_move_route(route) # 移動ルートを設定 end end # 全員の移動が終了するまで待機 if wait characters.size.times do |i| Fiber.yield while characters[i].move_route_forcing end end end #-------------------------------------------------------------------------- # ● スクリプトの引数 #-------------------------------------------------------------------------- def make_parame(x, y, diago) if diago == 8 ["attain8(" + x.to_s + ", " + y.to_s + ")"] else ["attain4(" + x.to_s + ", " + y.to_s + ")"] end end end end