2024年5月29日水曜日

【ウディタ】亡霊セクション

こわいバグにはいくつか種類があって
・再現性のないバグ
・いつの間にか直っているバグ
・原因が絞れないバグ
等があると思いますが、今回はなかなか不可解なバグに遭遇してしまったので記事を書いてます。
(執筆時点では直ってます)

なにがあったのかを話す前に環境を話しておきましょう。
ウディタ本体Ver3.302、テキスト編集はVSCode
OSはwin11 home
テキスト表示、マップのEv配置データ、マップ移動イベント等をすべてテキスト管理しています。

バグの舞台となったコモンは、約7年手を入れながら使っている古代兵器、「テキスト表示コモン」。自作です。

今まで通っていなかったルートの通しテスト中、とある場面で「イベントテキストを表示した後マップ移動イベントを呼ぶ」イベントを起動したところ、マップ移動イベントを呼んでマップ移動の暗転が表示されたところで操作不能に陥る不具合に遭遇。

ははあ、もしや呼び出すテキストの指定を間違えたな?
まれによく呼び出すテキスト指定を間違えて変なことになるので、確認してみるとこれは違う。
テキスト本体に異常があることはあまりないですが、一応見てみるとやはり変わったところはなく……。

こうなるとたぶんコモンにバグがあるのだろう、となると思いますが、「イベントテキストを表示した後マップ移動イベントを呼ぶ」なんて動作、ここまでアホほどやっていて、あきれるくらい通過してきたのにバグは一切起きず、ここにきて初めて起きるというのは謎です。

とりあえずデバッグ文を差し込みまくって調査をしていくと
テキストでマップ移動処理を呼んだ直後に謎のコマンドが追加で発生しているようです。デバッグウィンドウ上では完全に「無」。無のコマンドをコモンが読もうとしています。

ただ、これ自体は実は同じ事象が他のイベントでも起きています。
ごくまれに何も文章が表示されないウィンドウが表れる不具合が起きており、おそらくはこれと同じことが起こっています。場所移動ではなく普通のテキストのみですが……。
既知の不具合リストに入れていましたが「キー入力を受け付けるので、気にはなるが進行には支障ないバグ」として後回しになっていました。
ただ今回は暗転したまま操作不能になる重度のバグなので、直さないと先に進めません。

個々は使い古された「枯れた処理」ですが、組み合わせの問題でバグが起きていそうです。こういうバグはかなりやっかいで、下手すると1週間以上消えます。

今回はあんまり時間がないので時間を浪費するのを嫌って、対症療法的な処理を追加することに決定。
「無のコマンドが出るなら無のコマンドが来た場合飛ばすようにすればいいじゃない」
ということで、「無」「改行」「半角スペース」「全角スペース」を文字列分岐に仕込んで判定させたところ、全て不一致。
「無が無と同じならいけるんちゃうんか????」
と頭を抱えながらその日は寝ました。

翌日。
バグを駆逐する心意気で臨んだ2日目の作戦は
「全テキスト中に存在するキャラ名をリスト化して、そこに含まれない名前はすべて飛ばせばいいじゃない」(要はホワイトリスト方式)
全テキスト中に存在するキャラ名をリスト化するコモンと、現在表示しようとしているキャラ名がリストに存在するか判定するコモンを用意。
仕事から帰った後の疲れた頭で作ったので苦戦しましたが動作は完璧。いざ組み込んで動かしてみると、なぜかすべてテキストが飛ばされる。
変数条件は間違っていない、返り値も間違っていない、デバッグウィンドウには、見た目一致しているのになぜか不一致を告げるテキストが表示される始末。
疲れ果てたのでその日は寝ました。

翌日。
3日目の作戦は
「文字列が信用ならないから数値変数で攻める」
テキスト表示コモンですが、処理の流れとしては
指定されたテキストドキュメントの内容を文字列変数に代入し、これを1コマンドずつ分割してCDBへ書き込んで、これをテキスト表示コモンで読み込んで表示しています。
回りくどいですが、このへんの設計理由は忘れました。

なんにせよこのCDBに書き込む処理は最上流にいるので、文字列も(テキストドキュメントでおかしくなっていなければ)変ではないはず。
ここでは、CDBに1コマンドずつ書き込んでいく際に使う変数を使うことにしました。
現在読んでいるコマンドの位置もわかっているので、CDBに書き込んだ回数を上回れば不正なコマンドということになります。

実装前、ためしに現在のコマンド位置をデバッグウィンドウに出してみると、2コマンドしかないマップ移動イベントを呼んでいるのに3コマンド目の表示が……。
実装してみると今回は予定通りに動作して、暗転したまま操作不能になることもなく進行できました。
やはり謎の3コマンド目は出現していましたが、根本原因がわからないので、とりあえず置いておくことにします。

2024年5月4日土曜日

【ゲーム制作】ボスの強さの調整

うでぃおふ同窓会で話題になったので、アクションRPG 2作+制作中1作でやっていたやり方を紹介します。

①ボスのコンセプトをきめる
 決め方はバラバラだったのですが、
 『レイユウサイ』では、戦闘システムで使える機能を1つずつボスに使わせるようにしていました。手探りだったので……。ラスボスは全部乗せ。HP減ると行動パターンが変わるとか、間合いに応じて行動パターンが変わるとか、仲間呼んでくるとか……。

 『ケルリート』では、プレイヤーに覚えてもらいたい戦い方とシナリオのすり合わせのうえで決めていました。
  ・ヒットアンドアウェイを覚えてもらう
  ・位置取りを覚えてもらう(例:レベッカ戦)
  ・複数の強敵を相手取る戦闘
  ・場効果変更を使ってくる相手との戦闘
  ・絶え間ない攻撃のしのぎ方を覚えてもらう(無茶振り)
  等々……。
 「プレイヤーにはこうなってほしい」という目標を設定して、それに沿って、あとは使ってくる属性などが偏らないように作ってます。まあそううまいこといくわけないんですけど……。
 ラスボスはやっぱり全部乗せ。

 『イルシェラート』では、イベント順次第で一部ボス戦が省略可能になってます。今作は戦闘重視ではないので、ケルリートほど細かくはやってないです。
  ・ヒットアンドアウェイを覚えてもらう
  ・間合いに応じて使用してくる技が変わる(=弱い間合いが存在することを知ってほしい)
  ・いやらしい技を使うボス(SPダメージ、状態異常)
  あとは全部イベント戦闘ですが、使う属性や戦法が偏らないように決めています。

②ステータスをきめる
 前提として、戦闘は2、3分程度(BGMが1周するくらい)で終わるようにしてます。難易度は最高難易度を基本にします
 HPは到達想定Lv主人公の最大火力を10回程度ぶつけたら終わるくらいを仮設定として、あとは戦ってみて調整。
 防御力は計算式によると思いますが、防御デバフが死なない程度に設定しています。
 攻撃力は到達想定Lvの主人公を最大火力の場合無対策なら乱1、1種類はバフデバフを使って抑えることを想定しています。あたらなければどうということはないを地で行ってもいいけど……。
 回避/命中は、概念そのものを入れていないので設定なし。

③スキル仕様をきめる
 目の前に発生するやつ、飛び道具、設置技、範囲技の4つをベースに、発生フレームとか多段ヒット有無とか、発生方向とか、いろいろなオプションからボスの性格に合わせてスキル仕様を決めています。当たりやすい技は威力低めか再詠唱時間長め、そうでない技はその逆。1つは大技を入れます。
 レイユウサイは目の前を攻撃するくらいしかできなかったので、威力調整くらいしかしてません。

④行動パターンをきめる
 スキル仕様が生きるように、行動パターンを決めます
 ただ、うちのゲームは行動パターンをUDBで細かく設定できません。作者がものぐさなので、UDBではいくつかの数値設定(間合いの取り方、技使用間合いとか)と、特殊行動(HPが減ると大技撃つとか)しか設定できず、あとは共通の条件に沿って動いてます。
 各作品でコモンを書き直しているので、機能は違うのですが
 『レイユウサイ』・・・近づいて殴るだけ。技は仕様がアホなので使い分けできてません
 『ケルリート』・・・接近型、間合い維持型、遠隔型がいます。技は確率で使い分け
 『イルシェラート』・・・接近型、間合い維持型、遠隔型がいます。技は状況に応じて使用確率が変動、攻撃技は間合いに応じて自動で使い分け
 スキル仕様のほうが重要な設計になってます。

⑤実際に戦う
 想定Lvよりちょい低いLvのデータを用意して、何度も戦いながら調整します。
 アクションRPGなので、ダメージ量だけでなく当たり判定の密度も重要です。ぬるすぎると思ったら当たり判定の密度が上がるような調整をしますし、きついと思ったらダメージ量か当たり判定密度を落とします。
 このあたりは完全にフィーリングに頼ってます。日をあけたらきつすぎるとか、ぬるすぎとか、ままあります。そういうときはむやみに触らず放置します。