Overdungeon

Overdungeon

Not enough ratings
そうだ、改造しよう。或いはUnity製ゲームの改造入門
By penta0
*ゲームのファイル構成が変わった為、このガイドで説明している改造方法は使用不可になった。
このゲーム面白いんだけどここが気に入らないんだよなぁ、と具体的な不満を感じているがもはや更新は望めない。
そんな時はどうするか?
   
Award
Favorite
Favorited
Unfavorite
このガイドで説明している改造方法は使用不可
ゲームのファイル構成が変わった為、このガイドで説明している改造方法は使用不可になった。「*_BackUpThisFolder_ButDontShipItWithYourGame」のフォルダはゲーム中に読み込まれていないらしく書き換えても無意味のようだ。
初めに
このゲームは面白いのだが不満に感じる点がいくつかある。

  1. 転生が週1回のみに制限されている。
  2. 闘技場で挑戦できる回数が1日10回に制限されている。
  3. 闘技場でお気に入りのデッキを保存したり優先して表示させる機能がない。

1.はマルチプレイがあるわけでもないこのゲームで進行に制限をかけることで面白くなるとは思えないためだ。
2.も自由に挑戦できなくなることで面白くなることがあるのだろうか?
もしかすると一回一回の挑戦を大事に慎重にプレイしてほしいという開発者からのメッセージなのだろうか。
また3.の機能がないせいでプレイ回数が増えていくと前に完成して強かったデッキってどれだっけ、と探し回ることになってしまう。連戦する時も非常に不便だ。

このように具体的な不満があるのだが、どうやら開発者は次回作にかかりきりでもはや更新は望めないようだ。
そんな時はどうするか?

そうだ、改造しよう。
幸い私はプログラムをかじったことがある。
それにこのゲームは素人でも簡単に開発に手を付けられるというUnity製だ。
何とかなるかもしれない。

早速Unityについて調べてみると使用言語はC#だった。
私はC++までしか経験がないが、知らない言語でもプログラムを変更するぐらいはできるだろう。
いちいち言語まで勉強する気にはならないというのが本音だが。

まずは開発環境を整える所からだ。
開発環境の構築
調べてみると既存のゲームのスクリプトを修正する場合は本家Unityは必要ないらしい。
グラフィックなどアセットの修正までするとなると別だがスクリプト修正するだけなら逆コンパイラのdnSpyというものだけで問題ないそうだ。

早速下記のサイトでダウンロードし適当なフォルダに解凍する。
https://github.com/0xd4d/dnSpy/releases

ここではdnSpy-net472.zipをダウンロードしよう。
これを書いている時の最新バージョンv6.1.6のnetcore版には以下のコンパイルできなくなるバグが残っているようだ。
https://github.com/0xd4d/dnSpy/issues/365

そしてdnSpy.exeを実行するだけで開発環境の構築はおしまいだった。
実に簡単だ。

後はOverdungeonのフォルダからゲームのソースファイルが格納されているdllファイルを探して開けば準備完了となる。

結論から言うと
Overdungeon\Windows-64bit_Data\Managed\Assets.dll
が目的のファイルだ。フォルダの中で一番容量の大きなファイルである。
ここで修正前のAssets.dllをコピーしてAssets.dll.bakにリネームすることでバックアップをとる。

最後にこのファイルをdnSpyのAssembly Explolerペインにドロップすれば準備完了である。

ツリーにAssetsが追加されているのが分かるだろう。

そしてAssetsを開いて適当に中身を見てみると…

良かった、難読化などはされていない。
これならば素人でも問題なくいじくれそうだ。
転生の制限を解除する
最初に転生の制限を解除してみよう。

まず何をすればいいかというと、用語の調査である。
ソース内でゲームの各用語が何と書かれているかが分からなければ変更したい処理がどこに書かれているのかも特定できないからだ。
これはもうソースをあちこち開いてみて調べまわるしかない。

色々見て回った結果、転生はrebirthと書かれていることが分かった。
早速この言葉でソース内を検索してみよう。
しかしその前にAssembly Explolerペインから不要なファイルを消しておこう。
選択してDELキーを押せば消えてくれる。

その後Searchボックスの右の端にある検索範囲セレクトボックスにSelected Filesを選択する。これは余計な検索結果が出てこないようにするためである。もしAssets.dll以外に参照しているファイルまで検索したい場合はFiles in Same Folderをお勧めする。
そしてAssembly ExplolerでAssets.dllを選択してSearchボックスに「rebirth」と入力すると検索結果が表示される。

されるのだが、なぜか転生制限処理を行っている箇所が出てこない。

更に調べたところ出てこない理由が分かった。
このソースを書いた人スペルミスをしている…。
rebirthをrebrithと書いているのだ。
よくあることではあるが、ソースの検索性が落ちるから気を付けよう。

改めてSearchボックスに「rebrith」と入力する。

よし、それっぽいクラス名「EndlessDungeonRebrithHandler」が出てきた。
検索結果の「EndlessDungeonRebrithHandler」の文字をダブルクリックすると該当箇所を表示してくれるのでスクロールして調べてみる。

転生ボタンが押せるかどうかを切り替えている箇所を発見できた。


メニューのedit->Edit Class (C#)...を選択する。

するとEdit Classダイアログが表示されるので下記のようにnextRebrithTimeの値に関わらず必ずcanRebrith = trueになるように115行目を追加する。
これで常に転生ボタンが押せるようになるはずだ。

修正が終わったので右下のCompileボタンを押す。

そしてメニューのFile->Save All...を選択する。

するとSave Moduleダイアログが表示される。

特に変更する必要もないのでOKボタンを押す。

これで改造は完了のはずだ。

ゲームを起動して確認すると

転生ボタンが何度でも押せるようになっている!
スクリーンショットじゃわからないけど!
闘技場の制限を解除する
転生と同じように調べた結果、闘技場の残り挑戦回数はremainingChallengeと書かれていることが分かった。
Searchボックスに「remainingChallenge」と入力すると検索結果が表示される。
そこで「DecreaseRemainingChallenge」が残り挑戦回数の減算処理っぽいので開いてみる。

間違いなくこれだ。

Edit Classダイアログを開いて113行目をコメントアウトする。

これで残り挑戦回数が減らなくなったはずだ。

Compileとセーブ後にゲームを起動して確認すると

闘技場で戦っても残り挑戦回数が減らなくなっている!
やっぱりスクリーンショットじゃわからないけど!
闘技場でお気に入りのデッキを保存したり優先して表示させる機能を付けられたらいいな。
1.変更仕様を検討する。
このゲームには闘技場でお気に入りのデッキを保存したり優先して表示させる機能がない。
なのでこの機能を組み込んでついでにデッキにコメントも入れられたら便利だと思う。

…思うが。
そんな複雑なことをやるにはUIの変更が不可欠である。
これはちょっと入門編でやれる範囲を超えている。
それと言語も解らない素人が気軽に手を出せるような案件ではないだろう。

ではどうするか?
要はお気に入りのデッキが分かればいいのだからデッキを選択する時に前回闘技場で選んだものが最初に表示されればいいのではないか?そうすれば連戦もしやすいし。
UIの変更が不可欠だからデッキのコメントは諦めよう。

このゲームのプレイ履歴は最新の履歴から順に遡って表示されるようになっている。
この表示順を何らかの方法で変更すればいいのではないかと初めは考えたが、プレイ履歴を更新する機能がこのゲームには存在しない。よって既存プレイ履歴のデータを更新してその項目でソートして表示するという手段は使えない。そこで闘技場で戦っている時に何かのタイミングで呼び出しているプレイ履歴データをコピーして最新の履歴としてセーブすることにする。こうすれば更に履歴が増えると後ろに押し流されて行くものの最初の方には表示されるので探しやすくはなるだろう。余計な履歴が増えてしまうため美しくないが。

次に問題になるのはどの操作をトリガーに履歴コピーを行うかだ。
ところで2020/08/08の時点で闘技場でのバトル画面の右上の歯車ボタン->あきらめるボタンは押すと処理が途中で終わってしまってタイトル画面に戻らないというバグがある事に気づいているだろうか?
どうせ動作しないのであきらめるボタンに履歴コピー機能を組み込んでしまえばちょうどいい。

よって具体的な変更仕様は以下になった。

闘技場のあきらめるボタンを押して出てくる確認ダイアログのOKボタン押下後のバトル終了処理に、使用しているプレイ履歴データをコピーして最新の履歴としてセーブする処理を追加する。

2.変更仕様を実装する。
あきらめるは「GiveUp」なのでこれで検索するとQuestHeaderCanvasにGiveUpメソッドが見つかった。

中身を見てみるとどうやらバトルの処理を強制終了しているらしき記述があるのでこれはOKボタンを押した後の処理だろう。
ここにプレイ履歴コピー処理を追加する。

まずはセーブデータの構造を調べる。
Searchボックスに「SaveData」と入力して検索結果からSaveDataクラスを開くと以下が表示される。

TimeDataがプレイ時間関係のようなのでクリックして開いてみる。
なおCtrlを押しながらクラス名やメソッド名などをクリックすると別タブで開いてくれるので覚えておくと便利だ。

TimeDataを開くと以下が表示される。

クリックで開いた場合はメニューの青地に黒の←を押すと元の場所に戻ってくれるので覚えておこう。
ここに定義されているメンバ変数のうちpauseTimeがプレイ終了日時のようだ。
プレイデータのセーブ時にはプレイ終了日時を必ず更新しているはずなのでpauseTimeで検索する。
するとプレイ終了日時を現在日時で更新している箇所はQuestSaveHandler.SetTimeDataだとわかる。

他にも何やらセーブ処理らしきものが見えるがとりあえずこのクラスのメソッド呼び出し方法を調べてみよう。

そこでSearchボックスに「QuestSaveHandler」と入力して検索してみる。
ところがQuestSaveHandlerクラス以外では使用箇所が見つからなかった。
dnSpyの検索は変数やクラスなどの検索でプレーンテキスト検索ではないのでこういう事もある。
仕方ないのでソースをエクスポートして他のツールでgrepしてみる。


メニューのFile->Export To Project...を選択し表示されたダイアログのFolderに適当なパスを入力すればソースファイルをプロジェクトとして抽出してくれるのでこのプロジェクトフォルダを対象に「QuestSaveHandler」でgrepする。
するとGameEndManagerクラスで使用している箇所が見つかった。

それはLoadModuleというメソッドで
QSceneSharedChild<QuestSaveHandler>.Ins.SaveEndMode(mode); if (ASceneSharedChild<ArenaHandler>.Ins.isArenaBattle) { GameEndManager.EndArena(mode); return; }
という形で参照している。よく分からないがクエストのシーンで共有される子クラスのQuestSaveHandlerのインスタンスのSaveEndModeメソッドを呼び出すという意味だろうか。
また下のisArenaBattleとかいうのを見るとこの値を確認することで闘技場限定の処理にすることができるらしい。

これでQuestSaveHandlerのメソッド呼び出し方法が分かった。
次はQuestSaveHandlerの中身からセーブ方法を調査する。

調査の結果、以下をGiveUpメソッドに追加すればいいことが分かった。
if (ASceneSharedChild<ArenaHandler>.Ins.isArenaBattle) { QSceneSharedChild<QuestSaveHandler>.Ins.SaveData.fixedData.isCleared = true; QSceneSharedChild<QuestSaveHandler>.Ins.SaveData.fixedData.isGameEnd = true; QSceneSharedChild<QuestSaveHandler>.Ins.SaveData.timeData.playTime = 0L; QSceneSharedChild<QuestSaveHandler>.Ins.OnEndlessLevelUp(); ResultDataSaver.Save(QSceneSharedChild<QuestSaveHandler>.Ins.SaveData); }
SaveData.fixedData.isClearedとSaveData.fixedData.isGameEndは両方trueでないと闘技場から呼び出せない。
SaveData.timeData.playTimeを0にするのはコピー履歴を他と区別できるようにするためである。
OnEndlessLevelUpはこの中でprivateメソッドのQuestSaveHandler.Saveを呼び出しているので必要になる。
このSaveはプレイ中のデッキの中身などをセーブデータに落とし込む処理を行っている。
そしてResultDataSaver.Saveは実際にセーブファイルに書き込む処理である。

ただこの処理を行うにはQuestSaveHandler.saveDataがpublicでなければならないのでそこを変更する。



そしてGiveUpに上記のコードを追記してCompileする。
なお今回はソースのメソッド上で右クリックt->Edit Method (C#)...を選択している。
これを使えば対象のメソッドだけ表示されるため一つのメソッドだけを修正するには便利である。

3.動作確認をする。
セーブ後にゲームを起動して確認すると
闘技場であきらめるとプレイ履歴がコピーされるようになっている!
あきらめるたびにゲームを再起動する必要があるのが面倒だけど!
終わりに
Overdungeonの改造を通してUnity製ゲームの改造の流れを簡単に説明したが、ソースの難読化がされていなければかなりハードルが低いことは分かってもらえたと思う。

やる気があればカード性能の調整なども簡単にできるので、プログラムをかじったことがある人は挑戦してみてほしい。
5 Comments
penta0  [author] 19 Feb @ 12:05pm 
どうぞ自由に書いてください。
DonutHole0229 18 Feb @ 12:42am 
最新バージョンでもできる改造方法についてのガイドを書いているのですが、このガイドをもとにして書いてもいいでしょうか?
macchaQANT 9 Apr, 2024 @ 9:12pm 
なんかサーバー通信が必須になったぽいですね
返信ありがとうございます……ソロゲーなのにそんなに厳しくしなくても…
penta0  [author] 6 Apr, 2024 @ 2:42pm 
残念ながらゲームのファイル構成が変わった為、このガイドで説明している改造方法は使用不可になったようです。
macchaQANT 2 Apr, 2024 @ 6:29am 
やってみましたが変わらず1週間に1回のままでした…