public class SkillBase : ScriptableObject,IBehavior
{
}
public class MoveCurveXFollowAnim : SkillBase
{
[SerializeField]
public AnimationCurve curve=new AnimationCurve();
}
public class MoveCurveYFollowAnim : SkillBase
{
[SerializeField]
public AnimationCurve curve=new AnimationCurve();
}
[Serializable]
public class SaveSkillData
{
public list<SkillBase> skills
}
// to save the data
main
{
skills.add(new MoveCurveXFollowAnim() );
creatasset(skills,path);
}
can not save the asset ,cause the typemismatch ,how to fixed this?
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
public abstract class SkillBase : ScriptableObject,IBehavior{}
[CreateAssetMenu(menuName = "ScriptableObject Asset/Skills/Follow on Curve for X", fileName="default FollowX filename")]
public class MoveCurveXFollowAnim : SkillBase
{
public AnimationCurve curve=new AnimationCurve();
}
[CreateAssetMenu(menuName = "ScriptableObject Asset/Skills/Follow on Curve for Y", fileName="default FollowY filename")]
public class MoveCurveYFollowAnim : SkillBase
{
public AnimationCurve curve=new AnimationCurve();
}
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
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.
thanks u! I use CreateAssetMenu or [CreateAssetMenuAttribute(menuName = “My Name”)]attribute with using unityengine,but mono said the context is not exist…
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
[Serializable]
public class SkillBase :IBehavior
{
}
[Serializable]
public class MoveCurveXFollowAnim : SkillBase
{
[SerializeField]
public AnimationCurve curve=new AnimationCurve();
}
[Serializable]
public class MoveCurveYFollowAnim : SkillBase
{
[SerializeField]
public AnimationCurve curve=new AnimationCurve();
}
public class SaveSkillData:ScriptableObject
{
public list<SkillBase> skills=new list<SkillBase> ();
}
// to save the data
main
{
SaveSkillDatasv sv=ScriptableObject.CreateInstance<SaveSkillData>();
sv.skills.Add(newMoveCurveXFollowAnim());
creatasset(sv,path);
}
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.
CreateAssetMenuAttribute.cs: Save this anywhere in your scripts folder
using System;
//This attribute was added in Unity 5.1
#if !UNITY_5_1 && !UNITY_5_2 && !UNITY_5_3_OR_NEWER
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
public sealed class CreateAssetMenuAttribute : Attribute
{
public string menuName { get; set; }
public string fileName { get; set; }
public int order { get; set; }
}
}
#endif
CreateAssetMenuHelper.cs: Save this in an Editor folder
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;
public class CreateAssetMenuHelper:EditorWindow
{
private static GUIContent c_Winidow = new GUIContent("Create New ScriptableObject Asset");
private static CreateAssetMetaData[] SortedCreateTypes;
private Vector2 scrollPosition;
[MenuItem("Tools/Create New ScriptableObject Asset")]
public static void GenerateMenu()
{
EditorWindow.GetWindow<CreateAssetMenuHelper>(c_Winidow.text);
}
private void OnEnable()
{
LoadTypes();
}
private void OnGUI()
{
var labelStyle = GUI.skin.label;
labelStyle.alignment = TextAnchor.MiddleCenter;
labelStyle.fontStyle = FontStyle.Bold;
EditorGUILayout.LabelField(c_Winidow,labelStyle);
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
if(SortedCreateTypes.Length<1)
{
EditorGUILayout.HelpBox("There are no ScriptableObjects in the project that have the CreateAssetMenuAttribute",MessageType.Warning);
}
for(int i=0;i<SortedCreateTypes.Length;i++)
{
GUIContent c_menuName = new GUIContent(SortedCreateTypes[i].type.Name);
if(GUILayout.Button(c_menuName))
{
CreateAsset(SortedCreateTypes[i]);
}
}
EditorGUILayout.EndScrollView();
}
private void LoadTypes()
{
if(SortedCreateTypes==null)
{
Type[] AllTypes = AppDomain.CurrentDomain.GetAssemblies().SelectMany(a=>a.GetTypes()).ToArray();
Dictionary<Type,CreateAssetMetaData> CreateTypes = new Dictionary<Type, CreateAssetMetaData>();
for (int i = 0; i < AllTypes.Length; i++)
{
Type type = AllTypes[i];
if (!type.IsSubclassOf(typeof(ScriptableObject))) continue; //skip types that are not ScriptableObjects
CreateAssetMenuAttribute att = (CreateAssetMenuAttribute) Attribute.GetCustomAttribute(type,typeof(CreateAssetMenuAttribute));
if(att != null)
CreateTypes.Add(AllTypes[i],new CreateAssetMetaData(type,att));
}
SortedCreateTypes = CreateTypes.Select(kvp=>kvp.Value).OrderBy(v=>v.attribute.order).ThenBy(v=>v.type.Name).ToArray();
ExampleObject test = null;
if(test!= null) test.myString = string.Empty;
}
}
public void CreateAsset(CreateAssetMetaData data)
{
ScriptableObject asset = ScriptableObject.CreateInstance(data.type);
string fileName = data.attribute.fileName;
if(string.IsNullOrEmpty(fileName))
fileName = "New " + ObjectNames.NicifyVariableName(data.type.Name) + ".asset";
if(!Path.HasExtension(fileName))fileName += ".asset";
AssetDatabase.CreateAsset(asset,GetCurrentPath()+"/"+fileName);
}
private string GetCurrentPath()
{
string path = "Assets";
foreach (UnityEngine.Object obj in Selection.GetFiltered(typeof(UnityEngine.Object), SelectionMode.Assets))
{
path = AssetDatabase.GetAssetPath(obj);
if (File.Exists(path))
{
path = Path.GetDirectoryName(path);
}
break;
}
return path;
}
public class CreateAssetMetaData
{
public Type type;
public CreateAssetMenuAttribute attribute;
public CreateAssetMetaData(Type type, CreateAssetMenuAttribute attribute)
{
this.type = type;
this.attribute = attribute;
}
}
}
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.
using UnityEngine;
using System.Collections.Generic;
using System;
public class SkillBasex : ScriptableObject
{
}
[CreateAssetMenu(menuName = "ScriptableObject Asset/Skills/Follow on Curve for x", fileName="default Followx filename")]
public class MoveCurveXFollowAnimx : SkillBasex
{
public int k;
public AnimationCurve curve=new AnimationCurve();
}
public class SaveSkillData:ScriptableObject
{
public List<SkillBasex> skills=new List<SkillBasex>();
}
//editor
main
{
SaveSkillData sv=ScriptableObject.CreateInstance<SaveSkillData>();
MoveCurveXFollowAnimx mov=new MoveCurveXFollowAnimx();
mov.k=100;
sv.skills.Add(mov);
path="Assets/"+character.name+"Sv.asset";
AssetDatabase.CreateAsset(sv,path);
AssetDatabase.SaveAssets();
}
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?
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.