Search Unity

[solved]how to creatasset for inherient ScriptableObject .

Discussion in 'Scripting' started by dreamerflyer, Feb 8, 2017.

  1. dreamerflyer

    dreamerflyer

    Joined:
    Jun 11, 2011
    Posts:
    927
    serial.jpg
    Code (CSharp):
    1. public class SkillBase : ScriptableObject,IBehavior
    2. {
    3. }
    4. public class MoveCurveXFollowAnim : SkillBase
    5. {
    6.     [SerializeField]
    7.     public AnimationCurve curve=new AnimationCurve();
    8. }
    9. public class MoveCurveYFollowAnim : SkillBase
    10. {
    11.     [SerializeField]
    12.     public AnimationCurve curve=new AnimationCurve();
    13. }
    14.  
    15. [Serializable]
    16. public class SaveSkillData
    17. {
    18. public   list<SkillBase> skills
    19. }
    20.  
    21.  
    22. // to save the data
    23. main
    24. {
    25.     skills.add(new MoveCurveXFollowAnim() );
    26.   creatasset(skills,path);
    27. }
    28.  
    29.  
    can not save the asset ,cause the typemismatch ,how to fixed this?
     
  2. JoshuaMcKenzie

    JoshuaMcKenzie

    Joined:
    Jun 20, 2015
    Posts:
    916
    first off if SkillsBase is just a marker class I would highly recommend that you make it abstract because the class itself shouldn't have any instances.

    secondly if you want the editor to be able to create the scriptableObjects in the project simply mark the classes with a [CreateAssetMenu] attribute
    Code (CSharp):
    1. public abstract class SkillBase : ScriptableObject,IBehavior{}
    2.  
    3. [CreateAssetMenu(menuName = "ScriptableObject Asset/Skills/Follow on Curve for X", fileName="default FollowX filename")]
    4. public class MoveCurveXFollowAnim : SkillBase
    5. {
    6.     public AnimationCurve curve=new AnimationCurve();
    7. }
    8. [CreateAssetMenu(menuName = "ScriptableObject Asset/Skills/Follow on Curve for Y", fileName="default FollowY filename")]
    9. public class MoveCurveYFollowAnim : SkillBase
    10. {
    11.     public AnimationCurve curve=new AnimationCurve();
    12. }
    then you simply go the Assets>Create>ScriptableObject Asset>Skills and it will make an asset instance in the selected project folder

    If you want to create the Skill through code you can use the following example
    Code (CSharp):
    1.  
    2.         ScriptableObject asset = ScriptableObject.CreateInstance<MoveCurveXFollowAnim>();
    3.  
    4.         string path = "/My Data Folder";
    5.         UnityEditor.AssetDatabase.CreateAsset(asset,path);
    While you can create instance at runtime, and reference them just fine you can only save them as Assets in the Editor, meaning you can't create Assets this way while in a build (you can still create the instance). this is because you need the AssetDatabase class to save it as an asset which is not available during a build.

    TypeMismatch typically shows in the editor when you try to drop a Scene-instance into a Asset-instance, In relation to assets Scene-instances aren't safe references because Asset instance can't reliably serialize/de-serialize the data. it might load what ever Scene GUID that was saved and then Unity can't find that GUID (cause it no longer exists) hence the "Type Mismatch" that it blurts out. When you use CreateInstance<>() you are effectively making a Scene-instance of the ScriptableObject, while AssetDatabase.createAsset() will turn that scene-instance into an asset-instance via generating a metafile to supply the GUID reference.

    if you simply want to avoid that TypeMismatch issue, using [CreateAssetMenu] is more than enough to help with that.
     
  3. dreamerflyer

    dreamerflyer

    Joined:
    Jun 11, 2011
    Posts:
    927
    thanks u! I use CreateAssetMenu or [CreateAssetMenuAttribute(menuName = "My Name")]attribute with using unityengine,but mono said the context is not exist...
     
  4. dreamerflyer

    dreamerflyer

    Joined:
    Jun 11, 2011
    Posts:
    927
    thank u! but meet this error.. m.jpg
     
  5. JoshuaMcKenzie

    JoshuaMcKenzie

    Joined:
    Jun 20, 2015
    Posts:
    916
    What Version of unity are you using? [CreateAssteMenu] was added in version 5.1. If you are using an earlier version then you would have to manually make an Editor Script for this
     
  6. dreamerflyer

    dreamerflyer

    Joined:
    Jun 11, 2011
    Posts:
    927
    use unity4.6.1。how to make the editor script, any hint?
     
  7. dreamerflyer

    dreamerflyer

    Joined:
    Jun 11, 2011
    Posts:
    927
    Code (CSharp):
    1. [Serializable]
    2. public class SkillBase :IBehavior
    3. {
    4. }
    5. [Serializable]
    6. public class MoveCurveXFollowAnim : SkillBase
    7. {
    8.     [SerializeField]
    9.     public AnimationCurve curve=new AnimationCurve();
    10. }
    11. [Serializable]
    12. public class MoveCurveYFollowAnim : SkillBase
    13. {
    14.     [SerializeField]
    15.     public AnimationCurve curve=new AnimationCurve();
    16. }
    17.  
    18. public class SaveSkillData:ScriptableObject
    19. {
    20. public   list<SkillBase> skills=new  list<SkillBase> ();
    21. }
    22. // to save the data
    23. main
    24. {
    25.    SaveSkillDatasv sv=ScriptableObject.CreateInstance<SaveSkillData>();
    26. sv.skills.Add(newMoveCurveXFollowAnim());
    27.   creatasset(sv,path);
    28. }
    29.  
    mac.jpg
    try this ,can remove the typemismatch, but can not see the data in mac !any help?
     
  8. JoshuaMcKenzie

    JoshuaMcKenzie

    Joined:
    Jun 20, 2015
    Posts:
    916
    I can't tell exactly whats going on in your case there but I assume that its not drawing a field anymore because you're getting an error in the editor scripting (causing the inspector to break the instant it tries to draw the field).

    Also I'm not sure if you are aware of this. but every ScriptableObject class (and every Monobehaviour class for that matter) must be saved into a separate file of the same name. If you don't do this unity can't generate a proper meta file for it and thus Unity won't find the class.

    Also also, that main{...} construct should not be used. run your code inside the class itself, inside a MonoBehaviour, inside a ScriptableObject, or in an Editor Script class.

    Anyway back to the solution at hand. I wrote a few scripts to allow you to create your objects quickly. Although I can't write the behavior to be exactly like it is in 5.1, I can provide an Editor window that will come pretty close.

    Code (CSharp):
    1. using System;
    2.  
    3. //This attribute was added in Unity 5.1
    4. #if !UNITY_5_1 && !UNITY_5_2 && !UNITY_5_3_OR_NEWER
    5.     [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
    6.     public sealed class CreateAssetMenuAttribute : Attribute
    7.     {
    8.         public string menuName { get; set; }
    9.         public string fileName { get; set; }
    10.         public int order       { get; set; }
    11.     }
    12. }
    13. #endif

    Code (CSharp):
    1.  
    2. using System;
    3. using System.Collections.Generic;
    4. using System.IO;
    5. using System.Linq;
    6. using UnityEditor;
    7. using UnityEngine;
    8.  
    9. public class CreateAssetMenuHelper:EditorWindow
    10. {
    11.     private static GUIContent c_Winidow = new GUIContent("Create New ScriptableObject Asset");
    12.     private static CreateAssetMetaData[] SortedCreateTypes;
    13.     private Vector2 scrollPosition;
    14.  
    15.     [MenuItem("Tools/Create New ScriptableObject Asset")]
    16.     public static void GenerateMenu()
    17.     {
    18.         EditorWindow.GetWindow<CreateAssetMenuHelper>(c_Winidow.text);
    19.     }
    20.  
    21.     private void OnEnable()
    22.     {
    23.         LoadTypes();
    24.     }
    25.  
    26.     private void OnGUI()
    27.     {
    28.         var labelStyle = GUI.skin.label;
    29.         labelStyle.alignment = TextAnchor.MiddleCenter;
    30.         labelStyle.fontStyle = FontStyle.Bold;
    31.  
    32.         EditorGUILayout.LabelField(c_Winidow,labelStyle);
    33.         scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
    34.  
    35.         if(SortedCreateTypes.Length<1)
    36.         {
    37.             EditorGUILayout.HelpBox("There are no ScriptableObjects in the project that have the CreateAssetMenuAttribute",MessageType.Warning);
    38.         }
    39.  
    40.         for(int i=0;i<SortedCreateTypes.Length;i++)
    41.         {
    42.             GUIContent c_menuName = new GUIContent(SortedCreateTypes[i].type.Name);
    43.             if(GUILayout.Button(c_menuName))
    44.             {
    45.                 CreateAsset(SortedCreateTypes[i]);
    46.             }
    47.         }
    48.         EditorGUILayout.EndScrollView();
    49.     }
    50.  
    51.     private void LoadTypes()
    52.     {
    53.         if(SortedCreateTypes==null)
    54.         {
    55.             Type[] AllTypes = AppDomain.CurrentDomain.GetAssemblies().SelectMany(a=>a.GetTypes()).ToArray();
    56.             Dictionary<Type,CreateAssetMetaData> CreateTypes = new Dictionary<Type, CreateAssetMetaData>();
    57.             for (int i = 0; i < AllTypes.Length; i++)
    58.             {
    59.                 Type type = AllTypes[i];
    60.                 if (!type.IsSubclassOf(typeof(ScriptableObject))) continue; //skip types that are not ScriptableObjects
    61.  
    62.                 CreateAssetMenuAttribute att = (CreateAssetMenuAttribute) Attribute.GetCustomAttribute(type,typeof(CreateAssetMenuAttribute));
    63.                 if(att != null)
    64.                     CreateTypes.Add(AllTypes[i],new CreateAssetMetaData(type,att));
    65.             }
    66.             SortedCreateTypes = CreateTypes.Select(kvp=>kvp.Value).OrderBy(v=>v.attribute.order).ThenBy(v=>v.type.Name).ToArray();
    67.             ExampleObject test = null;
    68.             if(test!= null) test.myString = string.Empty;
    69.         }
    70.     }
    71.  
    72.     public void CreateAsset(CreateAssetMetaData data)
    73.     {
    74.         ScriptableObject asset = ScriptableObject.CreateInstance(data.type);
    75.         string fileName = data.attribute.fileName;
    76.         if(string.IsNullOrEmpty(fileName))
    77.             fileName = "New " + ObjectNames.NicifyVariableName(data.type.Name) + ".asset";
    78.  
    79.         if(!Path.HasExtension(fileName))fileName += ".asset";
    80.  
    81.         AssetDatabase.CreateAsset(asset,GetCurrentPath()+"/"+fileName);
    82.     }
    83.  
    84.     private string GetCurrentPath()
    85.     {
    86.         string path = "Assets";
    87.         foreach (UnityEngine.Object obj in Selection.GetFiltered(typeof(UnityEngine.Object), SelectionMode.Assets))
    88.         {
    89.             path = AssetDatabase.GetAssetPath(obj);
    90.             if (File.Exists(path))
    91.             {
    92.                 path = Path.GetDirectoryName(path);
    93.             }
    94.             break;
    95.         }
    96.         return path;
    97.     }
    98.  
    99.     public class CreateAssetMetaData
    100.     {
    101.         public Type type;
    102.         public CreateAssetMenuAttribute attribute;
    103.  
    104.         public CreateAssetMetaData(Type type, CreateAssetMenuAttribute attribute)
    105.         {
    106.             this.type = type;
    107.             this.attribute = attribute;
    108.         }
    109.     }
    110.      
    111. }
    112.  


    To create a new instance a new menu item will appear at the top in "Tools>Create New ScriptableObject Asset". select that and a window will open listing all the types of ScriptableObjects in your project that has the [CreateAssetMenu] attribute, as buttons. clicking on a button will create a new file in the current selected folder in the project window under the default filename provided by the Attribute. Be warned that it will overwrite any file in that folder that is already using that name (something I was going to fix if I had time) so be sure to name it properly before creating a copy.
     
  9. dreamerflyer

    dreamerflyer

    Joined:
    Jun 11, 2011
    Posts:
    927
    hi~,I should say clear the problem.The problem is :saving the childclass as baseclass,i want to show the chilclass data,how to do that.
     
  10. dreamerflyer

    dreamerflyer

    Joined:
    Jun 11, 2011
    Posts:
    927
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections.Generic;
    3. using System;
    4.  
    5. public class SkillBasex : ScriptableObject
    6. {
    7. }
    8.  
    9. [CreateAssetMenu(menuName = "ScriptableObject Asset/Skills/Follow on Curve for x", fileName="default Followx filename")]
    10. public class MoveCurveXFollowAnimx : SkillBasex
    11. {
    12.     public int k;
    13.     public AnimationCurve curve=new AnimationCurve();
    14. }
    15.  
    16.  
    17. public class SaveSkillData:ScriptableObject
    18. {
    19.     public  List<SkillBasex> skills=new List<SkillBasex>();
    20. }
    21. //editor
    22. main
    23. {
    24. SaveSkillData sv=ScriptableObject.CreateInstance<SaveSkillData>();
    25.                 MoveCurveXFollowAnimx mov=new MoveCurveXFollowAnimx();
    26.                 mov.k=100;
    27.                 sv.skills.Add(mov);
    28.  
    29.                 path="Assets/"+character.name+"Sv.asset";
    30.                 AssetDatabase.CreateAsset(sv,path);
    31.                 AssetDatabase.SaveAssets();
    32. }
    I try use the attribute to save the childcalss data ,the type still not matching ,but double click can see the childclass data,and play the game will miss this data.any way to get? mis.jpg mistypedata.jpg
     
  11. dreamerflyer

    dreamerflyer

    Joined:
    Jun 11, 2011
    Posts:
    927
    ok, this is not oop ,so i should try other method to acheive this
     
  12. dreamerflyer

    dreamerflyer

    Joined:
    Jun 11, 2011
    Posts:
    927
    no found solution,any help?
     
  13. dreamerflyer

    dreamerflyer

    Joined:
    Jun 11, 2011
    Posts:
    927
    ok,i found other method to save this data.
     
  14. JoshuaMcKenzie

    JoshuaMcKenzie

    Joined:
    Jun 20, 2015
    Posts:
    916
    Sorry for not replying sooner, Not always on the forums. Actually surprised no one else chimed in in my absence.

    Unity's Inspector doesn't handle Abstraction and Polymorphic classes so well out of the box without a severe amount of custom editor scripting (usually involving reflection). A lot of it boils down to how data is serialized into the YAML files (the format unity saves its data for prefabs, levels, pretty much everything). scene-instance data is serialized directly into the level file. So if a monobehaviour is holding a derived instance of a serialized class, but the field is typed as a base class, then unity file will only pre-allocate space for data in the base class, i.e. a base class has two public ints while a derived class has two public strings. the data type listed in the scene is the base class so unity will only serialize the two ints (it never assumes the possibility of the derived version being stored)

    you can however still use polymorphism with scriptable objects if you keep the instance data out of scene and simply have the scene reference it. so you would have a Monobehaviour have a base class reference that allows you to drop a derived class scripatable object into it. Now the YAML file doesn't serialize the data for the base/derived class itself just a reference to where its stored. so instead of the YAML trying to serialize just 2 ints, instead it serializes a GUID. Not only will this allow you to load in your skills polymorphically but now the levels have less data that it needs to load, which does improve load times.

    what this means editor wise is that you would have to go directly to the asset thats holding the data to edit it. You won't be able to make your edits to the derived skill directly in the monobehaviour which is using the skill. not without dipping your hand into some editor scripting. its not impossible to do but its does take quite some work.