【RGSS3】『敵n体ランダム』の回数増減

せくもん
記事: 72
登録日時: 2018年6月09日(土) 21:48

【RGSS3】『敵n体ランダム』の回数増減

投稿記事by せくもん » 2021年5月05日(水) 20:55

いつもお世話になっております。

RPGツクールVXaceにて、効果範囲が
『敵2体ランダム』『敵3体ランダム』『敵4体ランダム』のスキルに対し
味方の攻撃回数を増やす&敵の攻撃回数を減らすステートや
装備すると攻撃回数が1回追加される装飾品等を作りたいのですが

ディフォルトで『効果範囲が敵n体ランダムの時に
ターゲット選択・命中判定・ダメージ計算の処理をn回連続で繰り返す』みたいなループ処理は
スクリプトのどの部分で行われているのでしょうか?

ご存じの方がおられましたら、お知恵をお借りしたくお願い申し上げます。

名無し蛙
記事: 302
登録日時: 2015年11月23日(月) 02:46

Re: 【RGSS3】『敵n体ランダム』の回数増減

投稿記事by 名無し蛙 » 2021年5月06日(木) 13:29

どうもこんにちは
せくもん さんが書きました:ディフォルトで『効果範囲が敵n体ランダムの時に
ターゲット選択・命中判定・ダメージ計算の処理をn回連続で繰り返す』みたいなループ処理は
スクリプトのどの部分で行われているのでしょうか?

Game_Actionの178行目

コード: 全て選択

Array.new(item.number_of_targets) { opponents_unit.random_target }
でしょうね。
効果範囲の敵ランダムn体と連続回数と行動回数追加(特徴)とで該当処理が違うのでややこしいんですよね。
returnが省略されていますけどこれでn回分のランダムターゲットを格納した配列を返却します。
n回の部分はitem.number_of_targetsに格納されているので
Array.new()内の数値を増減する事で希望の処理を実現出来ると思います。ただし前提として

コード: 全て選択

if item.for_random?
があるので「装備すると攻撃回数が1回追加」は元がランダム攻撃じゃないと追加されませんが。

装備品やステートが付いている場合に挙動が変わるという処理は
以前自分が書いた独自特徴化を応用する方法をオススメしておきます。

コード: 全て選択

module Test
  FEATURE_RANDOM_ATK_NUM_EX = 101
end

class RPG::BaseItem
  #--------------------------------------------------------------------------
  # ● note解析
  #--------------------------------------------------------------------------
  def note_analyze
    @note.each_line do |text|
      case text.chomp
      when /\<ランダム攻撃回数増加\:(\d+)\>/
        @features.push Feature.new(Test::FEATURE_RANDOM_ATK_NUM_EX, 0, $1.to_i)
      when /\<ランダム攻撃回数減少\:(\d+)\>/
        @features.push Feature.new(Test::FEATURE_RANDOM_ATK_NUM_EX, 0, -($1.to_i))
      end
    end
  end
end

class << DataManager
  #--------------------------------------------------------------------------
  # ○ 通常のデータベースをロード
  #--------------------------------------------------------------------------
  alias :_ex_features_load_normal_database :load_normal_database
  def load_normal_database
    _ex_features_load_normal_database
    container = $data_actors + $data_enemies + $data_armors + $data_states
    container.compact.each do |data_iterator|
      data_iterator.note_analyze
    end
  end
end

class Game_BattlerBase
  #--------------------------------------------------------------------------
  # ● 拡張ランダム攻撃回数の総和の取得
  #--------------------------------------------------------------------------
  def random_attack_num_ex
    features_sum_all(Test::FEATURE_RANDOM_ATK_NUM_EX)
  end
end

取得メソッドを作るまでの適当な実装例ですが正規表現や特徴のフォーマットを覚えるとかなり応用が利くと思います。

追記で
せくもん さんが書きました:ターゲット選択・命中判定・ダメージ計算の処理をn回連続で繰り返す』みたいなループ処理は
スクリプトのどの部分で行われているのでしょうか?

上で生成したターゲット配列を実際に使用しているのはScene_Battleの579行目use_item内です。
こちらのtargetsを必要に応じて削る、という方法もありますけど増やすのが面倒なので
やはりGame_Actionの方を弄った方が良い気がしますね。
せくもん
記事: 72
登録日時: 2018年6月09日(土) 21:48

Re: 【RGSS3】『敵n体ランダム』の回数増減

投稿記事by せくもん » 2021年5月06日(木) 19:53

名無し蛙様、ご返答ありがとうございます。

添付いただいたスクリプトを導入したところ、以下のような
エラーメッセージが表示されました。

download/file.php?mode=view&id=18041

NoMethodErrorは何度か対処した事がありますが
こちらのエラーメッセージは初見なのでちょっと対処方法がわかりません。

お手数ですが、対処方法を教えていただけないでしょうか?
添付ファイル
エラーメッセージ.png
名無し蛙
記事: 302
登録日時: 2015年11月23日(月) 02:46

Re: 【RGSS3】『敵n体ランダム』の回数増減

投稿記事by 名無し蛙 » 2021年5月06日(木) 20:58

平たく言えば無限再帰ですね。
関数の中で関数を呼び脱出条件がなくスタックオーバーフローになった、って事だと思います。
aliasのバッティングなんかでそこそこ見掛けるエラーですね。
例えば以前、私が書いたコード(メモ欄解析)とは別に今回のコードを丸々コピペしたら
思いっきり競合するので内容を読み解いて適宜調整する必要があります。
というかサンプルコードとして渡した物だったのでそのまま使用する事は想定していませんでした。

Array.new(n) { m }と書いた場合、配列をnサイズで生成し初期値をmで埋める、という意味になります。
Array.new(item.number_of_targets) { opponents_unit.random_target }もやっている事は同じです。
もしも自分なりにこの処理に干渉する算段がついているのなら今回はそちらで実装した方が良いかもしれません。
具体的に何が分からないと聞かれれば答えますけど方法が多岐に渡る事を聞かれると答え辛いので。
せくもん
記事: 72
登録日時: 2018年6月09日(土) 21:48

Re: 【RGSS3】『敵n体ランダム』の回数増減

投稿記事by せくもん » 2021年5月07日(金) 10:33

名無し蛙様、ご返答ありがとうございます。

初めから干渉する算段がついていたわけではありませんが
名無し蛙様から教えていただいた情報を元にGame_Actionの173行目以降を
下記の通り改変しました。

コード: 全て選択

  #--------------------------------------------------------------------------
  # ● 敵に対するターゲット
  #--------------------------------------------------------------------------
  def targets_for_opponents
    if item.for_random?
      if item.number_of_targets == 1 #敵1体ランダムの時はそのまま
        Array.new(item.number_of_targets) { opponents_unit.random_target }
      else
        Array.new(item.number_of_targets + aaa) { opponents_unit.random_target }
      end
     elsif item.for_one?
      num = 1 + (attack? ? subject.atk_times_add.to_i : 0)
      if @target_index < 0
        [opponents_unit.random_target] * num
      else
        [opponents_unit.smooth_target(@target_index)] * num
      end
    else
      opponents_unit.alive_members
    end
  end


9行目の『aaa』を『2』や『-1』に変えて敵n体ランダム(1体の場合は除く)の時に
攻撃回数を増減させられることは確認出来ましたが
どうやってステートや装備品の特徴で、この『aaa』の数値を設定すればいいのかが分かりません。
名無し蛙
記事: 302
登録日時: 2015年11月23日(月) 02:46

Re: 【RGSS3】『敵n体ランダム』の回数増減

投稿記事by 名無し蛙 » 2021年5月07日(金) 15:25

独自特徴は自分自身でメンテナンス出来る程度にスキルが無いと扱いが難しいかもしれません
汎用性や実行効率、見通しが劣るのであまり好みではないのですが直接メモ欄を覗く方法もあります
下準備が不要で競合リスクが低い点がメリットですね
ついでに見通しが悪くなるのでitem.for_random?以外の処理はaliasに任せた方が良いかな

states...RPG::State配列を取得する
equips.compact...RPG::EquipItem配列を取得する(装備が無い部位はnilが入ってるのでcompactで除外する)
inject...配列の値を合算する為に使用するメソッド
/\<ランダム攻撃回数増減\:(\-?)(\d+)\>/...今回使用するパターン。$1に-符号の有無、$2に数値文字列が格納される
すっごい説明すっ飛ばしてるとは思うんですけど1から10まで書くと膨大になるんですよね
Rubyは実用的なメソッドが多く、プリセットスクリプト内でもかなり活用しているので暇があれば吸収した方が良いです
自分も正規表現には詳しくありませんが、それでも基礎を覚えれば今回の入力補助程度は融通が利きます。

コード: 全て選択

class Game_Action
  #--------------------------------------------------------------------------
  # ○ 敵に対するターゲット
  #--------------------------------------------------------------------------
  alias :_old_targets_for_opponents :targets_for_opponents
  def targets_for_opponents
    if item.for_random?
      objects = subject.states
      objects += subject.equips.compact if subject.actor?
      attack_num_ex = objects.inject(0) do |r, obj|
        if obj.note =~ /\<ランダム攻撃回数増減\:(\-?)(\d+)\>/
          r + (($1.empty? ? 1 : -1) * $2.to_i)
        else
          r
        end
      end
      attack_number = [1, item.number_of_targets + attack_num_ex].max
      Array.new(attack_number) { opponents_unit.random_target }
    else
      _old_targets_for_opponents
    end
  end
end

これで装備品・ステートに<ランダム攻撃回数増減:-3>等と書いた場合、反映されるはずです。
実行テストまではしてませんが念の為1回攻撃保証は付けています。
名無し蛙
記事: 302
登録日時: 2015年11月23日(月) 02:46

Re: 【RGSS3】『敵n体ランダム』の回数増減

投稿記事by 名無し蛙 » 2021年5月07日(金) 17:01

焼け石に水ですが正規表現の補足説明
この辺の知識はメモ欄にタグを作る時によく使うところです
①文字列と正規表現(RegExp)が合致した場合trueが返る
判定方法は以下のどれでも良いです

コード: 全て選択

str = "テスト"
regexp = /テスト/
if str.match(regexp) # ヒット
if regexp.match(str) # ヒット
if str =~ regexp # ヒット

②原則として正規表現中の記号には特殊な意味がある。
記号を文字として解釈する場合は直前に\を付ける。逆に記号以外の直前に\がある場合特殊な意味がある。

コード: 全て選択

str = "<テスト>"
regexp = /\<テスト\>/
if str =~ regexp # ヒット

③()で括る事で(グループ化)ヒットした文字を$1に格納する事が出来る
グループが複数ある場合は$2$3と続いていく

コード: 全て選択

str = "<テスト:1>"
regexp = /\<テスト\:(1)\>/
if str =~ regexp # ヒットして$1に"1"が格納される

④文字列をメタ文字に置き換える事で曖昧な表現が可能になる
例として\dを使用した場合0-9までの数値にヒットする

コード: 全て選択

str1 = "<テスト:1>"
str2 = "<テスト:10>"
regexp = /\<テスト\:(\d)\>/
if str1 =~ regexp # ヒットして$1に"1"が格納される
if str2 =~ regexp # ヒットしない。二桁以上の数値を判定する為には後述の方法を使用する

⑤文字の後に*を書く事で0回以上の繰り返し、+で1回以上の繰り返し、?で0か1回という表現になる

コード: 全て選択

str2 = "<テスト:123456>"
regexp = /\<テスト\:(\d+)\>/
if str2 =~ regexp # \dを六回繰り返した6桁の数値にもヒットする。$1に"123456"が格納される
せくもん
記事: 72
登録日時: 2018年6月09日(土) 21:48

Re: 【RGSS3】『敵n体ランダム』の回数増減

投稿記事by せくもん » 2021年5月07日(金) 19:43

名無し蛙様、ご返答ありがとうございます。

添付いただいたスクリプトを導入し、動作テストをした所
無事に想定通りに動く事を確認出来ました。
どうもありがとうございます。

当方の知識不足の為、余分にお手を煩わせてしまい
申し訳ありませんでした。

“VX / Ace:質問” へ戻る