うどんてっくメモ

技術的なメモをまったりと

【Rider】Code Cleanup Profileを使ってカスタムしたコード整形を行う

RiderにはCode Cleanupというコードの整形機能があり、いい感じにコードを整えてくれます。

pleiades.io

便利ではあるものの、デフォルトのまま使うといささかプロジェクトのコード規約にそぐわなかったり、意図しない不要な差分を生み出てしまうこともあります。 プロジェクトに適切な整形処理だけ走らせたい、そんな時にはカスタマイズした設定、Code Cleanup Profileを作成することで実現することができます。

Code Cleanup Profileの設定

まずはCode CleanUpを行う画面をCode -> Reformat and Cleanup Codeから開きます。

f:id:myudon:20220206214705p:plain

左下に表示されるのが設定となるCode Cleanup Profileです。デフォルトではFull Cleanup、Reformat & Apply Syntax Style、Reformat Codeの3つが用意されており、特に指定がなければFull Cleanupが指定され実行します。 右には実際にそのProfileに設定されている処理の項目が表示されます。

f:id:myudon:20220206215002p:plain

新しくProfileを作るには左上のEditボタンを押します。

f:id:myudon:20220206215714p:plain

Profileの設定画面が開くので、左上のプラスボタンから新規Profileを名前をつけて作成します。

f:id:myudon:20220206215907p:plain

後は使いたい処理を右から選択して適用します。選択後は右下のSaveを必ず押してください。 筆者はUnityでのC#コーディングに活用しているため、C#周りの設定をよしなに採用して使っています。

f:id:myudon:20220206220309p:plain

そしてCleanupを行う際に、作成したProfileを選択して実行すれば完了です。

f:id:myudon:20220206220634p:plain

Reformat and Cleanup on Saveでの活用

Rider2021.3以降には、保存時にReformatおよびCode Cleanupを呼び出す新機能が備わっています。

www.jetbrains.com

この機能においてもCode CleanUp Profileの適応が可能です。 Tools -> Actions on SaveからReformat and Cleanup on Saveを設定する画面を開き、Profileと指定されている部分を設定します。 デフォルトだとビルトインのFull Cleanupが指定されています。

f:id:myudon:20220206211639p:plain

設定したら、実際に保存してみて処理が走るか確認してください。

おわりに

RiderのCode Cleanupは適切に使いこなすことで、効率的にコーディングを進められます。 プロジェクトに則したProfileをもとに、Code Cleanupの導入を検討してみてはいかがでしょうか。

Unity2022でさらに進化したSearch Windowが便利だった

はじめに

2022年も始まりということで、Unity2021から導入され、さらにUnity2022で進化を遂げているSearch Windowの使い方について紹介します。 Search WindowについてはUnity2021以降で使用できる機能になりますので、ご留意ください。

本記事で紹介および検証を行なっているツールのバージョンは次の通りです。

  • Unity 2022.1.0b1

バージョンによっては挙動に差異がある場合もありますので、ご了承ください。

Search Window

Search Windowはプロジェクト内のアセットの参照およびさまざまな操作について、名前や特定の条件で絞り込んで検索する機能です。

f:id:myudon:20220102213051p:plain

WindowsであればCtrl + KMacであればCmd + Kのショートカットキーで開きます。 また、Projectビューの検索欄の右にあるボタンでも表示されます。一番検索欄に近い矢印が入ってるボタンです。

f:id:myudon:20220102215304p:plain

アセットについてパス、種類、名前などを指定して一覧として確認することが可能です。 次の画像は入れているPackage含めて全ての場所にあるTextureアセットを確認しているところです。

f:id:myudon:20220103011028p:plain

タイプと名前と言ったように、複合した条件も指定できます。

f:id:myudon:20220103011632p:plain

また、各種メニュー行える操作、および各種設定のウィンドウについてもSearch Windowからショートカットすることができます。

f:id:myudon:20220103011751p:plain

f:id:myudon:20220102220607p:plain

筆者はよく「あの画面ってどうやって出すんだっけ...」みたいになっておもむろにググるみたいなことがちょこちょこあったのですが、これを活用すれば名前さえ把握していればささっと出すことが可能です。

また、現在開いているシーンのヒエラルキー状のオブジェクトの検索も可能です。 ヒエラルキーにある検索機能でも同等のことはできるので、条件を色々と変えてヒエラルキーの内容を色々と確認したい時には有用です(ちょっとした検索をその場でしたいだけであれば、ヒエラルキーの検索機能でやってしまった方が早いです)。

f:id:myudon:20220103013424p:plain

2022で進化した絞り込み機能 Query Builder Mode

Unity2022からSearch Windowの絞り込み機能が視覚的に進化しました。 そもそもUnity2021では絞り込みを行う際に直に条件を打ち込む必要がありました。 タブキーでサジェストしてくれるものの、一定記述する必要はあったので、やや直感的ではありませんでした。

f:id:myudon:20220103001044p:plain

Unity2022ではこれがGUIによって設定できるQuery Builder Modeというのが追加されました。 これによって絞り込みたい要素をかなり視覚的に設定できます。

f:id:myudon:20220103001231p:plain

使う時には左上のボタンで切り替えます。

f:id:myudon:20220103010901p:plain

Query Builder Modeでは検索条件を追加する時は検索欄にあるプラスボタンを押して、フィルタ条件を指定します。

f:id:myudon:20220103012106p:plain

f:id:myudon:20220103012048p:plain

フィルタはアセットの種類やアタッチされているComponentなど、色々な条件がメニューに用意されています。 追加したフィルタは右クリックで後からアクティブの切り替えや中身の差し替えが可能です。

f:id:myudon:20220103020646p:plain

物によっては対象となるパラメータを元に条件を指定することも可能です。 たとえばアセットのIDやファイルサイズだったり、Prefabの種類だったり、特定のコンポーネントについてインスペクターから指定できる設定値だったり、様々なフィルタが用意されています。

f:id:myudon:20220103020502p:plain

f:id:myudon:20220103021234p:plain

f:id:myudon:20220103021255p:plain

パラメータ系のフィルタについては値やその条件となる等号などを調整可能です。

f:id:myudon:20220103021315p:plain

もちろんこれらの情報はQuery Builder Modeじゃなくとも記述すれば適応できるのですが、直感的に指定できるので便利です。

f:id:myudon:20220103014928p:plain

f:id:myudon:20220103015511p:plain

以上がQuery Builder Modeの紹介になります。

おわりに

本記事では紹介していない細かい機能もあるのですが、こちらは公式ドキュメントを参考にしてください。

docs.unity3d.com

何かしらアセットを探したり、メニューの操作や設定の調整で時間を消耗している人は活用してみてはいかがでしょうか。

2021年の振り返りと2022年の抱負

2021年にやったことを振り返りつつ、2022年頑張ることを書いていきます。

2021年やったこと

個人ブログ投稿 12本

昨年この技術ブログである「うどんてっくメモ」を再開し、今年は12個の記事を投稿しました。 単純に割ると月1つ出るかな、ぐらいのペースでした。

myudon.hatenablog.com

特にUnityが多くを占めていましたが、個人的に勉強を始めたRustに関してもちょろっと記事を出せたのは良かったなと思います。 閲覧数についても日々じわじわと上がっていっており、少し達成感を感じています。 今後も定期的に知識をアウトプットする場として活動していきたいです。

Zenn投稿 1本

基本的にアウトプットをブログに移行したのでQiita投稿はなかったのですが、Zennは一度投稿してみたくてRustの記事を書きました。

zenn.dev

結果的に想定していたよりも多くの方に見ていただけたようで、個人的には嬉しい記事になりました。 今後もちょっとしたTips系統はZennとかQiitaとかにあげれたらいいかもなと思っています。

技術書典の出典 会社と個人

技術書典で「UniTips」というUnityの技術書を定期的に会社で出しているのですが、2021年も執筆に参加しました。

creator.game.cyberagent.co.jp

第1回から携わってきていて、もう7冊目になるらしいです。時の流れを感じます。 技術書典12も既に予定されていますが、そこにも執筆予定です。 そして、個人執筆としてついに本を出すことができました。2021年の目標として掲げていた大きな点ですが、無事達成できました。

執筆自体は2月ぐらいからまずAmethystでサンプルゲームを作ったものをざっくり文章化し、それに伴って実装も調整したりし、なんやかんや半年ぐらいはかかった気がします。 隙間時間でちょこちょこと書いていたというのが大きな要因ではあるのですが、半年かかってようやく形になって本をビルドした時は感慨深いものがありました。 Twitter上でも多くの方に拡散していただけまして、結果としては想定を遥かに上回る数を手に取っていただけました。 今後も個人での本の執筆は挑戦していきたいですね。

商業誌執筆2冊

商業誌については業務でのお話と、技術書典で出した本の商業化のお話をいただきまして、執筆する運びとなりました。

creator.game.cyberagent.co.jp

nextpublishing.jp

商業誌を書く機会がまさか2回もあると思っていなかったので、今思うと貴重な体験をさせてもらえたなと思っています。 特に店頭に自分が書いた本が並んだり、Amazonでも自分の書いた本がヒットするというのはなかなかに嬉しかったです。 今後もお話をいただける機会があったり、チャンスがあったりすれば積極的に取り組んでみたいですね。

CA.unityの開催

どちらかというと業務に寄った話なのでアレですが、CA.unityというUnityのLTイベントを企画し、開催し始めました。 これは今年やったことの中でも大きな挑戦でした。

meetup.unity3d.jp

特に第1回は763人もの参加登録があり、運営としても驚愕のスケールとなりました。参加してくださったみなさま、本当にありがとうございました。 アーカイブに関しては全てUnityLearningMaterialsに掲載されてますので、ぜひチェックしてみて下さい。

learning.unity3d.jp

Gotanda.unity 登壇

Gotanda.unityというUnityのLTイベントについて、初登壇しました。 外部LTイベントについて、聞き手側としてはちらほら参加させてもらっているものの、登壇したことないなということで思い切って参加登録をポチりました。

learning.unity3d.jp

LT自体は社内ではちょこちょことやっていたので、ある程度は喋れるだろうと思っていたのですが、いざ本番になったらとても緊張したのを覚えています。 今振り返るともっと綺麗に話せたなとか、もっとスライド構成よくできたなとか、外部資料として残るならここの情報は可視化した方が良かったなとか、反省点が多いです。いい経験になりました。 今後も外部登壇にはチャレンジしていく予定です。

Qiitaアドベントカレンダー 2つ参加

毎年恒例アドベントカレンダー、2021年は会社のものとUnityのものに参加させていただきました。

myudon.hatenablog.com

myudon.hatenablog.com

会社のものについては去年同様に自分が運営を務めているのですが、今年も多くの方にご協力いただき、中身の大変濃いカレンダーが完成しました。 Unity、Golang、課金基盤、技術施策、新卒体験記などなど...さまざまな知見が詰まっていますので、よければ覗いてみて下さい。

qiita.com

2022年の抱負

アウトプットの戦略を立てる

2021年はブログ投稿、本の執筆、外部LT登壇、CA.unity、SNS宣伝などなど多角的にアウトプットに挑戦した一年でした。 結果的にこれまでの年で一番多くの人に知見を届けられたかなと思っています。多角的にやることで、アウトプットの手段についてのノウハウやそれぞれのメリデメを体感することができました。 これらの経験を活かし、自分にとっても業界にとってもより良いアウトプットに繋げられるように戦略をしっかり立てていきたいです。

  • アウトプットする内容の中でどこを簡潔に、どこを詳細に言語化するのか
  • どういったアウトプットの手法、規模感が好ましいか
  • 自分の知見でどの部分がアウトプットとして切り出せるか、どの部分がターゲットに有意義な知見になっているか
  • どういった広報で多くの人に届けることができるか

これらを考えつつ、良質なアウトプットを積極的にしていきたいなと思います。

インプットを大きめの年に

2021年は多角的にアウトプットを行った結果少しインプットは自分の満足いくものにはなりませんでした。 隙間時間に本を読んだり、記事を読んだり、というインプットは継続的にしているものの、ガッツリとインプットに時間を割いて知識を積む時間を作るというのは少し余裕がなかった一年でした。 今年はアウトプットを計画的にする中で効率化し、その時間をインプットに割いてより技術力を高める年にしていきたいです。 特に個人的にはRustに興味が強く、まだまだ勉強したいものも多いので、重視して計画していきます。

最後に

ここまでポエムを読んでいただきありがとうございました。 2021年はアウトプットについて大きな挑戦をしつつ、積極的に活動の幅を広げてみた一年でした。 2022年は2021年で培った土台をもとに、より多くのアクションを計画しながら知識を研鑽していければと思います。 2022年もよろしくお願いいたします。

【Unity】Unity2021.2で正式リリースされた2D Lightの紹介

この記事はUnity Advent Calender 2021 16日目の記事です。
15日目は @monry さんの「Unity Addressables Tips 〜2021年に於けるアップデート情報を添えて〜」でした。

先日Unity2021.2がリリースされ、さまざまな機能の改修が公式で紹介されていました。 その一つが「2D Light」の正式リリースです。

f:id:myudon:20211107200347g:plain

2D Lightはその名の通り2Dの描画物に対してライティングを行うもので、より高度な2D演出を可能にしてくれます。 初めての登場としてはUnity2019あたりで、Experimentalな機能としてここまで提供されていました。そんな正式リリースした2D Lightについて、その基礎的な機能や使い方について紹介します。 本記事で紹介および検証を行なっているツールのバージョンは次の通りです。

  • Unity 2021.2.0f1
  • Universal Render Pipeline 12.0.0

バージョンによっては挙動に差異がある場合もありますので、ご了承ください。

使い方

2D Renderer(SRP Asset)のとMaterialの設定

まず2D Lightを使うには、SRPの2D Rendererの設定を行う必要があります。 必要なのは2D Rendererのアセットです。メニューから2D Renderer付きのURP Assetを作成します。

f:id:myudon:20211105000856p:plain

作成するとRendererとPipelineのアセットが生成され、Pipelineには自動的にRendererがアタッチされた状態となります。 Edit -> Project SettingsからGraphicsの項目を開き、この生成したアセットを設定します。

f:id:myudon:20211105193736p:plain

Qualityごとの設定についても、必要に応じてアタッチしてください。

f:id:myudon:20211105194155p:plain

以上でRendererの設定は完了です。
Lightの影響を受けるスプライトについては専用のLitシェーダーが設定されていることを確認してください。

f:id:myudon:20211107185334p:plain

以上でスプライトが2D Lightの影響を受けるまでの前準備が完了です。

基本的なLightの作成と設定

2D Lightには次の4つの種類が存在します。

  • Global
  • Freeform
  • Sprite
  • Spot

それぞれのライトはヒエラルキー上の右クリックのメニューから作成できます。 作成されるもの自体は2D LightのコンポーネントがアタッチされたGame Objectで、その設定が違うだけです。

f:id:myudon:20211106103851p:plain

パラメータの設定はインスペクター上で設定可能です。 ColorとIntensity、そしてTarget Sorting Layerの項目があり、ライトの色と光の強さ、および対象のSorting Layerを調整できます。

f:id:myudon:20211106183050p:plain

ライトの種類ごとの挙動や設定についても説明します。

Global Light

Global Lightは全体を照らすライトです。設定した値が全体のライティングに反映されます。

f:id:myudon:20211107175031g:plain

全体的な2Dのベースのライティングの調整に活用するライトです。

Freeform Light

Freeform Lightはその名の通り自由変形してライティングする領域を矩形で指定できるライトです。

f:id:myudon:20211107175105g:plain

2Dの描画物に対してとりあえず任意の部分を明るくしたいといったケースでは非常にお手軽に反映させることができます。

Spot Light

Spot Lightは特定の位置に照明を当てたような円形のライティング効果を与えるライトです。 半径や円形に広がるライティング効果の減衰などを調整し、照らし方の調整ができます。

f:id:myudon:20211107175223g:plain

一点から照らすような効果を与えたい時にはSpot Lightを調整するといいでしょう。

Sprite Light

Sprite Lightはライティング効果を特定のスプライトの形状及び値を元に行います。

f:id:myudon:20211107175551g:plain

特定のシルエットによってライティング効果を出したい時には活用できます。

以上が2D Lightを使う上で基本となるライトの種類と設定になります。

Blending

2D Lightではそのライティング効果について、インスペクターからブレンドする計算を乗算にするか加算にするかを選択することができます。

f:id:myudon:20211107200834p:plain

画像にあるようにインスペクタからライトのブレンドを示すBlend Type、反映順序を示すLight Order、そして重なった際のライトの値の処理の種類となるOverlap Operationを設定できます。 Overlap Operationについては純粋な加算となるAdditiveとアルファ値の合成となるAlpha Blendが存在しており、単純にLightを合成する場合には前者を、Light Orderに沿って反映される効果を上書きしたい場合には後者を選択します。

Shadow Caster

2D Lightではライティングだけでなく、影を落とすこともできます。

f:id:myudon:20211113200446p:plain

影を落とすには2D Lightの設定と、スプライト側のコンポーネントのアタッチが必要になります。 まず2D Light側です。インスペクタのShadowsからStrengthを与えるとそれに伴った影が落ちるようになります。

f:id:myudon:20211107201618p:plain

そしてスプライト側ですが、これにはShadow Caster 2Dというコンポーネントをアタッチします。

f:id:myudon:20211107201731p:plain

落とす影の設定として、Use Renderer SilhouetteとCasts ShadowsとSelf Shadowsの三つの項目があります。 Casts Shadowsはそのまま影を落とすかどうかの設定となり、Use Renderer SilhouetteとSelf Shadowsは影の形状を調整する設定になります。Use Renderer Silhouetteでは影について描画されるスプライトの形状を加味するかどうか、Self Shadowsは自身に影を落とすかどうかの設定です。 こちらは公式ドキュメントの説明画像がわかりやすいです。引用した画像を次に示します。

f:id:myudon:20211109222017p:plain

また、複数のShadow Caster 2Dを利用する際には、Composite Shadow Caster 2Dというコンポーネントを利用します。 複数のそれらのオブジェクトの親にComposite Shadow Caster 2Dをアタッチすることで、それぞれをマージした一つの影として効果をシーン上に落とすことが可能です。

f:id:myudon:20211113195046p:plain

こちらについても公式ドキュメントの説明画像がわかりやすいです。 引用した画像を次に示します。 f:id:myudon:20211109223134p:plain

複数の影を干渉させるような表現を行う際には活用すべきコンポーネントとなります。

Normal Map

スプライトについて、ノーマルマップを用意したものについてはLight 2Dの効果にも反映されます。 例としてレンガのパターンで紹介します。これは反映せずにSpot Lightを当てた画像です。

f:id:myudon:20211106194404p:plain

これに適当に作ったNormal Mapを設定し、ライトの設定も調整すると次のようにライティング効果が変わります。

f:id:myudon:20211106194804p:plain

陰影が反映されて立体感が演出されているのがわかるかと思います。この手順についてみていきます。
まずはスプライト側です。これは難しいことはなく、該当するスプライトのSprite EditorからSecondery Textureとしてノーマルマップを指定するだけです。 スプライトのインスペクタからSprite Editorを選択し、左上のメニューからSecondary Textureの指定を行います。

f:id:myudon:20211107161718p:plain

_NormalMapと名前をつけ、設定するテクスチャをアタッチします。設定したSecondary Textureは選択して確認可能です。

f:id:myudon:20211107161613p:plain

以上でスプライト側の設定は完了です。次に2D Light側の設定を行います。 インスペクタの下の方にメニューがあるのでそこを調整します。

f:id:myudon:20211107162032p:plain

Qualityではその精度を設定できます。デフォルトではDisable、つまりは無効になっており、簡易的な計算によるFastとより正確な計算によるAccurateを選択します。 Distanceはライトとスプライトの距離を示しています。

Light Explorer

2D Lightも通常のLight同様にLight Explorerで設定の調整ができます。Light Explorerについて知りたい方は公式ドキュメントを参考にしてください。

f:id:myudon:20211107200730p:plain

一括でシーン中の光源の管理をしたいときにおすすめな機能です。

おわりに

Unity2021.2でようやく正式版になった2D Lightですが、さまざまな機能が用意されています。 単純に2Dの世界を明るくするだけではなく、よりリッチな表現をする上では非常に便利な機能となっているので、2Dの演出を考えている人はぜひ試してみてください。

さて、明日の Unity Advent Calendar 2021 は @Takaaki Ichijo さんの「ゲームのアドベンチャーパート(キャラクター掛け合い)の時系列演出をつくる」です。
お楽しみに〜!

参考文献

本記事の検証をするにあたって以下のサイトの素材を使用させていただきました。

かわいいフリー素材集 いらすとや

岩の無料イラスト | フリーイラスト素材集 ジャパクリップ

また、機能については公式ドキュメントを参考にさせていただきました。詳細な仕様を知りたい方はこちらを参照してください。

2D Graphics Features | Universal RP | 13.0.0

Riderで開発するなら知っておきたい2つのデバッグ機能

はじめに

本記事はQualiArts Advent Calender 1日目の記事となります。 今年もUnity、Golang、会社施策などQualiArtsで使われている様々な技術、取り組みについて紹介しますので、ぜひぜひチェックしてみてください。

qiita.com

自分からはUnityで開発する上で重宝するJetBrains製IDE、Riderに備わっているデバッグ実行時に使える便利な機能を2つ紹介します。 本記事で検証しているツールのバージョンは以下の通りです。

  • Rider 2021.2

バージョンによっては挙動に差異がある場合がありますのでご注意ください。

Riderのデバッグ機能

開発において必要不可欠と言えるのがデバッグ実行によるプログラムの検証です。 バグを調べるのに怪しい部分にブレークポイントを貼って、挙動を検証して、というのは誰しもやる開発手順だと思います。 Riderではステップごとに値を出力してくれるおかげで、とりあえずブレークポイントを貼って実行してみるだけでも多くの情報を得ることができます。

f:id:myudon:20211119195537p:plain

これだけでもありがたいのですが、Riderにはデバッグ実行をさらに効率化する2つの大きな機能が存在します。

Evaluate expressions

プログラムの実行中に「このタイミングで任意のパラメータの確認や評価をしたい...」ということは多々あるのではないでしょうか。そんな時に便利なのがこのEvaluate expressionsです。 Evaluate expressionsはブレークポイントで停止した地点で任意の式を評価し、その結果をいい感じに出力してくれる機能です。 使うには、ブレークポイントのステップ停止の時に画像にある電卓マークのボタンを押します。

f:id:myudon:20211119195710p:plain

押すとEvaluate expressionsのウィンドウが開きます。ここに任意の式を入力します。

f:id:myudon:20211119195824p:plain

gameObject.nameの中身が表示されているのがわかると思います。より機能を説明するために、次のUnityのプログラムとブレークポイントで動かしてみます。

public class test : MonoBehaviour
{
    void Start()
    {
        var hoge = new Hoge();
        var fuga = new Fuga();
        
        Debug.Log(hoge.value);
    }
}

public class Hoge
{
    public int value = 1;

    public void Test()
    {
        value *= 100;
    }
}

public class Fuga
{
    public List<int> list = new List<int>(){ 0, 1, 2 };

    public override string ToString()
    {
        return "FugaToString";
    }
}

f:id:myudon:20211119210716p:plain

プログラムを動かし、ブレークポイントの地点まで進んだところで、Evaluate expressionsを使用し、hogeの中身を確認します。

f:id:myudon:20211119211304p:plain

画像にあるように、Evaluate expressionsではそのステップでの入力補完をサジェストしてくれるので便利です。ローカル変数などもしっかりサジェストされます。 実行するとhogeの中身が確認できます。

f:id:myudon:20211119211417p:plain

fugaについても同様に見てみます。

f:id:myudon:20211119213019p:plain

fugaについてはObject.ToStringが実装されているため、その文字列が出力されています。デバッグ時に見やすくなるよう実装してあると捗ります。 また、fugaのメンバにはListがありますが、その中身についても確認できることがわかるかと思います。
今度は値の変更を行ってみます。hoge.valueについて別の値にしてみます。 右クリックして「Set Value」を選択すると値の変更ができます。

f:id:myudon:20211119213842p:plain

f:id:myudon:20211119213818p:plain

画像のように値の変更も可能です。この後にプログラムの実行を再開するとDebug.Logの結果として変更した値が出力されるのを確認できるかと思います。 このように、よしなに情報を確認したり変更したりするとても便利な機能がEvaluate expressionsとなります。 ちなみに筆者は業務でこのEvaluate expressionsは愛用しています。

Immediate window

Riderのデバッグ実行に付随する機能の中でも最も強力かなと思うのが、このImmediate windowです。 Immediate windowはいわゆる対話式でデバッグ操作を直感的に要求できる機能です。デバッグ実行した際にImmediate Windowは常時起動しています。 次の画像はデフォルトのレイアウトですが、値の表示の下に表示されています。

f:id:myudon:20211119215057p:plain

ここに任意の処理を対話式で入力すると、対応の出力を行なったり、処理を行なってくれます。 使い方について、Evaluate Expressionsと同様のプログラムとブレークポイントで試してみます。

public class test : MonoBehaviour
{
    void Start()
    {
        var hoge = new Hoge();
        var fuga = new Fuga();
        
        Debug.Log(hoge.value);
    }
}

public class Hoge
{
    public int value = 1;

    public void Test()
    {
        value *= 100;
    }
}

public class Fuga
{
    public List<int> list = new List<int>(){ 0, 1, 2 };

    public override string ToString()
    {
        return "FugaToString";
    }
}

f:id:myudon:20211119210716p:plain

まずは適当なパラメータについて確認してみます。出力したい変数の名前を入力するだけです。 ちなみにここでもサジェストはしてくれます。hogeを試しに出力します。

f:id:myudon:20211119215241p:plain

hogeの中身が出力されました。 また、fugaについても見てみます。

f:id:myudon:20211119215401p:plain

fugaのメンバの簡略的な情報が出力されました。また、Evaluate expressions同様にObject.ToStringで実装した文字列が出力されています。 Evaluate Expressionsのようにclassの要素全ては出力してくれませんが、一定の中身は確認することができます。
次はEvaluate Expression同様に値の変更をやってみます。Immediate Windowでは代入処理を書き込むだけで実現可能です。

f:id:myudon:20211120000113p:plain

hogeインスタンスの値を書き換えていることがわかるかと思います。 値の変更については先ほどのEvaluate Expressionsなど他の手順でも可能ですが、このImmediate Windowを介したやり方が一番手軽です。 また、便利なのが任意の関数呼び出しも行えるところです。例としてfugaのlistを操作してみます。

f:id:myudon:20211120000352p:plain

ListのAddを呼び出し、要素を更新しているのがわかるかと思います。ステップでの各値の状態を制御できるのはもちろん、任意の処理について特定のタイミングで正常な挙動をしているかの検証にも活用できます。 そしてさらにすごいのが、一時変数の定義までできてしまうことです。試しに別のListを定義してみます。

f:id:myudon:20211120002603p:plain

適当な一時変数のListを作り、その要素をfuga.listに追加しています。 このように、一時変数を置いて何かしらのパラメータを格納したり、それを元に処理を行うといったことも可能です。 ここまで説明したように、Immediate Windowはお手軽ながらも多くの便利なデバッグ機能が詰まっている素晴らしい機能です。ぜひとも触ってみてください。

おわりに

RiderはJetBrains謹製の強力なIDEであり、本当に多くの機能が存在します。 デバッグのための機能だけでも様々なものがあり、本記事では紹介しきれなかったものもいくつか存在します。 他の機能についても知りたい方は公式のドキュメントを参考にしてみてください。

www.jetbrains.com

【Unity】Unity2021.2からのScene Debug View Modes

はじめに

先日Unity2021.2.0f1がリリースされ、いよいよ2021.2系のUnityが正式に使えるようになってきました。
その新機能の一つであるScene Debug View Modesについて紹介します。

検証しているツールとパッケージのバージョンは以下の通りです。

  • Unity 2021.2.0f1
  • Universal Rendering Pipeline 13.0.0

バージョンによっては挙動が違うことがありますので、ご注意ください。

Scene Debug View Modes

Scene Debug View ModesはUnityの描画情報について、シーンビュー上の描画物に対して表示できるという機能です。

f:id:myudon:20211031152411p:plain

たとえば法線の情報だったり、色の情報だったり、ライティングの情報だったり、描画周りの情報を調整する際に活用できます。

使い方

WIndow -> Analysis -> Rendering Debuggerからウィンドウを開きます。

f:id:myudon:20211031192003p:plain:w480

Quick Searchを使用している方はRendering Debuggerと検索してしまうのが一番早いです。

f:id:myudon:20211031192031p:plain:w480

Frequently Used

使用頻度が高い項目がまとまっているページです。

f:id:myudon:20211031183740p:plain

項目についてはこの後の個々の部分の説明を参照してください。

Materials

マテリアルに関する情報のページです。

f:id:myudon:20211031190013p:plain

f:id:myudon:20211031190029p:plain

Material Filtersではシーン上のMaterialについて、任意のパラメータのみをフィルタリングして確認することができます。

f:id:myudon:20211031191433p:plain

たとえば上の画像ではMaterialに設定された色情報となるAlbedoの値を表示し、他のMetalicなどの情報は反映されていない見た目になっています。

また特定の条件を満たすMaterialを抽出するMaterial Validationという機能も存在します。

f:id:myudon:20211031191032p:plain

たとえば上の画像では特定の輝度(Luminance)を閾値に可視化しています。 Minを下回るものは赤く、Maxを上回るものは青く表示されています。

Lighting

ライティングに関する情報のページです。ライティングの効果を主に確認することができます。 Shadow Cascade、Normalマップの反映の有無によるライティング、反射といった情報を選択可能です。

f:id:myudon:20211031192448p:plain

また、反映させる光の種類も任意のものにすることが可能です。

f:id:myudon:20211031192533p:plain

特定のLightによる効果や、環境光の効果など、ライティング効果をそれぞれ確認する際に役立ちます。

f:id:myudon:20211031192718p:plain

Rendering

描画全体に関わる設定を確認できるページです。

f:id:myudon:20211031192834p:plain

たとえばMap Overlayでは深度マップやシャドウマップといった情報を参照できます。
サイズを調整可能で、普通の描画結果を出しながら右上に表示することも、全体に重ねて表示することもできます。

f:id:myudon:20211031193346p:plain

f:id:myudon:20211031193359p:plain

MSAAやポストプロセスなどの反映を切り替えたり、ワイヤーフレームを表示したりできます。
従来のシーンビューにもありましたが、Overdrawを表示する設定もここで可能です。

f:id:myudon:20211031194137p:plain

また、特定の値のピクセルをフィルタリングするPixel Validationという機能も存在します。

f:id:myudon:20211031193727p:plain

あまりよろしくないNaNや負数を示すピクセルを見つけたり、特定の閾値でのフィルタリングを行うことが可能です。

f:id:myudon:20211031193701p:plain

上の画像は試しに特定のG値でフィルタリングした結果となります。 Minを下回るものは赤く、Maxを上回るものは青く表示されています。

おわりに

ふとした描画周りの調査をしたいという時に、手軽に情報をフィルタして知ることができる便利な機能です。 Unity2021.2ではさまざまな機能改修が行われていますが、その中でもRP(Rendering Pipeline)周りは大きく改修が入っています。 この他にもさまざまな改修があるので、興味のある方はこちらのページを見るのがおすすめです。

docs.unity3d.com

【Rust】ゲームエンジンBevyのサンプルゲームを作った話 -ECSの基本実装の説明-

はじめに

先日Rust製のゲームエンジンBevyを使ったサンプルゲームを公開しました。 github.com

こちらの公開したサンプルゲームをベースに、Bevyで実装する際の基礎的な知識となるECSアーキテクチャの実装を説明しようと思います。

Bevy

BevyはRust製のデータ指向なゲームエンジンです。

bevyengine.org

無料のオープンソースで、ソースコードGitHub上に公開されています。

github.com

ECSアーキテクチャを採用しており、開発も結構盛んに行われています。執筆時点ではver0.5が最新の安定バージョンとして公開されています。

2Dおよび3Dにも対応しており、クロスプラットフォーム対応、ホットリロード可能など、発展途上ながらも多くの機能を備えています。筆者はAmethystというゲームエンジンの本も書いたりしたのですが、このBevyも触ってみて期待しているゲームエンジンのひとつです。

サンプルゲームについて

サンプルゲームではBevyの提供するECSアーキテクチャに則って、シンプルな2Dゲームのフローを実装しています。

f:id:myudon:20210918185533g:plain

プレイヤーを操作して落ちてくる岩を回避しながら林檎をとる、ごくごくシンプルなゲームです。
レベルデザインなどをしっかりやっている訳ではなく、ゲームのサンプルを実装したというよりはシステムのサンプルを実装したというのが正しいかもしれません。
Rustの文法としても比較的シンプルに実装しているので、初学者の方でも読み解きやすいかなと思います。(The Rust Programming Languageを一通り読めば多分大丈夫です。)

このゲームフローを構成するEntity、Component、Systemそれぞれの実装について説明します。

BevyにおけるECSの実装

BevyではEntityとComponentについては構造体、Systemについてはメソッドで定義を行います。それぞれの実装について、サンプルゲームの例を元に見ていきます。

EntityとComponent

まずはEntityですが、識別するidを持つシンプルなstructとして定義されています。

#[derive(Clone, Copy, Hash, Eq, Ord, PartialEq, PartialOrd)]
pub struct Entity {
    pub(crate) generation: u32,
    pub(crate) id: u32,
}

EntityはWorldというゲーム中のEntityとComponentを統括する機能で管理されます。

次にComponentです。サンプルゲーム内では林檎、岩、プレイヤーがあり、これらの機能はComponentとしてシンプルなstructで実装されています。 例としてプレイヤーの実装を示します。

const PLAYER_LIFE: i32 = 3;

// プレイヤーを表すComponent、基本的にライフの値のModel
pub struct Player {
    pub life: i32,
}

impl Default for Player {
    fn default() -> Self {
        Player::new(PLAYER_LIFE)
    }
}

impl Player {
    pub fn new(life: i32) -> Self {
        Player {
            life
        }
    }
    
    pub fn reset(&mut self) {
        self.life = PLAYER_LIFE;
    }
}

このPlayerをプレイヤーの機能を果たすComponentとして取り扱います。
structをComponentとして処理する工程は至ってシンプルで、Entityの生成時に紐付けるだけです。Unofficial Bevy Cheat Bookのサンプルを下記に示します。

struct SampleComponent;

pub fn setup(mut commands: Commands) {
    commands
        .spawn()
        .insert(SampleComponent);
}

CommandはWorldのEntityとComponentに対してmutableな操作を行う機能です。Entityの生成や削除、Componentの操作などを行う際にはCommandを介して処理します。 実際のPlayerの紐付けの実装を次に示します。

// 必要なデータを用意
let player_texture_handle = asset_server.load("textures/player.png");
let player_position = Vec3::new(0., -200., 0.);
let player_rotation = Quat::from_axis_angle(Vec3::Y, 0.);
let player_scale = Vec3::splat(1.);
commands
// スプライト表示系のComponentと共にEntityを作る
    .spawn_bundle(SpriteBundle {
        material: materials.add(player_texture_handle.into()),
        sprite: Sprite::new(Vec2::new(90., 90.)),
        transform: Transform {
        translation: player_position,
            rotation: player_rotation,
            scale: player_scale,
        },
        ..Default::default()
    })
// ゲーム側でプレイヤーのEntityとして紐付けたいComponent
    .insert_bundle((
        Player::default(),
        Mover::default(),
        GameScene
    ));

CommandからEntityを生成してメソッドチェーンの形でComponentを紐付けています。ちなみにPlayerと一緒に紐づけているMoverはその名の通りサンプルゲーム中のプレイヤーや林檎といったものを動かすためのComponentです。このようにEntityを生成して必要な機能のComponentを紐付ける、というのが基本的なオブジェクトの生成フローとなります。

System

サンプルゲームではsrc/system配下にゲームロジックを形成する以下のsystemの実装があります。

├── collision.rs // プレイヤーと落ちてくるものの衝突判定
├── display.rs // UIなどの表示
├── object_spawn.rs // 落ちてくる林檎と岩の生成
├── player.rs // ユーザー入力の処理
├── time.rs // 時間の処理
└── translate.rs // プレイヤーや落ちてくるものの移動処理

それぞれがComponentへの作用を担っており、対象となるComponentの配列を舐めて処理を行います。ECSアーキテクチャの特徴的な処理ですね。 前述にもあるようにBevyではその作用をシンプルなメソッドとして実装します。例としてtranslate.rsの実装を示します。

// Moverの速度に沿った位置更新
pub fn translate_mover_system(mut query: Query<(&Mover, &mut Transform)>) {
    // 速度に伴ってtransformの位置情報を更新
    for (mover, mut transform) in query.iter_mut(){
        let velocity = &mover.velocity;
        transform.translation.x += velocity.x;
        transform.translation.y += velocity.y;
    }
}

引数としてQueryという型を渡していますが、これがComponentの参照に用いる機能です。ジェネリクスで対象となるComponentの型を宣言し、イテレータを使用して作用を処理します。 また、Entityの生成や破棄を行いたい場合にもCommandを引数にして処理します。collision.rsの実装の一部を抜粋します。

pub fn collision(
    mut commands: Commands,
    mut game: ResMut<Game>,
    mut player_query: Query<(&mut Player, &Transform, &Sprite)>,
    mut apple_query: Query<(Entity, &mut Apple, &Transform, &Sprite)>,
    mut block_query: Query<(Entity, &mut Block, &Transform, &Sprite)>) {
    
    if let Ok((mut player, player_transform, player_sprite)) = player_query.single_mut() {
        for (block_entity, block, block_transform, block_sprite) in block_query.iter_mut() {
            if block_transform.translation.y < OBJECT_DESPAWN_Y {
                commands.entity(block_entity).despawn(); 
                break;
            }
    ...

このように、Systemの実装ではCommandおよびComponentのQueryを引数としたメソッドを実装することになります。 実装したメソッドは専用の機能を用いてゲームフローに組み込みます。Unofficial Bevy Cheat Bookより引用した実装を以下に示します。

fn main() {
    App::build()
        // ...

        // run it only once at launch
        .add_startup_system(init_menu.system())
        .add_startup_system(debug_start.system())

        // run it every frame update
        .add_system(move_player.system())
        .add_system(enemies_ai.system())
        .run();
}

into traitを介してメソッドはSystemに変換されています。Amethystだと専用のtraitを実装したstructとして表現していますが、こちらはよりシンプルな形と言えます。 また、サンプルゲームではSystemSetという機能を用いてタイトル画面やゲーム中といったゲーム中の状態ごとのSystemの分割を行っています。

// ゲーム内の状態
// lib.rs
#[derive(Clone, Eq, PartialEq, Debug, Hash)]
pub enum GameState {
    // クリックしてゲーム開始を待つ画面
    Title,
    // ゲーム中
    Playing,
    // クリックしてゲーム終了を待つ画面
    GameOver,
}

// SystemSetの構築
// system.rs
pub fn title_enter_system_set() -> SystemSet {
    // タイトル画面の文字を出す
    SystemSet::on_enter(GameState::Title)
        .with_system(setup_title_ui.system())
}

pub fn game_enter_system_set() -> SystemSet {
    // ゲーム中に必要な文字の表示とかを出す
    SystemSet::on_enter(GameState::Playing)
        .with_system(setup_game_ui.system())
}

pub fn game_update_system_set() -> SystemSet {
    // ゲームのルールを構成するためのSystem
    SystemSet::on_update(GameState::Playing)
        .with_system(collision.system())
        .with_system(check_spawn_object.system())
        .with_system(player_input_system.system())
        .with_system(translate_mover_system.system())
        .with_system(game_time_display_system.system())
        .with_system(game_score_display_system.system())
        .with_system(player_life_display_system.system())
        .with_system(update_game.system())
}

// SystemSetの組み込み
// main.rs
fn main() {
    App::build()
        // ...
        // Titleの開始、更新、終了でのSystem
        .add_system_set(title_enter_system_set())
        .add_system_set(title_update_system_set())
        .add_system_set(title_exit_system_set())
        // Playingの開始、更新、終了でのSystem
        .add_system_set(game_enter_system_set())
        .add_system_set(game_update_system_set())
        .add_system_set(game_exit_system_set())
        // GameOverの開始、更新、終了でのSystem
        .add_system_set(game_over_enter_system_set())
        .add_system_set(game_over_update_system_set())
        .add_system_set(game_over_exit_system_set())
        .run();
}

一定複雑なSystemの管理を行う場合にはSystemSetを活用するのがおすすめです。 以上がSystemの実装になります。

おわりに

ざっくりとBevyで開発する上で一番大切なECSアーキテクチャを実装する部分について説明しました。ゲーム内のオブジェクトとロジックをBevy上でどうやって組み上げるか、なんとなく伝わっていたら幸いです。
BevyはECSアーキテクチャをかなり直感的に実装できるな、という感覚が筆者的にはあって良かったです。

参考文献

公式ドキュメントのIntroductionです。Bevyを使う際にはまず一読しておくのがおすすめです。

bevyengine.org

Bevyの実装をする上で参考になる非公式のドキュメントです。Bevyの根幹となる機能についてある程度まとめてくれています。文中でも引用させていただきました。

Introduction - Unofficial Bevy Cheat Book

また、こちらのブログでもBevyでHello Worldするまでの手順を取り上げています。参考にさせていただきました。

blog.livedoor.jp