- Home /
Force reimport ScriptableObject
Hello!
I am trying to change C# Script of ScriptableObject
and then fill newly created fields on an instance of that ScriptableObject
. Earlier I've been trying to use Reflections
or SerializedProperty
to fill these fields on object but the new fields were null, so I've changed my attempt to manualy fill the .asset file using YAML, it's slightly better. Does not cause errors (how would it?) but in my ScriptableObject
the newly added fields have null values until i restart Unity. I've tried multiple different attempts, including using attaching to assembliesCompiled
event, using Assets Postprocessor
, but each time I got the same result. Does anyone know how should I solve it? I suppose I need to force Unity to unload ScriptableObject
and parse it from YAML again, but I have not found a way to do that.
My code looks like that (this version is stripped):
void DoYourJob()
{
string filePath = Application.dataPath + "/Scripts/ExampleScriptableObject.cs";
FileStream fileStream;
fileStream = File.Open( filePath, FileMode.OpenOrCreate );
fileStream.SetLength( 0 );
fileStream.Flush();
StreamWriter streamWriter = new StreamWriter(fileStream);
streamWriter.WriteLine( "using UnityEngine;" );
streamWriter.WriteLine( "" );
streamWriter.WriteLine( "public class ExampleScriptableObject : ScriptableObject {" );
//Writing other fields here
[...]
//Creating an example field
streamWriter.WriteLine( "public ScriptableObject anObject;" );
streamWriter.WriteLine( "}" );
streamWriter.Dispose();
fileStream.Dispose();
AssetDatabase.ImportAsset( filePath, ImportAssetOptions.ForceUpdate );
string path = AssetDatabase.GUIDToAssetPath(AssetDatabase.FindAssets("t:ExampleScriptableObject")[0]);
fileStream = File.Open( path, FileMode.Create );
fileStream.SetLength( 0 );
fileStream.Flush();
streamWriter = new StreamWriter( fileStream );
//Writing YAML header and other fields here
[...]
//Setting any asset (the guid is correct) to newly created example field
streamWriter.WriteLine( " anObject: {fileID: 11400000, guid: " + someScriptableObjectGuid + ", type: 2}");
streamWriter.Dispose();
fileStream.Dispose();
AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceUpdate);
}
Answer by andrew-lukasik · Jun 06, 2021 at 10:55 AM
#if UNITY_EDITOR
using System.IO;
using System.Linq;
using UnityEngine;
using UnityEditor;
using NaughtyAttributes;
public class CodeGen : MonoBehaviour
{
[SerializeField] Object _anObject = null;
void Start () => DoYourJob();
[Button("Do Your Job")]
void DoYourJob ()
{
string className = "ExampleScriptableObject";
string scriptFileAbsPath = Path.Combine( Application.dataPath , $"Scripts/{className}.cs" );
using( var stream = File.Open( scriptFileAbsPath , FileMode.OpenOrCreate ) )
{
stream.SetLength(0);
stream.Flush();
using( var writer = new StreamWriter(stream) )
{
writer.WriteLine( "using UnityEngine;" );
writer.WriteLine( "" );
writer.WriteLine( $"[CreateAssetMenu( menuName=\"{this.GetType().Name}/{className}\" , order=0 )]" );
writer.WriteLine( $"public class {className} : ScriptableObject {{" );
// Writing other fields here
{
}
// Creating an example field
writer.WriteLine( $"\tpublic Object {nameof(_anObject)};" );
writer.WriteLine( "}" );
}
}
string scriptFileAssetPath = $"Assets/Scripts/{className}.cs";
AssetDatabase.ImportAsset( scriptFileAssetPath , ImportAssetOptions.ForceUpdate );
string scriptFileGuid = AssetDatabase.AssetPathToGUID( scriptFileAssetPath );
string objectAssetGuid = AssetDatabase.FindAssets( $"t:{className}").FirstOrDefault();
string assetGuid = AssetDatabase.FindAssets( $"t:{className}").FirstOrDefault();
string assetPath = AssetDatabase.GUIDToAssetPath( objectAssetGuid );
if( string.IsNullOrEmpty(assetPath) )
{
Debug.Log($"asset instance t:{className} not found, create one first");
return;
}
using( var stream = File.Open(assetPath,FileMode.Create) )
{
stream.SetLength(0);
stream.Flush();
using( var writer = new StreamWriter( stream ) )
{
// Writing YAML header and other fields here
{
writer.WriteLine("%YAML 1.1");
writer.WriteLine("%TAG !u! tag:unity3d.com,2011:");
writer.WriteLine("--- !u!114 &11400000");
writer.WriteLine("MonoBehaviour:");
writer.WriteLine(" m_ObjectHideFlags: 0");
writer.WriteLine(" m_CorrespondingSourceObject: {fileID: 0}");
writer.WriteLine(" m_PrefabInstance: {fileID: 0}");
writer.WriteLine(" m_PrefabAsset: {fileID: 0}");
writer.WriteLine(" m_GameObject: {fileID: 0}");
writer.WriteLine(" m_Enabled: 1");
writer.WriteLine(" m_EditorHideFlags: 0");
writer.WriteLine($" m_Script: {{fileID: 11500000, guid: {scriptFileGuid}, type: 3}}");
writer.WriteLine(" m_Name: New Example Scriptable Object");
writer.WriteLine(" m_EditorClassIdentifier:");
}
// Setting any asset (the guid is correct) to newly created example field
if( AssetDatabase.TryGetGUIDAndLocalFileIdentifier( _anObject , out string targetGuid , out long targetLocalId ) )
writer.WriteLine( $" {nameof(_anObject)}: {{fileID: {targetLocalId}, guid: {targetGuid}, type: 2}}");
}
}
AssetDatabase.ImportAsset( assetPath , ImportAssetOptions.ForceUpdate );
}
}
#endif
For NaughtyAttributes install
https://github.com/dbrizov/NaughtyAttributes.git#upm
package
Yes, the code looks like that, as I've mentioned I've stripped it to post only the important part in 30 lines instead of 80, the problem is that the Asset representation in Unity does not refresh after I run the script. If I try getting these values in code they are null aswell, but after restart when they get parsed from YAML they have correct, updated values.
m_EditorVersion: 2021.1.10f1
Idk, looks ok to me
Your answer
Follow this Question
Related Questions
Linking a file outside the Asset folder to update an asset on reimport 0 Answers
Editor window doesn't open 2 Answers
How to change editor highlighting behavior? 0 Answers
Sprite-Lit-Default back to Sprite-Default 1 Answer
How do I code my own custom built blend tree node as seen in the Animation Controller Editor Window? 1 Answer