PaperSloth’s diary

主にゲーム開発関連についての記事を書きます。

UE4 Niagaraのサンプルプロジェクトを公開しました!

環境

UE4.25.3

概要

第14回UE4ぷちコンに応募したプロジェクトのコアロジックのみの最小構成サンプルを公開しました!

プロジェクトの詳細は前回の記事を参照
papersloth.hatenablog.com


GitHubとBOOTHにて公開しましたので、お好きな方からDLいただければと思います。
github.com

papersloth.booth.pm

また、ゲーム自体の実行ファイルも下記BOOTHにて公開しました。
こちらのゲームもぜひとも遊んでいただければ思います。
papersloth.booth.pm


内容としては下記のようにシンプルな構成になっています。

ざっくりと中身を説明しますと
LevelBlueprintで無限ループを行い花火のSpawnを行っており
Blueprint x 3は回転するSkydome、花火、GameModeの3つです。

GameModeではマウスの位置を始点にMultiSphereTraceで花火のBlueprintに付けたSphereCollisionとのヒット判定を行っています。
花火ではNiagaraからの位置情報の取得とヒット判定後の花火の爆発を行っています。


Niagara System x 3は
打ち上がる花火のエフェクト、時間経過での小さな爆発、カーソルヒット後の大きな爆発の3種類です。
打ち上がる花火のエフェクトでは「Generate Location Event」と「Receive Location Event」を使用しており
Emitter間での位置情報のやり取りについて学べるかと思います。
これはカーソルヒット後の大きな爆発でも同様です。
特にRibbon Rendererでの位置情報の追従は便利で何かと使い所があるかと思います。


自作のNiagara Moduleでは「Add Velocity in Cone」をベースに簡単な計算の補正を行ったものと
前々回の記事で解説したBlueprintに位置情報を渡すためのModuleの2種類です。
詳細はこちらをご覧いただければと思います。
papersloth.hatenablog.com


自作Moduleに深入りしたりSimulation Stageに手を出すと複雑化していくのですが
Niagara自体は結構シンプルで分かりやすいものですので、気軽に触ってみてください!

以上です!

制作日記 第14回UE4ぷちコンに応募しました

環境

UE4.25.3

概要

こんなゲーム作りました
www.youtube.com

技術的な詳細実装の解説記事というよりはあくまで日記程度のものです。

ゲームのコアロジック部分に関してはほぼほぼNiagaraで作成してます。
ゲーム全体のロジック(シーケンスやスコア管理やUI等)はBlueprintとUnreal C++で作成しました。
今回の比率的には7 : 3(Blueprint : C++)くらいの割合です。

C++でなければ実装ができないような部分は1,2%程度ですが
Blueprintでも代替可能な部分ではあるので、実装は全てBlueprintでも可能です。

コア部分の花火の実装

Niagaraの実装

花火は大きく分けると打ち上げ部分と爆発部分の2つのEmitterで作成しています。
実際には細々とした装飾を施しているため、以下の構成です。
・打ち上げ部分 : 核となる玉、Trail(Ribbon)、Trail(Sprite)、煙(なくても良かったかも)の4 Emitter(無駄に贅沢)
・爆発部分 : 飛び散る玉、Trail(Ribbon)、Trail(Sprite)、四散する玉の4 Emitter
Particle自体はプロのアーティストでもない素人が作ったものなので深く解説する部分は特にないです。

それよりはBlueprintとNiagaraの連携部分に比重を置いて解説していこうと思います。
基本的には前回記事にしたParticleDataのやり取りがメインの部分です。
papersloth.hatenablog.com

まず打ち上げ部分のParticleについてですが、1つだけ自作のNiagara Module Scriptを使用しています。
f:id:PaperSloth:20200830071231p:plain

NMS_ExportというNiagara Module Scriptを作成しました。(元は検証用で作成していたとはいえ酷い命名ですね)
詳細は先程貼った前回の記事を見ていただければですが、処理内容としてはParticleの位置と速度をBlueprintに受け渡しするために設定しているだけです。
f:id:PaperSloth:20200830071411p:plain

ひとまずこれでBlueprintに位置と速度を渡せるようになりました。
最終的に速度は使用しなかったので、位置情報だけで良いのですが。

Blueprintの実装

花火のBlueprint側はActorを継承してNiagaraSphere Collisionだけのシンプルなものです。
f:id:PaperSloth:20200830071826p:plain

通常今回のゲームのような処理を組む時はNiagara で見た目だけ作成してBlueprint で動きの制御をすることが多いと思うのですが

今回のアプローチとしては逆でNiagara の動きの制御に合わせてBlueprint で当たり判定を追従させるという作りにしています。
Niagara Particle Callback HandlerをInterfaceに追加して
Receive Particle Dataで先程設定した打ち上げ部分の玉の位置情報を受け取るようにしています。
受け取るデータは配列ですが、打ち上げ部分の玉は1つしか生成していないため
その位置情報を元に当たり判定となるSphere Collisionの位置を設定しているだけです。
f:id:PaperSloth:20200830071923p:plain

これで打ち上げた玉の位置と実際のコリジョンが一致するようになります。
f:id:PaperSloth:20200830072947g:plain

あとは一定時間が経過するか、カーソルにヒットした少し後に爆発用のエフェクトを生成するだけです。
これだけでこのゲームのコア部分自体は完成です。

その他の実装

このゲームらしい部分の解説らしい解説はこれで終わりなんですが、その他の細々とした実装もちょっとだけ解説します。

天球

それっぽい夜空があれば何でも良かったんですが、Infiltrator Demoの背景とかそのまま使えそうだなと思ったのでそのまま使うことにしました。
なので、実は背景にあのロボがいるんですね
f:id:PaperSloth:20200830074645p:plain
Scene Capture CubeでCube Mapを作成してMaterialにペタッと貼っておしまいです。
これだけで背景完成!お手軽!
f:id:PaperSloth:20200830074551p:plain
詳細は以前に記事にしたので、こちらを参照ください。
papersloth.hatenablog.com

大きな花火とタイトルのキラキラ

特定コンボ数で出現する大型の花火とタイトルにはキラキラしたエフェクトを付け足しています。
大型花火
f:id:PaperSloth:20200830073815g:plain
タイトルロゴ
f:id:PaperSloth:20200830074945g:plain

Particle自体はNiagaraのテンプレートのOmnidirectionalBurstにちょっと手を加えただけのものです。
見た目的には良さそうなんですけど、実装としては大問題が1点あります。
使用してるテクスチャが「DefaultBloomKarnel」なんですよね。
Engine/EngineMaterialsに入ってるテクスチャなんですが、2Kのテクスチャを使用してる割にこれだけしか描かれてないです。
f:id:PaperSloth:20200830075319p:plain
なので、通常は使用しないでリサイズしたりしてパッケージサイズを圧迫しないようにしたりすることが多いと思います。

で、今回は贅沢にもこのテクスチャをそのまま使用しています。
f:id:PaperSloth:20200830075538p:plain

まとめ

今回は技術記事というより日記なので薄っぺらい感じの解説になっています。
特に全く意識したわけではなかったんですが、このゲームでは光源が0です。
タイトルからリザルトに至るまで、全部Emissiveだったんですよね。
みんなそれぞれ輝いています。

以上です!久々にぷちコン参加できて楽しかったです!

UE4 ParticleDataを使用したNiagaraとBlueprintの連携

環境

UE4.24.3 (解説は基本的にすべて4.25です)
UE4.25.2

NiagaraEmitterの情報にBluerprintからアクセスする方法

UE4.25 以前のアクセス方法
UE4.24.3等ではGet Niagara Emitter XXでEmitterの情報にアクセスが可能でした
こちらはEmitter名を指定するだけなので非常にお手軽でした
f:id:PaperSloth:20200801100245p:plain


しかし、UE4.25からはこれらのノードが廃止されています
f:id:PaperSloth:20200801100437p:plain

そこで、UE4.25以降ではNiagara Emitterの情報をどのようにしてBluerprintから取得するのかという点について解説していきます

Niagara側の設定

data export用のModule作成

まずはFX -> Niagara Module Scriptから新規にModuleを作成します
f:id:PaperSloth:20200801100658p:plain

名前はひとまず「NMS_ExportExample」としました
f:id:PaperSloth:20200801100841p:plain

Niagara Module Scriptを作成したら先ずは忘れないように
「Expose to Library」にチェックを入れます
ここにチェックをしないと、Niagara Emitterから使用することができません
f:id:PaperSloth:20200801101004p:plain

続いて、dataをexportするための「New Export particle data」を追加します
f:id:PaperSloth:20200801101141p:plain
f:id:PaperSloth:20200801101149p:plain

次に「Store Particle Data」を使用してDataを格納します
f:id:PaperSloth:20200801101435p:plain

  • Store Data : データを保持する条件です。

チェックを入れた場合は常時値を保持するようになります
条件式を加える例として、PaticleにCollision Moduleを追加してHitした時のみ位置情報を格納するなどの使い方が考えられます

Position, Size, Velocityは特別解説するようなこともないので、素直に値を入れてあげればよいです
今回の例では位置情報のみを使用するため、以下のように設定しました
f:id:PaperSloth:20200801101737p:plain

しかし、これだけではMap Setしていないためエラーになってしまうので値を追加してあげます
Particle Attributesに新規にbool型の変数を追加し「StoreSuccess」と名付けました
名前はなんでもいいです
f:id:PaperSloth:20200801102019p:plain

追加したAttributeをMapSetに追加して、StoreParticleDataの返り値を設定します
f:id:PaperSloth:20200801102135p:plain
f:id:PaperSloth:20200801102230p:plain

これでModule Scriptは完成です。
f:id:PaperSloth:20200801103107p:plain

Niagara Systemの作成

新規にNiagara Systemを作成し、New system from selected emitter(s) を選択します
f:id:PaperSloth:20200801105318p:plain

Emitterは「Omnidirectional Burst」を選択します
Emitter to Addから追加し、作成完了です
f:id:PaperSloth:20200801105437p:plain

名前は「NS_Example」としました
f:id:PaperSloth:20200801105619p:plain

NS_Exampleを開いて「Particle Update」に先程作成した
「NMS Export Example」を追加します
f:id:PaperSloth:20200801105723p:plain

このままではCallback Handler Parameterに設定可能なParameterがないため、新規にParameterを追加する必要があります
f:id:PaperSloth:20200801105836p:plain

Parametersの「User Exposed」に「Object」型のParameterを追加します
f:id:PaperSloth:20200801105856p:plain

ひとまず「ExportHandler」と名付けました
名前はなんでもいいですが、後ほどBlueprintと連携する際にこの名前が必要になります
f:id:PaperSloth:20200801110005p:plain

Object型のParameterを追加したことでNMS Export Exampleに設定可能なHandlerができました
「NMS Export Example」を選択し、Callback Handler Parameterに「User.ExportHandler」を設定します
f:id:PaperSloth:20200801110204p:plain

これでNiagara Emitter側の設定は完了です

おまけとしてCollisionを追加しておきました
後のデバッグでの挙動確認用です
f:id:PaperSloth:20200801111953p:plain

Blueprint側の設定

長かったですが、ここまできてようやくBlueprintからのEmitterの値の取得が可能になります。
「Actor」を継承したBlueprintを作成し、「BP_ExampleActor」と名付けました
f:id:PaperSloth:20200801110437p:plain
忘れないうちにLevel上の適当な位置に配置します

「BP_ExampleActor」を開くと、まずはComponentの追加で
Niagara Particle System」を追加します
f:id:PaperSloth:20200801110558p:plain

追加したComponentを選択し、先程作成した「NS_Example」を割り当てます
f:id:PaperSloth:20200801110636p:plain

続いてNiagara Systemの「ExportHandler」の初期化を行います
NiagaraのComponentから「Set Niagara Variable(Object)」を選択し
「BeginPlay」から呼び出し、図のように繋ぎます
f:id:PaperSloth:20200801110937p:plain
これで「NS_Example」の「ExportHandler」に対して「BP_ExampleActor」を割り当てることができました

次にNiagaraのCallback関数の呼び出しを行います。
「Class Setteings」を選択して
f:id:PaperSloth:20200801111059p:plain

Interfaceの追加から「Niagara Particle Callback Handler」を選択します
f:id:PaperSloth:20200801111215p:plain

MyBlueprintタブのInterfacesに「Receive Particle Data」が追加されていれば設定ができています
f:id:PaperSloth:20200801111318p:plain

続いて「Receive Particle Data」から「Implement Function」を選択して
Receive Particle Dataに処理を追加します
f:id:PaperSloth:20200801111416p:plain
f:id:PaperSloth:20200801111437p:plain

Niagara EmitterのPosition情報を取得してDebug用のSphereを描画してみました
f:id:PaperSloth:20200804174154p:plain
これで値が正しく取得できていることが確認できました
f:id:PaperSloth:20200801112144g:plain

まとめ

今回の作例ではNiagara Module ScriptからPositionにのみ値を設定したため
Position以外には値が割り当てられていません
逆にModule Script内で処理を書くことで様々なカスタムした値の割り当てが可能です

また、ModuleのStore Dataフラグの工夫次第でヒット判定など様々な条件の判定が可能です

ここまで読んでいただいた方はよく分かったと思うのですが
UE4.24に比べてUE4.25でのNiagara Emitterの情報を取得するのが大変手間です
他にもっとスマートな方法を知っている方はTwitter等で教えてもらえると嬉しいです。以上です。

おまけ
この機能を使ってこんなゲームを作って簡単な解説記事を書きました
www.youtube.com

papersloth.hatenablog.com


以上です

UE4 Editorの言語設定を一時的に変える方法

環境

UE4.25.1

言語設定を一時的に変える方法

普段UE4を英語環境で使用しているけれど、他の人にEditorで説明する場合等に一時的に日本語環境に変えたいというケースがあります。

そういった時にUE4ではコンソールコマンドから変更することが可能です。
ただし、この機能は一時的なものでEditorを再起動した時には元に戻ります。
また、全てが正しく変更されるわけではないため、ブログやスライド作成時には後述の永続的に変える方法から変更してEditorを再起動することをオススメします。

変更方法は簡単で実行中またはOutput Logから
"culture=(言語)"です。
日本語に変更したい場合は"culture=ja"
英語にしたい場合は"culture=en"
実行時のコンソールコマンド入力は以下のどちらからでもよいです。
f:id:PaperSloth:20200621213537p:plain
f:id:PaperSloth:20200621213638p:plain

以下のようにOutput Logから入力した場合でも問題ありません。
f:id:PaperSloth:20200621213734p:plain

このコマンドで使うことはないかと思いますが、ConstrunctionScriptやBeginPlay等でも動作します。
f:id:PaperSloth:20200621213840p:plain

言語切り替えは他の多くの言語にも対応していますが、よく使うのは上記だと思いますので、他の言語は割愛させていただきます。

言語設定を永続的に変える方法

こちらは多くの情報が出てくると思いますが、一応紹介しておきます。
Editor Preferences(エディタの環境設定)から変更可能です。

General - Ragion & LanguageからEditor Languageを変更することで変更されます。
この方法で変更した場合はEditorを終了しても適用されます。
念のためEditorの再起動をしておくと部分的に言語が変わる恐れもなくて安心です。
f:id:PaperSloth:20200621214153p:plain

日本語の場合は一般 - 地域 & 言語のエディタの言語から変更可能です。
f:id:PaperSloth:20200621214424p:plain

UE4 LockOnCursorの実装

環境

UE4.25.1

概要

UE4でのロックオンの記事はちらほら見かけるけど、ロックオンカーソルの記事は少ないというのを見て書き始めました。
本記事ではロックオンする対象は1体を想定しています。
マルチロックオンでの運用も可能ですが、Widgetの場合は少し工夫が必要になります。

ロックオンの処理自体は事前に組まれていることを想定しています。
良いサンプルとは言えませんが、とりあえずのロックオンのサンプルとして下記を利用していただいても良いかと思います(このサンプルにはロックオンカーソルのロジックは実装されておりません)
github.com


完成イメージは以下のようになります。
f:id:PaperSloth:20200619223101p:plain


※実装に使用している「LockOnTarget」という変数がありますが
これはActor型の変数でPlayerやLockOn用のComponent等
ロックオン処理を行っているBlueprintから事前にHUD/Widgetに渡されている上での実装になります。
f:id:PaperSloth:20200620145352p:plain

一例としてPlayer側でロックオン処理を組んでいる場合のものを載せておきます。
PlayerRef : プレイヤーBlueprintへの参照
IsLockOn : プレイヤーがロックオン状態か判定するためのフラグ
LockOnTarget : ロックオン対象となるActor(必ず対象がいるとは限らないため、IsValidチェック)
f:id:PaperSloth:20200620145535p:plain

ロックオンカーソル実装(HUD編)

先ずはHUDでの実装についてです。
個人的にはWidgetでの実装をオススメしますが、こちらも紹介しておきます。

下図のようにHUD Blueprint内で処理を組むことでロックオンカーソルを表示できます。
f:id:PaperSloth:20200619211120p:plain

ここで注目すべきは「Project」ノードです。
「Project」ノードでロックオン対象の3次元座標を画面の2次元座標に変換してくれます。
あとはその位置データを元にDraw TextureやDraw Materialでロックオンカーソルを描画するだけです。

注意点として、このノードはHUD内でしか使えず「Event Receive Draw HUD」の中でしか機能しません。
そのため、Tick等で同じノードを組んだ場合には動作しません。

ロックオンカーソル実装(Widget編)

続いてWidgetでの実装です。
Widget内のDesignerでは図のようにCanvasPanelとImageだけのものを用意します。
f:id:PaperSloth:20200619220738p:plain

この時にLockOnCursorのVisibilityを「Hidden」か「Collapsed」にしておきます。
表示されてほしいのはロックオン中のみですので、デフォルトでは非表示でよいです。
f:id:PaperSloth:20200619220906p:plain


あとは図のようにBlueprintを組めば完成です。
f:id:PaperSloth:20200619221524p:plain

ロックオン対象が存在する時は表示して座標を更新し、いない時は非表示に切り替えています。
HUDではProjectを使用して2次元座標に変換していましたが
ProjectはHUD専用ですので、こちらではPlayerControllerの「Project World to Screen」を使用します。

この辺りの処理は何度か使うようであれば、関数ライブラリ等にまとめてしまってもよいかもしれませんね。
f:id:PaperSloth:20200619222328p:plain
f:id:PaperSloth:20200619222357p:plain

ロックオン処理の実装にもよりますが、開始/終了判定が取れる場合は以下のようによりシンプルにまとまると思います。
f:id:PaperSloth:20200619222708p:plain

まとめ

個人的にはWidgetでの実装をオススメしますが

とHUD編で書いたのですが、この理由についていくつか解説します
1. ロックオン開始時のアニメーションを追加できる
 ロックオンした時のカーソルの回転や拡縮や色替えなど各種アニメーションをWidgetで組んで実装することができます。
 HUDでも出来るかもしれませんが、結構な手間なので避けたいところです。
 ロックオン開始時ではなく、常時回転などを行いたい場合は以下のようにMaterialで組めばよいです
 papersloth.hatenablog.com

2. 画面サイズの変更に強い
 HUD版の現実装では画面解像度等を変えた時にロックオンカーソルも大きさが変わってしまいます。
 これでちょうど良いサイズになってくれると良いのですが、少し処理を加えて調整してやる必要があります。
 Widget版ではViewportScaleを考慮して計算しているため、表示 / 非表示の切り替えくらいで特に何も手を加える必要がない想定です。

HUDでの利点もないわけではないです。
マルチロックオンを行いたい場合は比較的処理が楽です。
Draw Texture/Materialが同時にロックオンした数と同じだけ描画されることにだけだからです。
例で渡したLockOnTargetを配列にしてループを回すだけで済みます。
f:id:PaperSloth:20200619211913p:plain

同じことをWidget版でやりたい場合は少し手間です。
図のようにLockOnCursorだけのWidgetを作成しておき
f:id:PaperSloth:20200619215814p:plain

ロックオン開始時にメインのWidgetのPanelにAdd Childして終了時にRemove Childする等でしょうか。
f:id:PaperSloth:20200619220053p:plain


ロックオンカーソルについては以上になります。

UE4 MaterialでTextureを回転させる

環境

UE4.25.1

CustomRotatorを使用する

Engine Content内のMaterial FunctionにCustomRotatorというものがあります。

これを使うだけでTextureの回転が可能です。
f:id:PaperSloth:20200615001731p:plain

引数について
・UVs : UVを渡す
・Rotation Center : 回転の中心位置
・Rotation Angle : 0-1で回転量を渡す 0.5 -> 180°, 1.0 -> 360°

使い方は簡単で180°回転させたい場合は以下のように繋ぐだけです。
上の画像が元画像で下が180°回転させたものです。
f:id:PaperSloth:20200615002338p:plain


ただし、このテクスチャをそのまま回転させるとテクスチャの繰り返し部分が見えておかしな表示になっていしまいます。
f:id:PaperSloth:20200615002836g:plain

これを修正するにはImportしたTextureを開いて
X/Y-axis Tiling MethodをWrapから「Clamp」に変更することで防げます
f:id:PaperSloth:20200615003003p:plain
f:id:PaperSloth:20200615003153g:plain

Textureを回転させる方法

Material内で回転させるためにはRotation Angleに「Time -> Frac」を渡すだけでよいです
f:id:PaperSloth:20200615003412p:plain

これはTimeの小数点部分だけを渡しているのですが
何をしているのかを見ていきましょう

先ずは以下のようにTime -> Fracした値を「DebugScalarValues」に繋ぎます。
f:id:PaperSloth:20200615004309p:plain

続いて「DebugScalarValues」ノードの上で右クリックして「Start Previewing Node」を選択します
f:id:PaperSloth:20200615003654p:plain

すると以下のように小数点部分が0から0.9999999までカウントされていることがわかります。
こうすることでRotation Angleの0-1の範囲に収まった上で回転させることができます
f:id:PaperSloth:20200615003803g:plain

最後にMaterial Instance側で調整しやすいように「ScalarParameter」を追加し、「RotationSpeed」としておきました
f:id:PaperSloth:20200615004455p:plain

これを応用することで以下のようなクルクル回転するロックオンカーソル等が作成できます
f:id:PaperSloth:20200615001622g:plain

Unity serializeについてのまとめ

目次

環境

Unity 2019.3.13f1

概要

組み込み型、Property、struct/classの Inspectorへの公開方法をまとめた記事になります

Unityのシリアル化については下記のドキュメントに記載があります
スクリプトのシリアル化 - Unity マニュアル

C#の型情報についてのドキュメントはこちら
型 - C# プログラミング ガイド | Microsoft Docs

[SerializeFiled]について

主に変数をInspectorに公開するために使用することが多いです

public class ExampleBehaviour : MonoBehaviour
{
    // 書き方は2通りあり、改行してもしなくてもよい
    [SerializeField]
    int ExampleValue = 10;

    [SerializeField] int Value = 10;
}

f:id:PaperSloth:20200525113603p:plain

public / private を書かなくてもよいのか、publicとの違い

先ずはpublic / privateを書かなくてもよいのかについてですが
C#のclassはアクセス修飾子がない場合に明示的にprivateになります
そのため以下の2つは同じ意味を持ちます

[SerializeField]
int ExampleValue = 10;
// どちらの書き方でも同じ意味をもつ
[SerializeField]
private int Value = 10;


次にpublicとの違いについて
public の場合[SerializeField]を付けなくてもInspectorに変数を公開できます
そこまでは同じです
ですが、アクセス修飾子が異なるので他のクラスから変更できてしまいます

[SerializeField]
int ExampleValue = 10;

public int Value = 10;

このクラス単体に限っては何の問題もありません
ただし、他のクラスから変更できてしまうため以下のような問題が発生します

先ずはExampleBehaviour にアクセスするためのクラスを用意します

public class TestBehaviour : MonoBehaviour
{
    [SerializeField]
    ExampleBehaviour Example;

    void Start()
    {
        Example.Value = 100;
    }
}

f:id:PaperSloth:20200525114818p:plain


実行するとInspector上で"10"だったExampleValueが"100"に上書きされてしまいます
f:id:PaperSloth:20200525114939p:plain

この書き方の場合には意図しない値の変更が行われる可能性があり
バグを生み出す原因となり得ます
Unityに限らずアクセス修飾子には気を付けましょう

PropertyをInspectorに公開する方法

Propertyは以下のようにSerializeFieldを付けてもInspectorに公開できません

[SerializeField]
int ExampleValue { get; set; }

f:id:PaperSloth:20200525120820p:plain

解決方法として[field: ] を付与することでInspectorに公開することができます

[field: SerializeField]
int ExampleValue { get; set; }

f:id:PaperSloth:20200525120905p:plain

backing field の表記になりパッと見では分かりにくくなってしまいます
(Propertyを公開していることを意図的に表現したい場合はこのままでよいですが)

解決策として以下のForumにある「RenameFieldAttribute」のようなclassを用意することで解決できます
https://forum.unity.com/threads/c-7-3-field-serializefield-support.573988/

public class ExampleBehaviour : MonoBehaviour
{
    [field: SerializeField]
    [field: RenameField(nameof(ExampleProperty))]
    int ExampleProperty { get; set; }

    [field: SerializeField]
    [field: RenameField("RenameValue")]
    int Value { get; set; }
}

f:id:PaperSloth:20200525121344p:plain

class, structをInspectorに公開する方法

classもstructもそのままではInspectorに公開することができません

using UnityEngine;

public class ExampleBehaviour : MonoBehaviour
{
    public struct StructData
    {
        public int Value;
        public string Name;
    }
    [SerializeField]
    StructData structData;

    public class ClassData
    {
        [SerializeField]
        string Tag;
    }
    [SerializeField]
    ClassData classData;
}

f:id:PaperSloth:20200525122809p:plain

どちらの場合でもInspectorに公開する場合は[System.Serializable] を使用することでInspectorに公開できます

using UnityEngine;

public class ExampleBehaviour : MonoBehaviour
{
    [System.Serializable]
    public struct StructData
    {
        public int Value;
        public string Name;
    }
    [SerializeField]
    StructData structData;

    [System.Serializable]
    public class ClassData
    {
        [SerializeField]
        string Tag;
    }
    [SerializeField]
    ClassData classData;
}

f:id:PaperSloth:20200525123019p:plain

まとめ

ここまでに紹介してきた書き方をざざっとまとめました。

using System;
using UnityEngine;

public class ExampleBehaviour : MonoBehaviour
{
    [SerializeField]
    int ExampleValue;
    [field: SerializeField]
    int PropertyValue { get; set; }
    // ※RenameFieldの拡張用のclassを記述した場合
    [field: SerializeField]
    [field: RenameField("RenameProperty")]
    int RenameValue { get; set; }

    // struct, class どちらの場合でも[System.Serializable]で対応可能
    // ここではusing System;を定義しているためSystemを省略
    [Serializable]
    public class ClassData
    {
        public string Name;
        [SerializeField]
        string Tag;
    }
    [SerializeField]
    ClassData classData;
}

f:id:PaperSloth:20200525124828p:plain

おまけ Inspectorで上書きした値が保存される場所

Inspector上で書き換えた値はScene fileに紐付いて保存されています

例えば以下のように設定しておいた場合
f:id:PaperSloth:20200525123612p:plain

scene.unity ファイルをエディターで開いてgrepすると
以下のように保存されていることが確認できます

MonoBehaviour:
  ... (省略)
  m_EditorClassIdentifier:
  structData:
    Value: 2000
    Name: Enemy
  classData:
    Tag: Player

普段中身を見ることは少ないとは思いますが
デカいSceneを開かないでgrepして値を追いたい時など何かしら使うケースがあるかもしれません