今回は秋イベに参加した話を書きますが、半分くらいはUnityでニコ生ゲームを作ろうの実践編といった内容になってます。
秋のゲームイベント参加の流れ
今回も一応ニコ生 自作ゲームパーティー-秋-に参加しました。ゲーム作りは例によって直前になって始めたんですがそこまでの流れがいつもとはちょっと違ったので経緯を追って書いていきます。
Unityツール開発
夏イベが終わった後に自作ゲームフェス2023という大型企画の発表がありました。ニコ生ゲームに限らずありとあらゆる自作ゲームのお祭りということで色々な部門賞が用意されています。協賛企業も多く、あのUnityも含まれていました。個人的にはUnityはチョット触ったことがあるだけだったんですが、ニコ生ゲームとか関係なく普通にUnityゲームを作って応募するのもありかなぁなんて考えていました。
しかしここでセコい考えが浮かんできます。もしUnityでニコ生ゲームが作れたらゲーム1個で何部門もエントリー出来るのでは!?と…
まぁ本気で賞を取る気だった訳ではないんですが(笑)、そういう運営の想定外であろうことをするのはワクワクするのでやってみたくなったんですよね😁。それで言うと、7月に公式がRPGツクールでニコ生ゲームを書き出すツールを出してきてたんですが、そこに対してもUnityツールをぶつけてみたくなったというのもあります*1
というわけで、9月はツール開発をしてました。下旬あたりでほぼ完成はしたんですが、1ヶ月ほどプログラミングをお休みしてたので、ツールを公開したのは10/29になりました。
Unityでニコ生ゲームを作るツールを作りました。使い方が分からない点などあればXの返信なりgitのissueなりにどうぞ。(答えられるかはわかりませんが)後でブログ記事も出す予定https://t.co/rHbcXoBkyG
— 磯辺焼き (@isobe_yaki_game) October 29, 2023
検証用ゲーム制作
ツール開発中は下の画面のようなプロジェクトで動作検証をしていました。
しかし、このプロジェクトは散らかって汚くなったので、記事に載せるスクショを撮るためにも、新規ゲームを立ち上げました。これが後に「天体観測」になるんですが、そこまでにかなり紆余曲折してます。
コインプッシャー
紆余曲折1
せっかくUnityを使っているので3Dで物理演算をやりたいと思い、比較的簡単そうなコインプッシャーを作ることにしました。コインをBlenderで作成、台は適当にキューブを並べてプッシャーの動きを付けました。しかし、実際にWeb出力してスマホで動かしたところかなり重い!確認に使ったスマホはXperia10IVという2022年7月に出たばっかのやつなので結構性能はいいはずです。それでもコインがいっぱい重なった状態で全体が動くとガクッとします。通常物理演算はSIMDとかスレッドでがっつり高速化するものですがwasmはそれらが(基本的には)封印されてしまうので、そりゃそうだよなという結果になってしまいました。
重さ的には、ニコ生ゲームの「コイン押し機*2」くらいだと思うので、ありっちゃありだと思います。が、あまり印象が良くないことに変わりはないと思うので今回は諦めました。
ちなみにgifでは(実機と比べると)かなりコインがデカいですが、スマホで遊べるコインプッシャーって大体これくらいデカいですよね。それがいつも嫌だったんで最初は画像の3分の1くらいのサイズのコインでやってみたんですが酷いもんでした・・・。
見た目はかなり現実のそれに近くなったんですが超ガクガクでとてもプレイできるレベルじゃありません。巷のプッシャーゲームのコインがやたらでかいのはしょうがないんだな~と思いました。
マーブルフィーバー
紆余曲折2
とは言えプッシャー系ゲームは元々作りたかったゲームなので悪あがきとしてマーブルフィーバーにも挑戦します。
こちらはコイン(MeshCollider)と違ってガラス玉(SphereCollider)なので物理計算が大分軽いです。でもこっちはこっちでガラス玉を表現しようとすると描画負荷がヤバそうだし、メタリックな玉にしたらパチンコになっちゃうし、単純に見栄えするところまで作り込むのが大変そうだぞということで一旦保留にしました。いつかニコ生ゲーム外で作りたいね。
人間ミサイル
紆余曲折3
マーブルフィーバーを早めに諦めたもう一つの理由として、この辺からワンチャン秋イベに出そうかなと考え始めたというのがあります。卓球の記事でも書きましたが、今後は落選も全然あり得るので季節イベントのために作るというよりは何かのついでで応募するようにしとくとダメージ少ないかなと、今回なら「記事を書く検証用」+「Unityツールのサンプル」+「季節イベント」+「自作ゲームフェス」というバリア4段構えで望めます笑。なので2日くらいで作れそうな簡単なゲームなら秋イベ出してもいいなということで、「3Dを活かせる」「簡単操作」「処理の軽さ」なども考慮して、ユニティちゃんを人間大砲で飛ばしてフィールドをどれくらい壊せたかを競うゲームならいけるんじゃないのという結論に。
ただ破壊するフィールド考えたり作るのが地味にめんどいなということでこれは1時間くらい考えて没に。*3.
星座さがし
紆余曲折4
その後も当てもなくシーンを弄り回していました。その中でなんとなく背景を透過にしてみたくてやり方を調べているとこの記事を発見。これに従い背景を黒一色に変更。で、背景が真っ黒なので何となくそれに合わせてシーンのライトも夜っぽくしてみました。
実際にニコ生上で透過背景は成功したんですが、ポストエフェクトも使ってるとキャラの周囲が透過しないで黒いオーラを放ってるような見た目になってしまったので背景透過はすぐやめました。でも、夜っぽいライティング自体はいい感じなので残しました。ライティングはいいとしてゲームをどうするかですが、もう時間無いし「~さがし」系のゲームでいいかと、そして夜なので星座探したらええねんということで『天体観測』というテーマに行きつきました。制服着た女の子のモデルもあるしなんか恋愛ADVっぽく作ればまとまりそうです。*4
このように直前まで紆余曲折あったせいで天体観測ゲームのプロジェクト名は「Pusher」でシーン名は「Missile」になってます。結局散らかってるじゃねぇかよ笑
天体観測ゲーム開発
結局、締め切り前日の夕方くらいから作り始めることに…果たして間に合うのか?
キャラアセット
さっきからユニティちゃんとか謎のショートヘア少女のモデルとかしれっと出してますが、これは↓のSDユニティちゃんアセットに含まれていたものです。
ユニティちゃんは公式HPで色々なアセットを二次創作許諾で配布しています。(使用する場合はライセンスをよく読んでください。)
ショートの子については、なんでユニティちゃんじゃないキャラも入ってるんだ?と思いつつかわいいからいいやと思って使ってたんですが、後で調べたら小碓学園という二次創作設定?に出てくる『神林ゆうこ』という名前のキャラらしいです。(まだよくわかってない)
今回作ってるときにゆうこちゃんについてた猫耳をただのアクセサリーだと思って外してしまったんですが(なんてことを・・・)、どうやら猫娘キャラだったみたいで着脱して良いものではなさそうでした。すいませんでした<(_ _)>
背景
とりあえず満天の星空がバックに欲しい。Unityで360°の背景が欲しい場合はスカイボックスを使うのが良さそうです。そのためにキューブマップというテクスチャアセットを用意しないといけません。適当にググってみたんですが意外といいのが見つからない。JAXAとかも見てみたけどいいの無いしそもそも使っていいかもわからない。めんどいのでShaderToyで探します。すると求めていたものを発見!
しかもライセンスもCC0なので使い放題です。あとはこれをこういう風に左右上下前後の順で横長の画像に焼き付ければ行けそうです。こういうのはc++が得意なのでサクっと作ります。
// ↓上記シェーダーをc++に書き換えた関数 uint32_t sky(float3 v) { ... } void main() { // GDIPlusのBitmapクラスを使う(png保存のため) Bitmap bmp(Resolution * 6, Resolution); auto data = bmp.Lock(); for_each(execution::par, 0, 6, [&](int faceIndex){ for (int y = 0; y < Resolution; y++) { // この行の書き込み開始アドレス算出 uint32_t* row= data.ptr + y * Resolution * 6 + faceIndex * Resolution; for (int x = 0; x < Resolution; x++) { // 面の番号とピクセル座標から視線方向の3次元ベクトルを算出する float3 v = indexToVector(faceIndex, x, y); // ベクトル方向の空の色を算出する row[x] = sky(v); } } }); bmp.Unlock(); bmp.Save("skybox.png"); }
上記は実際のc++コードを簡略化したものですが、6スレッドを起動して6面の夜空を並列に計算しています。今回は一面当たり1024*1024なので合計で約600万ピクセルを計算することになりますが、3秒以内で完了します👍シェーダーコードからc++への移植ですが以前c++で迷路ゲームを作っていた時にシェーダー風に書ける数学ライブラリを作っていたのでそれのおかげで簡単に移植できました。
出来た画像をごにょごにょすればシーンに反映されます。
あとは、天体望遠鏡の無料素材もあったのでいい感じの位置に配置して背景完成です。
さがしゲーム
星座さがしとは言ったもののはっきりした仕様は考えてなかったのでぶっつけ本番で作ってます。最初はこんな感じのイラストを飛び回らせて、女の子が聞いてきた星座をタップという「手足さがし」みたいなゲームでいこうかと思ってました。でも夜空の星を直接指でつないで星座にするというコンセプトを思いついたのでなんかいい感じにできないかと考え直します。
で、色々考えはしたんですが、あくまで今回はUnityでニコ生ゲーム作れるよ~という技術デモだし、あまり悩んでもしょうがないわということで以下のルールに落ち着きました。
「10×6のグリッド上にランダムに配置された星から見本と同じ並びの星をタップするだけ」
見本と同じパターンが複数現れることもあるのでその場合も正解とするようにしました。
スコア計算
自分のゲームはいつもスコア計算がいい加減です。今回も回収した星1個=1点というシンプル計算。
それだけだと流石に単調すぎるのと、スコアランキングで上位が全部同じ点数になる現象が起きてしまうので、一応コンボとかボーナスどうするかも考えました。
- ボツ案
- 連続正解でスコア倍率UP
- そもそも正解しないと次の問題にいかないので全てが連続正解
- 素早く正解するとボーナス
- もともと速い人ほど高スコアになるゲームなのにスピードにボーナス付けてもうまい人が更に高スコアになるだけ
- 連続正解でスコア倍率UP
- 最終案
- 精度にボーナス
- 速度と精度はある程度トレードオフになると思うのでミスなくタップできたときだけボーナスを付ければ意味はありそう
- 精度にボーナス
かなり大味な仕様ですが、ないよりはマシ、、、だと思う。実装自体はすぐなんですが、この仕様があることをユーザーに何となくでも伝えないといけません。手抜きですが、一度選んだ星を選択解除したときに「出来るだけ間違えないようにしないと…」というメッセージを下に出すようにしました。なんとなく「間違えると良くないんだなー」ということをシステムメッセージではなく主人公の心の声で表したんですが、伝わったんだろうか…?
怖い部屋3Dの時もやってましたが、演出優先で分かりやすさを放棄するという愚行を今回もやっちゃいました。(反省0)
描画
今回のレンダリング周りの小話。
キャラクター
キャラシェーダーですが、これは上で紹介したユニティちゃんアセットに含まれているトゥーンシェーダーをそのまま使っています。
詳しく見てないですが、かなり高機能っぽいです。特に調整もなくリムライトのような効果も出てるし見栄えいいです。
今回は夜のシーンでもあったので影は落とさなかったんですが、影に関しても細かく色味の設定なども出来るようです。
星
SDFシェーダー
星は無駄にsdfという技法で描画してます。 つまり画像を使わずシェーダーの計算でピクセルの色を決定しているということですね。星が瞬くアニメーションもシェーダー内で完結していてスクリプトでは何もしてません。更に言うと白い部分はただの白ではなく、中心に近いほど0xffffffより大きい値、つまりHDRレンダリングになっています。おかげで後述のブルームによって発光表現が適用されます。
ビルボード
星シェーダーを設定した板ポリが常にカメラの方を向くようにビルボードスクリプトをアタッチしています。ビルボードの詳しい説明はこちらがよくまとまってます。
// 記事を参考に作ったスクリプト(記事のコピペだとダメだった) // UnityにBillboardRendererというのもあるが高機能すぎるので不採用 using UnityEngine; public class BillboardTransformer : MonoBehaviour { private void OnWillRenderObject() { var cam = Camera.current; if (cam != null) { transform.LookAt(cam.transform.position); } } }
軌跡
星が回収される時の軌跡はTrailRendererコンポーネントを使っています。かなり手軽に派手なエフェクトをつけられるので便利です。
以下は今回作った軌跡の設定値です。発光する軌跡にしたかったのでマテリアルも専用のものを用意しました。
一番下のレイヤーの順序で-1を設定しています。0のままだと星と軌跡エフェクトが入り混じって描画されてしまい、インスタンシングが効かなくなっていたので変更しました。エフェクト発生時のドローコールが数十減ります。
分かりにくいですが色選択ダイアログの下の方の「強さ」の項目を1.6589にして発光するようにしてます。
また軌跡の軌道は3次ベジェ曲線を使ってます。 赤点が始点で青点が終点で緑点が2つの制御点です。制御点は1つは始点と終点を結んだ線上で、もう1つは始点を中心とした円上のランダムな点を選んでます。
ブルーム
ブルームは全てを解決する。ブルームを信じろ。
という格言があるようにブルームエフェクトは手軽にビジュアルを強化する定番エフェクトです。軽いし実装簡単だし派手だしとりあえずぶっこんどけ的なやつです。
Unity自体にもブルーム機能はありますが今回は簡易なもので良いので自作します。アルゴリズムとしてはほぼこれですが、更に簡易的な実装になってます。
高輝度抽出&ダウンサンプリングパスシェーダー
Blend Off sampler2D _MainTex; fixed4 offset; float threshold; fixed4 frag (v2f i) : SV_Target { return max((tex2D(_MainTex, i.uv + offset.xy) + tex2D(_MainTex, i.uv + offset.zy) + tex2D(_MainTex, i.uv + offset.zw) + tex2D(_MainTex, i.uv + offset.xw)) * 0.25 - threshold, 0.0); }
ダウンサンプリングパスシェーダー
Blend Off sampler2D _MainTex; fixed4 offset; fixed4 frag (v2f i) : SV_Target { return (tex2D(_MainTex, i.uv + offset.xy) + tex2D(_MainTex, i.uv + offset.zy) + tex2D(_MainTex, i.uv + offset.zw) + tex2D(_MainTex, i.uv + offset.xw)) * 0.25; }
アップサンプリングパスシェーダー
Blend One One BlendOp Add sampler2D _MainTex; fixed4 offset; float power; fixed4 frag (v2f i) : SV_Target { return (tex2D(_MainTex, i.uv + offset.xy) + tex2D(_MainTex, i.uv + offset.zy) + tex2D(_MainTex, i.uv + offset.zw) + tex2D(_MainTex, i.uv + offset.xw)) * power; }
どのパスのoffsetにもxyがMainTexの解像度の逆数でzwがそれを負にした値が入っています。このアルゴリズムだと縮小6パス、拡大6パスのたった12パスで非常に広範囲に光が溢れる表現ができます。ぼかし処理というのは半径が大きくなるほど指数的に重くなる処理ですが、よく使われる処理なだけに様々な方法で高速化が考えられていて奥が深いです。
余談
Unityはデフォルトでシーンを半精度小数テクスチャに描画しています。pngなどはRGB各8bitですが、半精度小数テクスチャではRGBが各16bitで、しかもIEEE754の小数点形式になっています。つまり、HDR撮影された写真のようなものということです。 そのため、ブルームのようなポストエフェクトが自然な形で実装できるのでめちゃくちゃ便利ですね。浮動小数テクスチャはWebGL2.0では標準サポートということらしいので、その辺も安心です。
カメラ
舞台裏。ゲームのカメラが切り替わると背景にちょうど星雲が映るようにワールド全体を回転させてます。
また、空の回転ですが実際には空以外の全てを回転させてます。スカイボックス自体を回転させる方法もあるんですが、シェーダーの改造も必要で面倒だったのでやりませんでした。
表情
アセットに入ってます
SDユニティちゃんアセットには表情が6種入ってます。各表情をそれぞれ何%ずつブレンドするか調整することで色々な表情が作れます。
しかし、これだけだと欲しい表情が出せないことがあります。例えば、デフォルトの笑顔の表情が下図なんですが、、、
そうじゃないんだ!かわいいけど・・・!ブレンドをどういじっても欲しい笑顔が手に入らなかったので今回はBlenderを使って改造することにしました。
表情改造
まず以下の手順で元となるモデルを読み込みます。
- Blenderを開きファイル>インポート>FBXを選択
- Unityゲームフォルダ\Assets\SD Unity-Chan Haon Custom\Models\Parts\Face_01.fbxを開く
- シーンにデフォルトのCubeが置かれてるので消しておく
画像の①にある緑のアイコンを押すと、②の所に各表情が一覧表示されます。目元は笑顔のものでいいんですが口は閉じた状態で微笑んでてほしいんですよね。なので多分笑顔+無表情の口元+口角上げが出来ればそれっぽくなりそうな気がします。
検索するとそのものずばりの解説記事がヒット!ちょっとBlenderのバージョン古いですが、こちらの説明を参考に笑顔と無表情を合体させます。 bluebirdofoz.hatenablog.com
- まずシェイプキーで「sml」を選択します。
- 「編集モード」に切り替えます。
- 「透過表示を切り替え」でオブジェクトの裏側も選択できるようにします。
- 投げ縄ツールで以下のように選択します。
- 「頂点」>「シェイプキーからブレンド」を選択します。
- 左下に入力欄が出るのでこのように設定。
- ツールバーでこの状態にしておきます。
- 後は口角付近の頂点をつまんでいい感じに調整します。
- 工事完了です・・・
あとはFBXでエクスポートするだけなんですがここが結構鬼門です。
- シーンコレクションでFace_01を選択しておく。
- 「オブジェクト」>「適用」>「全トランスフォーム」を実行する。
- 「ファイル」>「エクスポート」>「FBX」を実行する。
- どうだったか忘れたけどエクスポート設定のトランスフォームで「前方」と「上」をいじらないとダメだった気もする・・・
- 出力したFBXをUnityでインポートします。インポート設定はFace_01の設定を参考にしてください。
- 「Assets/SD Unity-Chan Haon Custom/Prefabs/Face/Face_01 BlendShapes.prefab」のメッシュを今回インポートしたものに差し替えて完了。
カットシーン
今回せっかくUnityなのでOPムービー的なものを入れてみました。
カメラワーク
MainCameraにAnimationをアタッチしてカメラワークのキーを打ちました。
キャラ演技
キャラはアセットに入っていた待機モーションを垂れ流していますが、それ以外に空を見上げるモーションを再生してます。待機モーションと干渉しないようにクビの骨を追加してそっちにキー打ってます。視線もほんのちょっと上に動かしてます。
あとほとんど分かんないと思うけどよーーく見ると目の中の光も上下にうるうる動かしてます。
ちなみにこのうるうるはスクリプトで動かしてるんですが、Update()でやっても再生しているモーションで上書きされます。首と同様に干渉しない骨を追加してそちらで動かしてもいいんですが、公式ドキュメントのイベント関数の実行順序を見るとLateUpdate()でやればいいことがわかります。イベント順序周りはかなり複雑なんでどれを使うかは要注意ですね。
マルチエンディング
今回スコアによって3つのエンディングがあります。一応アドベンチャーゲームっぽさを出すためにおまけ要素として入れたんですが、面白さとは関係ないしコスト無駄に高くて虚無りかけましたね…。とは言えBlenderでモデル改変したりするスキルが身についたので良しとします。
がっかり
10点以下のエンディング。ほぼ放置プレイ時用。この子とは初対面なのにめっちゃ呆れられます。
よそよそしい
200点以下のエンディング。凡プレイだと大体これ。社交辞令の感謝を述べるだけで距離感凄い。
デレ
200点越えのエンディング。仲良くなれた気がする…!ほほも染めよるね。
Unityroomにも出してみる
せっかくUnityで作ったのでUnityroomというUnityゲーム専用投稿サイトに登録してみました。
プログラム対応
基本的にはニコ生ゲームのままで大丈夫ですが、一部プログラムで対処が必要です。
Akashic機能にアクセスしているところで事前に呼び出し可能かチェックするようにします。前回の記事で書いたjsプラグイン機能を使って以下のようにすると良いでしょう。
mergeInto(LibraryManager.library, { IsNiconico: function(){ return typeof(g) != 'undefined'; } });
[DllImport("__Internal")] private static extern bool IsNiconico();
ランキング対応
Unityroomは独自のランキング機能をサポートしてます。アツマールのランキングに似てますが、ランキングがリアルタイム更新されるので人が集まってるゲームだとプレイ中にランキングが移動して面白いです。
実装も10分くらいでできてめちゃくちゃ簡単なのでUnityroomに出された際にはぜひ対応した方がいいですね。
反省会
半年前の春イベで3Dゲームは動かない人いるから遊ばれたいならやるなと書いておきながらまたやっちゃいました。今回はUnityツールの宣伝がメインだったんでいいっちゃいいんですが、Unity製だと動かないということも大々的に知られてしまった気がするんで使う人いるか心配です。
ただやっぱり腐ってもUnity。開発速度がすごいですね。土曜から始めて水曜でマルチエンディングまで作れたのでゲームエンジンとしては十分優秀と言えるんじゃないでしょうか?まぁ正直触ってる時は重くてストレス溜まることが多いんですが、、、分からないことがあっても調べれば大抵20分くらいで解決するしなんだかんだでやっぱ使えると思います。もちろんニコ生ゲーム以外も簡単に作れますし(むしろニコ生ゲームがおまけだし)。
ゲーム性についてはもっと手書きで星座を描く感触を出してみたかったんですがそこまで実現するにはあと1週間必要でしたね…。ルール説明もまた手抜きになっちゃいました。理想を言えば『任天堂がこのゲームを作ったらこんな説明画面にしそう』というものを作れればいいんですが、、、そんな気力は無い😩
あとBGM!今回使わせていただいた曲ももちろんいい曲なんですが、作ってる間はもっとドラマチックなの想像してました。バンプの天体観測はもちろん君の知らない物語もいいですよね。ただもちろんそんなの使えるわけもなく笑。次に考えてたのはしゃーろ兄貴の星空.flvだったんですが残念ながらこちらもどこにも利用可能な配布は見つかりませんでした。残念。おかげでBGMさがしは今までで一番苦戦しました。
反省点も多いですが、今回もまた新しい事に色々挑戦出来たので面白かったです。ニコ生ゲームは制約きついんで次作る時は他でもいいなーとは思いました。
ニコニコ自作ゲームフェス2023 ニコ生ゲーム特別賞
こマ?完全にネタで言ってたのにほんとに受賞するとは・・・最後まであきらめてはいけない(教訓)
後から結果発表の生放送も確認してみたんですけどゴー☆ジャスさんがプレイするゲームが予め5本に絞られてたっぽいですね。つまり予め運営が何らかの基準で5本選んでたんでしょうけど、そこに残ってたこと自体が凄く意外でした。
というのも今回の冬イベからスマホでプレイできないゲームはエントリーできませんと書いてて完全にワイのせいやんけ😅と思ってたので当然このゲームも選考から外れるもんだと思ってましたが何故か残していただけました。
うーん…真意はわかんないけどとにかく感謝ですね。
ちなみに3月21日に本社?で表彰式が行われてたんですが自分は普通に仕事で行けませんでした。しょうがないね。
追記
4/2 賞品類も届きました。 新人賞のみかんゼリーは3ヵ月かかったので今回も忘れた頃に来るかと思ったんですが今回は速かったですね。と思ったけど授賞式に出席したらその場で商品手渡しだったらしいのでモノは用意してたのか。ただ特別賞は20日に放送枠の中で発表だったのでどっちにしろ後日送付になってたかも。じゃなかったら放送時点で決まってたことになるし…。
元々秋イベ落選の保険として参加(タグ付けるだけ)してたけどまさかこんなことになるとは。やってみるもんですね