Beat Saber modって、どうやって作っているの?と興味がある方もいらっしゃるのではないでしょうか?今回は、一から簡単なmodを作るところを見ていただいて、mod制作者がどのようなことをしているのか、イメージをつかんでいただければと思いました。mod作成に興味があれば、このガイド通りに実際に作業を行ってみるのも楽しいと思います。
まず、必要なのは開発ソフトです。Visual Studio 2019を使用します。以下から無料(Community版)でダウンロードすることが可能です。
Visual Studio 2019 for Windows および Mac のダウンロード
https://visualstudio.microsoft.com/ja/downloads/
ダウンロードしたファイルを実行してインストールを行います。下のような画面が出たときは、「.NETデスクトップ開発」にチェックを入れます。他は初期設定のままで良いと思います。
インストール後、初回起動時に、Microsoftアカウントでのサインインを促されます。特に問題がなければ登録、サインインを行いましょう。サインインしなくてもいちおう使用できますが、30日後に使用できなくなります。
何の機能もないBeat Saber modのテンプレート、ひな形が用意されているので、そちらを使わせていただきましょう。
以下から、「Assets」のところをクリックして、BeatSaberModdingTools.vsixをダウンロードし、ダブルクリックしてインストールします。インストール時は、Visual Studioは終了させておきます。
https://github.com/Zingabopp/BeatSaberModdingTools/releases
それではこれから新しくmodを作っていきます。今回は、画面に時計を表示するだけの、シンプルなmodを作ろうと思います。
Visual Studio 2019を起動して「新しいプロジェクトの作成」をクリックすると、以下のような画面になると思います。
ここではBSIPA4 Plugin (Core)を選択して「次へ」を押します。見つからない場合は、「テンプレートの検索」の欄にBSIPAと入れると、絞り込み表示されます。
プロジェクト名にこれから作るmodの名前を入れます。場所は、ファイルの保存場所になります。適宜自分の使いやすい場所に変えておくと良いでしょう。他の欄はそのまま触らず、作成を押します。
追記:
.NET Framework 4.7.2が入っていないと、ここでエラーが発生するようです。その場合は、下記ページからインストーラーをダウンロードして、インストールを行ってください。
次は、今作成したプロジェクトに、ビートセイバーのインストール先の情報を保存します。
上に並ぶメニューの中から「拡張機能」→「Beat Saber Modding Tools」→「Settings」と選択します。
Beat Saberのインストール先が表示されていることを確認して、OKを押します。
先ほどの画面に戻り、「ソリューション~」の下の「SimpleClock(modの名前)」を右クリックし、「Beat Saber Modding Tools」→「Set Beat Saber Directory...」と選択します。
これでBeat Saberディレクトリの設定は完了…なんですが、一旦プロジェクトを閉じて、開きなおさないとこの設定は読み込まれないようです。Visual Studio 2019を終了させて、再度起動し、最近開いた項目(R)のリストからSimpleClock.sln(modの名前)をクリックして開きます。
なお、この「プロジェクト名を右クリック」→「Beat Saber Modding Tools」→「Set Beat Saber Directory...」→「Visual Studioを再起動」の作業は、サンプルコードをダウンロードして使用するときも最初に行います。
お待たせしました。ここからやっとプログラムを書く作業に入れます。
まず右の欄から「SimpleClockController.cs」をダブルクリックして開きます。このテンプレートでは、ビートセイバーの起動と同時にこのファイルの内容が実行されるようになっており、ここに動作を書くことでmodを作っていきます。
Monobehaviour Messagesという行の左にある[+]をクリックしてください。この[+]の中に隠れていたコードが表示されるようになります。
private void ~ で区分けされた記述に気づかれると思います。この部分は自分で自由に追加することもできるのですが、一部の文字は特定の挙動に対応するようになっています。
Awake: 初期化時に1回だけ呼び出される
Start: 最初に描画されるとき、1回だけ呼び出される
Update: 毎フレーム呼び出される
OnDestroy: 破棄(終了)時に1回だけ呼び出される
例えば、Updateの下の { } 内に何か動作を書くと、毎フレームその動作が実行される、といった具合になります。
private void Start()の下の { } 内に、以下をコピーして貼り付けてください。
----
gameObject.AddComponent();
CurvedTextMeshPro textMesh = new GameObject("Text").AddComponent();
textMesh.transform.SetParent(transform);
textMesh.alignment = TextAlignmentOptions.Center;
textMesh.transform.eulerAngles = new Vector3(90, 0, 0);
textMesh.transform.position = new Vector3(0, 0.1f, 1.5f);
textMesh.color = Color.white;
textMesh.fontSize = 0.5f;
textMesh.text = "test";
----
こちらは文字を生成して表示する一連の処理になりますが、まだこのままでは動きません。エラーを表す赤の波線が3か所表示されているので、順に修正していきます。
ここで上記のようなメッセージが表示される場合は、右の「はい、有効にします」をクリックしてください。
まず、CurvedTextMeshPro(2つあるどちらでも)の上を右クリックして「クイック アクションとリファクタリング...」を選択、その後「using HMUI;」を選択すると、CurvedTextMeshProにあった赤の波線が消えます。
同様にTextAlignmentOptionsも右クリックして「クイック アクションとリファクタリング...」を選択、その後「using TMPro;」を選択すると、TextAlignmentOptionsにあった赤の波線が消えます。
何をしているのかというと、CurvedTextMeshProやTextAlignmentOptionsは、今コードを書いているファイルの外に存在するものなので「見つからない」とエラーが出ていたものを、どこに存在するか指定する作業を行いました。
ビルドとは、コード(テキストファイル)の状態のプログラムから、mod(dllファイル)を生成する処理になります。
画面上のメニューから「ビルド」→「ソリューションのビルド」を選択すると、画面下の欄に「ビルドを開始しました...~」というメッセージが出てきます。最終的に「========== ビルド: 1 正常終了、0 失敗、0 更新不要、0 スキップ ==========」
または
「========== ビルド: 0 正常終了、0 失敗、1 更新不要、0 スキップ ==========」
と表示されれば、エラーなくビルドが成功しています。
テンプレートのおかげで、ビルドが成功すると自動的にビートセイバーのPluginsフォルダに、今作成したmodのdllがコピーされるようになっています。
ビートセイバーを起動して、このように地面に文字が表示されていれば成功です。
今回は時計を表示したいので、表示する文字を時刻に変更します。
----
textMesh.text = "test";
----
としていた部分を
----
textMesh.text = DateTime.Now.ToString("HH:mm:ss");
----
に変更してみましょう。ここでの「HH:mm:ss」はHHが時、mmが分、ssが秒に置き換えられて表示されます。
再度、ビルドを行ってから、ビートセイバーを起動します。
勝ったッ!第3部完!
…ではないんです。すぐにわかると思いますが、時刻がまったく動いていませんね。この画像が静止画だからではなく、modに不具合があります。
どこが間違っていたのかわかりますでしょうか?今回、時刻を表示する処理を書いた部分は「Start」の中でした。この部分は上に書いた通り、「最初に描画されるとき、1回だけ呼び出される」処理です。そうなんです。1回しか呼ばれないんです。
時刻を表示する処理を、毎フレーム呼び出される「Update」の中に書けば良さそうですね。ただし、「Start」の中に書いた処理をすべて「Update」に持っていくと大変なことになります。
「Start」に書いた処理は、文字を生成する部分と、生成された文字を書き換える部分がありますが、この生成する部分は1度だけにしないと、毎フレーム文字がどんどん増えていく、という困った事態になってしまいます。
private void Update()の下の { } 内に、以下をコピーして貼り付けてください。
----
CurvedTextMeshPro textMesh = gameObject.GetComponentInChildren();
textMesh.text = DateTime.Now.ToString("HH:mm:ss");
----
1行目は「Start」の中で生成した文字を探してくる処理になります。2行目は、文字を現在時刻に書き換える処理になります。
ここまでできたら、さきほどと同じように、画面上のメニューから「ビルド」→「ソリューションのビルド」を選択してビルドを行い、画面下のメッセージでビルドが成功しているのを確認してください。
ちなみに、すこしでもプログラミングの知識がある方は「textMesh変数を外のスコープに出して保持すればよいのでは?」と考えるかもしれません。正解です。ただ、プログラミング未経験の方にわかりにくいスコープの説明を避けるため、このような処理にしています。(もっと細かいことを言えば、毎フレームではなく1秒ごとの書き換えでいいとか、もっとGCを減らす書き方があるとか、ありますが。ちなみに、人間が体感できる差はありません)
今度はばっちりです。相変わらず静止画なので動いていませんけど、雰囲気を感じ取っていただけると助かります。
「12:10:58」ではなく「12・10・58」なのはおかしいのでは?と思った方がいらっしゃるかもしれませんが、これはビートセイバー内のフォントで「:」の形が「・」になっているためなので、仕方ありません。
文字が大きすぎる、位置を変えたい、など、自分の好みに合わない部分は変更してみましょう。「Start」に記述した処理の数字を書き換えることで、文字の表示を変更することができます。
textMesh.transform.eulerAngles = new Vector3(90, 0, 0);
ここは回転を指定しています。x軸に90度回転させて地面と平行になるようにしています。たとえば(0, 0, 0)にすると地面と垂直、正面向きになります。
textMesh.transform.position = new Vector3(0, 0.1f, 1.5f);
ここは位置を指定しています。x軸が0(左右位置は中央)、y軸は0.1(地面から10cm上)、z軸は1.5(前方1.5m先)に配置する指定です。このように小数を入れるときは、数字の後ろに「f」を追記してください。
textMesh.color = Color.white;
ここは文字色を指定しています。RGBで色指定したいときは
textMesh.color = new Color(1.0f, 0.0f, 0.0f);
のようにRGB各値(0.0-1.0)で指定します。この場合はR=1の赤色になります。
textMesh.fontSize = 0.5f;
ここは文字のサイズを指定しています。
メニュー画面のときだけ表示して、曲プレイ中は非表示にしたい場合は、「Update」の中を次のように変更してください。
----
CurvedTextMeshPro textMesh = gameObject.GetComponentInChildren();
if (UnityEngine.SceneManagement.SceneManager.GetActiveScene().name == "GameCore")
{
textMesh.text = "";
}
else
{
textMesh.text = DateTime.Now.ToString("HH:mm:ss");
}
----
現在の状態が"GameCore"'(ビートセイバーでは曲プレイ中)の場合、文字を空欄にして、そうでない場合は時刻を表示する、という処理になっています。
ちなみに、ビートセイバー本体のアップデートで、仮にこの"GameCore"が別の名前に変更されてしまうと、この部分は動かなくなってしまいます。本体バージョンアップでmodが動かなくなる、というのは、こういった仕様変更が一因です。
ちょっと長くなりますが、文字を増やす方法も書いておきます。以下のように書き換えてください。応用すれば3つ以上、いくらでも増やすことができると思います。
private void Start() の { } 内
----
gameObject.AddComponent();
CurvedTextMeshPro textMesh1 = new GameObject("Text1").AddComponent();
textMesh1.transform.SetParent(transform);
textMesh1.alignment = TextAlignmentOptions.Center;
textMesh1.transform.eulerAngles = new Vector3(90, 0, 0);
textMesh1.transform.position = new Vector3(-1.0f, 0.1f, 1.5f);
textMesh1.color = Color.white;
textMesh1.fontSize = 0.5f;
gameObject.AddComponent();
CurvedTextMeshPro textMesh2 = new GameObject("Text2").AddComponent();
textMesh2.transform.SetParent(transform);
textMesh2.alignment = TextAlignmentOptions.Center;
textMesh2.transform.eulerAngles = new Vector3(90, 0, 0);
textMesh2.transform.position = new Vector3(1.0f, 0.1f, 1.5f);
textMesh2.color = Color.white;
textMesh2.fontSize = 0.5f;
----
private void Update() の { } 内
----
CurvedTextMeshPro textMesh1 = gameObject.transform.Find("Text1").GetComponent();
textMesh1.text = DateTime.Now.ToString("HH:mm:ss");
CurvedTextMeshPro textMesh2 = gameObject.transform.Find("Text2").GetComponent();
textMesh2.text = TimeSpan.FromSeconds(Time.realtimeSinceStartup).ToString("hh\\:mm\\:ss");
----
positionの数字をtextMesh1とtextMesh2ですこし変えて、左右に二つ文字が配置されるようにしました(変えないと重なってしまいます)。左(textMesh1)が時刻で、右(textMesh2)がゲーム起動からの経過時間になります。
どうして同じ時分秒を表示するのに、DateTimeだと"HH:mm:ss"で、TimeSpanだと"hh\\:mm\\:ss"になるのか疑問に思う方もいらっしゃるかもしれませんが、このように機能によって使い方が微妙に異なることはよくあります。ですので、プログラミングにGoogleは欠かせません。こんなの、全部覚えてられませんからね。
Q. 色は0~255で指定できませんか?
A.
textMesh.color = new Color(1.0f, 0.0f, 0.0f);
のところを
textMesh.color = new Color32(255, 0, 0, 255);
とすれば0~255で指定可能です。4つ目の数字は透明度になります。
こちらのサンプルコードは自由に改変してかまいませんし、もちろん完成したmodも自由に配布可能です。
サンプルコード (プロジェクト)
SimpleClock.dll
https://drive.google.com/file/d/1EFwuBWbj1eFkVCpqKAleYtH1atYFVYVZ
サンプルコードの使用法については、下記記事の冒頭に詳しく記載しています。
fanbox post: creator/53638632/post/2496172
いかがでしたか?意外と簡単だったのではないでしょうか。えっ、難しかった?説明通りにいかない部分があって進めなくなった?もしわからない部分があれば、この記事のコメントで質問していただければと思います。
今回は時刻を表示するだけのmodですが、時刻以外を出してもいいですし、工夫次第で自分だけのオリジナルなビートセイバーを演出できるかもしれません。プログラミング経験のある方は、ここをとっかかりに、もっと複雑なものもすぐに作れるようになると思います。今回の記事で、みなさんがmodについてすこしでも興味を持っていただければ幸いです。
(第2回はこちら)
fanbox post: creator/53638632/post/2493113
Neko-Hangten
2022-06-19 01:49:43 +0000 UTCなるるるるな / NALULUNA
2021-12-16 02:19:27 +0000 UTCNeko-Hangten
2021-12-16 01:51:05 +0000 UTCなるるるるな / NALULUNA
2021-06-27 19:34:46 +0000 UTCなるるるるな / NALULUNA
2021-06-27 19:32:23 +0000 UTCJAN
2021-06-27 13:01:04 +0000 UTCなるるるるな / NALULUNA
2021-06-26 12:49:30 +0000 UTC