Search Unity

Advice on asset management

Discussion in 'Scripting' started by DryerLint, Jun 15, 2019.

  1. DryerLint

    DryerLint

    Joined:
    Feb 7, 2016
    Posts:
    68
    I have a custom asset format that contains some custom binary data (i.e., arrays), as well as some conventional data (e.g., textures and a mesh). I would like to create a class (perhaps a ScriptableObject) that holds everything in one place. I am using a ScriptedImporter to read binary files and convert them to my custom asset object.

    Code (CSharp):
    1. public class MyCustomObject : ScriptableObject
    2. {
    3.     public Mesh MeshData;
    4.     public Texture2D TextureData;
    5.     public int[] NumberData;
    6. }
    The latter code works for NumberData, but produces a null reference error for MeshData or TextureData. I create MyCustomObject inside my custom ScriptedImporter in the following way:

    Code (CSharp):
    1. public override void OnImportAsset(AssetImportContext ctx)
    2. {
    3.     MyCustomObject customObject = new MyCustomObject();
    4.    
    5.     customObject.MeshData = ReadMeshData(ctx.assetPath);
    6.     customObject.TextureData = ReadTextureData(ctx.assetPath);
    7.     customObject.NumberData = ReadNumberData(ctx.assetPath);
    8.  
    9.     ctx.AddObjectToAsset("My Custom Object", customObject);
    10. }
    I have noticed that if I explicitly add customObject.MeshData and customObject.TextureData to the context using AddObjectToAsset, the null reference error is corrected. However, it seems redundant to have to do that.

    Should I even use a ScriptableObject here?

    Thanks for your attention to this.
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,749
    ScriptableObjects seem like a reasonable solution so I'm not sure what's going wrong.

    One possible thing to look into is the order of importation. I think Unity does not guarantee any order, so if you create the Mesh and Texture and ScriptableObject at the same "time," as in Unity sees them all for the first time at once, perhaps it tries to import the ScriptableObject before the others are ready??
     
    DryerLint likes this.
  3. DryerLint

    DryerLint

    Joined:
    Feb 7, 2016
    Posts:
    68
    Thanks Kurt! Good question. If that is the case, I am not entirely sure how to test for it. However, I assume that I should at least be able to create a new empty object and assign it to the ScriptableObject's field. I tried this with a Texture2D and I still had the same problem.

    I actually realized I don't get a NullReferenceException. I get an UnassignedReferenceException when I try to access the TextureData (or MeshData) field in the ScriptableObject:

    UnassignedReferenceException: The variable TextureData of CustomObject has not been assigned.
    You probably need to assign the TextureData variable of the CustomObject script in the inspector.


    I also noticed that the Mesh and Texture2D fields are empty in the ScriptableObject's Inspector view.
     
  4. palex-nx

    palex-nx

    Joined:
    Jul 23, 2018
    Posts:
    1,748
    https://docs.unity3d.com/Manual/ScriptedImporters.html
    I think this is what you're looking for. You don't need a scriptable object to hold all things since standard unity imported asset can have multiple objects within. Look at FBX for example. It is a large blob and after importing to unity there are animation, materials, textures, 3d models and other stuff just inside sigle asset file. Scripted importer is right tool to create such thing.
     
    DryerLint likes this.
  5. DryerLint

    DryerLint

    Joined:
    Feb 7, 2016
    Posts:
    68
    Thanks Palex. Very good point. I suppose my problem is a little nitpicky, so I'll try to explain it more accurately.

    I realize that ScriptedImporter will create a "blob" of different principal data types inside one asset file (e.g., a Mesh and Texture for a textured character). My problem is that I have a few custom data types (not representative of the quintessential asset types like Mesh, Texture, Animation, etc.) that must be stored in a ScriptableObject. Since I want things to be tidy and centralized, I'd rather just lump everything inside that one ScriptableObject, rather than have an asset that expands into a list that consists of a Mesh, Texture, and a ScriptableObject for all the remaining misfit custom data. I hope that clarifies things a little.

    Last night I figured out a solution with the help of searching some old threads. I add the Mesh and Texture to the context using AddObjectToAsset(), as I already do with the ScriptableObject. However, since all I really want is the ScriptableObject, I hide the other chunks from view using HideInHierarchy.

    Code (CSharp):
    1. public override void OnImportAsset(AssetImportContext ctx)
    2. {
    3.     MyCustomObject customObject = ScriptableObject.CreateInstance<MyCustomObject>(); // I made a mistake in the first post. This is actually the correct way to instantiate a new ScriptableObject.
    4.  
    5.     customObject.MeshData = ReadMeshData(ctx.assetPath); // MeshData is a Mesh
    6.     customObject.TextureData = ReadTextureData(ctx.assetPath); // TextureData is a Texture2D
    7.     customObject.NumberData = ReadNumberData(ctx.assetPath);
    8.  
    9.     customObject.MeshData.hideFlags = HideFlags.HideInHierarchy; // Hide MeshData and TextureData from view in the hierarchy so the asset simply appears as a single file, rather than an asset that expands into a list of components
    10.     customObject.TextureData.hideFlags = HideFlags.HideInHierarchy;
    11.  
    12.     ctx.AddObjectToAsset("My Custom Object", customObject);
    13.     ctx.AddObjectToAsset("My Custom Object's Texture Data", customObject.TextureData); // Seems redundant, right? Why does this need to be added to the context if it's already in customObject?
    14.     ctx.AddObjectToAsset("My Custom Object's Mesh Data", customObject.MeshData);
    15. }