過去の独りごち/独りごとは こちら
過去のJavaアプレットは こちら

現在、BBSに書き込みキーを設けています。「書き込みキー」欄に、「MYOMOTO」をカギカッコは抜いて、 半角、小文字で 打ち込んで書き込みをしてください。 このキーワードは時々変更されますが、その都度こちらにて報告します。


10/21 the meaningless HDRP shooter制作過程

10月13日に作り始めて20日公開と、たった1週間で作ったゲームの制作過程をまとめても…… という気がしなくもないのですが、後々見返すと初学者として陥った罠とか そういう物が意外と役立つかもしれないので一応ここにまとめておきます。 オンラインRPGなんかは長いこと続けても楽しい思い出というのは往々にして初めの2週間くらいに98%くらい詰まっていて 残りの期間はその幻影を追いつづけて味のしないガムを噛み続けるようなゲーム体験が待っているものです。 大したものを出せたわけでもないのに文章ばっかり書いていると口ばかりで手を動かさないダメな奴と思われそうですが、 実際その通りなので仕方ありません…… でもVFX Graphについては多少は実のある内容になったらいいなぁ。

作曲について

多分みんな興味がないだろう作曲についていきなり書いてしまいます。どうぞマウスホイールコロコロして下さい。 このゲームのBGMを聞いた方は「フリー素材を貼り付けるにしてももうちょっと合ってるものが無かったのか?」 とうんざりするでしょう。何を隠そう僕が2~3時間で作った初めての曲なので仕方ないんです。

リリース日を決めないといつまでも公開を先延ばしにして企画倒れになるのである程度目鼻がついたら公開することが良いと決めて 公開用のページに「10月20日リリース」というような事を書き込みながら18日頃に編集しまくったのに 19日になってようやく作曲ツールを探し始めたという体たらくでして。 SEが鳴り始めたのが実に18日の事で、17日までは完全無音でした。 18日の仕事が終わって帰宅してから、ザ・マッチメイカァズさんのフリー効果音素材をありがたくペタペタと張り込みまして、 その後BGMもフリー素材にしようと酒を飲みながら探し回ってたんですね。 しかし、効果音と違ってBGMは聴き終わるまで時間がかかるし、3分間のタイムアタックという設定のゲームなので3分を超える曲は使いづらい。 また、なんらかの歌詞がパッと入りそうなストレートな曲よりもっと抽象的といいますかとっつきづらい曲が面白いと思ったので「これなら丁度いい」 と思える曲をじっくり探すより、もう自作した方が後々の勉強にもなるから良いんじゃないかと。 酔っ払った脳でそう判断したにも関わらず自作してしまいました。

シューティングゲームですからHM/HRとかテクノやトランスが良さそうな気がするんですが BPMが早いと音符が多くなって作るのが大変そうという極めて積極的な理由により、 Lo-Fi hiphop的な曲を目指すことにしました。 ゲーム内容自体はレトロだしゆるいゲームを目指しているのでchillなvibesも(意味はわかりませんが)悪くないよね! 大抵2コードのループだからきっと行ける、楽勝楽勝……その時はそんな風に考えていたんです。 出来上がったものは、お聞きの通り15個、コード3つ分しか音符がございません。どうしてこうなった。 営業の表現……得られたサポート……

とりあえずフリーな作曲ソフトとして公開されている、DTMに興味のなかった僕でも知ってる あのCakewalkを使わせていただこうと喜んでインストールしたんですが、 いかんせん作曲ソフトを使うのなんて25年ぶりくらいで、あまりにも本格的すぎて何がなんだか分からない。 サンプル曲の音が鳴らせるようになるところまではなんとか漕ぎ着けたものの 「こんなペースでは曲ができるまで何年かかるかわからんぞ」となってしまって30分ほどで途方に暮れてしまいました。 しかしCakewalkを提供しているBandlab社は凄かった。Webアプリで作曲してシェアできる環境まで公開してくれているんですね。


このWebアプリ版は凄く簡単で設定もいらず、豊富に用意されている既成のドラムパターンなどを貼り付けるだけで何やら凄い音楽が作れるようです。 「作れるようです」というのはこの時点でもう19日の夜中になってしまったのでこの期に及んではとにかく 「何かBGMが鳴っている」というところまで出来れば良いのだと割り切ってバタバタと作業をしたので 凄い音楽なんて作りようがなかったんですね。まるで時間さえあれば僕が凄い音楽を作れるかのような言い草になってしまいましたが、 優れたツールを貶すわけにもいかないので。

で、当初はタイトル用に一曲作ってそれをゲーム内でもリアルタイムでピッチを変えたりしてヘロヘロ状態にして使いまわしていたんですが、 コード3つ貼っただけの25秒のループを延々聞かされるのでテストプレイが猛烈に眠い。2ループくらいすると目眩がする。 そんなわけで大して代り映えしないんですがゲーム内の曲はコード3つなのは変わらないものの コルトレーンチェンジ(多分)にして何時までもノタノタと続く感を出してみました。 それと既成のリズムやループを少し貼り付けてちょっとだけ変化を付ける練習もしてみました。 せめてもうちょっとLoFiっぽいクタクタの音にしたかったんですがエフェクタの使い方がよく分からなかったという。

それにしても作曲って本当に努力と才能の要る仕事ですね。^C師やひらにょんさんやADDICTさんみたいにプログラミングも 作曲も出来るという事がいかに凄いのか改めてよく分かりました。 懲りずにいつかまた頑張るつもりなので生暖かいご声援をお願いします。

モデリングとかゲームシステムとかについて

最初はあんまりゲームを完成するところまでこぎつける気がしなかったんですが、 13日に自機のモデリングをやったらカーソルキーで動かしたくなって、動いたら今度はローリングさせたくなって…… 気づいたら弾を打ってて、翌朝には雑魚キャラを撃ち落とせるようになってました。 当たり判定とかは全部Unity任せでいいのがラクですね。 イベントハンドラ埋めるだけでゲームっぽい物が出来ちゃう。 モデリングはProBuilderというUnity公式で直々に配布して下さっているUnityのエディタ内で使えるモデラを使っています。 慣れている方はMetasquoiaなどを使うのでしょうし僕も以前はMetaseqoiaを使わせていただいたことがありますが もうすっかり使用法を忘れてしまったのでProBuilderでプリミティブを1~5個置くだけでモデリングしたと主張することとしました。 どのくらい簡素化しても戦闘機や敵機に見えるのかというミニマリズムの極限に挑むのです。 ゼビウスのグラフィックスのデザインがいかに優れているかという事は度々思い出します。

ProBuilderで思い出したのですが、ProBuilderで作ったモデルなどがテスト実行後に消失してしまい、 ポルナレフ状態になった事が何度かありまして「Unityってこんなに不安定なの?恐ろしくて使えないワン」 と思い込んでUE4に逃げようかと真剣に考えてしまって実際UE4もインストールして調べたりもしたんですが、 「テスト実行が走っている間にいじった内容はテスト実行前に巻き戻る」というUnityの仕様のためだったようで。 公式のUnityインターハイブログにもそのような記述があるのですがこういう極めて大事なことは玉転がしチュートリアルにでも 256回くらい繰り返しデカデカと書いてもらえるとみんな助かるんじゃないかなあと思いましたから、 僕もここに書き留めておきます。 とりあえずテスト実行中です!!!いまいじっても巻き戻りますよ!!! という事がパッと分かるようにテスト実行中のウィンドウ色をどぎついマゼンタ色にしました。

ゲームシステムはGameRulerというクラスを定義してそれに全部任せています。 もうちょっと言うとタイトル画面・操作方法画面・ゲーム画面の切り替えとか、 他の物体とぶつかったときのダメージを各物体が知るためのテーブル的な役割をさせてます。

ゲーム画面に移るとEnemyCommanderとPlayerオブジェクトをプレハブからInstantiateします。 EnemyCommanderは敵の群れ(Waveと呼びます)についての情報を配列プロパティとして持っていて、 1つのWaveに含まれるオブジェクトが全て破壊されると次のWaveを生成します。 わりとこの辺りの仕組みはUnity公式の2Dシューティングチュートリアルの仕組みそのままですね。 ……と、これを書きながらUnity公式のチュートリアル群を見直しに行ったんですが、 2Dシューティングのサンプルが戦闘機の出てくる無機質なヤツではなくて2D GameKitによるコーディング不要なモノに刷新されていました。 前のチュートリアルは一見地味だしコードを多少書かされるんですが、 最序盤からいきなりコルーチンによるマルチスレッドプログラミングについて言及があったりと栄養価の高い内容だったのに残念です。 「C#については多少知っているけどUnityは全く初めてです」というニッチな需要もあると思うので コードガリガリ版のUnityチュートリアルももっと出てくれると嬉しいなあと勝手なことを思いました。

もうここに書いただけでゲームシステム的にはほぼ全て網羅しちゃいましたが、 学習用などに需要があればそのうちソースも公開したいです。 今現在の時点ではとても人様に見せられる状態ではないので……

そういえば、衝突判定についてはちょっとクセというか気の利いた仕様なんだろうけどパッと見良くわからない所がありますね。 ColliderクラスのIsTriggerプロパティについてなんですが、プロパティ名を見ただけだと僕は「チェックしないと衝突イベントが起こらないの?」 と思ってしまいました。実際にはこのプロパティの役割は、Unityで提供される物理エンジンによる衝突応答は起こさなくなるが 衝突を検出したときにはOnTrigger○○イベントを起こす、という意味合いのプロパティなんですね。 例えば毒ガスゾーンなどのように「領域に入ったことは検出したいけどぶつかって弾かれるのは困る」ようなモノを作るときに使うと良いようです。 チェックしないデフォルトの状態ならば普通に剛体同士の衝突応答を起こしてOnCollision○○イベントが起こります。 Unityの衝突検出・衝突応答については良くまとまっている記事が多く有りますから、初学者の僕が今更したり顔で書くことでもないですね。

また、ゲームの処理を全部Updateイベントでやってしまっているのはあんまり良くなかったなぁと思いました。 現在のフレームの描画そのものと密接に係る内容以外はFixedUpdateを使うべきです。ただし、入力の処理にはちょっと気をつける必要があるようです。 この辺りの話はUnityの中の人である安原先生がまとめていらっしゃいます。 安原先生の動画はここ2週間ほどの間、2日に1本くらいのペースで拝見しましたが、どれも物凄く役立つ汎用性の高い内容で Unityを使うことを特に考えて無かろうと見ないと損をするレベルです。 誘導ミサイル完全マスターなどはまさに鳥肌モノの内容でした。 そこまでバラしてくれて良いんですか!?という他無い。 最初にいきなりビューフラスタムという一見関係なさそうな内容から入るも、講義の終盤においてありとあらゆる最適化手法を雪崩のように突っ込んでいく過程で これが再登場して伏線を回収する様は圧巻です。

VFX GraphによるGPUパーティクル

はい、ここからが本編です。 ゲーム作り自体がオマケであり僕が一番やりたい言いたい語りたいのはコレなんですよコレ。

やっぱりパーティクルでやりたいことといえば爆発ですよね爆発。爆発は芸術だ! そう思ってだいぶ前から標準レンダラーやLWRPでもVFX Graphは動くようになっているらしいとの 情報を得ているにも関わらず、HDRPテンプレートでプロジェクトを作ってアセットにCreate->VisualEffect->VFX Graphで 一丁VFX Graphをシーンに置いて……あれ、黒い?


これはどうもDirectionalLightが10000Luxとか高い値に設定してあるのをSceneSetting内のAutoExposureで 明るさをいい感じに暗くして調整してしまうので、VFX自体の出力も10000Luxに負けないように 超明るくしないと昼行灯状態になるわけですね。 ためしにAutoExposureを切ると画面が真っ白になります。


昼に行灯(現代では本当に買ったら高そうですが)点けても黒くはならんだろと思いますが PBRとは何かという命題はさておくとします。 DirectionalLightを切ってもまだProcedualSky由来の光が入ってきます。実はこれがかなり強烈です。 ProcedualSkyを切ってというか、とっくに良い大人になってしまったキーボードクラッシャー君のように「一体いくつ空が有るんだ!」と叫びながら、 Skyと名のつくものを全部オフにしたり黒いベタ塗りの色にしたりして 更にアセットに含まれているConstructionLightも6000ルーメンと明るいスポットライトが設定されていますからこれも切って ……それから、これが見落としがちというか今日やっと気づいたんですが Window->Rendering->LightingSettingsからライトマップをベイクし直すと、 やっとデフォルト設定のVFXが明るく見れるような感じになります。 光源をあらかた取り払うと角材が何気に自分で発光しているのが分かって楽しいです。

そういえば、初学者的には「ベイクし直す」の一言でさらっと流してはいけないですね。 Unityは本当に気の利くヤツでライティングの計算について動かない物体と動かない光源同士については間接照明のように 光が回り込んだり拡散してぼんやりとシーンを照らしたり、ぼんやりと影を作る効果については ゲームが始まる前にガッツリと前計算してくれる機能があるんです。 現在のCPU,GPUの性能でもそういう間接照明の計算はクッソ重いのでゲーム中にいちいちやってるとゲームどころではなくなります。 なので、前計算した結果は「ライトマップ用のテクスチャ」に焼き込まれます。 ゲームが始まったら、ライトマップ用テクスチャを普通のテクスチャ(デカールマップ)とブレンドすることで間接照明とかの効果を いい感じに違和感なく付けてくれるという事なんですね。この、ゲームが始まる前に前計算ガッツリで ライトマップ用テクスチャに「焼き込む」プロセスを「ベイク」と呼ぶわけです。 ともかく、HDRPテンプレートの設定そのままの状態ではパーティクルの出力の際に明るさを 相当明るく指定しないと空より相対的に暗いということでVFXGraphの出力が真っ黒けになってしまうようです。 それしてもPBRとか言っても空がやたらいっぱいあるというのはおそらくきちっとした理由があるのでしょうけれど 一見すると「どうしてこんな家の中の、触れる場所に空があるの」というような理不尽な谷山浩子ワールドに迷い込んだかのようで目眩がします。 理不尽とシュールリアルを突き詰めていくTaniyamaBasedRenderingというのも考えて見ると良いのかもしれません。

ともかく今回は宇宙空間を舞台にしますから…… というか開始時点の知識ではどうやって最初からくっついてくるヤケクソな量の光を減らしたら良いのか分からなかった という理由からサンプルシーン付属のSceneSetting自体をバッサリ切って PostEffectのBloomだけ入れました。その上で光源は強さ控えめのDirectionalLight一個だけにしました。 ですからパーティクルの出力の明るさについては一部を除きデフォルトからそんなに大きくは上げてないです。

最初から少し苦労しましたが、こういうプロセスを順繰りに辿ると一体どうやってシーンは真っ暗な状態から照らされているのか? という事を洞察する手がかりになるので有意義だったと思います。Unityユーザの先輩諸氏にはこんな事はごく初歩的な内容と思われますが、 初学者には本当に混乱するところと思いますから、書き留めておくことで誰かの役に立てればと思います。

パーティクルが無事表示できると確信できて安心したので、背景が寂しいのが気になりました。 ですので爆発はちょっと後にとっといて、まず背景の星から作ることにしました。 昔つくったExtremeShootersとかいうゲームでは背景の星はスプライトをアニメーションさせて流していたのですが 今回はそれすらGPUパーティクルで実装してしまいます。 LineOutputで線状のパーティクルをいくつもZ+からZ-方向へ流すだけで簡単に星空が!


星の明滅にはOutputNodeのColor/alpha over lifeブロックの配色(Gradientプロパティ)で変えていますが、 時々明るい星も混ざってると良いよねということで2.5%の確率で輝度11倍のSSR星が出るようにしました。 また、線の長さと全体的な明るさをC#からいじれるようにしてゲームの進行具合で スピード感を増してるような演出ができるようにしました。


C#スクリプトからVFX Graph側の変数をいじる方法ですが、こんな感じでUnityEngine.Experimental.VFXをusing節に追加してから、ちょちょいとやります。

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.UI;
    using UnityEngine.Experimental.VFX;

    // Update is called once per frame
    void Update()
    {
        if (currentWave == null)
        {
            //ウェーブを生成するごとに背景の星の流れを早くする
            currentWave = GameObject.Instantiate(Waves[waveIdx]);
            starVFX.SetFloat("brightness", 0.5f + (float)waveIdx / (float)Waves.Length * 2f);
            starVFX.SetFloat("speed", 0.5f + (float)waveIdx / (float)Waves.Length);
            waveIdx++;
            if (waveIdx >= Waves.Length)
                waveIdx = 0;
        }
        else
        {
            //Waveに配置されている敵キャラが全部死んだら次のウェーブをすぐ生成
            if (currentWave.transform.childCount == 0)
            {
                Destroy(currentWave);
                currentWave = null;
            }
        }
    }
  

次に雑魚の爆発エフェクト。単発で爆発するようなエフェクトは、SpawnノードでSingleBurstブロックを作ります。 デフォルトのConstantSpawnRateだと連続的にジワジワといつまでも出てくるので。 そしたらInitializeノードでパーティクルの速度を0~120に設定して 早いパーティクルと遅いパーティクルのコントラストをくっきり付けることで破裂してる感を出します。 最低値が0であるのが一つのポイントと思います。動かないやつがいることで動くやつが際立つんです、 120という数値の根拠としては自機の弾が120m/secなのでとりあえずそこをMAXということにしました。 ゲームの内容というかカメラとの距離なんかによってここは変えるのがいいと思います。 InitializeノードのSet Velocity Randomだけで射出方向を決めるとなんだか四角い広がり方になってしまうし 弾を当てる方向(この時点では通常ショット以外の攻撃方法を諦めていなかったのです……) によって爆発の広がる向きが変わると嬉しいのでとりあえず爆発方向の中心軸とそこからの広がり具合を指定できるようにしてみました。 ただちょっとここで引っかかったことがありまして……


こうなっちゃうんです。 いや普通ベクトルVをAtoBっていうノードを通したあとそのままBtoAっていうノードに通せば Vはそのまま返ってくるやろって思うじゃないですか。返らないんだなこれが。 Rectangular to Sphericalノードのphi,theta返り値はラジアン単位なのにSpherical to Rectangularノードの 入力は360度系なんですね。だからこうします。


うーん、片付け出来ない系! 色についてはQuadOutputのColorMappingModeをGradientmapにするだけでいい感じに炎っぽい色になってくれます。


パーティクルの形についてはテクスチャを標準のアセットに入ってるSparkleに指定して、 OrientにAlong Velocityにすると破片みたいになっていい感じです。瓦みたいなテクスチャがあると尚よさげです。 あとは、VFXを格納するGameObjectにAudioSourceもつけて、爆発時の効果音を指定して、PlayOnAwakeにチェックを入れます。 こうすると敵が爆発するときにプレハブからinstantiateするだけで爆発エフェクトとともに爆発音まで鳴ってお得です。


タイトル画面も最初は板ポリとHLSLで無意味にジュリア集合とかのシェーダーアートでもやろうかと思ったんですが、 VFX Graphでモリモリとパーティクルを出すことにしました。コンセプト通りを貫く! まず最初はこんな感じでロゴの下に一直線にパーティクルの湧き出し口を作って真上に噴出させました。 initialzeノードでPosition(Line)ブロックを使って線状にパーティクル噴射機を置きます。 そして出力にpoint output nodeを指定して点を打たせまくります。


古き良き時代のメガデモのファイアエフェクトみたいですね。Bloomのお陰でとても映えます。 ThunderForce3みたいなゲームを作りたくなりますよねえ(なんのことだろう)。 さて、このままノスタルジーに浸るのも良いんですが、一捻り入れましょう。UpdateノードのTurbulenceブロックで文字通り一捻りするんですね。 Turbulenceブロックの使い方……というかVFX Graphの使い方自体^C師の動画に詳しくありますけれど、 なんとなくでもとにかくTurbulenceをくっつればウネウネしてくれるので重宝します。 Dragプロパティも付いてくるのでズルズルした抵抗もつけてくれて助かりますね。 ズルズル感が出たのでちょっと粒に方向が分かるような感じにしようかなと思い、PointOutputではなくLineOutputに変えました。 LineOuputのいい感じの設定方法についてはすぐ後で述べます。

自機の爆発パターン行ってみましょう。 自機の弾が青いので青ベースにします。これはoutput node のGradientMapの色相を反転させると 赤っぽい色がいい感じのブルーになってくれます。今回は一部だけブルーにしてみました。 出力はほぼ青になりますが赤と青を行き来するような感じでややポケモンフラッシュ気味にならないかと懸念がありますが……


あとは速度にコントラストを付けてバッと放出します。雑魚の爆発エフェクト同様です。 そして、LineOutputで集中線みたいにして強いられてる感を出します。自機の爆発ですから数を多くします。 Line outputはoffset targetの指定で線の長さや向きを変えられますが、offset targetには各パーティクルの速度ベクトルを適当にスカラー倍して重み付けをした物を 指定するだけで線の方向とパーティクルが飛ぶ方向を一致させられるのでしっくり来ます。


offset targetは多分こんな感じで速度ベクトルの正の実数倍の値を入れるのを想定していると思うのですが、 速度ベクトルとカメラ目線との外積を取って突っ込んだり速度ベクトルのマイナスの値を突っ込んだりしても楽しい効果が得られます。 テクスチャに頼らないで稲妻型のレーザーとか色々作れそうですね。

そしてボスの大爆発! ……と言ってもこれは特に何も言うことはなくて単にLine outputでパーティクルを10万粒一気に放出しているだけです。 まさに力技!爆発こそパワー!Bloomのおかげでカメラに焼け跡みたいな余韻が残って実にいい感じです。僕も何も苦労がなくてWin-Win感あります。


パーティクルシステムの楽しくて楽しくて仕方ないところというのはフィードバックエフェクトに通ずるところがあって、 何もかもが考えないで済む力技の中に取り込まれてしまう所にあると感じます。 物理や線形代数の小難しい本を読むよりとりあえずパラメータをガチャガチャいじった方がマシなんですよ? こんなエキサイティングな愉しみはなかなかありません。 でも10万粒のパーティクルに耐えられない環境だとボスを倒した後の大爆発が表示されずになんとも言えないわびしい気持ちになれます。

最後はハイスコア時の花火についてですが、まずはSpawnノードにPeriodicBurstブロックを付けて ポンポンとパーティクルの塊が出てくるように指定します。 次に、パーティクルが生成されるタイミングでパーティクルの生成される場所を変えたいので、 InitializeノードのSetPositionブロックにランダムな座標を入れるようにします。 Positon(AABox)などを使うとパーティクルの粒毎に違う位置に配置されてしまうので、 SetPositionブロックにランダムな座標を計算するノードからの入力を入れるようにしました。 これ、もうちょっと簡単に書く方法ありそうなんですけどどうなんでしょう……?


RandomノードのSeedに入れる値はVFXオブジェクトが生成されてからの経過時間をそのまま入れると秒単位で丸められるようで 1秒以下の間隔で打ち上げる場合同じ場所から2,3発出てくる事になってしまうので、 発射間隔に合わせて適宜スカラー倍します。適当に大きな値を入れとけばOKです。 同様に色もSetColorにランダムな色相を入れて変化させます。 丸い花火にしたい場合はSetVelocityRandomではなく雑魚の爆発を作ったときみたいに極座標系で ランダムなベクトルを作りますが、ノードがごちゃごちゃになってきたので今回は四角くてもいいや……と思ってしまいました。 まあ石鹸とかスポンジとかカステラみたいな微妙に親しい間柄の人への贈答品は四角い事が多いですから、祝いの花火も四角くていいでしょう。 あとは他の爆発とかとだいたい同じですが、Updateノードに重力や空気抵抗を軽めに入れて なんとなく花火感がでるようにしました。 そのうちもうちょっと気合入れた花火エフェクトを作ります。

そして

さて、いかがでしたでしょうか。「需要があればまとめを書きます」というようなことを昨日書いたばかりなんですが、 需要の有るなしに関わらず休日だったもので一気に書いてしまいました。 ゲーム本体もそうですが今回のまとめも含めて楽しんでいただければ幸いですが、 このWeb遺跡が果たして誰かの目に止まる日は来るのか、謎です。


おまけ。

こんなのを作って遊んでみました。迷路がいつまでも下から出続けます。 テクスチャは使わずあくまでLineOutputで出してます。原理は簡単なんですけど実際やってみると面白いですね!




Hayase Taku(SANDMAN)

戻る