はじめに
Unityで開発をしていると、こんな経験はありませんか?
- SerializeFieldを付けたのに、Inspectorに表示されない
- 表示はされているのに、再生を止めると値が戻る
- Dictionaryを使ったら、なぜか保存できなかった
多くの場合、これらはコードが間違っているわけではありません。
原因は「Unityのシリアライズの仕組みを、なんとなくで使っている」ことにあります。
Unityでは、[SerializeField] や [Serializable] を使えばデータを扱えるようになりますが、 その裏側には Unity独自のルールと制限 が存在します。
このルールを知らないまま開発を進めると、
- なぜInspectorに出ないのか分からない
- ScriptableObjectとJSONの違いを説明できない
- セーブ機能を作る段階で設計が崩れる
といった「後からハマる問題」に直面しがちです。
この記事では、単なるAPIの使い方ではなく、
- Unityのシリアライズとはそもそも何なのか
- なぜ保存されるもの・されないものがあるのか
- セーブ/ロード設計とどうつながっているのか
をデータ管理の基礎知識として整理していきます。
「SerializeFieldは使っているけど、正直よく分かっていない」 「ScriptableObjectとJSONの違いを説明できない」
そんな状態から一歩抜け出したい方に向けて、 Unityシリアライズの全体像を、理由とセットで解説していきます。
結論:Unityのシリアライズで最も重要な考え方
先に結論からお伝えします。
Unityのシリアライズは「データを保存するための便利機能」ではありません。
正確には、Unityエンジンがオブジェクトを安全に扱うための、制約付きの仕組みです。
この前提を理解しているかどうかで、次のような差が生まれます。
- Inspectorに表示されない理由が自分で判断できる
- ScriptableObject・JSON・PlayerPrefsを迷わず使い分けられる
- セーブ機能を後付けしても設計が壊れにくい
逆に、この前提を知らないまま進むと、
- 「とりあえずSerializeFieldを付ける」
- 「publicにすれば表示されるからOK」
- 「保存できないのはUnityのバグっぽい」
といった場当たり的な対応が増えてしまいます。
Unityのシリアライズには、はっきりした特徴があります。
- Unityが理解できる型しか扱えない
- C#の標準シリアライズとは別物
- Inspector表示とセーブ用途はイコールではない
つまり重要なのは、 「どう書くか」ではなく「Unityはなぜそう振る舞うのか」を理解することです。
このあとからは、
- Unity独自シリアライズの正体
- Inspector表示の本当の条件
- データ構造ごとの制限と理由
を順番に解きほぐしながら、 最終的にセーブ設計の判断軸までつなげていきます。

まずは、Unityのシリアライズが「C#標準と何が違うのか」から見ていきましょう。
Unityのシリアライズとは何か?C#標準との決定的な違い
まず押さえておきたいのは、UnityのシリアライズはC#(.NET)標準の仕組みとは別物だという点です。
C#には本来、オブジェクトを保存・復元するための仕組みが用意されています。 しかし、Unityはそれらをほぼ使用していません。
代わりにUnityは、エンジン内部で独自に実装したシリアライズシステムを使っています。
この違いを理解していないと、
- 「C#的には正しいのに、Unityでは保存されない」
- 「クラスとしては成立しているのに、Inspectorに出ない」
といった現象に強い違和感を覚えることになります。
C#のシリアライズとUnityのシリアライズは目的が違う
C#標準のシリアライズは、主にデータをそのまま保存・転送することが目的です。
一方、Unityのシリアライズは目的が少し違います。
- Inspectorで安全に値を編集できる
- シーン・アセットの状態をエディタ上で保持できる
- スクリプト再コンパイル後も状態を復元できる
つまりUnityにとってシリアライズとは、 エディタとランタイムを安定させるための基盤機構です。
アセンブリリロード時に何が起きているのか
Unityでは、スクリプトを保存するとアセンブリがリロードされます。
このときUnityは、
- 対象オブジェクトの状態を一度シリアライズ
- スクリプトを再読み込み
- シリアライズされた情報からオブジェクトを再構築
という処理を内部で行っています。
ここで重要なのが、 Unityが「理解できる型・構造」しか復元できないという点です。
Unityが理解できない型は、
- Inspectorに表示されない
- 値が保持されない
- 保存対象から静かに除外される
という挙動になります。
「Unityが理解できる型」という前提
Unityのシリアライズは、かなり保守的です。
基本的には、
- プリミティブ型(int, float, bool, string など)
- UnityEngine.Object を継承した型
- Unityが許可した構造(配列・List など)
に限定して扱われます。
これが、後ほど解説する
- Dictionaryが使えない理由
- Serializableを付けても保存されないケース
につながっていきます。
なお、Serializable と ScriptableObject の役割整理については、 以下の記事でデータ管理視点から詳しく解説しています。

次の章では、 この「Unityが理解できる/できない」という前提を踏まえて、 Inspectorに表示される・されない本当の条件を整理していきます。
Inspectorに表示される/されないの本当の条件
Unityでシリアライズを理解するうえで、 多くの人が最初につまずくのがInspector表示のルールです。
「publicなら表示される」 「SerializeFieldを付ければOK」
ここまでは知っていても、なぜそうなるのかまで説明できる人は多くありません。
まずは、Inspectorに表示・シリアライズされる基本条件を整理しましょう。
Inspectorに表示・シリアライズされる条件
- public フィールド
[SerializeField]が付いた private フィールド
この2つは、Unityが「この値は管理対象だ」と判断する合図になります。
ただし、これだけでは不十分です。
実際には、
- フィールドのアクセス修飾子
- フィールドの型
- Unityが対応している構造かどうか
これらがすべて揃って、初めてInspectorに表示されます。
private + SerializeField は「表示許可」を出しているだけ
[SerializeField] はよく誤解されがちですが、 「保存を保証する魔法の属性」ではありません。
あくまで、
「本来は見えないprivateフィールドを、Unityに見せていいですよ」
と伝えているだけです。
そのため、
- Unityが理解できない型
- シリアライズ非対応の構造
であれば、SerializeFieldを付けても表示されません。
表示されない代表的なケース
Inspectorに出ないときは、次のどれかに当てはまることがほとんどです。
- static フィールド(インスタンスに紐づかない)
- Dictionary 型(Unity標準では非対応)
- Serializableが付いていないカスタムクラス
- Unityがサポートしていないジェネリック構造
特に static は見落とされがちですが、 Unityのシリアライズは「インスタンスの状態保存」が前提です。
そのため、クラス全体で共有される static 変数は、 そもそも対象外になります。
UnityEngine.Object は特別扱いされる
Unityでは、
- GameObject
- Transform
- AudioSource
- 自作の MonoBehaviour / ScriptableObject
といった UnityEngine.Object を継承した型が、 参照として特別扱いされます。
これは値を丸ごと保存しているのではなく、 Unity内部IDによる参照を保持しているためです。
この仕組みのおかげで、
- シーン上のオブジェクト参照
- アセットへの参照
をInspectorから安全に設定できるようになっています。
Inspectorに表示される = セーブされる、ではない
とても重要なポイントなので、強調しておきます。
Inspectorに表示されることと、ゲームのセーブ対象になることは別です。
Inspector表示は、あくまで
- エディタ上で値を編集できる
- アセンブリリロード時に状態を復元できる
という意味であり、 ビルド後の実行データが自動で保存されるわけではありません。
この勘違いが、後半で解説する ScriptableObjectやセーブ設計の混乱につながります。

次は、カスタムクラスを扱う際に必須となる Serializable属性の正しい役割を見ていきましょう。
Serializable属性の正しい役割と限界
Inspectorに表示されない原因として、 もう一つ非常によく出てくるのが [Serializable] の付け忘れです。
ただし、Serializableも万能ではありません。 まずは、この属性が「何をしているのか」を正しく理解しましょう。
Serializableは「このクラスは分解していい」という合図
[Serializable] は、 クラスや構造体の中身を、Unityが中まで見てよいかどうかを示す属性です。
Unityは、フィールドにカスタムクラスが指定されている場合、
- その中身を展開してInspectorに表示してよいか
- シリアライズ対象として分解できるか
を [Serializable] の有無で判断しています。
つまり、
「このクラスはUnityが理解できる形に分解できます」
という許可証のような役割です。
Serializableが必要になる典型パターン
次のようなケースでは、必ず [Serializable] が必要になります。
- MonoBehaviourではないカスタムクラスをフィールドとして持つ
- List や 配列の要素としてカスタムクラスを使う
- ScriptableObjectの中で独自データ構造を持つ
Serializableが付いていない場合、 Unityはそのクラスをブラックボックスとして扱い、
- Inspectorに表示しない
- シリアライズ対象に含めない
という挙動になります。
Serializableを付けても保存されないケース
ここで注意点があります。
Serializableを付けたからといって、必ず保存されるわけではありません。
次のような場合は、Serializableが付いていても対象外になります。
- クラス内にUnity非対応の型が含まれている
- Dictionaryをフィールドに持っている
- プロパティ(get / set)のみで、フィールドが存在しない
特にプロパティは見落とされがちですが、 Unityのシリアライズはフィールドのみを対象とします。
そのため、
public int Value { get; private set; }
のような定義は、Serializableが付いていても保存されません。
クラスと構造体での違い
Serializableは、クラスだけでなく構造体にも使用できます。
ただし挙動には違いがあります。
- クラス:参照型。値の共有・参照切れに注意
- 構造体:値型。Inspector上でコピーが発生しやすい
特に構造体は、
- 値を書き換えたつもりが反映されていない
- List内の要素変更が保存されない
といったトラブルの原因になりやすいため、 Serializableだから安全、とは考えないことが大切です。

次は、Serializableを使ったうえで 配列・List・クラス・構造体がどこまで対応しているのかを整理していきます。
配列・List・クラス・構造体はどこまで対応している?
Unityのシリアライズは、 「使えるデータ構造」と「使えないデータ構造」がかなり明確に分かれています。
ここを曖昧なまま進めると、
- 最初は動いていたのに、後から保存されなくなる
- 構造を拡張した瞬間にInspectorから消える
といった事故が起きやすくなります。
配列とListは問題なく使える
Unityのシリアライズでは、
- 配列(
T[]) List<T>
は標準でサポートされています。
要素の型がUnity対応であれば、
- Inspectorでサイズ変更
- 要素の編集
も問題なく行えます。
これは、Unityが内部的に 「順序付きのデータ構造」として扱いやすいからです。
カスタムクラスを要素に使う場合の注意点
配列やListの要素として、 自作クラスを使うこともできます。
ただし、その場合は
- クラスに
[Serializable]が付いていること - 中身がUnity対応型のみで構成されていること
が前提になります。
1つでも非対応の型が混ざると、 そのクラス全体がシリアライズ対象から外されます。
構造体(struct)は使えるが注意が必要
構造体もSerializableを付ければシリアライズ可能です。
ただし構造体は値型であるため、
- Inspector上でコピーが発生する
- List内の要素を書き換えても反映されない
といった混乱が起きやすくなります。
データ定義として使う場合は、
- 基本的にクラスを使う
- 構造体は不変データ向けに限定する
という考え方がおすすめです。
JsonUtilityを使う場合の落とし穴
JSONに変換する際にも、Unityのシリアライズ規則が適用されます。
特に重要なのが、
配列やListをそのままルートにできない
という制約です。
JsonUtilityでは、
- 必ずクラスや構造体をルートにする
- その中に配列・Listを持たせる
必要があります。
この制約を知らないと、
- JSON化できない
- エラーが出ず、空データになる
といったハマり方をします。
配列・List・Dictionaryの設計指針については、 以下の記事で使い分け視点から詳しく整理しています。

次は、多くの人が一度は疑問に思う 「なぜDictionaryは使えないのか」を解説します。
なぜDictionaryはシリアライズできないのか?
Unityでデータ管理をしていると、 多くの人が一度はこう思います。
「Dictionaryが使えたら楽なのに、なぜ使えないの?」
これはUnityの仕様によるもので、 バグでも制限ミスでもありません。
Dictionaryが非対応な構造的理由
Unityのシリアライズは、
- 順序が保証されている
- Inspector上で編集できる
- 差分を安全に管理できる
といった前提で設計されています。
一方、Dictionaryは
- 順序を持たない
- キーの重複や変更が複雑
- Inspectorでの安全な編集が難しい
という特徴があります。
このためUnityは、 標準シリアライザーではDictionaryを扱わないという選択をしています。
Serializableを付けても使えない理由
Dictionaryがシリアライズされないのは、
- Serializableが足りない
- 書き方が間違っている
からではありません。
Unityのシリアライズ対象外として、明示的に除外されているためです。
そのため、
- フィールドとして定義してもInspectorに出ない
- JsonUtilityでJSON化しても中身が消える
といった挙動になります。
実務でよく使われる代替パターン
それでもキーと値のペアで管理したい場面は多いですよね。
その場合、よく使われるのが次の方法です。
- キーと値を持つSerializableクラスを作る
- それをListとして管理する
例としては、
- ID と 数値
- 文字列キー と 設定値
といった組み合わせです。
この方法であれば、
- Inspectorで編集できる
- Unity標準シリアライズに乗せられる
というメリットがあります。
どうしてもDictionaryを保存したい場合
どうしてもDictionaryをそのまま使いたい場合は、
- JSONに変換して保存する
- 外部のシリアライズツールを使う
といった選択肢になります。
ただしこの場合も、 Unity標準シリアライズとは別レイヤーで扱う という意識が重要です。

次は、ここまでの知識を踏まえて、 ScriptableObject・JSON・PlayerPrefsの役割分担を整理していきましょう。
ScriptableObject・JSON・PlayerPrefsの役割分担
ここまでで、Unityのシリアライズが
- 「何でも保存できる仕組みではない」
- 「Unityが扱いやすい形に制限されている」
ということが見えてきたと思います。
この理解がないまま次に迷いやすいのが、 「結局どの方法でデータを管理すればいいのか」という点です。
ここでは、
- ScriptableObject
- JSON
- PlayerPrefs
を用途ベースで整理します。
大前提:どれを使うかではなく「何を管理するか」
よくある失敗は、
「この方法が便利そうだから、全部これでやろう」
という考え方です。
Unityでは、データの種類によって
- エディタ用データ
- 実行時に変化するデータ
- ゲーム終了後も残したいデータ
を分けて考える必要があります。
ScriptableObject:エディタ用データ管理の主役
ScriptableObjectは、
- マスターデータ
- 設定値
- バランス調整用の数値
といった「エディタで管理したいデータ」に最適です。
Inspectorで編集でき、
- 複数のオブジェクトから参照できる
- データを一元管理できる
という強みがあります。
ただし重要なのは、
ScriptableObjectはセーブデータではない
という点です。
ビルド後の実行環境では、
- 基本的に読み取り専用
- 実行中に変更しても次回起動時には戻る
という挙動になります。
ScriptableObjectを使った設計思想については、 以下の記事でデータ駆動設計の視点から詳しく解説しています。
JSON:実行時データを保存するための形式
JSONは、
- プレイヤーの進行状況
- 所持アイテム
- 設定値の保存
といった実行時に変化するデータを保存するための手段です。
Unityでは主に JsonUtility を使いますが、
- Unityシリアライズの制約を受ける
- Dictionaryが使えない
といった特徴があります。
そのためJSONは、
- 「何でも突っ込む箱」
- 「Unityシリアライズの逃げ道」
ではなく、 設計されたデータ構造を保存する手段として使うのがポイントです。
PlayerPrefs:小さく、単純なデータ専用
PlayerPrefsは、
- 音量設定
- 画面設定
- 簡単なフラグ
といった少量・単純なデータ向けです。
便利ではありますが、
- 暗号化されていない
- 構造化データには不向き
という制限もあります。
そのため、
PlayerPrefsだけでセーブシステムを作ろうとしない
ことが重要です。

次は、ここまで整理した内容を踏まえて、 セーブ/ロード設計とUnityシリアライズの関係を見ていきましょう。
セーブ/ロード設計とUnityシリアライズの関係
ここまで理解してくると、 Unityのシリアライズとセーブ/ロードは似ているようで別物だと気づくはずです。
この違いを曖昧にしたまま設計すると、
- エディタでは問題ないのに、ビルド後にデータが残らない
- 後からセーブを追加しようとして、構造を作り直す
といった事態に陥りやすくなります。
「Inspectorで見える」と「保存される」は完全に別
何度も強調しますが、 Inspectorに表示されること=セーブされることではありません。
Unityのシリアライズが保証しているのは、
- エディタ上での値保持
- アセンブリリロード時の状態復元
までです。
ゲームを終了してもデータを残したい場合は、
- JSONとしてファイルに書き出す
- PlayerPrefsに保存する
といった明示的な保存処理が必要になります。
Editor用データと実行時データを分けて考える
設計で重要なのは、
「これは誰が、いつ編集するデータなのか」
をはっきりさせることです。
- 開発者がエディタで調整する → ScriptableObject
- プレイヤーが遊んで変化する → セーブデータ(JSONなど)
この役割分担を最初に決めておくと、
- どこまでシリアライズに任せるか
- どこから自前で保存するか
の判断が非常に楽になります。
JSON保存で意識すべきポイント
JSONを使う場合でも、 Unityのシリアライズ規則は無視できません。
- Serializableが付いているか
- Unity非対応型が含まれていないか
- 配列やListをルートにしていないか
これらを守らないと、
- エラーが出ないままデータが欠落する
- 保存できていると思い込む
といった、非常に厄介なバグにつながります。
JSONを使ったセーブ設計については、 以下の記事で実践レベルまで踏み込んで解説しています。

次は、 Unity標準だけでは限界を感じたときの選択肢について見ていきましょう。
標準だけで限界を感じたら:Easy Saveという選択肢
ここまで読んで、
- Unityのシリアライズには明確な制限がある
- 設計を間違えると後から修正が大変
ということが見えてきたと思います。
一方で、実務や個人開発では
- Dictionaryをそのまま保存したい
- データ構造がどんどん複雑になる
- バージョン違いのセーブデータも扱いたい
といった要求が出てくるのも事実です。
こうした場面で選択肢になるのが、 Unity標準に依存しすぎないセーブ用ツールです。
その代表例が Easy Save です。
Easy Save – The Complete Save Game & Data Serializer System
✅アセットストアでチェックする
Easy Saveは「理解を省略するツール」ではない
ここで大切なのは、 Easy Saveを魔法の解決策として使わないことです。
Unityのシリアライズを理解していないまま導入すると、
- なぜ保存できているのか分からない
- トラブル時に原因が追えない
という別の問題を抱えます。
本来の使い方は、
- Unity標準の制限を理解したうえで
- その制限を超える必要が出たときに
- 明確な理由を持って導入する
という位置づけです。
Easy Saveが向いているケース
具体的には、次のような場合に効果を発揮します。
- Dictionaryや複雑なクラス構造をそのまま保存したい
- セーブデータの暗号化・圧縮を行いたい
- アップデートを前提にしたセーブ管理をしたい
逆に、
- 単純な設定値
- 小規模なプロトタイプ
であれば、JSONやPlayerPrefsで十分なケースも多いです。
重要なのは、
「標準で足りないから使う」のではなく
「設計上、必要だから使う」
という判断基準を持つことです。
よくある誤解・ハマりポイントまとめ
Unityのシリアライズは、 仕組みを知らないと「動いているように見える」のが一番厄介な点です。
ここでは、特に初心者〜中級者がハマりやすい誤解を整理します。
SerializeFieldを付ければ安心だと思ってしまう
[SerializeField] は便利ですが、 保存やセーブを保証するものではありません。
Unityが理解できない型や構造であれば、
- Inspectorに表示されない
- 値が保持されない
という挙動になります。
「とりあえず付ける」ではなく、 なぜ必要なのかを意識することが大切です。
ScriptableObjectをセーブデータとして使おうとする
ScriptableObjectは非常に便利ですが、 セーブ用途とは役割が違います。
エディタ上では変更が残るため、
- これで保存できているように見える
のが落とし穴です。
ビルド後の実行環境では、
- 基本的に読み取り専用
- 実行時の変更は破棄される
という挙動になる点を忘れないようにしましょう。
Dictionary問題を後回しにする
最初はListでなんとかなっていても、
- キー検索が増える
- データ構造が肥大化する
と、後からDictionaryが欲しくなります。
このとき、
「後で考えよう」
としてしまうと、 セーブ設計と衝突して大きな修正が必要になることがあります。
最初の段階で、
- Listで表現するのか
- 外部ツールを使うのか
を決めておくと安心です。
Inspector表示=セーブ完了だと勘違いする
これも非常に多い誤解です。
Inspectorで値が残っているのは、
- Unityエディタが状態を保持しているだけ
であり、 ゲームのセーブとは別物です。
「いつ」「どこに」「どう保存するのか」を 明示的に設計する必要があります。
まとめ
今回は、Unityのシリアライズについて 「なんとなく使う」状態から抜け出すために、仕組みと理由を中心に整理してきました。
大事なポイントを振り返ります。
- Unityのシリアライズは、C#標準とは別のUnity独自仕様
- Inspectorに表示される条件と、セーブされる条件はまったく別
[SerializeField]や[Serializable]は許可を出しているだけ- 配列やListは使えるが、Dictionaryは標準では非対応
- ScriptableObject・JSON・PlayerPrefsは用途で使い分ける
特に重要なのは、
「Unityがなぜそう振る舞うのか」を理解したうえで設計すること
です。
この視点があるだけで、
- Inspectorに出ない理由を自分で切り分けられる
- 後からセーブ機能を追加しても壊れにくい
- 外部ツールを導入する判断もブレなくなる
といったメリットがあります。
私自身、最初は
「SerializeFieldを付けたのに、なんで動かないの?」 「ScriptableObjectって保存されるんじゃないの?」
と何度もハマってきました。
でも、Unityのシリアライズを 「データ管理・セーブ設計の基礎知識」として捉え直してから、 設計で迷うことがかなり減りました。
この記事が、
- シリアライズ周りのモヤっと感を解消したい人
- これからセーブ・ロード設計に踏み込みたい人
の土台になれば嬉しいです。
参考文献・公式ドキュメント
- Variables and the Inspector(Unity公式マニュアル)
- SerializeField(Unity Scripting API)
- ScriptableObject(Unity公式マニュアル)
- JsonUtility(Unity Scripting API)
- PlayerPrefs(Unity Scripting API)
- Serialization in .NET(Microsoft Learn)
- Introduction to ScriptableObjects(Unity Learn)
- Serialization(Unity公式マニュアル)
- Why does Unity not save changes to ScriptableObjects at runtime?(GameDev StackExchange)
- Serialization in Unity(Unity公式ブログ)
- UnityYAML(Unity公式マニュアル)
- What is Serialization in Unity?(GameDev Beginner)
よくある質問(FAQ)
- Qpublic と SerializeField、結局どちらを使うべき?
- A
結論から言うと、 基本は private + SerializeField を使うのがおすすめです。
public フィールドは手軽ですが、
- どこからでも変更できてしまう
- 意図しない書き換えが起きやすい
というデメリットがあります。
SerializeFieldを使えば、
- Inspectorからは編集できる
- コード上の責務は守れる
というバランスの取れた状態を作れます。
「外部から変更されるべきか?」を基準に考えると、 判断しやすくなります。
- QScriptableObjectは結局、セーブデータに使えるの?
- A
基本的には使えません。
ScriptableObjectは、
- エディタで管理するデータ
- 複数オブジェクトから参照される設定値
のための仕組みです。
エディタ上では変更が残るため勘違いしやすいですが、 ビルド後の実行環境では読み取り専用と考えるのが安全です。
プレイヤーの進行状況などは、 JSONや専用のセーブ手法で明示的に保存しましょう。
- QDictionaryをどうしても保存したい場合はどうする?
- A
Unity標準のシリアライズでは、 Dictionaryはそのまま保存できません。
対応策としては、
- キーと値のペアを持つクラスを作り、Listで管理する
- JSONに変換して自前で保存する
- Easy Saveなどの外部ツールを使う
といった選択肢があります。
大切なのは、
「なぜDictionaryが必要なのか」を先に考えること
です。
データ構造の選択と、 セーブ方法はセットで考えるようにしましょう。











※当サイトはアフィリエイト広告を利用しています。リンクを経由して商品を購入された場合、当サイトに報酬が発生することがあります。
※本記事に記載しているAmazon商品情報(価格、在庫状況、割引、配送条件など)は、執筆時点のAmazon.co.jp上の情報に基づいています。
最新の価格・在庫・配送条件などの詳細は、Amazonの商品ページをご確認ください。