Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 12 Next capture
2021 2022 2023
1 capture
12 Jun 22 - 12 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
0
Question by alex_dossamer · Nov 15, 2018 at 06:29 AM · nullreferenceexceptionscriptableobjectnullgenerics

Why does ScriptableObject.CreateInstance, where T is definite at runtime, return null?

So I'm aware that for ScriptableObject.CreateInstance to work, 'T' can't remain generic.

I have the following code, in which 'T' is eventually made definite:

Abstract base class

 public abstract class MouthSetGenericEditor<T> : Editor
     {
         public static void CreateMouthSet(T fillerResource)
         {
             var mouthset = ScriptableObjectUtility.CreateAsset<MouthSetGeneric<T>>();
         }
     }


Child class where type is made definite

 [CustomEditor(typeof(MouthSetGeneric<Sprite>))]
     public class MouthSetSpriteEditor : MouthSetGenericEditor<Sprite>
     {
         private static readonly Sprite placeholderSprite = Resources.Load<Sprite>("Placeholder");
 
         [MenuItem("Assets/Rhubarb/Create Sprite Mouthset")]
         public static void CreateSpriteMouthSet()
         {
             CreateMouthSet(placeholderSprite);
         }
     }


Utility where ScriptableObject.CreateInstance() is called

 public static class ScriptableObjectUtility
     {
         public static T CreateAsset<T>() where T : ScriptableObject
         {
             T asset = ScriptableObject.CreateInstance<T>();
 
             if (asset == null)
                 Debug.Log("asset null");
 
             // more code...
          }
     }


In the same block where ScriptableObject.CreateInstance() is called, if I log typeof(T).ToString(), the console outputs MouthSetGeneric`1[UnityEngine.Sprite]. So it looks like type T was made definite, but the ScriptableObject instantiation call isn't behaving as though it is.

Any idea what could be going on? Thanks!

Comment
Add comment
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

1 Reply

· Add your reply
  • Sort: 
avatar image
2

Answer by Bunny83 · Nov 15, 2018 at 01:29 PM

Ok first of all you should know that Unity can not serialize generic types, at all. They made an explicit / hardcoded exception for the generic List class but any other generic class can not be serialized. Therefore you can not have a generic MonoBehaviour or ScriptableObject instance. However what you can do is create a derived (non generic) class from your generic class for each type parameter you want to use and use those classes instead.


The second thing that seems a bit strange is that you call your method "CreateAsset" which you want to call at runtime. Assets in the Unity word means serialized objects in the project. Assets can only be created in the editor and not at runtime. However instances of objects or even of assets can be made at runtime (That's why the method is called CreateInstance).


It's not clear what you want to do here. If you want to create a clone / copy of an asset, just use Instantiate. You can instantiate scriptable object instances / assets just like any other object derived from UnityEngine.Object.


Finally while it is true that the generic type parameters are always bound at runtime, in normal C# the type parameters is actually fix at compile time based on the usage. C# doesn't have a special variable type for type parameters. Types are given in code. In your example you have defined a concrete class of your abstract editor class which you called "MouthSetSpriteEditor" and the generic type parameter is filled with the type Sprite.


While it is possible to actually bind the type parameter dynamically using reflection in order to generate a new type, however this is generally not the way to go and isn't supported at AOT platforms like iOS since it requires JIT compiling as you would create a new unknown type at runtime.


Finally keep in mind that when you use a generic class with two different types as T, those two generated concrete classes do not have anything in common in the sense of OOP. A List<int> and a List<string> are two seperate classes which happens to be created by the same generic class List<T> but are completely seperate class types which can't interact in any way. It's like the difference between public class A {} and public class B {}. Even though .NET generics work completely different from C++ templates, the basic idea is the same. The generic class is not an abstraction in the OOP sense but more a generic template for different implementations.


Keep in mind we only see a fraction of the whole picture of your case. We don't know how this code is supposed to work and what the end result should look like. If you want to create some sort of factory, I'm afraight this isn't going to work since as i said at the top Unity doesn't support the serialization of generic types at all. At least up to this point in time ^^.

Comment
Add comment · Show 1 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image alex_dossamer · Nov 15, 2018 at 04:43 PM 0
Share

Thanks so much for all the info!

I should clarify none of the code is intended to be used at runtime--it's all for editor workflow.

Additionally, ScriptableObjectUtility is a way to create ScriptableObject asset files in the project without having to use [CreateAsset$$anonymous$$enu()]--it comes from this article on the unify community. The reason I'm using it is because I can't specify a [CreateAsset$$anonymous$$enu] attribute for the generated concrete classes that come from the generic ScriptableObject $$anonymous$$outhSetGeneric<T>.

$$anonymous$$y use case:

$$anonymous$$outhSetGeneric<T> contains a `SerializableDictionary` with an explicitly typed set of keys, but a generic set of values. Why?

I have a given set of 'mouth shapes'. Those shapes map to a resource that visually represents that shape, for example of type Texture, Sprite, or $$anonymous$$esh.

The idea behind having a concretely-typed Editor class is that it can pass its given type to a newly created $$anonymous$$outhSetGeneric<T>, so that the resource type its dictionary maps to can be specified by the user (e.g. as Sprite).

Here's the code for $$anonymous$$outhSetGeneric<T>:

 public class $$anonymous$$outhSetGeneric<T> : ScriptableObject {
 
         public SerializableDictionary<$$anonymous$$outhShape, T> shapeDictionary;
 
 }



And here's the full code for the $$anonymous$$outhSetGenericEditor<T> function, Create$$anonymous$$outhSet():

 public static void Create$$anonymous$$outhSet(T fillerResource)
         {
             var mouthset = ScriptableObjectUtility.CreateAsset<$$anonymous$$outhSetGeneric<T>>();
 
             mouthset.shapeDictionary = new SerializableDictionary<$$anonymous$$outhShape, T>()
             {
                 {$$anonymous$$outhShape.A, fillerResource},
                 {$$anonymous$$outhShape.B, fillerResource},
                 {$$anonymous$$outhShape.C, fillerResource},
                 {$$anonymous$$outhShape.D, fillerResource},
                 {$$anonymous$$outhShape.E, fillerResource},
                 {$$anonymous$$outhShape.F, fillerResource},
                 {$$anonymous$$outhShape.G, fillerResource},
                 {$$anonymous$$outhShape.H, fillerResource},
                 {$$anonymous$$outhShape.X, fillerResource},
                 {$$anonymous$$outhShape.Error, fillerResource}
             };
         }



The desired outcome for all this is to be able to create a $$anonymous$$outhSetGeneric asset in the project where both types in the asset's dictionary are specified, with the potential for the user to extend the types of resources that can be specified by creating new Editor classes. (I'm planning to create classes that support passing sprites and textures, but other users might want to pass something different, like a blendshape.)

Does that make any sense? Thanks so much for your time!!

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

99 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

Getting a reference to a group of ScriptableObjects implemented at runtime? 0 Answers

PlayFab CloudScript NullReferenceException 0 Answers

Scriptable Object is getting set to null 0 Answers

Null Reference Exception For Blocks C# 1 Answer

Do I need to explicitly test for null or is testing the object itself good enough? 2 Answers


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges