3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Unity #3Advent Calendar 2019

Day 17

ScriptableObjectは1ファイル1クラス

Last updated at Posted at 2019-12-16

#世にも奇妙な

先日ScriptableObjectを使用していたところ奇妙な現象が起こりました。

こういうスクリプトを書きました。

SO.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SO : ScriptableObject
{
}

[CreateAssetMenu(fileName = "SOInherit", menuName = "CreateSO")]
public class SOInherit : SO
{
	public string val;
}

ScriptableObjectのベースクラスとそれを継承したクラスを一つのファイルに書いたわけです。
これをProjectビュー上で右クリックから作成しました。
SO1.png
そして普通に動作することを確認した後、Unityエディタを再起動しました。
すると、
SO2.png
このようなAssertが表示されScriptableObjectとしての動作もしなくなっていたのです!
何もしてないのに壊れた...

#解決法
タイトルまんまなんですがScriptableObjectは1つのファイルに1つのクラスだけを書くようにします。

#原因
なんで1ファイルに複数クラスを書いてはいけないのでしょうか?
ファイル分割後のScriptableObjectファイルの中身を見てみましょう。

SOInherit.asset
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInstance: {fileID: 0}
  m_PrefabAsset: {fileID: 0}
  m_GameObject: {fileID: 0}
  m_Enabled: 1
  m_EditorHideFlags: 0
  m_Script: {fileID: 11500000, guid: d740773b00796084096feb7e6b5b3618, type: 3}
  m_Name: SOInherit
  m_EditorClassIdentifier: 
  val: "\u5024\u3092\u3053\u3053\u306B\u5165\u308C\u308B\u3002"

注目すべきはm_Scriptのguidです。

続いてSOInherit.csのmetaファイルを見てみます。

SOInherit.cs.meta
fileFormatVersion: 2
guid: d740773b00796084096feb7e6b5b3618
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData: 
  assetBundleName: 
  assetBundleVariant: 

guidの値を見てみるとSOInherit.assetのm_Scriptに保持されているguidと一致しています。
Unityはguidという一意なidをそれぞれのアセットに割り振っており、このidを利用してアセットの参照を実現しています。
ScriptableObjectはスクリプトファイルのguidを保持することで自身がそのクラスのインスタンスであることを表現しているわけです。
ですがこの時一つのファイルに複数のクラスが存在していたらどうでしょう?guidはアセット毎、つまりファイル毎に割り振られるわけですからScriptableObjectがスクリプトファイルのguidを保持していても、どのクラスのインスタンスかは分からないのです。
ScriptableObjectを作成してから再起動するまでは正常に動作していましたがScriptプロパティを見てみるとNoneになっていることからやはり参照は上手く機能していないことが分かります。

#まとめ
言いたいことはタイトルが全てです。MonoBehaviourが1クラス1ファイルなのは有名ですがScriptableObjectはあまり知られていない(と思う)上に再起動するまで不具合に気づけない分厄介です。お気をつけあれ。

#追記
1つのファイルに複数のクラスを記述した場合でもファイル名と同じ名前のScriptableObjectクラスが存在する場合、そのクラスのインスタンスのアセットファイルは問題なく読み込めるようです。
UnityがScriptableObjectを読み込む際はまずguidでどのスクリプトファイルにクラスが記述されているかを調べ、そのスクリプトファイルと同名のクラスのインスタンスであるものとして読み込まれるようです。
なので1つのスクリプトファイルに1つのクラスしか記述していなくてもファイル名とクラス名が異なる場合にはScriptableObjectを上手く読み込むことができませんのでご注意を。

いずれにせよ1つのファイルに複数クラスを記述するとかファイル名とクラス名を別にするとかトリッキーなことはしてはいけませんよということです。

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?