RPGツクールVXAceのスクリプト言語であるRGSS3(Ruby1.9.2p0 + ツクールライブラリ)に関する個人メモ。あくまでも個人メモなので管理人以外には編集権限はありません。

×


VXAceの仕様 システム/データベース/バトル編

戦闘テストの罠

  • VXAceの戦闘テストには下記の不具合があり、使い勝手が非常に悪くなっている。
    • 戦闘テストを行う前にプロジェクトがセーブされない。このため敵データなどを変更してそのバトルをテストしたい場合、一度データベースウィンドウを閉じて手動で保存してから、再度データベースを開いて戦闘テストを行う必要がある。
  • 対処方法としては、デバッグ用のマップを用意しておき、戦闘テストはそこで行うのが良い。
    • デバッグマップでセーブしたデータを残しておけば戦闘テストよりも楽に戦闘テストが行える。
    • デバッグマップに全アイテム入手、レベルの上げ下げ、追加能力値のリセット、主要なマップごとの出現ザコ設定と何度も戦える固定敵、各マップのボス戦などを機能として盛り込んでおくと、更に捗る。
  • デバッグマップで戦闘テストをする利点の一つに汎用性の高さがある。逆に言うとエディタの戦闘テストには汎用性が無い。
    • スクリプトなどで装備欄を独自のものに差し替えていた場合、エディタの戦闘テストでは装備の変更に対応できない場合がある。デバッグマップでのテストならそれらにも対応可能。
    • パラメータアップイベントやアイテムによる強化を考慮した戦闘テストにも対応可能。エディタでは対応できない。
    • 事前に強化魔法をかけておく、必ず奇襲されるor先制攻撃できるといった特殊な戦闘にも対応可能。エディタでは対応不可。
    • ボスラッシュのような連戦テストも問題なく行える。エディタでは不可能。
    • アクターを戦闘中に入れ替えるような特殊な戦闘も対応可能。エディタではアクターを4人までしか設定できないので対応できない。
  • 基本システムだけでツクる場合には問題は少ないが、凝れば凝るほど戦闘テストの使いにくさを感じる事になるので、デバッグマップでの戦闘テストを習慣付けておくのをお勧めする。特にVXAceの場合は凝ったスクリプトも沢山公開されているので余計に。

リージョンの仕様

  • リージョンの設定が参照されるのは、エンカウント時のプレイヤー位置のリージョン数値。
    • 例えばシンボルエンカウントの敵を実装する場合、敵の位置ではなくプレイヤーの位置になるので注意。
    • 特に動かないシンボルの場合、シンボルの周囲4マスにリージョンを設定しておかないと、設定していない方向からイベントを起こした場合、リージョンを参照した敵が出現しない。

能力の最大値

  • VXAceでは最大値は以下の通り
    • 職業最大値 職業の設定での最大値
    • 追加最大値 能力上昇アイテムやイベントで上昇する数値
    • 装備最大値 装備品で上昇する最大値
    • 最終最大値 特徴やステートなどの補正も含めた全ての最大値
      • つまり補助魔法などを駆使しても戦闘中にこの値を超える事は無い。能力値が元から最大になっていれば補助魔法の効果は無効。
能力職業最大値追加最大値装備最大値最終最大値備考
最大HP9999無制限5000*装備数9999エネミーは99万9999
最大MP9999無制限5000*装備数9999
最大TP100--100システム的に100固定
攻撃力999無制限500*装備数999
防御力999無制限500*装備数999
魔法力999無制限500*装備数999
魔法防御999無制限500*装備数999
敏捷性999無制限500*装備数999
999無制限500*装備数999運の1/10がステート付与率/無効化率に影響

エディタの入力最大値

  • エディタ上で各種データを設定する際には上限が決まっている。これを超えたデータを設定したい場合、スクリプトで書き換える必要がある。
データベースの設定項目
項目最小値最大値備考
消費MP09999
消費TP0100得TPも同様
速度補正-20002000スキルorアイテム使用時に使用者の敏捷に加算される値
成功率0100
連続回数19
分散度0100
価格0999万9999
装備 最大HP、MP05000
装備 攻撃力〜運0500
使用効果 HP回復0%+0100%+9999MP回復も同様
使用効果 成長01000
特徴 有効度0%1000%
特徴 通常能力値0%1000%
特徴 追加能力値0%100%
特徴 特殊能力値0%1000%
特徴 攻撃時ステート0%1000%
特徴 攻撃速度補正-999999アイテムやスキルと異なり999まで
特徴 攻撃追加回数-99
特徴 行動回数追加01000%100%を超えても増えるのは1回のみ 実質101%以上は無意味
敵HP199万9999
敵MP09999
敵攻撃〜運1999
敵お金、経験値0999万9999
敵ドロップ率1/10001/1
敵グループ編成数18戦闘開始時の制限
イベントの処理
項目最小値最大値備考
数値入力の桁数1桁8桁
名前入力の文字数1文字16文字デフォルト6文字
変数、乱数-9999万99999999万9999全てのイベントの変数、乱数入力欄も同様
タイマーの操作0分0秒99分59秒
お金0999万9999所持金の増減では最低1
個数199武器や防具、アイテムの増減
HP、MP19999HP、MPの増減
経験値1999万9999経験値の増減
能力値19999実際には加算能力値の増減 HPMP以外のパラメータは計算後に999が最大
ウェイト1999フレーム
敵HPの増減199万9999
敵MPの増減19999
スクリプトの文字数--52文字 x 12行 が一度に入力可能な最大値

命中率の謎

  • VXAceの命中計算はかなりわかりづらい。デフォルトでは以下のような計算になっている。
1.攻撃側の命中率計算。
  a.アクター、職業、装備品、ステートの特徴から命中率を全て合算する。100%が2つあれば200%。
  b.スキルが物理攻撃の場合はスキルの成功率に命中合算値を乗算して最終的な命中率を算出。
  c.スキルが魔法攻撃または必中だった場合にはスキルの成功率のみで命中率を算出。
2.乱数と上記算出値を比較し、乱数が算出値合計以上であれば攻撃ミスフラグセット。
3.攻撃がミスしていなければ防御側の回避計算。
  a.攻撃タイプが物理なら回避率、魔法なら魔法回避率をアクター、職業、装備品、ステートの特徴から全て合算する。100%が2つあれば200%。
4.乱数と上記合算値を比較し、乱数が合算値未満なら回避フラグセット。必中の場合は必ず回避失敗。
5.結果がミスも回避もしておらず、ダメージなしのスキルでなければダメージ計算処理へ。
  a.攻撃側クリティカル合算値に防御側クリティカル回避率合算値を乗算して乱数と比較、成功したらクリティカルフラグセット。
  user.cri * (1 - cev) # 会心80%、会心回避20%の場合、0.8 * (1 - 0.2) = 0.8 * 0.8 = 0.64 で会心率64%になる
  # この計算式だと仮に会心率1000%だったとしても会心回避が100%以上になると結果が必ずゼロを下回り回避率100%になる。
  b.クリティカル、属性、ダメージ計算式、分散値から最終ダメージを計算。
  • 上記で問題になるのが、防御側の回避に攻撃側の命中が一切影響していないこと。
    • つまりたとえ命中が1000%のキャラがいたとしても回避95%の敵がいたら最終命中率は5%から変わらない。
    • 逆に言えば回避の高い敵に対抗する方法がシステム的に必中しかなくなる。
  • まとめると命中は攻撃の命中率を上げるパラメータではなく、スキル成功率に対する補正値でしかない。
    • そのため最終的な命中可否を決めるのは敵の回避率の影響が非常に高い。回避の高い敵を出す際には必中スキルを必ず持たせるように注意したい。
    • 言い換えるとプレイヤー側の回避率も敵の命中に関係なく一定の確率で攻撃を回避できる効果になるので滅茶苦茶強い。
  • なお命中特徴の無いキャラクターの基礎命中率は0になっているので命中特徴を最低一つは持たせないと攻撃が命中しない点も注意したい。

アクターの命中特徴の合計、スキル成功率、相手の回避率、最終的な命中確率の表
命中特徴成功率回避率命中確率備考
100%100%0%100.00%
95%100%5%90.25%データベースの初期状態 1割の攻撃失敗は割りとイライラ案件
100%100%5%95.00%初期状態だと命中100%で5%回避される
120%100%5%95.00%命中が上がっても敵の回避を相殺できない
100%80%0%80.00%成功率はそのままスキルの成功率になる
120%80%0%96.00%成功率×命中特徴で攻撃の成否が決まる
つまり命中特徴は成功率に対する補正と言える
200%50%0%100.00%
120%80%10%86.40%敵の回避は「攻撃が成功した後」に計算される
つまり命中特徴をどんなに上げても敵の回避は相殺できない
1000%100%95%5.00%
100% - 50%100%0%50.00%ちなみに暗闇ステートなどは命中特徴に対する減算処理
そのため命中特徴が高いと効果を発揮しなくなる
1000% - 50%100%0%100.00%
  • 正直この仕様はゲーム製作側からすると非常にバランス調整が難しいので命中判定修正スクリプトは必ず導入したい。下記修正スクリプトを導入すると命中率計算が次のように変更される。
    • 攻撃側スキル成功率の確率でスキルの成功可否を判定する。判定失敗した場合はミスフラグON。成功率に幸運÷10を加算するパターンも記載。
    • 攻撃側命中率−防御側回避率で最終的な命中率を計算。判定失敗したら防御側回避フラグON。
    • つまりスキルが成功するかどうかは「スキル成功率」(+運)でのみ決まり、スキル命中率は命中合計−回避合計で決まるため、直感的に分かりやすい。
class Game_Battler
  #--------------------------------------------------------------------------
  # ● スキル/アイテムの効果適用 命中計算修正版
  #--------------------------------------------------------------------------
  def item_apply(user, item)
    @result.clear
    @result.used = item_test(user, item)
    @result.missed = (@result.used && rand(100) >= item.success_rate) # スキルの成功率のみでミスフラグを判定
#   @result.missed = (@result.used && rand(100) >= (item.success_rate + user.luk / 10)) # スキルの成功率に使用者の運を反映させたパターン
    @result.evaded = (!@result.missed && rand >= item_hit(user, item) - item_eva(user, item)) # 攻撃側命中合計から防御側回避合計を減算して最終命中を求める
    if @result.hit?
      unless item.damage.none?
        @result.critical = (rand < item_cri(user, item))
        make_damage_value(user, item)
        execute_damage(user)
      end
      item.effects.each {|effect| item_effect_apply(user, item, effect) }
      item_user_effect(user, item)
    end
  end
  #--------------------------------------------------------------------------
  # ● スキル/アイテムの会心率計算 修正版
  #--------------------------------------------------------------------------
  def item_cri(user, item)
    item.damage.critical ? user.cri - cev : 0 # 会心率を減算で求めるように変更
  end
end

ステート付与率

  • 通常攻撃(使用効果 ステートの付加:通常攻撃 のスキルやアイテム)使用時。
    • ステートの付加:通常攻撃の確率が基準値になる。
    • ターゲットのステート有効度を乗算。 ※装備品やアクターの特徴を全て加算式で合計
    • 使用者の攻撃時ステート確率を乗算。 ※装備品やアクターの特徴を全て加算式で合計
    • 両者の運による有効補正値(運の差x0.1%)を乗算して最終確率を算出。
  • 味方にステートを付加するスキル/アイテム使用時。
    • 使用効果「ステートの付加」の確率が最終確率になる。
  • 敵にステートを付加するスキル/アイテム使用時。
    • 使用効果「ステートの付加」の確率が基準値になる。
    • ターゲットのステート有効度を乗算。 ※装備品やアクターの特徴を全て加算式で合計
    • 両者の運による有効補正値(運の差x0.1%)を乗算して最終確率を算出。
  • 例)使用効果 毒100%、敵のステート有効度 毒50%、 運 使用者150 敵50の場合
    • 100% x 50% x(100% +(150-50)*0.1%)= 50% x 110% = 55%
    • 最終的な確率は55%になる。
  • 運の効果はかなり低い。使用者999相手1でも最終確率が2倍弱になる効果しかない。
    • ただし不用意にPCよりも高い数値をエネミーに設定してしまうと無意味にPCが不利になるため設定値には注意が必要。

ステートの詳細、スキルの継続ターンの詳細

  • ステートは個別に解除条件を指定できる。
    • 戦闘終了時に解除のチェックが入っている場合、戦闘終了時にステートが解除される。
      • このチェックを入れれば戦闘不能でも戦闘終了後に治るようにできる。
    • 行動制約によって解除のチェックが入っている場合、行動制約がなし以外のステートになった場合に解除される。
      • 例えば防御は初期状態でこのチェックが入っているため、眠りや混乱、戦闘不能のステートになると解除される。
    • 自動解除のタイミングは行動終了時とターン終了時で継続ターン数の意味合いが変化する。
      • 行動終了時で継続1ターンの場合、行動後にステートになった場合は次のターンで行動するまで継続する。
      • ターン終了時で継続1ターンの場合、行動後にステートになった場合は次のターンになった際に解除される。
      • 例えばスタン(1ターン行動不能)を実装する場合、ターン終了時を条件とすると敵よりも先にスタンを与えないと全く意味が無いが、行動終了後を条件とするなら行動速度に関係なく必ず1回の行動を無効化できる。
  • ステートを重ねがけした場合、効果や継続ターンは上書きされる。
    • 例えば毒4ターンのステートを受けている敵が残り1ターンで毒4ターンのステートを受けた場合、残りターン数は4ターンに上書きされる。
    • 敵が高確率で行動制約のあるステートを使ってくる場合、プレイヤーは何もできないままハメ殺される可能性があるので注意が必要。
  • スキルの能力上昇、能力下降の効果は一律25%変化で、2回(50%)まで重複する。
    • 継続ターン数はターン終了時を条件としたステートと同じ処理で解除される。重ねがけで継続ターン数が上書きされるのも同じ。
    • 2回重複した状態で重ねがけして継続ターン数を上書きした場合、2回重複した状態のまま継続ターンが上書きされる。

バフ/デバフと特徴の能力変化の違い

  • ツクールではバフ(能力強化)とデバフ(能力弱体)は Game_BattlerBase の @buffs インスタンス変数で管理されるものを指す。
    • @buffs 変数は能力値8つ(HP、MP、攻撃、防御、魔力、魔防、速さ、運)の配列となっていて、それぞれ -2〜+2 の範囲で変動する。プラスならバフ、マイナスならデバフとして扱われる。
      • 上記の仕様により、バフとデバフは互いに打ち消しあう。
      • バフ/デバフは上記の通常能力値に該当する8つの能力にのみ適用される。
    • @buffs の上限(バフ上限)と下限(デバフ上限)は Game_Battler#buff_max?、Game_Battler#debuff_max? でそれぞれ判定される。
    • バフ/デバフは1段階ごとに対応する能力値が25%上昇(下降)する。最大で2段階の±50%まで変動する。この変動率は Game_BattlerBase#param_buff_rate で計算される。
    • バフ/デバフの能力変化はパラメータ上限の制限を受ける。例えば攻撃なら通常の上限は999、強化前の能力が800の場合、1段階強化でも1000となり上限を超えるため999に丸められるし当然2段階強化の意味はまるで無い。強化前が999だった場合は強化効果はまるで意味を成さない。
    • バフ/デバフは段階に応じてアイコンが表示される。
    • バフ/デバフの残りターン数は Game_BattlerBase の @buff_turns で管理される。ターンが残っている状態で同じ能力値のバフ/デバフを受けると、残りターンが上書きされる。そのため2段階強化の状態で上書きすれば、2段階状態を維持する。これは2段階強化状態で1段階デバフを受けた時も同様であり、弱体を受ける事で逆に効果ターンが延びるという事もありうる。つまり敵の強化が剥がれる直前にデバフをかけてしまうと、逆にターン数が伸びてしまう可能性もある。
      • 例外として残りターン数が減るような場合には上書きされない。例えば1段階強化は5ターン、2段階強化は2ターンのように効果で継続ターンを変化させている場合には注意が必要となる。1段階→2段階の順にバフがかかっても継続ターン数は減らないし、逆なら普通に5ターンに上書きされる。このような特殊な仕様の能力変化を作りたい場合は特徴による能力変化を使う方が良い。
    • バフ/デバフは[使用効果]-[能力強化の解除][能力弱体の解除]でそれぞれ消去される。
  • 特徴による能力変化はあくまでも特徴の付随効果として扱われる。特徴はアクター、クラス、武器、防具、エネミー、ステートに設定でき、文字通りそれぞれの特徴を表す。
    • 特徴の[通常能力値]の変化は100%を初期値とした0〜1000%(内部的には0.01単位の実数)で管理され、複数の特徴の能力変化が重なると乗算される。最終的にバフ/デバフによる変化も乗算される。
      • これらはバフ/デバフ同様に能力値上限の制限を受ける。例えば攻撃1000%が2つあれば簡単に攻撃100倍の効果を得られるが能力上限を超えることは無い。
    • 特徴による能力変化は[使用効果]-[能力強化の解除][能力弱体の解除]の影響を受けない。また内部的にも強化と弱化の区分けをしていない(できない)ため、アクターにとって有利か不利かの判別はあくまでも製作者側で個別に管理する必要がある。
    • 特徴による能力変化を[使用効果]-[能力強化の解除][能力弱体の解除]と同じように解除したい場合、ステートを利用して戦闘中の一時的能力変化と、ステート解除による能力解除と言う形で実装するのが一般的。

スキル/アイテムのダメージ項目の詳細

  • ダメージ項目は[タイプ]、[属性]、[計算式]、[分散度]、[会心]の5項目に分かれる。
  • [タイプ]はなし、HPダメージ、MPダメージ、HP回復、MP回復、HP吸収、MPO吸収の6タイプ。
    • なしの場合、ダメージ項目の値は一切使われない。また[使用効果]も発揮されなかった場合、Vocab::ActionFailureのメッセージが表示される。
      • [使用効果:コモンイベント]は内容に関わらず使用効果なしとして判定されるため、コモンイベントのみのスキル/アイテムは必ず失敗メッセージが表示される点に注意。
    • HPダメージ、MPダメージは[計算式]の計算結果に[分散度]を適用してダメージを計算する。
    • HP回復、MP回復は[計算式]と[分散度]の計算結果をマイナスにして、負のダメージとして計算される。
    • HP吸収、MP吸収は相手にHPダメージ、MPダメージを与えて、与えたダメージ分の数値を回復する。ただし吸収の上限は相手の残りHP、残りMPに制限される。
  • [属性]は通常攻撃、なし、用語で設定した属性一覧の並びになる。
    • 内部的には通常攻撃はID -1、なしはID 0、設定した属性は属性番号が内部的なIDに割り振られる。
    • [属性:通常攻撃 ID -1] の場合、攻撃属性は装備している武器およびアクターや職業、ステートの特徴[攻撃時属性]によって決定される。
      • 特徴によって複数の[攻撃時属性]がある場合、[属性有効度]を考慮して最も効果の高い属性が適用される。
      • 複数の属性をもつ場合、攻撃側が常に有利である。敵が複数の属性を持つ場合、アクターが効果的に防ぐことは難しくなる。
    • [属性]IDが0の場合、[属性有効度]無視攻撃という扱いになる。
    • [属性]IDが1以上の場合、対応する[属性有効度]のみが参照される。他の特徴による属性は無視される。
  • [計算式]はダメージ計算のタイミングでevalに渡される。
    • 呼ばれるタイミングはすでに命中やクリティカルの判定が行われた後である。
    • evalに渡される関係でRubyで使われるコードならば何でも通るが、コードにエラーがあった場合には計算結果0として処理される。
    • 例えば敵の数によって一体当たりのダメージを減らしたい場合、以下のような計算式も可能。
      • (a.atk * 4 - b.def * 2) / [$game_troop.alive_members.size, 3].min * 1.5
      • 敵の数が1体なら1.5倍ダメージ、2体なら1体当たり0.75倍、3体以上なら0.5倍
  • [分散度]は上下の割合を示す。初期状態の20の場合、-20%〜+20%の範囲でダメージが分散するため、ダメージ幅が最大40%差になる。
    • 実際の計算式は ダメージ×分散度÷100=ampとし、ダメージ + (0〜amp + 0〜amp - amp) となる。
    • 例)ダメージ100、分散度20ならamp=20、乱数が0 0 なら0+0-20で-20となりダメージは80。乱数が20 20なら20+20-20で+20となりダメージ120となる。
  • [会心]はいわゆるクリティカルヒットの可否を示す。ここが[なし]の場合はクリティカルヒットは発生しない。

ターンカウントのタイミング

  • ターン数は、敵味方の行動が決定され、バトルイベントが終了した直後、実際の行動前にカウントアップされる。
    • 現在のターン数は $game_troop.turn_count で参照できる。
  • 敵の行動パターン設定の行動条件には不具合があり、ターン数 0 + n *X が正常に判定されない。
    • 例えば ターン数 0 + 3 *X の条件は、0ターン目はfalseになる。
    • この問題は ターン 0 + 0 *X の条件を別に設定すれば解決できる。0 + 0 *X の条件は、0ターン目にtrueになる。
  • バトルイベント作成時には、ターン終了後のチェックが入っているかどうかでターン数が変化している点に注意。
  • コマンド入力時(敵行動判定時)と行動実行時ではターン数が変化している点に注意。
# 不意打ち、先制攻撃    ターン0
# コマンド入力          ターン0 ※不意打ちの場合にはスキップ
#   敵行動決定          ターン0 ※ターン数基準の行動判定に不具合アリ
#   バトルイベント実行  ターン0 ※ターン判定のバトルイベント処理
#   ターン数+1          ターン1
#   ターン行動処理              ※決定した行動の実際の処理
#   ターン終了処理              ※不意打ち、先制攻撃フラグの初期化
#   バトルイベント実行 ターン1 ※ターン終了時のバトルイベント処理
# ループ

複数回行動の速度補正

  • 1ターンに2回以上行動する場合、速度補正は最も遅いものが全ての行動に適応される。
    • これにより、ファスト技(速度補正がプラスのスキル)とディレイ技(速度補正がマイナスのスキル)を同時に使うとディレイ技のタイミングでファスト技も発動することになる。
    • 具体例を挙げると、防御は速度補正+2000の最速ファスト技だが、1回目に攻撃、2回目に防御を選択した場合、防御が発動するのは攻撃の後になる。当然順番を入れ替えても結果は同じ。
  • VXAceの仕様では実際には1ターンに複数回行動判定しているわけではない。
    • 行動判定自体は1回のままで、1行動内で「選択された複数のアクションをまとめて実行する」というコードになっている。
    • 行動速度を決定する Game_Battler.make_speed 内で、アクションごとの速度の最小値を返す。
    • TRPG風に言うと、メインプロセスが2回発生するわけではなく、1回のメインプロセス内でメジャーアクションが2回発生すると言えばわかりやすいだろうか。
  • なぜこんなことになっているかというと、行動順判定に使用されるアクションリストが、実際にはbattlerリストである事が原因。
    • 行動するbattlerリストをアクションの行動速度順に並び替えてからeachで実際の行動を処理しているため1人のbattlerにつき1回の行動しか判定できない。

行動順の決定

  1. 全てのコマンド入力が終了した時点で全ての行動の速度を比較して決定される。行動順は Game_Action.speed の戻り値を比較して行われる。
  2. 対象者の敏捷に乱数(0〜敏捷÷4+5)を加算する。敏捷が最大の999の場合、999〜1252。
    1. Rubyの除算式(/)は整数のみの場合は整数計算になるため999÷4は249になる。249+5=254でrand(254)は0〜253の値になるので最大値は999+253=1252
  3. スキルやアイテムの速度補正を加算する(-2000〜+2000)
  4. 通常攻撃の場合、特徴の「攻撃速度補正」を合計して加算する(-2000〜+2000 x 特徴の数だけ)
  5. 上記で算出された値が行動順基準値となる。
  6. 行動順基準値の高い順に行動する。
  • 上記の通り、通常の上限値においては速度補正1252以上を設定すれば敏捷999の敵を相手に敏捷1のキャラでも先手を取れる。
    • 乱数がかかるのは敏捷の値だけという点に注意。速度補正の値には乱数はかからない。
    • 乱数による上昇値は最大でも253なので、それ以上の速度補正を設定すれば同レベルの敏捷相手には確実に先手を取れる。
  • 1252〜2000の速度補正範囲内であれば、1の差がそのまま行動順に影響するため、スキルごとの優先順位をつけるのは比較的容易。
  • スクリプト等で能力上限を上げている場合は、速度補正の影響量をそれに合わせて増やさないと意味がなくなるので注意。
    • 例えば能力上限を9999にしているなら、速度補正の影響量は10倍に設定するのが丁度いい。
    • 乱数が大きすぎると感じるなら乱数範囲を下げるのも有効。
class Game_BattlerBase
  #--------------------------------------------------------------------------
  # ● 通常能力値の最大値取得 最大値を9999にする
  #--------------------------------------------------------------------------
  def param_max(param_id)
    return 999999 if param_id == 0  # MHP
    return 9999   if param_id == 1  # MMP
    return 9999
  end
end

class Game_Action
  #--------------------------------------------------------------------------
  # ● 行動速度の計算 速度補正の影響量を10倍にする、乱数範囲を敏捷÷10まで下げる
  #--------------------------------------------------------------------------
  def speed
    speed = subject.agi + rand(5 + subject.agi / 10)
    speed += item.speed * 10 if item
    speed += subject.atk_speed * 10 if attack?
    speed
  end
end

かばうの発動条件

  • かばうの発動条件はHPが少なくなった味方が攻撃を受けたときとなっている。ここの判定がちょっとややこしい。
  • 判定式が hp < mhp / 4 となっているため、MHP1000であれば1〜249の時にかばう事ができるが、MHP999の場合は1〜248でかばう事になる。
    • Rubyでは整数同士の除算は解も整数になる。よって999/4は249となる。比較式が < なので249では発動せず1〜248で発動になる。
    • ちなみにキャラクターの瀕死判定も同じ式なので、瀕死(HPの色が黄色)になったらかばうと解釈して問題ない。
  • なお必中攻撃はかばえない
  • ちなみに完全に余談だが、VXAceではなぜか瀕死判定を各所で個別に行っている。これは本来Game_Battler_Baseなどにcrisis?メソッド辺りを追加して、計算式を一元化する方が良いと思うのだが。

狙われ率の詳細

  • 狙われ率は基本的に職業の特徴で100%(1.0)に設定される。
    • 実はこれは意味が無い。データベースで言う特殊能力(狙われ率〜経験獲得率)は初期値100%で、データベースで設定した値が乗算される。そのため狙われ率100%の設定は、100%×100%で100%になるだけ。敵は狙われ率が設定されていないが初期値が100%なので100%として扱われる。
    • 特徴が複数ある場合は個別に乗算される。1000%が2つなら最終的に10000%になる。
  • 敵の攻撃対象決定ルーチンは、全員の狙われ率を足して乱数をかけ、先頭から一人ずつ狙われ率を引いていき、0未満になった対象が選ばれる。
    • 例えば4人パーティで全員狙われ率100%なら合計4.0、ランダムで2.57になったとすると、3人目で-0.43になるので3人目が選ばれる。
  • アルゴリズムの関係で狙われ率を多少上下してもほとんど効果を実感できない。
    • 例えば1人目が狙われ率200%の場合だと一見すると大きな数字のように感じるが、最終的な確率は2/5、1/5、1/5、1/5となり、他3人の最終的な確率は25%→20%にしかならないので違いを体感できるほどの差はない。
    • 多くのツクール作品で見られるような狙われ率10%や20%程度の上下は気休め程度の効果も実感できないのはそのため。パッシブならまだしもTPを消費して1ターン狙われ率150%のような効果だとターンやTPを消費してまで使う価値は無い。
    • 個人的な見解ではタンク職は最低でもパッシブで300%(最終的にパーティ全体の50%の攻撃を引き付ける)ぐらい欲しい。スキルを使用して更に900%(合計2700%で全体の9割を引き付ける)ぐらいはないとタンク職とはとても呼べない。

反撃、魔法反射の仕組み

  • ツクールの反撃、魔法反射はスキルの命中タイプによって動作が変化する。
  • 命中タイプ「物理攻撃」の場合は回避判定に失敗した場合に「反撃」の判定が行われる。
    • 反撃が成功するとスキルのダメージ処理をスキップして通常攻撃(正確にはGame_BattlerBase#attack_skill_idで返されるスキル)で反撃する。つまり回避の完全上位互換。
    • 複数対象のスキルの場合、反撃は攻撃1回ごとに判定される。連続攻撃なら攻撃回数分だけ発生するし、全体攻撃をかばう場合はかばった攻撃全てに反撃の判定がある。
    • 反撃は味方からの攻撃(混乱などによる攻撃)には発生しない。
    • 通常攻撃にステートを追加する特徴を持っている場合、反撃時にもその効果が乗る。
  • 命中タイプ「魔法攻撃」の場合は魔法回避判定に失敗した場合に「魔法反射」の判定が行われる。
    • 魔法反射が成功すると、敵の魔法がそのまま相手に跳ね返る。この場合、攻撃力は使った相手の能力で判定される。自分はダメージを受けない。
    • 魔法反射は味方の攻撃に対しても発動する。これは回復魔法や補助魔法なども関係なく反射するので注意が必要。
    • 具体的に言うと、相手のターゲットが相手に切り替わってダメージ計算が行われる。そのためMP吸収やHP吸収効果のスキル/アイテムが魔法反射した場合、自分のHPやMPを吸収することになり±ゼロにはなるが、内部的には吸収処理は行われている。
    • マホカンタやリフレクは相手サイドに跳ね返るが、ツクールでは使用者に跳ね返る。そのためデルタアタックはできない。そもそも味方を攻撃対象に選べないが。
  • 命中タイプ「必中」の場合、回避、魔法回避、反撃、魔法反射は全て発動しない。
    • より正確に言えば必中の場合、全ての確率取得メソッドで0が返される。
  • ここまで読んで気付いた人もいると思うが、例えば回復魔法や味方強化魔法を「魔法攻撃」に設定してしまうと、味方にかけられた魔法であっても回避してしまう問題がある。
    • 回復魔法や補助魔法など味方にかけるスキルやアイテムは「必中」に設定しておくこと。

回復効果率と薬の知識の仕様

  • 回復効果率は、回復効果を受けたアクターの回復効果率が回復量に影響する。
    • 使用効果のHP回復とMP回復、ダメージタイプのHP回復とMP回復の効果量に反映される。
  • 薬の知識は、回復アイテムを使用したアクターの薬の知識が回復量に影響する。
    • アイテムを使用した場合の使用効果のHP回復とMP回復にのみ反映される。
    • スキルの使用効果は勿論、ダメージタイプの回復にも反映されない点に注意。
  • 両方が重複した場合は下記の計算式で回復量が決まる。
    • ダメージタイプの回復量×回復効果率 + 使用効果(最大値×割合+加算値)×回復効果率×薬の知識
    • ダメージ分散値はダメージタイプの計算式の結果にのみ反映される。使用効果の分は固定値。
  • 例)ダメージタイプ:HP回復 計算式:100 使用効果:HP回復10%+100 薬の知識:300% 回復効果率:200% 最大HP:2000 の場合
    • 100×200% + (2000×10%+100)×200%×300%
    • = 200 + 300×600%
    • = 200 + 1800 = 最終回復量は2000
  • 上記の仕様により、例えばダメージアイテムの効果などは薬の知識では変化しない。
    • 回復アイテムの回復量をダメージタイプの計算式で指定すると、薬の知識の効果が反映されないので注意。
    • 攻撃アイテムやダメージタイプでの回復量にも薬の知識を反映するには以下のスクリプトを使うと良い。下のメソッドも追加すればTP回復にも薬の知識が有効になる。
class Game_Battler < Game_BattlerBase
  #--------------------------------------------------------------------------
  # ● ダメージ計算
  #--------------------------------------------------------------------------
  def make_damage_value(user, item)
    value = item.damage.eval(user, self, $game_variables)
    value *= item_element_rate(user, item)
    value *= pdr if item.physical?
    value *= mdr if item.magical?
    value *= rec if item.damage.recover?
    value *= user.pha if item.is_a?(RPG::Item)  # この行を追加
    value = apply_critical(value) if @result.critical
    value = apply_variance(value, item.damage.variance)
    value = apply_guard(value)
    @result.make_damage(value.to_i, item)
  end
  #--------------------------------------------------------------------------
  # ● 使用効果[TP 増加]
  #--------------------------------------------------------------------------
  def item_effect_gain_tp(user, item, effect)
    value = effect.value1.to_i
    value *= user.pha if item.is_a?(RPG::Item)  # この行を追加
    @result.tp_damage -= value
    @result.success = true if value != 0
    self.tp += value
  end
end

クリティカルの仕様

  • クリティカル(会心の一撃、痛恨の一撃)が発生した場合、最終ダメージが3倍になる。
    • この値は Game_Battler の apply_critical で設定されている。

バトルイベントの実行タイミング

  • 条件:ターン終了時の場合
    • 戦闘に参加している敵味方全員の行動が完了し、ターン終了時のステート処理が終了した後に実行される。
  • 条件:ターン終了時以外の場合
    • 戦闘開始時と、コマンド選択終了直後のタイミングで条件を判定し、条件を満たしていれば実行される。
    • 不意打ちの際は戦闘開始時と敵の先制行動直前の2回判定されてからコマンド選択になる。
      • 処理としては戦闘開始時(ターン0)に一回、ターン+1されて敵行動直前(ターン1)に一回、イベント判定が呼ばれる。
  • 条件:ターン n + s * X の場合
    • 0+0の場合は戦闘開始時に必ず実行される。スパンをバトルにすれば戦闘開始時に一度だけ必ず実行されるイベントを作れる。
    • ターン数はコマンド選択後のバトルイベント終了時にカウントアップされる。
    • 敵の行動選択はターン数カウントアップ前に行われる。
  • 複数のページが条件を満たす場合
    • 番号の小さい方から順に、条件を満たす全てのページのイベントが実行される。
    • 小さい番号のイベント内容によって大きい番号のイベント条件を満たした場合、大きい方のイベントも実行される。
  • スパン:モーメントの場合
    • 条件を満たす限り、同じページのイベントがループし続ける。イベント内でイベント開始条件をオフにする処理を忘れずに。

使用効果「コモンイベント」の詳細

  • 使用効果のコモンイベントはアイテム(スキル)の効果によって呼び出されるメソッドとタイミングが異なる。
    • 以下はアイテムもスキルもItemで表記統一。
    • user は Item使用者のGame_Battlerを指す。
    • target は Itemの効果を受けたGame_Battlerを指す。
  • 効果範囲が「なし」の場合
    • Itemを使用した(効果を発揮した)後に、user.item_global_effect_apply → コモンイベントの順で実行される。複数の使用効果がある場合には記述されている順番に処理される。
  • 効果範囲が「敵単体」〜「使用者」の場合
    • Itemの効果発生ごとに(回復やダメージ処理のたびに)target.item_effect_common_event が呼ばれ、全員の処理が終わってからコモンイベントが呼ばれる。
    • このItemで敵を全滅させた場合、最後のダメージ判定の直後コモンイベント実行前に戦闘終了処理が入るため、コモンイベントは実行されない。






VXAceの仕様 イベント/スクリプト編

デバッグ状態/シーン判別方法

  • 現在が戦闘シーンなのかどうかなどの判別方法。
  • 通常の戦闘判定なら $game_party.in_battle で問題ない。スクリプト等でこの値がfalseのタイミングで戦闘中かどうかを判別したい場合などには SceneManager.scene_is?(Scene_Battle) を利用できる。
# 実行環境の状態
$TEST   # 実行テスト中ならtrue 戦闘テスト中もtrue
$BTEST  # 戦闘テスト中ならtrue

# 戦闘中の有無 BattleManager.battle_start メソッド内でtrueに
$game_party.in_battle  # 戦闘中ならtrue 戦闘テスト中もtrue
$game_troop.in_battle  # 戦闘中ならtrue 戦闘テスト中もtrue

# シーンの判定
SceneManager.scene_is?(scene_class)   # 現在のシーンが scene_class なら true
SceneManager.scene_is?(Scene_Battle)  # 例)戦闘シーン(Scene_Battle)なら true

スイッチ、変数の仕様

  • VXAceで用意されているスイッチと変数は、両方ともArrayとして実装されている。
    • 普通にRubyのArrayクラスなので、スクリプトからならどんな型でも代入/参照可能だが注意が必要となる。
    • 例えばスイッチに数字を代入したり、変数にアクターデータを代入したりできる。
    • ただし標準と異なるデータを代入した場合、データベースやイベントの設定では正しく扱えないので注意。
      • イベントの出現条件に使われている変数に数値以外のデータを代入してしまうと、ゲームがエラー落ちしてしまう上に原因の特定が非常に困難になる。これを回避するためには数値以外のデータを代入する変数はその用途を固定化し、他の用途では使わないようにする必要がある。また使用後速やかに0を代入して不正な値を残さないことも重要となる。
    • オブジェクトなどを代入したままゲームを保存(実際のプレイ中にセーブ)すると正常に動かなくなる可能性があるので取り扱いは要注意。データベースで設定できないデータを代入する際には使い終わったら元に戻す事。
  • 両方とも添え字は1からだが、内部的には0番からなので、スクリプトなら0番も使える。
    • 範囲チェックは無いのでスクリプトからならデータベースの設定で設定した上限を超えてデータを代入/参照できる。
  • 配列のサイズは設定上限にかかわらず、実際に使われている最大の添え字までになる。
    • 例えばデータベースでスイッチ100番まで設定していても、実際にスイッチを10番までしか使っていなければArray.sizeは11(0〜10)になる。
    • 逆に上限100でもスクリプトでスイッチ9999番を使っていればArray.sizeは10000(0〜9999)になる。
    • データベースで設定したスイッチの上限は $data_system.switches.size で取得できる。
    • データベースで設定した変数の上限は $data_system.variables.size で取得できる。
  • スイッチの名前は $data_system.switches[n] で取得できる。
  • 変数の名前は $data_system.variables[n] で取得できる。
  • スイッチの内容は $game_switches[n] で取得できる。
  • 変数の内容は $game_variables[n] で取得できる。

セルフスイッチの仕様

  • セルフスイッチは名前と異なり全てグローバルなHashで記録されている。
    • 構造的にはマップID、イベントID、スイッチ文字(A〜D)の3つからなる配列をキーとして、true/false を値に持つ。
    • メモリ効率で言っても通常スイッチよりもセルフスイッチの方が遥かに消費が多い。
    • 更にセルフスイッチはオフにしてもハッシュデータが削除されず、どんどん肥大化する。
  • セルフスイッチはデバッグ画面のスイッチ/変数一覧からも閲覧/変更できないので、使いすぎるとメンテナンスが面倒になるデメリットもある。
  • イベント中の指定は楽だが、なんでもかんでもセルフスイッチにしてしまうとメモリ効率が大幅に落ち、セーブデータも肥大化する。
    • 具体例を挙げるととある同人ゲームでセーブデータのサイズが 1,194,806 Byte、そのうちセルフスイッチのサイズが 442,258 Byte、ファイルサイズの約37%を占めるほどに膨れ上がっていた。セルフスイッチ1つのサイズは基本的に 15 Byte なので単純に3万弱のデータが記録されている事になる。更に問題なのがこのうちtrueのデータが30程度しかなかった点。つまり残りの29000以上のデータは無意味にメモリとファイルサイズを浪費している事になる。 別のゲームではなぜか戦闘ごとにセルフスイッチのデータが無制限に増殖していき、ゲーム中盤辺りでファイルサイズが1GBを超え、まともにプレイできなくなる事例もあった。
    • 対策としては、セルフスイッチの使用は最低限にしてできるだけ通常スイッチを使う。通常スイッチはいくら増えても1データ1バイトなのでメモリ的には問題は少ない。とはいえ手間的にはあまり現実的ではない。
    • もしくはVXAceのコードを修正して、セルフスイッチをオフにしたらハッシュから削除するように変更する。削除されたスイッチは参照された際にfalseで返されるため削除による問題は生じない。
      • 削除するように変更してもONでしか使わなければ意味はないので、一時的な条件分岐にのみ使用し、イベントが終了したら普通のスイッチをONにして使用したセルフスイッチを全てオフにするようにイベントを組むなどの対応も必要となる可能性はある。とはいえ前述の例のようにオフを削除するだけでも相当の軽量化にはなる可能性は高い。
    • 最も使用頻度が高いと思われる宝箱の開閉状態などはどうしようもないので諦める。コレを普通のスイッチでやろうとすると手間が大きくバグの元にもなるので無理に省メモリ化してもメリットが小さくデメリットの方が大きい。
  • おまけとして肥大化したデータの掃除用スクリプトも掲載しておく。
class Game_Interpreter
  def command_123 # セルフスイッチイベントの実行を書き換え
    if @event_id > 0
      key = [@map_id, @event_id, @params[0]]
      $game_self_switches[key] = true if (@params[1] == 0) # オンの時だけ書き込む
      $game_self_switches.delete(key) if (@params[1] == 1) # オフの時はハッシュから削除
    end
  end
end

# 既存のセルフスイッチからfalseのデータを全て削除するスクリプトコード
# イベントの設定で「セーブ画面を開く」の直前にこのスクリプトを走らせるだけでも大分違う
$game_self_switches.delete_if{|k,v|!v}

コモンイベント内でのセルフスイッチ

  • コモンイベント内でセルフスイッチを使用した場合、呼び出し元のイベントのセルフスイッチが操作される。
    • 例えばID:20のイベントからコモンイベント:7を呼び出し、コモンイベント内でセルフスイッチ'A'をONにすると、ID:20のセルフスイッチ'A'がONになる。同様にID:36のイベントから同じコモンイベント:7を呼び出せば、ID:36のセルフスイッチ'A'がONになる。
    • 例外として、イベント中にマップ移動してからコモンイベントでセルフスイッチを操作した場合は、セルフスイッチの操作は行われない。(マップ情報とイベント情報に齟齬が生まれるため)
  • うまく行けばコードを共通化できるが、逆にセルフスイッチの管理が複雑化してバグの温床にもなりやすいので注意が必要。
    • 宝箱やシンボルエンカウントの敵など、シンプルな共通イベントでの使用に留めるのが吉。
  • 余談だが、バトルイベントからセルフスイッチを操作した場合は何も起こらない。

マップ画面のアイテム使用者

  • マップ画面でアイテムを使用した場合、使用者は以下のルールで決定される。
    • パーティ内で「薬の効果」効果が最も高いアクター
    • 同じ数値のアクターが複数いる場合、並びが一番後ろにいるアクター
  • このため特に薬の知識を持たないパーティの場合、使用者が最後尾のアクターに固定される。
    • 通常はあまり気にする必要は無いが、スクリプト使用時に気になることが稀にある。
    • ここを変更したい場合、スクリプトで Scene_ItemBase.user に手を入れる必要がある。

イベントの行動範囲

  • 町の住人やシンボルエンカウント形式の敵は、すり抜けONになっていない限り他のイベントを超えることは出来ない。
    • これを利用すればNPCが進入可能なエリアを限定できる。
    • 例えば街中で狭い通路に入り込んで主人公の移動を妨げたり、シンボル敵が安全地帯まで追いかけてくるといった理不尽な行動を防げる。
  • 進入禁止にしたいマスに中身の無いイベントを設置すればOK。新規イベントは必ず「画像なし、プレイヤーの下」設定になるので簡単。
    • 空イベントではマップエディタ上でわかりづらいと感じる場合は、1ページ目の条件に達成できない条件(例えば未使用スイッチがON、仲間にならないキャラが仲間に居る場合など)を設定して、画像を×印のマップチップでも用意して変更しておくと、マップエディタでは表示されるが実際のマップでは表示されないのでわかりやすい。

Input.updateの謎

  • Input.update は Input.trigger? の直前に書くと正常に判定されない。
    • Inputの内部コードを確認できないため予想になるが、恐らく Input.trigger? は Input.update された時のキーの状態と、Input.trigger? が呼ばれたタイミングでのキー状態の差を判定して情報を返している。
    • Input.update はヘルプにもある通り、基本的に1フレームに1回呼び出す事になるため、最低1フレームの差を判定できないと正しい情報を返せない。
    • 逆に言うと1フレームに数回呼び出してしまうとそのたびに基準となる情報が更新されるため、プレイヤーがキー入力をする「隙間」がなくなってしまう。
    • キー入力は全てのシーンで発生するため、Scene_Base.update で Input.update されているので、独自のシーンを追加する際でも自分で記述する必要はほとんどない。
  • 上記の仕様によって、シーン切り替え時にはリピートがリセットされる。
    • シーンが開始されると Scene_Base.update の他に Scene_Base.post_start でも Input.update が実行される。
    • このため開始直後のフレームでは1フレームで2回 Input.update が実行される。
    • この2回の間に Input.repeat? が存在しないため、リピートが途切れたと判定されてしまうのだと思われる。
  • Input.trigger? の直後には Input.update を記述する必要がある?
    • VXAceのコードを見ると、Input.trigger? でキー入力が成功した場合には直後に Input.update を実行している。
    • 恐らくだがここで実行しないとコードによっては Input.trigger? の判定が1フレームに複数回成功してしまう可能性があるのかもしれない。
    • 実際に問題が出るコードは未検証なので各自注意されたし。

Game_ActorとRPG::Actor Game_BaseItemとRPG::BaseItem

  • Game_ActorとRPG::Actor、Game_BaseItemとRPG::BaseItemなど型の違う引数が同じ名前になっているため判別が難しい(と思われている)。
    • Game_ActorからRPG::Actorのデータを取得するにはactor.actorという風に名前から内容を読み取れないコードで参照する必要があるためソースの可読性がかなり悪くなる。
    • Itemの場合は各オブジェクトが持つデータはID配列なので、RPG::Itemの配列を得ようと思ったらID配列だったということが良くある。逆に元のID配列が必要な場面で勝手にRPG::Itemの配列に変換して返すようにラッピングされている事も良くある。特にスキルに関してはID配列とラッピング配列が混在していて扱いが面倒な事が多々ある。
    • アイテム所持数に関しては{id => 数}のHashなので更にややこしい。
  • Game_BaseItemが使われているのはGame_Actionの@item、Game_Actorの@last_skill、@equips、Game_Partyの@last_itemの4ヶ所のみ。
    • Game_Actorの@last_skill、Game_Partyの@last_itemはカーソル記憶用なので普通は参照しないため問題にはならない。
    • Game_Actionの@item、Game_Actorの@equipsはそれぞれGame_Action.item、Game_Actor.equipsでRPG::BaseItem継承クラスに変換されて返されるため、意識する必要性は薄い。
    • これらの内部コードをいじる場合には注意が必要だが、普通に開発をする場合には恐らく気にする必要は無いと思われる。
  • アクターの場合は普通はGame_Actorだと思って問題ない。分かりやすく言うとGame_Actorは現在のアクターの状態を表し、RPG::Actorはデータベースの設定内容を表すので、利用目的が全く異なる。そのためこの違いを理解していればコード上で両者が混じるということは(プログラミング経験者であれば)考えにくいと思われる。
    • Game_EnemyとRPG::Enemy、Game_EventとRPG::Eventも同じ考えで問題ない。
  • アイテム所持数はGame_Party内で@items、@weapons、@armorsの各フィールドにidをキーとしたHashで記録されている。
    • メニューでは大事なものが別になっているがデータ上は@itemsにまとめられていて、表示の段階で分類しているに過ぎない。
    • 応用すれば防具を装備箇所ごとに分類表示したり武器を種別ごとに表示したりもできる。
    • @itemsはitemsでRPG::Itemの配列にラッピングされているので、元データが必要な場合にはひと手間必要。weaponsやarmorsも同様。

イベントに関する仕様補足

  • プライオリティによる違いはイベントの優先度にも影響する。
    • 「通常キャラの下」→「通常キャラと同じ」の順に判定される。両方の起動条件を満たす場合、足元のイベントが発生し、正面のイベントは無視される。
    • 「通常キャラの上」は標準では一切判定されない。
  • トリガーはある程度同時判定される。
    • プレイヤーがプライオリティ「通常キャラの下」のイベントの上で決定キーを押した場合、「決定キー」がトリガーのイベントのみ実行される。
    • プレイヤーがプライオリティ「通常キャラの下」のイベントの上に乗った場合、「イベントから接触」と「プレイヤーから接触」がトリガーのイベントが実行される。
    • プレイヤーがプライオリティ「通常キャラと同じ」イベントを決定キーで調べた場合、「イベントから接触」「プレイヤーから接触」「決定キー」がトリガーのイベントが実行される。
  • 例えば「通常キャラの下」の接触系トリガーも決定キーでも起動するようにしたい場合、スクリプトを改造する必要がある。
class Game_Player
  #--------------------------------------------------------------------------
  # ● 決定ボタンによるイベント起動判定
  #--------------------------------------------------------------------------
  def check_action_event
    return false if in_airship?
    check_event_trigger_here([0,1,2])   # [0] → [0,1,2] に変えると決定キーで接触イベントを起動できるようになる
    return true if $game_map.setup_starting_event
    check_event_trigger_there([0,1,2])
    $game_map.setup_starting_event
  end
end

イベントの内部構造

  • イベントの内容は RPG::EventCommand のリストとして Game_Interpreter に渡され、スクリプトイベント内から @list で参照できる。
    • リストの内容はイベントの編集画面の1行が1項目に対応し、空行は @code=0 として扱われる。
  • この構造を把握しておけば、スクリプトイベントを利用してイベント内容を参照/書き換えが可能。
    • イベント書き換えを利用すると柔軟なイベントの作成が容易になるなどのメリットがある。
    • 実際のコードは次項の「イベント小技/コモンイベント向けスクリプト」などを参照。

参考画像:イベントの編集

参考画像:リスト構造


イベントコード対応表

  • codeはそのまま Game_Interpreter の command_xxx メソッドに対応する。
  • 下表のうち、401(文章データ)、405(スクロール文章)、408(注釈データ)、605(ショップデータ)、655(スクリプトデータ)はそれぞれの親コード(101、105、108、302、355)にて一括にまとめられコード自体はスキップ処理されるため、メソッドも定義されておらず、定義したとしてもコードが呼ばれることはない。
  • 下表のうち、0(空行)、412(分岐終了)、505(移動ルートデータ)は対応するメソッドが定義されていない。
    • 定義されていないだけで呼び出しはされるので、定義すればコードを実行することは可能。
    • 505(移動ルートデータ)に関しては205(移動ルートの処理)で一括で処理されるもののスキップ処理自体はされていないため、205の処理が終わったあとでまとめて呼ばれる。
      • 恐らくこの流れは製作者の意図しないものであり、スキップ処理忘れだと思われる。
  • 下表以外に404(選択肢分岐終了)、604(バトル分岐終了)も内部的には使用されているがメソッドは定義されていない。メソッドの定義は可能。
    • 前述の412(条件分岐終了)も含めてこれら分岐用のコードは分岐に使われる402、403、411、601、602、603も合わせて、分岐の処理に関係なく全て参照されるためメソッドを追加する場合には注意が必要となる。
      • 例えば選択肢(102、402、403、404)の場合は次のような順序で処理される。
      • 102で選択肢ウィンドウが表示され、選択結果が @branch[@indent] に記録される。選択肢が入れ子構造になっても問題ないように @indent を添え字とした配列に記録される。
      • 1つめの402が参照される。選択結果と402のデータが一致するまで次の402(403)まで処理をスキップし続ける。
      • 一致した402(403)に続く処理が終わったら、残りの402(403)を順に参照(スキップ)し、404を通って終了する。
      • 上記の通り、処理の内容にかかわらず全ての402、403、404に対応するメソッドは呼ばれる。
    • 例外として、101(文章の表示)の直後にある102(選択肢)103(数値入力)104(アイテム選択)に関しては対応するメソッドは呼ばれずに直接101内で処理される。

1 page
codeイベント行      codeイベント行      codeイベント行     codeイベント行   
メッセージゲーム進行フロー制御アクター
101● 文章の表示     121● スイッチの操作   111● 条件分岐     311● HP の増減
401: 文章データ     122● 変数の操作     411: それ以外の場合  312● MP の増減
102● 選択肢の表示    123● セルフスイッチの操作412: 分岐終了     313● ステートの変更
402: [**] の場合    124● タイマーの操作   112● ループ      314● 全回復
403: キャンセルの場合               413: 以上繰り返し   315● 経験値の増減
103● 数値入力の処理   パーティ 113● ループの中断   316● レベルの増減
104● アイテム選択の処理 125● 所持金の増減    115● イベント処理の中断317● 能力値の増減
105● スクロール文章の表示126● アイテムの増減   117● コモンイベント  318● スキルの増減
405: スクロール文章   127● 武器の増減     118● ラベル      319● 装備の変更
             128● 防具の増減     119● ラベルジャンプ  320● 名前の変更
             129● メンバーの入れ替え 108● 注釈       321● 職業の変更
0◆ (空行)                    408: 注釈データ    324● 二つ名の変更

2 page
codeイベント行      codeイベント行      codeイベント行     
移動画面効果音楽と効果音
201● 場所移動       221● 画面のフェードアウト  241● BGM の演奏     
202● 乗り物の位置設定   222● 画面のフェードイン   242● BGM のフェードアウト
203● イベントの位置設定  223● 画面の色調変更     243● BGM の保存     
204● マップのスクロール  224● 画面のフラッシュ    244● BGM の再開     
205● 移動ルートの設定   225● 画面のシェイク     245● BGS の演奏     
505: 移動ルートデータ                  246● BGS のフェードアウト
206● 乗り物の乗降                                 
キャラクターピクチャと天候249● ME の演奏      
211● 透明状態の変更    231● ピクチャの表示     250● SE の演奏      
216● 隊列歩行の変更    232● ピクチャの移動     251● SE の停止     
217● 隊列メンバーの集合  233● ピクチャの回転                  
212● アニメーションの表示 234● ピクチャの色調変更   時間調整 
213● フキダシアイコンの表示235● ピクチャの消去     230● ウェイト      
214● イベントの一時消去  236● 天候の設定                    

3 page
codeイベント行      codeイベント行      codeイベント行     codeイベント行     
シーン制御システム設定バトルムービー
301● バトルの処理   132● 戦闘 BGM の変更     331● 敵キャラの HP 増減   261● ムービーの再生
601: 勝った場合    133● 戦闘終了 ME の変更    332● 敵キャラの MP 増減   マップ
602: 逃げた場合    134● セーブ禁止の変更     333● 敵キャラのステート変更 281● マップ名表示の変更
603: 負けた場合    135● メニュー禁止の変更    334● 敵キャラの全回復    282● タイルセットの変更
302● ショップの処理  136● エンカウント禁止の変更  335● 敵キャラの出現     283● 戦闘背景の変更
605: ショップデータ  137● 並び替え禁止の変更    336● 敵キャラの変身     284● 遠景の変更
303● 名前入力の処理  138● ウィンドウカラーの変更  337● 戦闘アニメーションの表示285● 指定位置の情報取得
351● メニュー画面を開く              339● 戦闘行動の強制     上級
352● セーブ画面を開く 322● アクターのグラフィック変更340● バトルの中断      355● スクリプト
353● ゲームオーバー  323● 乗り物のグラフィック変更                655: スクリプトデータ
354● タイトル画面に戻す                                            

注釈の内部仕様

  • イベントで使われる注釈は単にイベント中にコメントを書けるだけの機能ではない。
  • 内部的に @comments インスタンス変数に配列として注釈の文字列が代入される。スクリプト等から参照可能。
  • @comments の内容はマップを変更するまで(Interpreterクラスのインスタンスが変更されるまで)保持される。
    • 新しく注釈があった場合には内容は上書きされる。
    • イベント中にセーブした場合セーブデータには記録されないので、ロード時には失われる。
@comments[0] # 注釈の1行目の文字列を取得

特徴コード対応表

Class: RPG::BaseItem::Feature
Usable: Game_BattlerBase
codedata_idvalueメソッド効果
11element_id1.0=100%features_pi属性有効度 受ける属性ダメージ × value 重複時乗算
12param_id1.0=100%features_pi弱体有効度 デバフを受ける確率 × value 重複時乗算
param_id 0最大HP 1最大MP 2攻撃力 3防御力 4魔法力 5魔法防御 6敏捷性 7運
13state_id1.0=100%features_piステート有効度 ステートを受ける確率 × value 重複時乗算
14state_id-features_setステート無効化 ステートを完全に無効化 戦闘不能を無効化すると不死身
21param_id1.0=100%features_pi通常能力値 能力値 × value 重複時乗算
param_id 0最大HP 1最大MP 2攻撃力 3防御力 4魔法力 5魔法防御 6敏捷性 7運
22xparam_id1.0=100%features_sum追加能力値 重複時加算
xparam_id 0命中率 1回避率 2会心率 3会心回避率 4魔法回避率
 5魔法反射率 6反撃率 7HP再生率 8MP再生率 9TP再生率
23sparam_id1.0=100%features_pi特殊能力値 重複時乗算
sparam_id 0狙われ率 1防御効果率 2回復効果率 3薬の知識 4MP消費率
 5TPチャージ率 6物理ダメージ率 7魔法ダメージ率 8床ダメージ率 9経験獲得率
31element_id-features_set攻撃時属性 通常攻撃属性のダメージ計算に使用される属性
32state_id1.0=100%features_sum攻撃時ステート 通常攻撃属性で追加されるステート確率 重複時加算
33-整数features_sum_all攻撃速度補正 通常攻撃選択時のspeed加算値 通常攻撃属性には反映されない
34-整数features_sum_all攻撃追加回数 通常攻撃選択時の攻撃回数加算値 通常攻撃属性には反映されない
41stype_id-features_setスキルタイプ追加 スキルタイプをコマンドに追加
42stype_id-features_setスキルタイプ封印 スキルタイプを使用不可
43skill_id-features_setスキル追加 skill_idのスキルをスキル一覧に追加
44skill_id-features_setスキル封印 skill_idのスキルを使用不可
51wtype_id-features_set武器タイプ装備 wtype_idの武器種を装備可能
52atype_id-features_set防具タイプ装備 atype_idの防具種を装備可能
53etype_id-features_set装備固定 etype_idの装備スロットを変更不可
54etype_id-features_set装備封印 etype_idの装備スロットを装備不可
551-features_setスロットタイプ 二刀流(盾の代わりに武器二つ可能)
61-1.0=100%action_plus_set行動回数追加 確率でコマンド選択回数増加 重複時個別に判定
62id-special_flag特殊フラグ 0自動戦闘 1防御 2身代わり 3TP持ち越し
63id-collapse_type消滅エフェクト 0ボス 1瞬間標示 2消えない
64id-party_abilityパーティ能力 0エンカウント半減 1エンカウント無効 2不意打ち無効
 3先制攻撃率アップ(4倍) 4獲得金額二倍 5アイテム入手率二倍
  • 攻撃に複数属性が設定された場合、属性有効度が全て乗算される。2.0と0.5なら1.0になる。
  • 行動回数追加0.5が2つの場合、25%で1回、50%で2回、25%で3回行動になる。
  • 先制攻撃率の計算は、味方戦闘メンバー敏捷平均 ≧ 敵グループ敏捷平均 なら5%、逆なら3%。先制攻撃率アップがあれば確率4倍。
    • 不意打ちを受ける確率は、味方戦闘メンバー敏捷平均 ≧ 敵グループ敏捷平均 なら3%、逆なら5%。不意打ち無効があれば0%。

使用効果コード対応表

Class: RPG::UsableItem::Effect
Usable: Game_Battler#item_effect_apply
codedata_idvalue1value2メソッド効果script
11-1.0=100%整数item_effect_recover_hpHP 回復(mhp * value1 + value2) * (isItem ? pha: 1)
12-1.0=100%整数item_effect_recover_mpMP 回復mmp * value1 + value2 * (isItem ? pha: 1)
13-1.0=1-item_effect_gain_tpTP 増加tp += value1.to_i
21state_id1.0=100%-item_effect_add_stateステート付加rate = value1
敵対者なら rate * state_rate * luk
通常攻撃なら rate * user.atk_states_rate
add_state(state_id) if rand < rate
22state_id1.0=100%-item_effect_remove_stateステート解除remove_state(state_id) if rand < value1
31param_idターン数-item_effect_add_buff能力強化add_buff(param_id, value1)
32param_idターン数-item_effect_add_debuff能力弱体add_debuff(param_id, value1)
33param_id--item_effect_remove_buff能力強化の解除remove_buff(param_id) if buff > 0
34param_id--item_effect_remove_debuff能力弱体の解除remove_buff(param_id) if buff < 0
410--item_effect_special特殊効果 逃走escape -> hide
42param_id整数-item_effect_grow成長@param_plus[param_id] += value1
43skill_id--item_effect_learn_skillスキル習得learn_skill(skill_id) if actor?
44common_id--item_global_effect_apply
item_effect_common_event
コモンイベント$game_temp.reserve_common_event(common_id)
  • HP回復、MP回復はそれぞれダメージタイプHP回復、MP回復による計算式とは別に計算される。両方指定された場合は両方の効果を得る。
  • TP増加の効果は発動の得TPとは別に計算される。両方指定された場合は両方の効果を得る。
  • ステート付加は対象が敵対者(actor⇔enemy)の場合のみstate_rateなどが計算される。
    • 逆に言うと混乱などで味方に攻撃した場合、耐性(ステート有効度)を貫通する。無効化は貫通できない。

セーブデータの内部構造

  • VXAceのセーブデータはRGSS3で記述されたクラスのMarshalデータで記録されている。Marshalの仕様については公式リファレンスを参照。
    • Marshalデータはメモリ上のクラス情報を保存、復元するためのRubyのエンコード/デコード機能。ようするにエミュレータや互換機などで良く見かけるステートセーブとほぼ同様のものになる。
    • 上記により一部必要の無いデータまで保存されており、無駄にセーブデータを肥大化させる原因にもなっている。例えばGame_Troopにはエンカウントした敵グループの情報が含まれているが、戦闘後にクリアされないため、前回の戦闘データがそのまま残っている。これは言い換えればプログラム的に見れば戦闘中も含めていつでも中断セーブを作るようなコードを書くことも可能だと言う意味でもある。
  • セーブデータはヘッダとメインデータに分けられる。これは実際にセーブデータ内部で読み込み/書き込みが2回行われている事を意味する。
    • ロード画面やセーブ画面ではこのヘッダ情報だけを読み込んでファイルリストを表示しており、ロード時はファイルが選択された時点で残りのデータが読み込まれる。
  • セーブデータに記録されるものは$game_temp以外のゲームオブジェクト($game_で始まるグローバル変数)のほぼ全ての情報になる。
    • スイッチ、変数は構造上、最小限のデータしか記録されないため、仮に1000個定義していてもセーブデータへの負担はほとんど無い。
    • セルフスイッチは「セルフスイッチの仕様」で説明したようにメモリ効率が非常に悪いため、無意味に肥大化しやすい傾向にある。
  • $game_mapの場合、そこに含まれるGame_Interpreterのインスタンスも記録されるため注意が必要。
    • 簡単に言うと現在のマップに設定された全てのイベント情報がセーブデータに記録されている。これはUniCode対応のテキストエディタなどで開けば全てのセリフも簡単に見える状態になっている。またMarshalデータに詳しければイベントの起動条件なども読み取れる。
    • 単純にイベントの規模が大きければ大きいほどセーブデータが肥大化する。また簡単イベント設定で宝箱を設定している場合、そのエリアで手に入る宝箱の中身も全てダダ漏れになる。
    • こういった内容を隠したい場合は、セーブできる場所を限定して基本的にどこでもセーブは出来ないようにしたり、セーブ可能な部屋は最低限のイベントしか置かないなどの対応が必要となる。
    • なぜこういう仕様かというと、恐らくアップデートによってイベントの内容が変化した場合に現在進行中のイベントが正常に進まなくなったり、最悪はフリーズしたり無限ループしたりと言った問題が出ないようにするためと思われる。ただしこれは一長一短であり、例えば他のマップに移動するためのイベントを付け忘れた部屋でセーブ可能にしていた場合、仮にゲームをアップデートしてもその部屋のイベントをリセットする方法が存在しないため、詰み状態から抜け出す事が出来ないという事案も発生しかねない。とは言えこれはそんなミスを見つけられずにリリースしてしまうような杜撰な開発環境でしか発生し得ないので、丁寧にゲームを製作していれば起こらない問題とも言える。
  • コモンイベントのうち、自動実行や並列処理に設定されているものは、たとえ条件を満たしていなくても全てのマップで通常イベントと同様にメモリ上に読み込まれる。そしてこれらはイベントの内容全てがセーブデータに記録される。
    • これらに大規模なイベントを記述してしまうとセーブデータが無駄に肥大化することになるため、気になる場合には上記通常イベント同様に注意する必要がある。
    • 例えばだがゲーム中特定のボタンを押すと仲間と会話できるようなイベントをコモンイベントで実装し、内部で条件分岐して状況に応じた会話イベントが起こるような設定にしていた場合、これらの会話パターンの全てがセーブデータに記録され容易に閲覧可能な状態になってしまう事になる。またパターン数によってはファイルサイズを圧迫する可能性もある。
      • 対応方法としてはコモンイベントではなくスクリプトで実装したり、自動実行で呼び出すコモンイベントには条件分岐だけ、もしくは会話イベントの書かれたコモンイベントを呼び出すだけのイベントを書くようにするなどがある。セーブデータに保存されるのはあくまでも自動実行や並列処理のイベントだけで、そこから呼び出されるイベントまでは記録されない。
  • Marshalで数値が記録される場合、Fixnumなら数値に合わせて1〜5byteの数値に変換されて記録される。※実際には判別コード0x69"i"で+1byte使用。
    • 例えば正の数なら0〜122までが1byte、123〜255までが2byte、256〜65535までが3byte、65536〜16777215までが4byte、そして16777216〜10_7374_1823までが5byteとなる。
    • プログラマであれば首を傾げる様な話だが、上限についてはFixnumの上限であり、これを超えるとBignumとして扱われるのでこうなっている。
    • 0〜255の場合が若干分かりづらいが、1〜122に関しては実際の数値+5で記録される。そして123以上になると、データ長+実際の数で記録される。このデータ長が-4〜+4で表されるため、正の数を+5、負の数を-5にしてより小さい数字が1byteで記録できるようにしてあるようだ。
      • 若干分かりづらいが、バイナリで表すと122=7Fであり、123=01 7Bになる。この辺りがバイナリを覗くと若干混乱する。
    • 余談だが上記仕様は主にFixnumの符号時のルールであり復号時は柔軟性がある。
      • 例えば符号時は0d16=0x15だが、バイナリが 01 10 となっていても問題ない。極端な話 04 10 00 00 00 でも問題なく復号できる。※Marshalはリトルエンディアン
      • ただし 04 ff ff ff 3f を超える値は-1に変換されるようで復号時に自動的にBignumに変換されたりはしないようだ。
  • 10_7374_1823を超える数値はBignumとして記録される。
    • Bignumは判別コード0x6C"|"、符号"+"or"-"、要素数、2byte整数x要素数で構成される。要素数はFixnum形式で実byte÷2となる。
    • 10_7374_1824なら0x4000_0000、4byteなので要素数2、要素バイナリが00 00 00 40となり、ダンプデータは6C 2B 07 00 00 00 40となる。
    • Bignumは実質無限となっているが、上記仕様を鑑みると少なくともMarshalダンプでは上限が約20億byte整数までしか記録できないような気がするが実際はどうなんだろうか。

各種スクリプトの実行コンテキスト

スクリプトコンテキストClass備考
イベント$game_map.interpreter.command_355Game_Interpreter
移動ルート$game_player.process_move_command
$game_map.events[n].process_move_command
Game_CharacterselfはGame_PlayerかGame_Eventのいずれか
条件分岐$game_map.interpreter.command_111Game_Interpreter
変数の操作$game_map.interpreter.command_122Game_Interpreter
バトルイベント$game_troop.interpreter.command_355Game_Interpreter
ダメージ計算式RPG::UsableItem.damage.eval(Kernel.eval)Kernel引数(a,b,v)参照可能
コモンイベント内スクリプト呼び出し元によるGame_Interpreter

RGSS3の終了処理問題

  • VXAceのゲーム終了方法は、メニューからシャットダウンを選ぶ、ウィンドウの×ボタンで閉じる強制終了(Alt+F4)、の二種類がある。
  • RGSS3ではどちらのゲーム終了時にも Kernel#at_exit、ENDブロック、及び ObjectSpace#define_finalizer で登録したファイナライザの全てが実行されない。
    • これらを必要とするコードを書いてはいけない。
  • シャットダウン時は以下のコードが実行される。強制終了の場合は以下のコードは実行されない。
    • Scene_Base#terminate
    • rgss_main 以降のコード
rgss_main { SceneManager.run }  # スクリプト最下部の Main にあるコード
# シャットダウン時にここのコードが実行される 強制終了時は実行されない



フリーエリア

管理人/副管理人のみ編集できます

広告募集中