- Home /
The question is answered, right answer was accepted
ScriptableObject.CreateInstance(Type T) keep crashing
Hey, I stuck with this for a good moment now, somewhere in my code, I have this :
if (!_instance.ContainsKey(T) || _instance[T] == null)
{
#if UNITY_EDITOR
_instance[T] = (DataHolder)ScriptableObject.CreateInstance(T);
Debug.Log(T.Name);
return null;
/*if (!Directory.Exists(Application.dataPath + "/DataHolders"))
Directory.CreateDirectory(Application.dataPath + "/DataHolders");*/
AssetDatabase.CreateAsset(_instance[T], "Assets/DataHolders/" + _instance[T].pathToBase + ".asset");
AssetDatabase.SaveAssets();
#else
t = LoadLast(T);
#endif
}
But it crash everytime on the ScriptableObject.CreateInstance(T). T is a class extending DataHolder. _instance is a Dictionary pathToBase is a simple string.
I've already use this code with another class, and it worked, so i don't understand. I used the debug class around those lines to see where it stuck, all work until this, and i don't get why.
Any help would be very pleasant.
thanks, FireCube
EDIT : there's the DataHolder class :
//Code written by Etienne Desrousseaux, alias FireCube, for the updater package.
//
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;
using System.IO;
using Crypting;
#if UNITY_EDITOR
using UnityEditor;
#endif
public class DataHolder : ScriptableObject
{
public static List<DataHolder> dataBases = new List<DataHolder>();
public const string pathToBases = "Data";
public string pathToBase
{
get
{
return GetType().ToString();
}
}
public static Dictionary<Type, DataHolder> _instance = new Dictionary<Type, DataHolder>();
public static T getInstance<T>() where T : DataHolder
{
T t = null;
if (_instance.ContainsKey(typeof(T)))
t = (T)_instance[typeof(T)];
if (t == null)
{
#if UNITY_EDITOR
t = CreateInstance<T>();
AssetDatabase.CreateAsset(t, "Assets/DataHolders/" + t.pathToBase + ".asset");
AssetDatabase.SaveAssets();
#else
t = LoadLast(typeof(T));
#endif
}
_instance[typeof(T)] = t;
return t;
}
public static DataHolder GetInstance(Type T)
{
if (!_instance.ContainsKey(T) || _instance[T] == null)
{
#if UNITY_EDITOR
_instance[T] = (DataHolder)ScriptableObject.CreateInstance(T);
Debug.Log(T.Name);
return null;
/*if (!Directory.Exists(Application.dataPath + "/DataHolders"))
Directory.CreateDirectory(Application.dataPath + "/DataHolders");*/
AssetDatabase.CreateAsset(_instance[T], "Assets/DataHolders/" + _instance[T].pathToBase + ".asset");
AssetDatabase.SaveAssets();
#else
t = LoadLast(T);
#endif
}
return _instance[T];
}
#if UNITY_EDITOR
public void open()
{
Selection.activeObject = this;
}
#endif
/// <summary>
/// Load the last data holder of this type that have already been downloaded
/// </summary>
/// <param name="T">the type, must extend DataHolder class</param>
/// <returns>the data holder loaded from last </returns>
public static DataHolder LoadLast(Type T)
{
DataHolder dh = null;
string path = Application.dataPath + "/" + pathToBases + "/" + ((DataHolder)CreateInstance(T)).pathToBase;
if (File.Exists(path))
{
AssetBundle ab = AssetBundle.LoadFromMemory(
File.ReadAllBytes(path)
.Decrypt(
SimpleConfig.getLine("pass", "UpdaterData")));
dh = (DataHolder)ab.LoadAsset(T.Name, T);
}
return dh;
}
/// <summary>
/// Download and save to disk a data holder, debug parameter exist for UNITY_EDITOR only
/// </summary>
/// <param name="T">the type for which we want to download the base</param>
/// <param name="onDone">the function to be executed when the download is finished</param>
/// <param name="onError">the function to be executed if an error happen durring the download, the saving to disk or when loading the base</param>
/// <param name="onUpdate">the function to be executed every <paramref name="updateRate"/>, sending the progress</param>
/// <param name="updateRate">the rate at which <paramref name="onUpdate"/> is called</param>
public static IEnumerable DownloadDataHolder(Type T, Action<DataHolder> onDone = null, Action<string> onError = null, Action<float> onUpdate = null, float updateRate = 0.05f
#if UNITY_EDITOR
, bool debug = false//reduce size on build
#endif
)
{
bool update = onUpdate != null;
string url = SimpleConfig.getLine("website", "UpdaterData") + "/" + T.Name + ".dat";
WWW www = new WWW(url);
#if UNITY_EDITOR
if (debug)
Debug.Log("Start downloading " + url + "\nCorresponding Data Holder is " + T.FullName);
#endif
if (update)
{
while (!www.isDone)
{
yield return new WaitForSeconds(updateRate);
Action<float> temp = onUpdate;
if (temp != null)
{
temp(www.progress / 3);
}
}
}
else
yield return www;
if (!string.IsNullOrEmpty(www.error))
{
Debug.LogError("Error while downloading data holder : " + www.error + "\ncalling error function");
Action<string> temp = onError;
if (temp != null)
temp("Error while downloading data holder : " + www.error);
}
else
{
#if UNITY_EDITOR
if (debug)
Debug.Log("Done downloading " + url + "\n" + www.bytesDownloaded + " bytes downloaded");
#endif
byte[] data = www.bytes;
www.Dispose();
byte[] decrypted = data.Decrypt(
SimpleConfig.getLine("pass", "UpdaterData"));
string path = Application.dataPath + "/" + pathToBases + "/" + ((DataHolder)CreateInstance(T)).pathToBase;
bool error = false;
try
{
File.WriteAllBytes(path, data);
}
catch
{
Debug.LogError("Error while writting base to file, calling error function");
error = true;
Action<string> temp = onError;
if (temp != null)
temp("Error while writting base to file");
}
if (!error)
{
#if UNITY_EDITOR
if (debug)
Debug.Log("Done saving " + T.FullName + " to " + path);
#endif
var bundle = AssetBundle.LoadFromMemoryAsync(decrypted);
if (update)
{
while (!bundle.isDone)
{
yield return new WaitForSeconds(updateRate);
Action<float> temp = onUpdate;
if (temp != null)
{
temp((bundle.progress + 1) / 3);
}
}
}
else
yield return bundle;
var dh = bundle.assetBundle.LoadAssetAsync(T.Name, T);
if (update)
{
while (!dh.isDone)
{
yield return new WaitForSeconds(updateRate);
Action<float> temp = onUpdate;
if (temp != null)
{
temp((dh.progress + 2) / 3);
}
}
}
else
yield return dh;
Action<DataHolder> temp2 = onDone;
if (temp2 != null)
{
temp2((DataHolder)dh.asset);
}
#if UNITY_EDITOR
if (debug)
Debug.Log("Done with data holder " + T.FullName);
#endif
}
}
}
}
and the class that call everything is there :
//Code written by Etienne Desrousseaux, alias FireCube, for the updater package.
//
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using UnityEditor;
using UnityEngine;
public class DataHolderWindow : EditorWindow
{
List<Type> types;
void OnGUI()
{
if (types == null)
types = new List<Type>(FindDerivedTypes(typeof(DataHolder).Assembly, typeof(DataHolder)));
foreach (var T in types)
{
if (GUILayout.Button(Regex.Replace(T.ToString(), "([A-Z])", " $0")))
{
open(T);
}
}
}
void open(Type T)
{
MethodInfo m = T.BaseType.GetMethod("GetInstance");
object j = m.Invoke(null, new object[] { T });
//T.GetMethod("open").Invoke(j, null);
}
public IEnumerable<Type> FindDerivedTypes(Assembly assembly, Type baseType)
{
return assembly.GetTypes().Where(t => t != baseType &&
baseType.IsAssignableFrom(t));
}
[MenuItem("Updater/Data Holder List")]
static void Init()
{
DataHolderWindow window = GetWindow<DataHolderWindow>();
window.titleContent.text = "Data Bases List";
window.Show();
}
}
The Debug are still in there
Thx
NEW EDIT :
I found the problem, but first, here's my Demo class, that is the one that should open and create :
//Auto generated resource data holder the 7/3/2016
//Using the Updater package
using UnityEngine;
public class Demo : DataHolder
{
#region fields
public static new string name
{
get { return instance._name; }
set { instance._name = value; }
}
public string _name;
public static GameObject prefab
{
get { return instance._prefab; }
set { instance._prefab = value; }
}
public GameObject _prefab;
public static float speed
{
get { return instance._speed; }
set { instance._speed = value; }
}
public float _speed;
#endregion
public static Demo instance
{
get { return getInstance<Demo>(); }
}
[InspectorCallable]
public void Reset()
{
name = "";
prefab = null;
speed = 5f;
}
}
The problem is, indeed, the function Reset. Unity freeze when it's not commented, but all is good if it is commented, and i can't explain myself why...
Answer by Bunny83 · Jul 03, 2016 at 11:11 AM
There are a few things that are required for CreateInstance to work are:
Your base class (DataHolder) has to be derived from ScriptableObject
Your actual concrete class has to be placed in a file that matches the classname.
both of those points are missing in your question, so we can't really tell what you did wrong.
If everything is right then there must be something wrong inside your concrete class. However your class is missing as well so again we can't help you.
ps: You should be more concrete what kind of "crash" you experienced. Does the editor just freeze, do you receive a crash report or does it just close without any message?
If the editor freezes there could be an infinite loop somewhere inside your class.
ok, sorry if I wasn't precise enough, the DataHolder class is derived directly from scriptable object (only), and the crash type is just a freeze, with no more responses from the program. The file name is DataHolder.cs, and the class is DataHolder
I add the whole code now
Answer by Fire_Cube · Jul 04, 2016 at 08:13 AM
Ok, look's like I was just unlucky, this only happen with Reset, or probably some special functions...