点線上の文字の羅列 RPGツクールVX講座 2.気に食わない機能を改造してしまおう 2-1.自動戦闘のAIさん

RPGツクールVX講座 2.気に食わない機能を改造してしまおう 2-1.自動戦闘のAIさん

こんにちは。faidaです。
今ちょっと中型のスクリプト開発してたりゲーム作ってたりしますが、もう面倒臭くなってまいりました。
思いつきで行動するとすぐこうなっちゃうんですよね。思いつきで行動しかやる気が出ないんですけどね!

というわけで思いつきで講座再開。
アンケートの項目に何故だか追加されてるし(一票かよ……)、まあこういうのって思いついたらすぐやんなきゃやる気が全くといっていいほど保てないのでぱっぱとやってしまうことに。

講座は続きから。


VX(Ace)のAIって脳筋ばっかみたいなAIじゃないですか。
なんでかって言いますと、以下に示す計算式で「その行動を行った時の価値」というのが設定されるからなんですね。これはGame_BattleActionってクラスに定義されているメソッドの一部です。
if skill.for_opponent?
return target.hp_damage.to_f / [target.hp, 1].max
else
recovery = [-target.hp_damage, target.maxhp - target.hp].min
return recovery.to_f / target.maxhp
end

これを見ると、「スキルの対象が敵なら対象に与えるダメージの対象のHPに占める割合、それ以外(味方)なら回復する量の対象の最大HPに占める割合」っていう風に価値が決まってます(ちなみに「攻撃」も同じ)。早い話、付加・解除ステートなんか見てないってことですよね。ただまあ効かない属性の攻撃とかはしないのがいいところですかね。
で、こうやって考えると理不尽と思うんですよ。なんで敵の弱点知らないプレイヤーと敵の弱点を予め知っているAIとにしたんだろうって。まあそんなこと言ってもアレですが。

それではここを改造して、自分好みのAIを作っちゃいましょう。
①ステートも考慮するにはどうすればいい?
一番簡単なのは「対象のステート付加成功率によって返す値を増やす」ことですよね。
バトラーのステート付加成功率は「state_probability(id)」で取得できますんで、これをさっきのスクリプトにちょいと追加します。
if skill.for_opponent?
n = target.hp_damage.to_f / [target.hp, 1].max
skill.plus_state_set.each{|id|n += target.state_probability(id) / 100.0}

else
recovery = [-target.hp_damage, target.maxhp - target.hp].min
n = recovery.to_f / target.maxhp
skill.plus_state_set.each{|id|n += target.state_probability(id) / 100.0}

end
return n

ここではnというローカル変数を追加しました。nという変数を置いておくことにより、見た目分かりやすく、かつ幾行にも渡って加算しやすくするためです。まあ僕なんかは全部一行にまとめて書く癖がありますから自分でスクリプト書いていて読みづらいのなんの。
あと、skill.plus_state_setは「付加するステートの『IDの配列』」なので、eachという繰り返しのメソッドで全部のステート付加成功率を加算しておきました。

付加があるなら解除も必要ですよね。
というわけで解除の方もつけましょ。
if skill.for_opponent?
n = target.hp_damage.to_f / [target.hp, 1].max
skill.plus_state_set.each{|id|n += target.state_probability(id) / 100.0}
skill.minus_state_set.each{|id|n += 1.0 if target.state?(id)}
else
recovery = [-target.hp_damage, target.maxhp - target.hp].min
n = recovery.to_f / target.maxhp
skill.plus_state_set.each{|id|n += target.state_probability(id) / 100.0}
skill.minus_state_set.each{|id|n += 1.0 if target.state?(id)}
end
return n

今度は繰り返しの中の文章が違いますね。これは、解除するステートには成功率が存在しないことと、「ステートにかかってなきゃ解除するステートとか意味無いやんけ」という理屈によって生み出されました。まあ見れば意味は分かりますよね。
ちなみにこれを普通に導入すると、多分普通にザラキクリフト強化ステートをかけまくるアホAIになってしまうのではと思われます。そういう場合、例えば付加ステートの100.0のところを200.0にするとか、「skill.plus_state_set.each」の中身の最後に「if !target.state?(id)」と付け加える(つまり一回ステートにかかった対象にステートはかけない)とかすればいいかと思います。

②相手の状態によって行動を変えるにはどうすればいい?
さっきの所をデフォルトに戻して説明しますよ。
例えばサポートの敵が動けない隙に他の敵を叩いて倒したい!とか、瀕死・戦闘不能の味方に対して優先的に回復させたい!とか色々状況判断する場合ですね。
動ける敵を優先したい場合は「target.movable?」を使います。要するにステートの制約「行動できない」が無い状態ですよね。これに混乱している時も動けないと判断するには、「target.movable? && !target.confusion?」となります。
瀕死の判定はHPが最大HPの4分の1以下になったときです。なので「target.hp < target.maxhp / 4」で判断できます。戦闘不能は勿論「target.hp == 0」ですよね。
これを考慮して元のスクリプトに突っ込んでみます。
if skill.for_opponent?
n = target.hp_damage.to_f / [target.hp, 1].max
n += 0.5 if target.movable? && !target.confusion?

else
recovery = [-target.hp_damage, target.maxhp - target.hp].min
n = recovery.to_f / target.maxhp
n += 0.5 if target.hp < target.maxhp / 4
n += 0.5 if target.hp == 0

end
return n

ここでどうして「target.hp == 0」の時も0.5にしたかと言いますと、結局HPが0ならば「target.hp < target.maxhp / 4」の条件も満たしてしまうからです(最大HPが3以下の時を除く)。それでHPが0ならば1.0が加算され、ほぼ確実に復活させてもらえます。①のステート考慮も使うと、尚更です。

③アイテムも使わせたいけどどうすればいい?
戦士タイプのアクターが薬をさっと取り出して自分や味方のピンチを救う……かっこいいですが、処理がやや煩雑っつーか面倒臭いので勘弁してください。
まあでもヒントくらいなら。
Game_Actorの「自動戦闘の作成」ってところを見てください。ここでは自動戦闘における行動を作成しています。
ここにアイテムの評価を突っ込めばいいのですが、デフォルトのスクリプトではアイテムの評価というメソッドは存在しません。じゃあどうすればいいのか。
そこで拙作のスクリプトセットの中にある「strategy」っていうファイルを見てください(超☆手前味噌)。Game_BattleActionに「アイテムの評価」というメソッドが追加されています。あと「行動の価値評価 (自動戦闘用)」っていうメソッドも再定義しています。これを参考に作ってみてはいかがでしょう。

どうでしょうかね。今回のまとめスクリプトはこちら
まとめには通常攻撃のアレも突っ込んでおきました。
では次回。多分次回までには新しいスクリプトできてると思います。
スポンサーサイト

コメントの投稿

非公開コメント

プロフィール

faida

Author:faida
VX/VXAceスクリプト担当。デレステP。担当の限定は逃す派。創作ブログの名の下に、デレマスSS書こうかな。

最新記事
最新コメント
最新トラックバック
月別アーカイブ
カテゴリ
アクセスカウンター
投票
ついったー
検索フォーム
メールフォーム

名前:
メール:
件名:
本文:

RSSリンクの表示
リンク
ブロとも申請フォーム

この人とブロともになる

QRコード
QR