冴えないModの作り方 ~Beat Saber~
こんちわ、デンパ時計です。Modの作り方の流れ書いていきます。
英語ですがこっちの方が詳しいし親切です。
なお、製作に関して何らかの損害を受けた場合、こちらは一切関与しませんのですべて自己責任で製作してください。
用意する物
・Microsoftアカウント(無料)
・GitHubアカウント(無料)
・Git for Windows(無料)
・Visual Studio 2022(無料)
・dnSpy(無料)
・すでにMod他のが入ってるBeat Saber(Steam版、OculusPC版のどっちか 3000円くらい)
・気合(金で解決できない)
・Visual Studio Code(無料、必須じゃないのでお好みで)
セットアップ
1.Microsoftアカウントを作る
ここ読んで気合で作ってきてください
2.GitHubアカウントを作る
ここ読んで気合で作ってきてください
3.Git for Windowsをインストールする
ここから気合でインストールしてください
4.Beat Saberを買う
できるならSteam版を買って欲しい。Oculus版にはないDLLがあったりして後々困るから。(僕はOculus版だけど)
あとセールとか来たことないからみんな定価で買ってる。
買ったら気合で適当なModを入れる。
5.Visual Studio2022をインストールする
ここから丁寧に解説するよ。
ダウンロードページからセットアップ実行ファイルを持ってくる。
Visual Studioはエディションによって料金がかかったりかからなかったりします。
今回は個人利用なので無料のComunity 2022を選びましょう。
では落としてきたセットアップ実行ファイル(VisualStudioSetup.exe)を使ってインストーラーをインストールしましょう。
いやなに言ってんの
実はこれ本体ではなくインストーラーのインストーラーなんです。
ややこしいですね。
なのでセットアップ中のタイトルもVisual Studio Installerになってますね
インストールが終わるとこんな画面になって何をインストールするか選べます。
チェックつけるのは2つ
・.NET デスクトップ開発
・Unityによるゲーム開発
チェック入れたらインストールボタンを押して今度こそ本体をインストールしましょう。
めちゃ長いので先にdnSpyをダウンロードしておきましょう。
dnSpy-net-win64.zipで大丈夫です。
中はこんな感じになってるんで好きなとこに展開してください。
Visual Studioのダウンロードが終わったらこんな感じの画面になると思います。
ではそのまま閉じてください。
何故ならまだセットアップが終わっていないからです!
続きましてMod製作に必要な拡張機能をインストールしていきます。
まずはUnityModdingTools.Templates.BeatSaber。
ページに飛んだらDownloadをポチーしてください。
BeatSaberModdingTools.vsixという名前のファイルがダウンロードできたはずなのでダブルクリックします。
Installぽちったら勝手にインストールされます。
これで一つ目の拡張機能がインストールされました。
はい、察しのいい方は気づいたかもしれませんが1個だけじゃないんですね。
続きまして、BeatSaberModdingTools.Tasksを落としてきます。
これも一緒です。画面に従ってぽちぽちボタン押してインストールしましょう。
はい、お疲れ様でした。ここまでで必要なセットアップはほぼ完了です。
Mod製作
というわけでVisual Studioを起動しましょう。
スタートメニューにVisual Studio 2022ってやつがあるんでクリック。
新しいプロジェクトの作成
ここでプロジェクトのテンプレートを選びます。
とりあえず上の検索欄にBeatとか打ち込むと絞り込めるはずです。
作るModによって変わるんですけど今回は「BSIPA4 Plugin(Disablable)」で行きましょう。
ここで作るModの名前を決めます。
そういえば何作るか言ってませんでしたね。
今回作るのは精度に応じてSSとかAとかの表示を変えるModです。
なので名前を「RankTextChanger」とかにしときましょうか。
名前を入力したらフレームワークのコンボボックスで4.8を選択します。
では作成をぽちー!
というわけで初期画面がこちら。
サインインしろって言われたら作成したMicrosoftアカウントでサインインしておきましょう。
Comunity エディションを使う条件としてサインインが必須なので。
まずはBeat SaberのインストールフォルダをVisual Studioにわからせましょう。
ちなみに、このあとの設定は初回のみでOKですのでこの後でほかのModを作る際は必要ないです。
拡張機能に「Beat Saber Modding Tools」があるのでSettingを開きます。
開くとBeat Saber Locationsみたいな項目があるのでなければBeat SaberのインストールフォルダをBrowseから選んであげてください。
Steamならローカルファイルを閲覧からどこにインストールされてるか一発でわかります。
Oculus版なら詳細を開いて場所にカーソルを持っていくとパスをコピーできます。
「Generate csproj.user on project load.」にチェック入れてOK押して閉じます。
さあ、ここからコードを書いていきます。
まずはmanifestから。
manifestが何なのかはこちらの記事を読んでください。
書かれている項目で大事なのは「dependsOn」です。
ぶっちゃけそれ以外適当でいいです。
までもゲームバージョンとかを合わせておくと丁寧ですよね。
というわけでそれっぽく編集。
{
"$schema": "https://raw.githubusercontent.com/bsmg/BSIPA-MetadataFileSchema/master/Schema.json",
"id": "RankTextChanger",
"name": "RankTextChanger",
"author": "denpadokei",
"version": "0.0.1",
"description": "",
"gameVersion": "1.21.0",
"dependsOn": {
"BSIPA": "^4.2.0"
}
}
こんな感じになりました。
製作者を意味するauthorとgameVersionだけ書き換えました。
では続いてPluginを開きます。
赤線いっぱいでヤバいですね☆
これはまだDLLの参照がうまく言ってないのでこうなっています。
ソリューションエクスプローラーに注目しましょう。
「RankTextChanger」を右クリックします。
BeatSaberModdingToolsを開くと2項目あるんでどっちも実行。
そうすると、RankTextChanger.csproj.userにBeat Saberのフォルダパスが勝手に入ります。
ではもう一回Plugin.csに戻って「Ctrl + Shift + B」を押して一回ビルドします。
そうするとビルドが終わってModがBeat SaberのPluginsにコピーされたことが表示されます。
よくわかんないけど
========== ビルド: 1 正常終了、0 失敗、0 更新不要、0 スキップ ==========
って出てればいいです。
続いてデバッグ作業です。これめっちゃやるんで覚えてください。
Beat Saberの起動引数に「 --verbose --debug fpfc」を加えましょう。
Steam版なら起動オプションってところですね
Oculus版はショートカットを作成してリンク先の項目の後ろにくっつけちゃえば大丈夫です。
起動するとBeat Saberと一緒に黒いウインドウが出てきますね。
このウインドウはログウインドウといって文字通りログがいっぱい出てきます。
肝心のBeat Saberの方はなんとキーボードのWASDで操作できます。
左クリックとかするとボタンを押すことができます。
ではいったんゲーム内の終了ボタンを押してゲームを終了します。
※もし固まったらログウインドウの中心を右クリックしてください。
インストールフォルダのLogフォルダを開きます。
_latest.logを開きます。
そっ閉じしたくなるくらい意味不明な文字がいっぱい並んでますね。
ここで探すのは
[INFO @ 20:16:23 | IPA] Beat Saber
[INFO @ 20:16:23 | IPA] Running on Unity 2019.4.28f1
[INFO @ 20:16:23 | IPA] Game version 1.21.0
[INFO @ 20:16:23 | IPA] -----------------------------
[INFO @ 20:16:23 | IPA] Loading plugins from Plugins and found 58
[INFO @ 20:16:23 | IPA] -----------------------------
の箇所です。
ここに読み込まれたMod一覧があるので
[INFO @ 20:16:23 | IPA] RankTextChanger (RankTextChanger): 0.0.1
があるかを探します。あればOKです。
ない場合はDLLのコピーがうまくいってない可能性があるのでVisual Studioの拡張機能の設定等を見直しましょう。
ではVisual Studioに戻ってコードを書いていきます。
今回はRankの文字を変更するのにHarmonyを使っていきます。
Harmonyって何?ってなりますがHarmonyはHarmonyです。
ライブラリの名前です。
参照を右クリックします。
Beat Saber Reference Managerをクリックします
そうすると、参照するDLLを選ぶ画面が開くのでLibsにある「0Harmony」にチェックを入れてOK
これでHarmonyが使えるようになります。
次にHarmonyでパッチを当てる関数をdnSpyで探します。
File→Openで「Beat Saber_Data\Managed」にあるdllを全部選択して開く
なんかどわっと表示されるので「Main」をクリックして展開していきます。
なんかそれっぽいのを探します。
下のSerchを押して「Rank」と入力。
Option Serchを「Class」「SelectedFiles」にします。
そうするとRankにかかわりそうなクラスが表示されます。
ここで該当関数があるかどうかはModderの経験と勘が問われます。
一旦「RelativeScoreAndImmediateRankCounter」をダブルクリックして開きましょう。
その中に「UpdateRelativeScoreAndImmediateRank」という関数があるかと思います。
そこに「RankModel.GetRankForScore」というものがありますね。
このRankModelがRankに関わる色々をやってそうなのでダブルクリックして開きます。
そうすると、「GetRankName」という関数がありますね。
そうです。これが今回の目的の関数です。
では、Visual Studioに戻りましょう。
どうも先ほどの関数は「GamePlayCore.dll」にあるらしいのでHarmonyを追加した手順と同様に参照に追加します。
まずは「RankTextChangerController.cs」を消します。
いらないので。
そうするとPlugin.csで赤線が引かれてるのでそこを消します。
では続いて、上にあるコメントを解除しましょう。
Harmonyでくくられている箇所もコメントを解除しちゃいましょう。
そうするとUnpatchAllに赤線が引かれているのでUnpatchSelfに書き換えます。
/// <summary>
/// Attempts to remove all the Harmony patches that used our HarmonyId.
/// </summary>
internal static void RemoveHarmonyPatches()
{
try
{
// Removes all patches with this HarmonyId
//harmony.UnpatchAll(HarmonyId);
harmony.UnpatchSelf();
}
catch (Exception ex)
{
Plugin.Log?.Error("Error removing Harmony patches: " + ex.Message);
Plugin.Log?.Debug(ex);
}
}
いい感じですね。
続いてプロジェクトに「GetRankNamePatch」という名前のクラスを追加します。
はいできました。ではパッチを書いていきましょう。
using HarmonyLib;
using System;
namespace RankTextChanger
{
[HarmonyPatch(
typeof(RankModel),
nameof(RankModel.GetRankName),
new Type[] { typeof(RankModel.Rank) }
)]
internal class GetRankNamePatch
{
public static void Postfix(RankModel.Rank rank, ref string __result)
{
switch (rank)
{
case RankModel.Rank.E:
__result = "超悪";
break;
case RankModel.Rank.D:
__result = "悪";
break;
case RankModel.Rank.C:
__result = "微悪";
break;
case RankModel.Rank.B:
__result = "普通";
break;
case RankModel.Rank.A:
__result = "可";
break;
case RankModel.Rank.S:
__result = "良";
break;
case RankModel.Rank.SS:
__result = "超良";
break;
case RankModel.Rank.SSS:
__result = "神";
break;
default:
break;
}
}
}
}
できました。
一個一個説明すると長くなるのでポイントだけおさえて説明していきます。
まずは
[HarmonyPatch(
typeof(RankModel),
nameof(RankModel.GetRankName),
new Type[] { typeof(RankModel.Rank) }
)]
ここは何の関数にパッチを当てるかを指定します。
HarmonyPathまではおまじないです。
typeofはクラスの種類を教えてあげます。今回はRankModelクラスですね。
nameofは関数の名前を教えてあげます。"GetRankName"と書くのと同義です。nameofを使った方がバグに気づきやすくなります。
new Type[]は関数の引数に何が入るかを教えてあげます。
今回はRankModel.Rank一個なのでnew Type[] { typeof(RankModel.Rank) }だけです。
増えるとnew Type[] { typeof(hoge), typeof(hage), typeof(mage) }みたいになります。
続いて
public static void Postfix(RankModel.Rank rank, ref string __result)
Harmonyはメソッドの名前や引数の名前がきっちり決まってないと動かないのでここは自由に変えることができません。
(書き方次第で自由に変えることもできますが初心者向けではない。)
まず「Postfix」というところ。
この名前にするとHarmonyは「指定した関数の実行後に実行するんだな。」と解釈してくれます。
次に「RankModel.Rank rank」はこの関数の引数を取得します。
rankという名前は元関数の引数の名前に合わせてください。
続いて「ref string __result」は元関数の実行結果を取得します。
なのでここに「SS」とか「A」とかが最初から入ってるわけですね。
__resultという名前以外では実行結果を取得できないのでここも名前は固定です。
refは参照渡しといって色々説明がめんどくさいのですが、中の値を書き換えたらメソッドの外に出てもそれが影響受けるくらいに考えてください。
switch (rank)
{
case RankModel.Rank.E:
__result = "超悪";
break;
case RankModel.Rank.D:
__result = "悪";
break;
case RankModel.Rank.C:
__result = "微悪";
break;
case RankModel.Rank.B:
__result = "普通";
break;
case RankModel.Rank.A:
__result = "可";
break;
case RankModel.Rank.S:
__result = "良";
break;
case RankModel.Rank.SS:
__result = "超良";
break;
case RankModel.Rank.SSS:
__result = "神";
break;
default:
break;
}
さて、本処理です。
まず引数のrankを使ってswitchで場合分けします。
その後ランクに応じて書き換えたい文字を結果の__resultに入れます。
以上です。
こんな感じになってればいいです。
ではPlugin.csに戻ってパッチを当てる関数のコメントを解除しましょう。
はい、では「Ctrl + Shit + B」でビルドしましょう。
めでたくビルドが通ったら、デスクトップモードでBeat Saberを起動して適当な曲を再生しましょう。
うまく変わってたら成功です。
お疲れ様でした。
GitHubで管理する
ここまで作ったコードやdllを配布するときはGitHubが便利です。
Gitは色々コマンドがあって全部理解するのは難しいですが、Visual Studioがほとんど自動でやってくれるので初心者でも簡単な機能なら問題なく使えます。
まず右下にある「ソース管理に追加」を押しましょう。
そしてそこにあるGitを押すとGit管理画面が開きます。
説明に簡単な説明を入力します。
外部に公開する場合はプライベートリポジトリのチェックを外します。
そして「作成とプッシュ」を押します。
すると自分のページのリポジトリの一覧に「RankTextChanger」が出てくるはずです。
アクセスすると今まで書いたコードが閲覧できます。
Readmeの書き方はめんどくさいので割愛します。
さて、続いてほかの人に配布する用のDLLファイルを作成します。
Visual Studioに戻りまして、ビルドオプションをDebugからReleaseに切り替えます。
切り替えたら「Ctrl + Shit + B」でビルドします。
そうすると今までのログと違って「~.zip」みたいなものが書かれてるはずです。
1> Target: BSMT_ZipRelease
1> ZipDir: Zipping Directory "bin\Release\Artifact" to "bin\Release\zip\RankTextChanger-0.0.1-bs1.21.0-56e70f5.zip"
1> Target: BSMT_CopyToPlugins
1> Copying 'bin\Release\RankTextChanger.dll' to 'D:\Oculus\Software\hyperbolic-magnetism-beat-saber\Plugins\RankTextChanger.dll'.
1> Copying 'bin\Release\RankTextChanger.pdb' to 'D:\Oculus\Software\hyperbolic-magnetism-beat-saber\Plugins\RankTextChanger.pdb'.
========== ビルド: 1 正常終了、0 失敗、0 更新不要、0 スキップ ==========
勝手にdllをzipにしてくれる便利機能がついてるんですね。
で、どこにできたかというとプロジェクトフォルダのReleaseフォルダの下にいい感じにできてます。
まず、プロジェクトを右クリックしてエクスプローラーフォルダーで開くを押します。
「bin\Release\zip」に完成したzipが置いてあるのが分かると思います。
後はGitHubにアップロードして終わりです。
右側にちっちゃくCreate new releaseがあるので押します。
うわ、あめりか語や…
いじるとこそんなに多くないので安心してください。
まずChoose a tagを押してModのバージョンである0.0.1を入力します。
下のいかにもファイルを添付できそうなところにzipを持っていきます。
タイトルを適当に書きます。自分はzipの名前をコピペしてます。
説明欄はめんどいので「Auto-generate release notes」を押して自動生成に任せます。
画面下の「Publish release」を押して終わりです。
お疲れ様でした。
今回作ったModのリポジトリ