tanaka's Programming Memo

プログラミングについてのメモ。

1週間ゲームジャム"あける"に参加しました!

2020年最後のUnity1週間ゲームジャム"あける" に参加して、「光花(KOHKA)」という音ゲーを作りました!

f:id:am1tanaka:20201228120004g:plain

光花-KOHKA- | フリーゲーム投稿サイト unityroom

参加しましたブログです。

目次

参加の狙い

今回は専門学校の1年生の皆さんが挑戦できるタイミングだったので、講義の一環としての参加でした。ということで、以下のような狙いを持って参加しました。

  • これまでに講義で扱った技術の応用例を示す
  • 一緒に参加することで開発意欲を促す
  • 今後の講義の題材にできそうなプロジェクトの作成
  • Slackの練習

作業の流れ

ざっくりと作業の流れです。

開催前

開催の2週前から1週間ゲームジャムを想定して、以下のようなことに留意して企画を考え始めてもらいました。

  • 「よけとる」をどのように改造できそうか
  • 使ってみたい技術、アセットの調査
  • 1~2日で作り切れそう
  • 面白くないと感じてもまずは完成させられそうなもの

自分も12/12にこんなツイートして企画案から考え始めてます。

このタイミングでは、色を変えるとか、大きさを変えるとか、角度を変えるとかしてマップの見方が変わるようなタイプのパズルが気になってましたが、よけとると全然違うので没にしました。それはまたいずれ。

1週間前の週は、企画を進めるとともに1週間ゲームジャム用のプロジェクトを作成して、WebGLに切り替え、解像度設定、BGMやSE再生、簡単なシーン切り替え、ネットランキング実装、仮ゲームの実装を行いました。自分もこのタイミングで以下のところまで開発を進めました。この時はBGMは d-elf.com さんのダンス系のを使ってました。

音との同期は既存のアセットなどは使わず、AudioSourceのtimeSamplesを参照して自前で計算しています。講義でやっていない内容は使えないルールなので、計算方法を以下のブログにまとめて紹介しました。

am1tanaka.hatenablog.com

BPMが分かっていてテンポが一定の曲であれば、掛け算や割り算で対応できます。

お題発表

お題は"あける"。ちょっと考えていた方向性と違ったのですが、タイミングが合った時に内側の円が開いて光る → 沢山タイミングを合わせると夜が明けていく → 最終的に花が開くとかでいいか? → 開く演出を花にして開花でいいのでは...というように演出の方向性を連想していって、素振りプロジェクトをそのまま本プロジェクトにして進めることにしました。

なんとなくダンス系より和風かな?ということで、それならH/MIX GALLERYさんに良いのがあるに違いないと選曲をやり直して、今回のタイトルと本編の曲を探し出しました。結果も英語ではなく感じに変更。あとは出現パターンの調整です。

1,2日目

12/21(月)から作業開始。この2日間は学生さんが講義中は作業できないだろうということで、同じ条件にするために16時半過ぎから作業を始めました。

  • タイトルを光花(KOHKA)に変更
  • タイミング判定や画面跳ね返りの不具合修正など
  • 出現場所をランダムから種に変更
  • 種のオブジェクト作成

3,4日目

水木は講義なので、講義中と帰宅後の作業です。

  • 選曲
  • 種の移動データと移動処理
  • 種の円速度設定パターン作成
  • ステージデータ作成
  • タイトルからランキングを呼び出す
  • WebGLビルドとunityroomへの投稿確認

この段階で、一旦ゲームとしては完成しました。まだ開花の演出が円のままだったりで、演出は現状のものとは違ってました。

5日目

この日は卒業制作だったので日中は作業無し。帰宅後も他のことをしていて、作業開始は22時から。以下のみ進めました。

  • 正解時の花の演出作成

6, 7日目

締め切り前の土日。土曜日は数学の問題解いたり別のことをしていて15時過ぎから作業開始。日曜日も作業開始は14時ごろから。

以上で、日曜の17時半ごろに公開予約ができました。このぐらいのタイミングで完了できたのはYOKETORU改か秒速忍者以来?良いペースで完成までたどり着けました。

恋しかった技術

1年生の講義で扱った技術縛りのために使えなかった技術のうち、特に使いたかったものを挙げます。

継承 / ポリモーフィズム

なんといってもこれ。これらが使えないとコピペや分岐が増えて増えて...。switchで分岐させて呼び分ければ解決はできるものの、ポリモーフィズムですっきりさせたい、という欲求が強かったです。

この辺は使いどころが想像できるぐらいの開発経験がないと、ただ習うだけでは必要性が実感できない技術だという印象を持っています。「なるほどこれは便利!」と思える体験を積んでから教えるカリキュラムにしているので正に狙い通りなのですが、早く講義で扱いたいです!

今回のプロジェクトは、シーンの管理やチュートリアルとゲーム中での振る舞いの違いなどをきれいに解決してみせるよい教材にできそうです。

ScriptableObject

こちらもある程度まで経験を積まないと、使いどころが分かりづらい機能ではないかと思ってます。別の場面で使い回したり、修正するような開発経験がないと、インスペクターで設定すればいいじゃん、となってしまいがち。ステージデータの作成や色の設定を別オブジェクトに適用するなど、今回は手作業でちまちまと書き写したりコピペで対応しました。ポリモーフィズムと組み合わせればPluggableな仕組みも作れる本当に強力な機能です。

TextAsset

これは講義で扱っておくべきでした^^; これとSplit()による文字列分割を知っていれば、データ設定ではScriptableObjectの代用が可能でした。Paizaの対策にもなるので、来年度には反映させたいと考えてます。

Slackの利用

Slackは今回が初運用で、良さげな方法を手探りしつつ使いました。

最初は「#1週間ゲームジャム」チャンネルを作ってそこにみんなで報告を書いてもらいましたが、1日だけでも前の発言が埋もれてしまい、発表時に学生の書き込みをまとめて見ることができずに不便なので変更することにしました。

個人チャンネルを作ってそこに報告するというのをUnityゲーム開発者ギルドさんでやっているというのを読んでいたので、それを真似てみたところ情報が追いやすくなりました。

報告の仕方もあれこれ試してみて、今のところ以下のスタイルに落ち着きつつあります。

  • 作業開始時に「作業開始」という言葉に続けて、箇条書きでTODOリストを書く
  • TODOリストの作業が1つ完了したら、その時点で箇条書きで完了した作業項目を書いて日時を残す。ついで、作業開始時のTODOリストを編集して、完了した項目に「(完了)」など書き加える
  • その日の作業が終わったら「今日の作業終了」と書き込む
  • 作業内容をまとめたい時は、続けて箇条書きで残りの作業を書き出す

TODOリストはまとめて置いてあった方がいいのでTrelloに持って行って、Slackにはリンクを貼っておく方がよさそうかな?と思いつつ、今回くらいの規模ならこれでいけました。

まとめ

準備期間をしっかりとって、企画も欲を出さずにまとめやすい方向にしたので余裕をもって公開できました。講義内容でできることを示した上で、オブジェクト指向のあれこれやScriptableObject、SOLID原則なんかを知ることで、よりスッキリしたコードが作れることを示すための土台にもできそうで、今回も良い収穫がありました。

Slackでの作業経過もなんとなく雰囲気が掴めたので今後に活かせそうです。特に今は新型コロナの動向次第では、いつリモートになるか分からない状況です。これまでは卒業制作などの演習科目をどうするかを検討していましたが、Slackによる進捗管理ができるようになれば、あとはGoogle MeetやZoomなどの会議サービスと連携させればなんとかなりそうな手応えを得られました。

狙いのうち「開発意欲を促す」についてはアテが外れましたが、今後の課題として明確化ができました。

企画の面では、他の皆さんの作品を見るとストーリー性というかキャラクター性が出ているものが魅力的に見えました。一発アイディアで世界観を出した上で面白いものを企画し、間に合うように完成させる瞬発力は本当に凄いです。回を重ねるごとにそのような作品が増えていて、収穫が多く、いい勉強、刺激になるゲームジャムです。自分はなかなかそういう感じのものは作れないのですが、継続していたらいずれ何か得られるかも知れません。継続できるペースを保ちつつ、また次回を楽しみにしています。

次回に向けて

企画的に気になっている方向性など現時点での考えを。

  • 大きさ、色、角度、奥行などを変化させることで、ステージの見え方が変わる何か
  • モチーフはプリミティブで
  • 音楽はやっぱ使った方がよさげ
  • 少しストーリー性というか、物語性が出せないか
  • VoxelorerBirdを進めたいので重くならないように

こんなところでしょうか。最初のやつを実現するためにモチーフの研究を軽くでいいので継続したいところですが、他にもあれこれやりたいことが積み上がっているのでできたらいいなというぐらいで。

次回は時期的には春休み辺りになりそうでしょうか。講義が始まっていても6月ぐらいまでは落ち着かないので、Slackでの報告や質問は実施しますが、学生さんは任意参加として今回のように講義で進めることはないと思います。それぞれの考えのもとに参加する人は次回も頑張っていきましょう!

学生さん作品

今回参加した学生さんたちの作品です。公開先が確認でき次第、追加予定です。掲載は名簿順です。

unityroom.com

unityroom.com

unityroom.com

ホペ君は自主的に継続してゲーム開発に取り組んでまして今回が4作品目でした。素晴らしいペース!

最後に

2020年もあとわずか。今年一年お世話になりました。また来年もよろしくお願いいたします。

参考・関連URL

曲のビートのタイミングを計算する方法

f:id:am1tanaka:20201216204931g:plain

曲に合わせた動きは、気持ちよい演出が作りやすいです。

Unityでこれをやるものとして、geekdrumsさんのMusicEngineが有名です。

github.com

1週間ゲームジャムで公開したYOKETORU改2019を開発する時に利用しました。開発を進めるうちに、MusicEngineが提供してくれるデータ以外のものも欲しくなったため、結構自前であれこれ計算してました。

Unityは曲の再生位置を秒数やデータの進み具合で知ることができるので、BPMさえ分かっていれば自前でビートのタイミングは算出できます。ならば全部自前で計算しようということで、直近で作成したJUMP OR DIEで全て自前で実装しました。

unityroom.com

その時の知見を元に、ビートの算出方法を紹介します。

目次

曲のテンポを把握する

BPM

曲のテンポを表す値としてBPMがあります。Beat Per Minutesの略で、1分あたりのビート=拍子数です(ちなみに小節はBar)。1分間あたりの拍数が分かれば、60をBPMで割ってやれば、1拍の秒数が分かります。

JUMP OR DIEのBGMで利用した曲は、 D-elf.com さんのものを使いました。D-elf.comさんのサイトは、曲のBPMが書かれているので有難いです。

D-elf.com

BPMを推定する

BPMが分からない曲は、以下のおとわびさんのBPMタップテンポはかるくんのページに行けば自分で推定できます。

otowabi.com

曲を流して、テンポに合わせてクリックを続けると、クリックのタイミングの平均から推定のBPMを表示してくれます。数字が安定したら、切りのよい数字をBPMとして採用します。

曲の経過を表すtimeSamples

Unityでは曲の開始からの経過を、AudioSourceのtimeならfloatの秒数で、timeSamplesならintで録音データの位置で読み取ることができます。マニュアルを確認すると、より厳密なタイミングを処理したい場合はtimeSamplesを使いましょうとあります。

Use this to read current playback time or to seek to a new playback time in samples, if you want more precise timing than what time variable allows.

録音データは1秒の間に設定回数の音の変化を記録したものです。この回数は、AudioClipのfrequencyプロパティで確認できます。44,100Hzという値がよく利用されますが、これはCDで使われている値で「1秒間に音データを44,100回記録したデータである」ということです。timeSamplesは、今、再生している音データが何回目のものかを表すものです。44,100Hzなら、timeSamples44,100になったら、曲が開始してから1秒経過したことになります。

120BPMでの計算例

以下の図は、BPMを120とした場合のあれこれの値の例です。

f:id:am1tanaka:20201215150649p:plain
120BPMの時の値

  • 120BPMとは、1分(=60秒)に120ビートあるということ
  • 1ビートあたりの秒数は 60 [秒] / BPM で計算できます。120BPMなら、60 / 120 で、1ビート0.5秒です
  • 音データのサンプル数が 44,100 [Hz] だった場合、0.5秒では 0.5 [秒] * 44,100 [Hz] = 22,050 です。これが1ビートで進むtimeSamplesの値です

つまりtimeSamples22,050の倍数を跨ぐ時に何かをやれば、リズムに合わせた動きを作ることができます。

注:割り切れる場合は「22,050ごと」という考え方でよいですが、割り切れないような場合は誤差を防ぐため、検出したいビート数から該当するサンプル値を算出した方がよいでしょう。

もう一つの値として、1回のFixedUpdateごとにサンプル値がどれぐらい進むかも求めておくと判定や動き出すタイミングを計算する役に立ちます。AudioClipの周波数が44,100 [Hz]で、Time.fixedDeltaTimeの値がデフォルト値の0.02なら、 44,100 [Hz] * 0.02 [秒] なので、1回の更新でtimeSamples882進むことになります。

22,050から前後882以内なら、ぴったり。さらに882離れるとちょっとズレている。もう882ずれているとまあまあズレている。というような判定ができます。

実装例

以下のGitHubリポジトリーで、実装例のUnityプロジェクトを公開しました。プロジェクトの設定方法はリポジトリーの方のドキュメントを参照ください。

github.com

曲は、先に紹介した d-elf.com さんから「Love This Beat / Free BGM ver.1」を利用しました。曲データはプロジェクトには含まれないので、ご自身で以下のページからダウンロードして、プロジェクトに設定してください。BPMは130で、サンプルプロジェクトに設定済みです。

www.d-elf.com

勿論、他の曲でも構いません。設定した曲のBPMを、BeatDetectorBeat欄に設定してください。

その他のTips

音源の調整

曲データには、冒頭に空白部分が含まれているものがあります。その場合、timeSamplesをそのまま使うとタイミングがずれてしまいます。曲がどこから始まるかを調べたり、空白部分をトリミングしたりする時にAudacityというツールが便利です。

https://www.audacityteam.org/

日本語でダウンロードする場合はこのあたりからどうぞ。

forest.watch.impress.co.jp

効果音の不要な空白をトリミングしたりするのにも重宝します。

有料アセット

今回はBPMを自前で計って実装しましたが、お金を出せば音源データから自動的にリズムを取り出してくれたり色々してくれるアセットもあります。

Rhythmator

assetstore.unity.com

つい先日買ったので持ってはいるんですがまだ使ってません。いずれ紹介記事など書くかも知れません。

まとめ

BPMを使ってビートを計算する時の考え方とサンプルを紹介しました。1分間に何ビートかと、1秒間のサンプル数が分かれば、単純な計算でタイミングは求めることができます。これを応用すれば、オブジェクトをあるビートのタイミングで判定ライン上を通過させるとか、曲に合わせてジャンプさせるとか、弾を撃ったりといった演出ができます。

お金で解決することも可能ですが、計算方法を知っていれば欲しいタイミングを全て算出できます。いざという時に何かの役に立つのではないかと思います。

参考・関連URL

デジゲー博2020出展してきました

今年もデジゲー博にVoxelorer Birdを出展してきました。3年連続3回目!またもやリリースはできませんでしたが、今回はWebGLの体験版が配布できたので一歩前進。

f:id:am1tanaka:20201129103305j:plain

時間が経ってしまいましたが今後に向けて、振り返りや展示ブースの作り方の記録です。

目次

出展の目的

応募時は「スマホ版をリリースしてその告知」を目指していたのですが、あれこれあって最終的な出展の目的は以下になりました。

  • デジゲー博にお金を落とす
  • Web体験版の配布
  • スマホ版の進捗報告
  • (Looking Glass自慢)

お陰様でこれらすべて達成できました!

今回は新型コロナの影響で、なるべく接触の展示になるよう考えました。WebGLの体験版を用意したのは、来場者のスマホで手軽に体験できるものがあれば展示物に触らなくても体験できる、という思い付きからでした。それで展示の体裁は整うと考えて、スマホ版のリリースよりも面白い展示を優先しようとLooking Glassを引っ張り出すことにしました。

展示の成果

Web体験版

Web体験版を遊べるQRコードを印刷した名刺サイズのカードを配布しました。展示から時間も経ちましたし、ここで公開します!

VoxdlorerBird2020 Web体験版

Web Trial VoxelorerBird

手元のiPhoneSE, iPad Air, Pixel3aでの動作は確認していますが、性能が低かったり、古いスマホだと動作しないかも知れません。UnityのWebGLビルドで作成したもので、公式にスマホで動かないことはないが保証はしない」という技術のものです。動作しない場合はPCからアクセスしてみてください。PCでは問題なく動作すると思います。

カードは例年の経験から50枚もあれば足りるだろうと考えて、カラー50枚、テスト印刷の白黒10枚、併せて60枚用意しました。結果、残り1時間を残して配布完了しました。これは嬉しい誤算でした。あと10枚あったら少し余ったように感じるので、次回は80~100枚程度用意しようと考えています。

プレイ回数

会場での試遊回数が約50回、配布した体験版のページのアクセスが23回、合計70回程度、試遊していただけました。体験版の紹介ページへのアクセスは33回でした。

体験版のアクセスはほぼデジゲー博当日のもので、後日のアクセスは数回でした。延べ人数ではありますが、カードを受け取っていただいた方の30~50%が実際にアクセスしてくださったことになります。

改めまして、プレイして下さった皆様、興味を持っていただいた皆様、ありがとうございました!

広告協賛

今回の一番の目的の「デジゲー博の継続のためにお金を落とす」のために、広告協賛も初参加しました。デジゲー博さんのWebページの下の方に「(カ)アミューズワン」の協賛ロゴを掲載いただいてます。

digigame-expo.org

↓こんなやつですが、正方形なんだからもう一段折り返しておけばよかった^^;

f:id:am1tanaka:20201208120423p:plain

今後の反省とします。

設営

展示の配置

入手したばかりのiPhone12ProのLiDERの試運転がてら、ブースのモデルを撮影して作ってみました。

skfb.ly

撮影は3d Scanner Appというフリーのアプリで行い、それをiCloudで共有してObj形式で取り出して、Blenderで不要部分をトリミングして、Sketchfabにアップしました。

3d Scanner App™

3d Scanner App™

  • Laan Labs
  • ユーティリティ
  • 無料
apps.apple.com

展示台の下にスペースがあります。そこにWin10ノートPCとmac book airを置いてました。そこまで撮っとけばよかった。

展示グッズ

展示も3回目となり、道具もかなり揃ってきました。

底上げ棚

展示のスマホを机の上に直置きすると位置が低くて体験しづらいので、去年までは100円ショップの収納ボックスの上に発泡ボードを置いて簡易の棚を作って高さを稼いでいました。安くて軽いのが大きなメリットでしたが、強度がなくて潰れて事故りそうだったのと、下のスペースが使えないのが勿体なく感じて、今回は「押し入れ整理棚」を導入しました。

幅70~120cmで高さと奥行きが40cm弱ぐらいの何か、ということでニトリやらハンズをうろうろして、TV棚や机上棚など見て回った結果、「押入れ整理棚」というジャンルに行きつきました。サイズ感ばっちりで、重量も軽く強度があり、2つセットでお値段もお手頃と文句のないナイスな棚でした。

今回の展示なら2つ並べて180cmフルに嵩上げすることもできたのですが、2つ持っていくのは重いし、テーブルクロスの幅が足りない。ということで、棚1つで通常時の90cm幅で設営することにしました。

あと、滑り止めのために直径2cm程度のゴムを足の部分に取り付けました。これは去年の展示で別の目的でハンズで購入したものでしたが、サイズがぴったりだったので使いまわしました。サイズはわかりませんがこんなやつ。

hands.net

強度は安心できるし、去年無駄になっていた下のスペースにPCを2台置けました。これで台上からPCをなくすことができて、Looking Glassを置いてもゆったりめに展示ができました。荷物は増えましたが効果はテキメンでした。次回は、棚の袋の下にキャスターをくっつけて、持ち運びを楽にしようと目論んでます。

ポスタースタンド

去年導入したものを今年も利用。初年度は段ボールで無理くり作ったのですが、嵩張って仕方なかったのでこれをゲットしました。今年も大活躍!とても便利です。

タブレットスタンド

今年導入しました。去年は厚紙で自作したのですが、iPadの重量に耐えかねて午前中で潰れてしまいました。がっちりしていて安心して展示できました。

置いたままプレイできるのでスムーズに体験していただけました。スマホは自作の厚紙のまま展示しましたが、ヘタってきてたので次回に備えてスマホのも入手しておこうと考えてます。

カードの印刷

最近はコンビニのネットプリントを活用してます。

https://networkprint.ne.jp/Lite/start?lang=jajp

展示場の秋葉原UDXの5階にファミマがあるので、最悪、当日に印刷して間に合わせることができます。

配布カードはA4光沢紙にプリントして切り分けました。名刺サイズだと1ページ120円で10枚作れるので50枚で600円でした。速めに入稿すれば印刷サービスの方が安いかも知れませんが、枚数の自由度の高さと、何より当日でもなんとかなる気軽さが強いです。

光沢紙ではありませんがA3カラーも印刷できるので、大きめの案内パネルも印刷しました。こちらは1枚100円でした。

ポスター

2018年に作ったやつをそのまま利用^^; こちらはラクスルさんで印刷しました。

raksul.com

3年目ですがまだ使えてます。それがいいのか悪いのか・・・。次回は新しいやつを制作し直したいところです!

その他

他にも細々と。

  • 充電・電源ケーブル
  • アルコール消毒液、タオル
  • 名刺ヨコ置きホルダー
  • A3スチレンボード
  • テーブル布(90x90cm ハンズで買った黒い起毛のやつ)
  • スコッチテープ、ガムテープ、ポスターのり
  • ドライバー、はさみ
  • マスク
  • WiFiルーター

会場のWiFi

デジゲー博のものではないので展示には使えませんが、会場の秋葉原UDXではフリーのWiFiが使えます。メアドの登録が必要ですが、1度登録してログインすれば30分間インターネットに接続できます。

設営日、届いたばかりのiPhone12Proに携帯を変えたのですが、回線の設定ミスでネットに接続できないことに電車に乗ってから気づきました。ネット印刷のIDをGmailに下書きで記録していたので、ネットに接続しないと確認できません。印刷は展示当日の朝か~と覚悟したのですが、会場でこのWiFiの存在を知って事なきを得ました。都心、便利。

まとめ

新型コロナの影響で出展ブースも来場者数も例年よりも少なかったと思います。その分、ゆったり体験していただけたような気がします。個人的には人が多いのが苦手なので、今年ぐらいのんびりした雰囲気はとてもよかったです。が、運営サイドはそうも言ってられませんねf^^; 新型コロナが早く終息して、これまで以上の活気が戻ることを祈ります。

足を運んで下さったり、試遊していただいた皆様、本当にありがとうございました!また、この記事をお読みくださったみなさまも試遊していただければ幸いです!

VoxdlorerBird2020 Web体験版

Web Trial VoxelorerBird

おまけ

今回の展示で圧倒的な破壊力を発揮した立体ディスプレイのLooking Glass。

lookingglassfactory.com

「なんか立体の面白いディスプレイがある」という口コミで足を運んで下さった方が多数いらっしゃいました。そもそもLooking Glassを購入した最大の決め手がデジゲー博の展示で使えそうということだったので、ついに実現!という感じでした。そんな展示のすぐあとにLooking Glassの新型が来春に出ることが発表されました!!なんたる奇遇。2021年1月15日 (金曜) の10:30 UTC +09:00 までクラウドファンディングが行われています。

www.kickstarter.com

詳しくはMoguraVRさんのこちらの記事をどうぞ。記事中の$199のやつはもう終わっていますが、1/15までは$150引きで、3万円ちょっとで本体+キャリングケース付きがゲットできます。

私はすでに発注済みなので、次回は初代が故障していなければ立体2台体制の展示になるかもです。

参考・関連URL

Unity2020.1のWebGLでwasm streaming compile failedが出る

WebGLでビルドしたプロジェクトを実行しようとしたところ以下のようなエラーが出ました。

wasm streaming compile failed: TypeError: Failed to execute 'compile' on 'WebAssembly': Incorrect response MIME type. Expected 'application/wasm'

解決策

Unity側の不具合っぽいのですが、現状ではPlayer SettingsName Files As Hashesオプションを解除することで直せました。

  • EditメニューからProject Settingsを開いて、Playerを選びます
  • WebGL settingsタブのPublishing Settings欄にあるName Files As Hashesのチェックを外します

f:id:am1tanaka:20201107112414p:plain
Name Files As Hashesを外す

Unity Issue Trackerによると、Unity2020.2で解決予定だそうです。

Unity Issue Tracker - [WebGL] "wasm streaming compile failed" browser errors occur when launching any Unity project on a server

原因

Unity Issue Trackerによると、WebAssemblyモジュールにContent-Type: application/wasm .wasmJavaScriptフレームワークコードにContent-Type: application/javascript .jsをサーバーが返さないことが原因とのことです。

試しに.htaccessファイルで.wasmの設定を追加したらこのエラーは治ったのですが、他のエラーが出たので一先ず解決策の方法で対応しました。

無料で使えるネットライブラリMirrorのざっくり紹介

f:id:am1tanaka:20200717212431p:plain

この記事は Unity アセット真夏のアドベントカレンダー 2020 Summer! – Unity公式 Asset Portal の15日目の記事です。

Unityには様々なネットワークサービスやライブラリアセットがありますが、今回はMirrorというネットワークライブラリアセットについてざっくりご紹介します!

assetstore.unity.com

※動画6のショットのRigidbodyを無効にする手順を7に移動しました(2020/9/2)

目次

ネットワークライブラリやサービスの種類

ネットワークライブラリやサービスは、ざっくりとサーバー主体のものとクライアント主体のものがあります。

サーバーを主体としたサービス

プレイヤー管理や課金の仕組み、プレイデータの保存、マーケティングのための分析ツールなどのサーバー機能を提供してくれるものです。代表的なものに、PlayFabやニフクラmobileなどがあります。

azure.microsoft.com

mbaas.nifcloud.com

スマホ向けであれば、以下のようなクロスプラットフォーム用のアセットもユーザー管理や課金アイテム、広告などのサービスを提供してくれます。

assetstore.unity.com

assetstore.unity.com

クライアント側の実装を助けてくれるライブラリ

プレイヤーのマッチングやプレイヤー間のデータの同期や遅延対策、ボイスチャットなどといったクライアント側でオンラインゲームを実装するのを助けるものです。代表的なものに、Photon Unity Networking(PUN)やPhoton Bolt、UNetなどがあり、今回のMirrorはこっち側のライブラリです。

www.photonengine.com

www.photonengine.com

mirror-networking.com

Photonはサーバー側もクライアント側も幅広くサービスやライブラリが揃っています。PlayFabは強力なサーバー側のサービスが揃っていて、クライアント側にはPhotonを使うといった組み合わせるができるようです。ニフクラはサービスを提供しているだけではなく、技術的な相談もしやすそうな印象です。

AWSGoogle Cloudももちろんあるのですが、サーバーのホストからリアルタイム利用まで多岐に渡りすぎてまとめきれないので、ここではありますぐらいに留めます。

Mirrorとは

今回紹介するMirrorは、vis2kさんが無料で公開しているオープンソースのネットワークライブラリアセットです。以下、アセットストアの冒頭より。

Mirrorは、低レベルのネットワークライブラリTelepathy上に構築したUnity用の高レベルのネットワークAPIです。 Mirrorは、uMMORPGやCubicaといったMMO規模のネットワーキングのために開発、そしてテストされました。

PUNやPhoton Bolt、UNetと同様、Unityでプレイヤー間の同期をするなど、オンラインゲームのPC間の同期などを助けてくれるライブラリです。以下のような機能を持っています。

  • サーバーとクライアントを1つのプロジェクトで実装するための属性やパラメーター
  • サーバーやクライアントを接続するためのNetworkManagerとNetworkManagerHUD
  • ゲームオブジェクトの位置やアニメを同期させるNetworkTransform, NetworkAnimatorなど
  • パラメーターを自動同期するSyncVarやSyncEventなど
  • ネットワーク間で情報をやりとりするCommandやClientRPC、各種コールバック
  • Room機能
  • などなど

Mirrorの得意分野と不得意分野

個人的に感じている得意不得意は以下の通りです。

  • 得意分野
    • インターネットがない環境でも動かせる
    • 機能がコンパクトで設計が素直なので分かりやすい
    • ゲームサーバーを自前で運用できる
    • 数千接続といった比較的大規模なものが構築可能
    • 更新が続いている
  • 苦手分野
    • サービスをインターネットに手軽に公開できない
    • 行動予測などがない
    • サポートが弱い(他は大手企業ばかりなので)

Mirrorを選択する決め手

MirrorとPhotonの最大の違いは、良くも悪くもプレイヤーのマッチングに公式サーバーが必要ないということだと思います。PhotonはLAN内で接続する場合でも公式サーバーを通す必要があります。そのためインターネットがない環境では接続ができません。ガチガチのセキュリティーが施されている学校内やイベントでの展示などでこれがネックになる恐れがあります。MirrorはNet DiscoveryといったLAN内でのサーバー検索機能を使ったり、サーバーのIPが固定であればIP直打ちで接続できるのでインターネットに接続されていないLAN環境でも動かすことができます。

自分の場合は、学園祭や学校の体験入学用のプロジェクトにMirrorを採用しました。

インターネットごしに接続する予定があるのであれば、素直にPhoton系のサービスでよいと思います。20名程度の試用レベルの接続であれば無料でマッチングやリレーサービスを利用することができます。非リアルタイムであったりWebGLで使うならPUN2、リアルタイムオンラインゲームに特化するならPhoton Boltがオススメです。

学習コストと教材

学習コスト

学習コストは前提知識次第ですが、UNetを使ったことがあればほぼそのままなので1~2日で使えるのではないかと思います。

オンラインゲーム作りのノウハウがない場合は、オンラインゲームを動かすための考え方を理解するところからなので1ヶ月以上はかかりそうです。

オンラインゲーム作りのノウハウがあれば、Mirrorの使い方自体は1週間もあれば使えるようになると思います。

教材

Mirrorの教材です。以下の順で目を通していくとよいのではないかと思います。

ネットワークは、どこで何が動いているのかの仕組みを理解していないと、マニュアルを読んだり説明を聞くだけでは理解するのが難しいです。まずはサンプルや動画を見て動きを確認したり、実際に自分でも実装をしてみながら、マニュアルを併せて読んでいくのがよいと思います。

インターネットにサービスを公開するには(調査中)

Mirrorから使えるList Serverというマッチングのサービスが月額20ドルで用意されています。

mirror-networking.com

List Serverがマッチングのためのアクセスポイントになり、自宅サーバーやホスティングサービスなどに立てているゲームサーバーのIPアドレスとポートを接続相手に伝えてプレイヤー同士を接続するようです。ゲームサーバーで指定する指定のポートは、インターネットからアクセスできるように設定しておく必要があります。NATパンチスルーがないので、一般のプレイヤーが気軽にホストになる、というような運用は難しそうです。

List Serverを利用するには、Mirrorのアセットに含まれているサンプルコードを自分のプロジェクトに組み込むよう紹介されていて、PhotonやUNetのように最初から接続できる機能が含まれている訳ではないようです。

サーバー環境を丸ごと自前で構築できるので、サーバーコストはList Serverと原価のみ、ということが可能ではあります。また、ディアブロのようにサーバー側の知識を持ったプレイヤーが野良サーバーを立ち上げて自由に遊んでもらうようなこともできます。手軽ではありませんが、オープンソースなので自由度は圧倒的です。

英語ですが、以下の公式ドキュメントにサーバーの立て方やAWSでのセットアップ例が紹介されています。

mirror-networking.com

以下、UNet版のゲームサーバーの立ち上げチュートリアルです。Mirrorも対応しているようです。これも英語ですが...

noobtuts.com

Mirrorの組み込み手順の紹介動画

簡単なサンプルプロジェクトを作って、Mirrorでオンライン化するチュートリアル動画など用意してみました。

サンプルプロジェクトは以下からダウンロードできます。

github.com

1. サンプルプロジェクトの準備

youtu.be

2. Mirrorのインポート

youtu.be

3. プレイヤーをオンラインに登場

NetworkIdentityやNetworkManagerなどのMirrorの根幹コンポーネントの紹介。

youtu.be

4. プレイヤーの操作をネットに対応

NetworkBehaviourでローカルプレイヤーを制御する。

youtu.be

5. プレイヤーごとに色を変えるカスタマイズ

カスタムプレイヤーオブジェクトの実装例。

youtu.be

6. ショットをネットに対応

変数を同期するSyncVarやサーバーのメソッドを呼び出すCommandの利用。

youtu.be

※Rigidbodyを無効にする処理を7に移動しました(2020/9/2)

7. ショットの色を設定

youtu.be

※Rigidbodyを無効にする処理を最後に追加しました(2020/9/2)

8. 持ち弾数の管理

youtu.be

9. ミスの処理の概要

サーバーとクライアントの役割のざっくり説明。

youtu.be

10. プレイヤーと弾を消す

youtu.be

11. 爆発をネットに対応

youtu.be

12. ネットからの離脱、GUIの表示調整

カスタムのNetworkManagerによる接続管理。

youtu.be

まとめ

Mirrorは、プレイヤー間の接続ゲームの状態やキャラクター、オブジェクトの同期といったオンラインゲームを開発する上で役立つ機能を提供してくれる軽量なオープンソースのネットワークライブラリです。

インターネット接続のないLAN環境で動かすことができ、自分でサーバーを立ち上げる知識があれば自由にゲームサーバーを構築できます。がっちりと企業がついてサポートしてくれる他のネットワークサービスとは趣が異なり、良くも悪くも自前であらゆることができる自由度の高いライブラリです。

展示であったり、利用用途が限定されている場合は自由度の高さが魅力です。また、本格的にがっちりとサーバーまで勉強したり開発したりする時に、オープンソースなのでよい題材になるのではないかと思います。

おまけ

Mirrorの作者さんがDOTSNETという新しいネットワークライブラリアセットを公開しました。$100の有料アセットなのですが、MirrorをECSで高機能化したものだそうです。Unity2020.1以降向けで、ECSの基礎知識が必要な上に$100という攻め込みまくったアセットですが、うまく動作すれば処理速度や消費電力など恩恵は大きそうです。

使ったことがないのでオススメできるだけの知識はありませんが情報共有までに。

参考URL

Mirrorでプレイヤーごとに違うプレハブからプレイヤーオブジェクトを生成する

f:id:am1tanaka:20200717212431p:plain

Mirrorは、UNetをベースに設計された高評価のMMOスケールにも対応できるというネットワークAPIライブラリです。

mirror-networking.com

MirrorでデフォルトのNetworkManagerを使った場合、Player Prefab欄に設定されているプレハブをプレイヤー用のゲームオブジェクトとして自動的に生成します。NetworkManagerを継承すればプレイヤーごとに違うプレハブからプレイヤーを生成することができるという自分用のメモです。

目次

実行環境

  • Unity2019.3.15f1
  • Mirror16.1.1

ざっくり手順

プレイヤー用のプレハブを用意する

違いが分かるようにメッシュを変えたり動きを変えたプレイヤー用のプレハブをいくつか用意します。NetworkIdentityをアタッチしてあれば一先ず動きます。

プレイヤー定義用のScriptableObjectを作成

クライアントからどのプレハブを使うかを送る手段が考えつかなかったので、ちょっと乱暴ですがScriptableObjectにプレイヤー用のプレハブを配列で持たせて、そのインデックスで生成するオブジェクトを指定するようにします(ResourcesやAssetBundleを使えば、プレハブ名などでいけると思います)。

PlayerPrefabList.cs

配列の参照用のenumと、GameObjectの配列を持つScriptableObjectを宣言しています。PlayerTypeの内容は、用意するプレイヤー用プレハブに応じて書き換えてください。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public enum PlayerType
{
    Green,
    Red
}

[CreateAssetMenu(menuName ="AM1Mirror/PlayerPrefabList")]
public class PlayerPrefabList : ScriptableObject
{
    public GameObject [] playerPrefs = null;
}

これができたら、ProjectウィンドウのCreateから AM1Mirror > PlayerPrefabList を選んでスクリプタブルオブジェクトを作成します。作成したら、enumの定義順に応じてプレイヤー用のプレハブをInspectorウィンドウから設定しておきます。

NetworkManagerのサブクラスを作成する

公式ガイドの以下をもとに、今回の要であるカスタムのNetworkManagerを作ります。

mirror-networking.com

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Mirror;

public class NetworkManagerCP : NetworkManager
{
    PlayerType spawnPlayerType;

    [Tooltip("プレイヤープレハブリスト"), SerializeField]
    PlayerPrefabList playerPrefabList = null;

    public void SetPlayerType(PlayerType pt)
    {
        spawnPlayerType = pt;
    }

    /// <summary>
    /// サーバー開始時、プレイヤーキャラクターのメッセージを登録
    /// </summary>
    public override void OnStartServer()
    {
        base.OnStartServer();

        NetworkServer.RegisterHandler<CreateCharacterMessage>(OnCreateCharacter);
    }

    /// <summary>
    /// クライアント側で接続した時に、選択してあるプレイヤーのプレハブをメッセージで送信
    /// </summary>
    /// <param name="conn"></param>
    public override void OnClientConnect(NetworkConnection conn)
    {
        base.OnClientConnect(conn);

        CreateCharacterMessage ccm = new CreateCharacterMessage
        {
            playerType = spawnPlayerType
        };

        conn.Send(ccm);
    }

    /// <summary>
    /// メッセージがクライアントからサーバーに到着したら、届いたプレハブでプレイヤー生成
    /// </summary>
    /// <param name="conn"></param>
    /// <param name="messages"></param>
    void OnCreateCharacter(NetworkConnection conn, CreateCharacterMessage messages)
    {
        Transform tr = GetStartPosition();
        GameObject go = Instantiate(playerPrefabList.playerPrefs[(int)messages.playerType], tr.position, tr.rotation);
        NetworkServer.AddPlayerForConnection(conn, go);
    }

}

通信を開始する

通信を開始するためのクラスSimpleNetManを作成します。これはNetworkManagerCPに統合できるのですが、設定がNetworkManagerの設定に埋もれるのが見辛いので分けました。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Mirror;

public class CreateCharacterMessage : MessageBase
{
    public PlayerType playerType;
}

public class SimpleNetMan : MonoBehaviour
{
    public enum ConnectType
    {
        Host,
        Client
    }

    [Tooltip("プレイヤーの種類"), SerializeField]
    PlayerType playerType = PlayerType.Green;
    [Tooltip("接続の種類"), SerializeField]
    ConnectType connectType = ConnectType.Host;
    [Tooltip("ホストのIPアドレス"), SerializeField]
    string ipAddress = "localhost";

    NetworkManagerCP networkManager = null;

    private void Awake()
    {
        networkManager = GetComponent<NetworkManagerCP>();
        networkManager.SetPlayerType(playerType);
        StartCoroutine(Online());
    }

    IEnumerator Online()
    {
        yield return null;  // 1フレーム待つ

        if (connectType == ConnectType.Host)
        {
            networkManager.StartHost();
        }
        else
        {
            networkManager.networkAddress = ipAddress;
            networkManager.StartClient();
        }
    }
}

IEnumeratorでやってるのは、なんとなく1フレーム待った方が初期化が終ってそうでいいかな、という雰囲気でやったことです。Awake()内でやってもいいかも。

仕上げ

作成したSimpleNetManNetworkManagerCPNetworkManagerのような名前のゲームオブジェクトに一緒にアタッチします。

SimpleNetManでは、生成したいプレイヤータイプを設定して、ホストかクライアントか選択して、ホストのIPアドレスを設定します。これらのパラメーターを設定するメニューを用意すれば実行時に変更できます。

NetworkManagerCPでは、下の方にあるPlayer Prefab List欄に、最初に作成したScriptableObjectをアタッチして、ScriptableObjectに設定したプレイヤー用のプレハブをRegistered Spawnable Prefabs欄に全て追加します。これをしないとプレイヤーオブジェクトがネットワーク上に生成できず、エラーになります。

まとめ

ざっくりですがこんな感じでできました。ScriptableObjectを利用するなど何らかの方法で、クライアントからホストへ生成したいプレイヤープレハブをNetworkMessageで送信して、それをもとにStartHost()やStartClient()が実行されたら、NetworkMessageを受け取って記録して、クライアントの生成の段階で指定されたプレハブからプレイヤーオブジェクトをInstantiateして、ネットワークに追加します。

この記事では、スクリプトが分かれていたり、Inspectorウィンドウでプレイヤーや接続の種類を設定する不自然な状態になっていますが、これは体験入学用に全員がUnityエディターで作業をすることを前提としているからです。通常の利用であれば、プレイヤーを選択するルームなどを作って、そこで選ばれたものをメッセージで送ってゲーム開始、という感じになると思います。

とりあえず、こんな流れで、こんな感じのコードで異なるプレハブからプレイヤーを生成できるという記事でした。

おまけ:Mirrorのイベントの発生順

重要なドキュメントですが、なんか下の方にあった...

mirror-networking.com

これを見ると、まずはStart()が呼ばれて、OnStartServer()OnStartHost()より後に呼ばれています。ということで、NetworkManagerのStartHost()やStartClient()で通信を開始します。

参考URL

Laravelのバージョン指定でプロジェクトを作る

Laravelの最新版を入れると7が入るけど、今は6.xを使いたいというとき、とりあえず以下でバージョン指定できる。

composer create-project --prefer-dist laravel/laravel blog "6.*"

参考URL