Saving a list of ScriptableObject into json - c#

My Item script
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(fileName ="Newitems",menuName ="Inventory/Items")]
[Serializable]
public class Item : ScriptableObject
{
new public string name = "New items";
//public Sprite Icon = null; dealt with this later
public bool isDefaulet = false;
...}
And here is my inventory script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using System;
[Serializable]
public class Inventory : MonoBehaviour
{
[SerializeField]
public List<Item> items = new List<Item>();
void Save()
{
string json = JsonUtility.ToJson(items);
Debug.Log(json);
File.WriteAllText(Application.dataPath + "/saveFile.json", json);
}
public void Load()
{
string json = File.ReadAllText(Application.dataPath + "/saveFile.json");
List<Item> List = JsonUtility.FromJson<List<Item>>(json);
Debug.Log(List);
}
...
}
Ok, i will try with the sprite later, but when saving, the console return {}, i have no idea now, please help.

The first things to save your scriptable in json is to add the class attribute Serializable on each scriptable you want to serialize in json.
But here I think it will be hard to serialize the Icon field. In a json you really can save only number or string and there is a reference.
A way to serialize a Sprite ( or any reference) is to serialize the name or the id of the object. If you look how unity serialize a scene it’s exactly how it’s done.
And then when you read your json you replace that id by your asset.
Some json library can help on that, NewtonSoft Json.Net can help, specially on the special serialization for reference I describe.
Try it without the Sprite first and add complexity after ;)
EDIT
To be more precise, I think the better way to do what your try, is to not serialize a ScriptableObject, but instead a class hold by the scriptable.
class MyScriptable : ScriptableObject
{
private MySerializableClass data;
}
[Serializable]
class MySerializableClass
{
private string name;
private float data1;
private float data2;
}
Then serialize the MySerializableClass, and use the name, or some id field to link each instance of scriptable with the good MySerializableClass :)

Related

Serialize a custom class in a custom inspector in Unity

I need to serialize a class in a custom inspector (using visually the Editor) like as doing in a Monobehaviour script like this:
[System.Serializable]
public class CustomClass
{
int myInt
}
public class OtherClass : MonoBehaviour
{
[SerializeField] CustomClass customClass;
}
which gives this result:
result wanted and given using the code above, where DamageEffect = CustomClass and Damage = myInt
In my custom editor, I'd like something like this:
[CustomEditor(typeof(CardObject))]
class AnotherClassEditor : Editor
{
public override void OnInspectorGUI()
{
[SerializeField] CustomClass customclass;
}
}
but, as expected, it points out an error.
I also tried with EditorGUILayout.ObjectField() but I haven't been able to, I'm not so experienced so please try to keep the answers simple.
Actually, I need this serialization to happen only when an enum is equal to a certain value, the overall script is something like this:
using System.Collections.Generic;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
[CreateAssetMenu(fileName = "Card", menuName = "CardObject")]
public class CardObject : ScriptableObject
{
public List<CardEffectType> effectsTypes;
//other...
[HideInInspector] public List<CardEffect> effects;
//other...
}
#if UNITY_EDITOR
[CustomEditor(typeof(CardObject))]
class CardObjectEditor : Editor
{
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
CardObject cardObject = (CardObject)target;
foreach(CardEffectType effectType in cardObject.effectsTypes)
{
switch (effectType)
{
case CardEffectType.DamageEffect:
{
//!!!
}
}
}
}
}
#endif
public enum CardEffectType
{
DamageEffect, //other...
}
I found some workarounds but the result is not as when a class is serialized in a Monobehaviour.
When you just want to show something like how Unity would, you can use EditorGUILayout.PropertyField(), but it asks for a SerializedProperty, what you need to get from the SerializedObject, not from the actual target. Something like this:
[CustomEditor(typeof(MyType))]
public class MyTypeEditor : Editor
{
private SerializedProperty _variableIWantToShow;
private void OnEnable()
{
_variableIWantToShow = serializedObject.FindProperty("<name-of-the-variable>");
}
public override void OnInspectorGUI()
{
// ...
if (ShowVariable) EditorGUILayout.PropertyField(_variableIWantToShow);
// ...
}
}
You can manage collections (array, list, etc.) as SerializedProperty, but it adds some complexity.
https://docs.unity3d.com/ScriptReference/SerializedProperty.html
https://answers.unity.com/questions/682932/using-generic-list-with-serializedproperty-inspect.html
Just make your int myInt public, or add [SerializeField] attribute to it - inspector is only designed to work with serialisable fields (public fields are serialised by default in unity editor), currently your myInt is private hence only visible from within
As mentioned in an answer by h4i, proper way to display objects in the editor is using SerializedProperty, casting 'target' seems like a good idea at start but is only useful if you want to call methods on the object, it will fail in several other cases.
What you may want to consider is declaring a PropertyDrawer while Editors service a single monobehaviour type, a PropertyDrawer handles displaying every instance of a serializable class, and will be used every time a default editor uses a PropertyField internally. This will probably match your use case better

Can a Scriptable Object class be inside a namespace in Unity3D?

I'm restructuring my code and created an namespace with the intent to put all the scriptable object classes inside it. (I'm attaching the code and screenshot below for the sake of organization.)
When I go back to Unity, it seems fine. It also displays the option to create in the menu.
But when I check the object, it seems to have no script attached to it.
Some people have shown that Unity is capable of having more than one like in this post from Unity's Forum.
The guy even lists the same behavior as mine in the Case #4. But I can't understand why my code isn't behaving like his Case #2. I'm thinking that's caused by the Attribute, but I don't know how to display the option to create the asset.. Can someone help me?
edit: I did try using a Editor class as the post but got the same result.
namespace BBMM.UI.ScriptableObjects
{
[CreateAssetMenu(fileName = "New Size Preset", menuName = "BBMM/Size Preset")]
public class SO_UI_SizePreset : ScriptableObject
{
public string presetName;
public Vector2 realSize;
public UnitType unit;
}
[CreateAssetMenu(fileName = "New Object", menuName = "BBMM/Object Container")]
public class SO_UI_ObjectList_Item : ScriptableObject
{
public string objectName;
public GameObject prefab;
}
}
Here's the asset created using the code above:
First of all make sure each class is in a separate file with matching name!
SO_UI_SizePreset.cs
namespace BBMM.UI.ScriptableObjects
{
[CreateAssetMenu(fileName = "New Size Preset", menuName = "BBMM/Size Preset")]
public class SO_UI_SizePreset : ScriptableObject
{
public string presetName;
public Vector2 realSize;
public UnitType unit;
}
}
SO_UI_ObjectList_Item.cs
namespace BBMM.UI.ScriptableObjects
{
[CreateAssetMenu(fileName = "New Object", menuName = "BBMM/Object Container")]
public class SO_UI_ObjectList_Item : ScriptableObject
{
public string objectName;
public GameObject prefab;
}
}
Then if you created the SO asset first and then changed the namespace Unity might loose the connection (depending on how exactly you made that change).
-> you can either recreate them or go to the Inspector top-right corner menu, enable Debug mode and drag&drop the correct script into the Script field.

Changing Player Sprite through Shop

I'm trying to create a shop where you can buy a different player sprite from in-game currency. (The shop is a separate scene from the level) I was told that using scriptableobject is the way to go, so I made the following:
[CreateAssetMenu(fileName = "New Sprite", menuName = "Player Sprites")]
public class PlayerSprites : ScriptableObject
{
public string spriteName;
public int cost;
public Sprite sprite;
}
And I just added to the player script
public SpriteRenderer spriteRenderer;
void Start()
{
spriteRenderer = GetComponent<SpriteRenderer>();
}
I'm not really sure where to go from here... how to render the sprite on to the player from a different scene when the sprite button is pressed... Any help is greatly appreciated thank you!
Though your question is quite hazy and I don't really see what you tried so far:
As you have it right now you will need one ScriptableObject for each Sprite item ... I don't think that's what you want. You should rather use one ScriptableObject storing the information of all Sprite items.
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(fileName = "Assets/New Store", menuName = "Sprites-Store")]
public class SpriteStoreContainer : ScriptableObject
{
public List<StoreSpriteItem> SpriteItems = new List<StoreSpriteItem>();
// you can/should also implement methods here as in any usual component!
}
Also make sure your fileName starts with Assets/
And a separate class for the Items which uses [System.Serializable] so you can display it in the Inspector.
using UnityEngine;
[System.Serializable]
public class StoreSpriteItem
{
public string spriteName;
public int cost;
public Sprite sprite;
public bool IsAvailable;
// also here you could/should implement some methods e.g.
public void BuyItem()
{
IsAvailable = true;
}
}
And back in Unity:
Now you first have to Instantiate the ScriptableObject asset:
Go to the Project View (Assets) -> right mouse click -> in the menu click on Create -> click on Sprites-Store
This should create a new asset called New Store(.asset) under Assets
Now in the Inspector of this created asset you fill in the information you need. You should see our List SpriteItems with Size = 0.
To create elements just increase the Size value and hit enter(Carefull: Unity doesn't ask if you change this value => take care you don't delete items by reducing this value accidently later)
Now you can adjust all information for those SpriteItems
Later wherever you need access to the information of this Asset you can just use the reference as any other Component => you can assign it e.g. via the Inspector using a public field e.g.
using UnityEngine;
public class ScriptThatUsesStore : MonoBehaviour
{
public SpriteStoreContainer Store;
public void DoSomthingWithStore()
{
// example: just get the first List element
Sprite someSprite = Store.SpriteItems[0].sprite;
}
}
and than access the data in it. Though I strongly recommend you rather implement some methods in the ScriptableObject like e.g. BuyItem, GetSprite, etc.

copying custom class list to another list

I have figured out (or at least it feels like I have figured out) a way to make a list of weapons without having to hardcode everything like I was last trying.
So I have got my custom class for a weapon specifying type name attribute etc.
I have created another script will show a list in Unity's inspector that I can keep adding weapons to and changing the values to what I see fit.
I also have a game manager script that is the route of everything to from player stats game state etc..
I want to have this list now transferred to the game manager as a new list in that script.
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
[System.Serializable]
public class GameManager {
public static GameManager GM;
public static int FileIndex;
public static List<Weapons> WeaponM = new List<WeaponDB>();
public PlayerData Player;
public bool SaveEnabled;
public bool IsPaused;
public int TimeHour;
public int TimeMinute;
public GameManager(){
Player = new PlayerData();
}
}
that would be my game manager. Would it not be as easy as typing
public static List<Weapons> WeaponM = new List<WeaponDB>();
and have all the list entries stored in there copied over to the list entry in my gamemanager?
Here is my WeaponDB script so you can see and I have another script but it just sets all the different attributes.
using UnityEngine;
using System.Collections.Generic;
using System.Collections;
public class WeaponDB : MonoBehaviour{
public List<Weapons> Weapon;
public WeaponDB(){
}
public WeaponDB(List<Weapons> wpl){
Weapon = wpl;
}
void Start(){}
void Update(){}
}
I am getting an error of
CS0246 the type or namespacename "weaponsdb" could not be found"

Unity3D - Editor Window that can save data to an array that isn't cleared when you play

I'm trying to create an editor window that's able to create in game items.
It's supposed to add the item to a list of items that doesn't get cleared every time you play the game.
Is it possible to do this without an external XML/JSON file to save the data in.
I currently have this in my editor window script, ItemEquipable(type, price) is a basic class that just has a type and a price variable:
if(GUILayout.Button("Create"))
ItemList.items.Add(new ItemEquipable(type, price));
And the ItemList class, ItemEquipable extends the base Item class.
using UnityEngine;
using System.Collections.Generic;
public class ItemList:MonoBehaviour {
public static List<Item> items;
}
I've attached the ItemList to a GameObject that's in the scene.
I've seen Custom editor: How to initialise a new array element? but that script extends Editor and I'm extending EditorWindow, so I don't have access to a target variable.
Update:
Okay so I've tried making the ItemList class serializable
using UnityEngine;
using System;
using System.Collections.Generic;
[Serializable]
public class ItemList:MonoBehaviour {
public static List<Item> items;
}
And making a singleton out of it
using UnityEngine;
using System.Collections.Generic;
public class ItemList:Singleton<ItemList> {
public List<Item> items;
}
With the Singleton being your basic singleton class/code
Buth neither one is working.
The item array is not marked as Serializable so Unity can't save it when you launch the game. The solution is to mark ItemList with the attribute [Serializable], or you can just use a List<Item> directly.
So the way I solved it is by saving the array to a prefab every time you create an item.
UnityEngine.Object prefab = PrefabUtility.CreateEmptyPrefab("Assets/Resources/Prefabs/Items/Item Manager.prefab");
PrefabUtility.ReplacePrefab(Selection.activeGameObject, prefab);
AssetDatabase.Refresh();
I've split up the ItemList classes into multiple lists:
[Serializable]
public class ItemList:MonoBehaviour {
[SerializeField] private List<ItemEquipable> equipableItems = new List<ItemEquipable>();
[SerializeField] private List<ItemWeapon> weaponItems = new List<ItemWeapon>();
[SerializeField] private List<ItemPower> powerItems = new List<ItemPower>();
[SerializeField] private List<ItemSpecial> specialItems = new List<ItemSpecial>();
public List<ItemEquipable> EquipableItems { get { return equipableItems; } }
public List<ItemWeapon> WeaponItems { get { return weaponItems; } }
public List<ItemPower> PowerItems { get { return powerItems; } }
public List<ItemSpecial> SpecialItems { get { return specialItems; } }
}
And every item is serialized, as well as it's fields:
[Serializable]
public class Item {
public static Item item = new Item();
[SerializeField] private ItemType itemType;
[SerializeField] private string itemName;
[SerializeField] private string itemDescription;
}
The static item variable is used in the editor to prevent cluttering my editor script with hundreds of variables:
Item.item.Type = (Item.ItemType)EditorGUILayout.EnumPopup("Item Type", Item.item.Type);
To set these variables I've just used getters and setters for each field in the item classes.
This might not be the most efficient way of doing it, but it works.

Categories

Resources