Can't I serialize the Dictionary used by Unity? - unity3d

I want to see the Dictionary used by Unity on the Inspector. But I don't know if it's possible or not, and I don't know how.
Please tell me how to see the Dictionary on the inspector. Or you can tell me about other data structures similar to Dictionary in Unity.

Short answer is no, you can't serialize them.
But you have some options, I'll provide you with one.
Suppose you want to create a Dictionary<string, int>. You can create a class/struct that holds this information:
using System;
[Serializable]
public class StringIntPair {
public string key;
public int value;
}
And then, in your code, you can create a List<StringIntPair>, that will be visible on your inspector. This way you can set the values you want in the inspector.
Lastly, you can create a Dictionary<string, int> and populate it in Awake / Start method as you wish:
using System;
[Serializable]
public class YourClass : MonoBehaviour {
public List<StringIntPair> exposedDictionary;
Dictionary<string, int> hiddenDictionary;
void Awake(){
foreach(StringIntPair pair in exposedDictionary)
hiddenDictionary[pair.key] = pair.value;
}
}
And then you can use hiddenDictionary in the code as you wish.

Related

Public variables created in Rider do not show up in Unity

I just started using Unity (which came with VSC), but I had better experience using JetBarin products such as IntelliJ IDEA, so I went and switched to Rider. However, I am now unable to connect public variables (int, float, GameObject) to my Unity projects.
I tried updating Rider and changing some setting, but came none the wiser.
UPDATE: There have been (obvious) request for my code to see the exact issue, so I hope this helps clear up the issue a little bit:
Code written in VSC
The resulting public variables showing up in Unity
Similar code written using Rider
No interactive variables showing up in Unity
Unity serializes only fields (not properties with get or set) of MonoBehaviours. All public fields are serialized unless have [System.NonSerialized] attribute.
DO NOT get confused with the [HideInInspector] attribute, it won't be visible in the inspector (if you don't have a custom inspector) but WILL BE serialized.
class Foo
{
// Bar won't be shown in the inspector or serialized.
[System.NonSerialized]
public int Bar = 5;
}
To serialize a non-public field use [SerializeField] attribute for primitive types (such as int, float, bool).
public class SomePerson : MonoBehaviour
{
// This field gets serialized because it is public.
public string name = "John";
// This field does not get serialized because it is private.
private int age = 40;
// This field gets serialized even though it is private
// because it has the SerializeField attribute applied.
[SerializeField]
private bool isMale = true;
}
If you wanna serialize own class or struct, use [System.Serializable] attribute.
[System.Serializable]
public struct PlayerStats
{
public int level;
public int health;
}

Argument exception when I try to set a variable with a custom set in unity

I tried to use the Generate() function only if a variable has changed without having to check it every frame. I used the following tutorial to achieve this. but for some reason, whenever i try to set the variable, I get this error:
ArgumentException: GetComponent requires that the requested component 'List`1' derives from MonoBehaviour or Component or is an interface.
the script:
public GameObject CEMM;
private int ListLength;
public static int ListLengthProperty
{
get
{
return JLSV.instance.ListLength;
}
set
{
JLSV.instance.ListLength = value;
JLSV.instance.Generate();
}
}
private void Awake()
{
instance = this;
}
I tried to set the value like this: JLScrollView.ListLengthProperty = JLScrollView.instance.CEMM.GetComponent<List<JLClass>>().Count;
The generic type parameter that you use when calling GetComponent must be a class that derives from Component (or an interface type). List is a plain old class object, which is why you are getting the exception from this:
GetComponent<List<JLClass>>()
I'm not really sure what value you are trying to assign to the property. If you are trying to get the number of components of a certain type on the GameObject you can use GetComponents.
JLScrollView.ListLengthProperty = JLScrollView.instance.GetComponents<JLClass>().Length;

Zenject: MonoBehaviour injection

I'm new to Zenject and this is my first project using this asset. I'm having injection problems! Maybe someone knows what I am doing wrong or where the error might be. In the code below, _spawnArea is not initialized.
public class BootstrapIniter : MonoInstaller
{
[SerializeField] private Camera _mainCamera;
[Space(10)]
[SerializeField] private Spawner _spawner;
public override void InstallBindings()
{
BindMain();
BindBallHandle();
}
private void BindMain()
{
Container.Bind<Camera>().FromInstance(_mainCamera).AsSingle();
}
private void BindBallHandle()
{
Container.Bind<Spawner>().FromInstance(_spawner).AsSingle();
}
}
[RequireComponent(typeof(SpawnArea))]
public class Spawner : MonoBehaviour
{
private SpawnArea _spawnArea;
private void Awake()
{
_spawnArea = GetComponent<SpawnArea>();
}
[Inject]
public void Construct(Camera camera)
{
Rect cameraRect = camera.pixelRect;
_spawnArea.Init(cameraRect);
}
}
Thanks in advance for the answer or direction in which to look for a solution
I think that you did not inject your instance.
From the documentaiton "FromInstance - Adds a given instance to the container. Note that the given instance will not be injected in this case. If you also want your instance to be injected at startup, see QueueForInject" (QueueForInject will queue the given instance for injection once the initial object graph is constructed). Basically you need to inject your instance for the injected methods to execute.
On the other hand I dont see the point of binding a monobehaviour from instance, as you have to generate the instance bind it to the container and then inject it. You have binding methods that do this all at once for you, check the section "Construction Methods".
Check for example: FromComponentInNewPrefabResource - Instantiate the given prefab (found at the given resource path) as a new game object, inject any MonoBehaviour's on it, and then search the result for type ResultType in a similar way that GetComponentInChildren works (in that it will return the first matching value found).
Note that for the injection to take place succesfully you have to previously wire up the dependency in the container with the Container.Bind statement so that the container knows what needs to be injected and how.
I suggest to read carefully the documentation which is very good and follow the examples along.

Instantiate ScriptableObject With Editor Set Values

In Unity am creating an item system using the base class:
[CreateAssetMenu]
class ItemBase : ScriptableObject(){
public string ItemName;
public Sprite ItemSprite;
void Spawn() {
//Spawn Item Using The Sprite
}
}
and then I am using the CreateAssetMenu to right click in my project folder for each item I want to add to the game and then assign their values in the editor.
When i drag my object from the editor onto a game object, everything works fine.
The issue im having is that I cannot figure out how to actually instantiate these at runtime from a script.
When I try to reference it like a class, the class is not found, when I add a script that mirrors the created item, the properties set by the editor are null upon instantiation.
Ideally what id like to do is something like:
Right click and create new ItemBase called FishingRod from editor
Assign name and sprite in editor
\\Instantiate From Script
FishingRod rod = ScriptableObject.CreateInstance(typeof(FishingRod)); // Properties should be the properties set by editor
FishingRod.Spawn(); // Inserts Item Into Game
But when I instantiate the class, its properties are null;
rod.ItemSprite //is null
rod.ItemName //is null
From your use case description it sounds like you shouldn't need to create instances of the ScriptableObject on runtime. You rather want to reference and use the ones you already created.
So what you should do is
(As you already did) create a new ItemBase instance via the Inspector's Asset create menu (Create -> ItemBase)
(As you already did) fill this "container" with your data (name and sprite)
Now in the file that want's to use this asset reference it via a field in the Inspector:
// In the Inspector go to according GameObject
// and drag the ScriptableObject you created into this slot
[SerilaizeField] private ItemBase fishingRod;
And then you simply use the values and methods of this instance via
fishingRod.Spawn();
So if you want to switch between different items you would e.g. have a kind of controller script like
[SerilaizeField] private ItemBase fishingRod;
[SerilaizeField] private ItemBase hammer;
[SerilaizeField] private ItemBase umbrella;
Then you could switch between them like e.g.
private ItemBase _activeItem;
public void SelectFishingRod()
{
_activeItem = fishingRod;
}
public void SelectHammer()
{
_activeItem = hammer;
}
public void SelectUmbrella()
{
_activeItem = umbrella;
}
and then e.g. do
public void UseActiveTool()
{
_activeItem.Spawn();
}

Serialization of a list of custom objects in unity

While trying to make a script for building assets, I ran into an issue with unity's serialization. I have a class in which I store some arbitrary information, which is then stored in an array in a MonoBehaviour on a prefab. I cannot for the life of me get the array to save however, as when I make the object into a prefab it loses the list's values. I have tried using [System.Serializable] and ScriptableObject, but both seem to pose their own new issues.
For instance, using ScriptableObject would mean having to save the data objects as assets, which would become way too much since these objects can get to hundreds in number.
Am I making a mistake in my understanding of unity's serialization? Is there a way to get this working without the ScriptableObject approach of saving every ArbitraryInfo object in an asset?
Data object:
[System.Serializable]
public class ArbitraryInfo{
public int intValue;
public Vector3 vectorValue;
}
OR
public class ArbitraryInfo : ScriptableObject {
public int intValue;
public Vector3 vectorValue;
void OnEnable() {
hideflags = HideFlags.HideAndDontSave;
}
}
Behaviour:
public class MyBuilder : MonoBehaviour {
public ArbitraryInfo[] infoArray;
}
Editor:
[CustomEditor(typeof(MyBuilder))]
public class MyBuilderEditor : Editor {
private SerializedProperty infoArrayProperty;
void OnLoad() {
infoArrayProperty = serializedObject.FindProperty("infoArray");
}
void OnInspectorGUI() {
serializedObject.Update();
for (var i = 0; i < infoArrayProperty.arraySize; i++) {
if (i > 0) EditorGUILayout.Space();
var info = infoArrayProperty.GetArrayElementAtIndex(i).objectReferenceValue as ArbitraryInfo;
EditorGUILayout.LabelField("Info " + i, EditorStyles.boldLabel);
info.intValue = EditorGUILayout.IntField(info.intValue);
info.vectorValue = EditorGUILayout.Vector3Field(info.vectorValue);
}
serializedObject.ApplyModifiedProperties();
}
}
EDIT 1, Thank you derHugo
I changed my code to incorporate the changes. Now there are errors for ArbitraryInfo not being a supported pptr value.
Secondly, ArbitraryInfo no longer being a ScriptableObject poses the question of how to initialize it. An empty object can be added to infoArrayProperty through infoArrayProperty.arraySize++, but this new empty object seems to be null in my case. This might be due to the pptr issue mentioned above.
EDIT 2
The issue I was having was caused by another piece of code where I tried to check if infoArrayProperty.objectReferenceValue == null. I changed this to another check that did the same thing and everything worked!
No, no ScriptableObject needed.
But note that GetArrayElementAtIndex(i) returns a SerializedProperty. You can not simply parse it to your target class.
so instead of
var info = infoArrayProperty.GetArrayElementAtIndex(i).objectReferenceValue as ArbitraryInfo;
and
info.intValue = EditorGUILayout.IntField(info.intValue);
info.vectorValue = EditorGUILayout.Vector3Field(info.vectorValue);
you have to get the info's SerializedPropertys by using FindPropertyRelative:
var info = infoArrayProperty.GetArrayElementAtIndex(i);
var intValue = info.FindPropertyRelative("intValue");
var vectorValue = info.FindPropertyRelative("vectorValue");
than you can/should use PropertyFields
EditorGUILayout.PropertyField(intValue);
EditorGUILayout.PropertyField(vectorValue);
allways try to avoid using direct setters and use those SerializedProperties instead! This provides you with Undo/Redo functionality and marking the changed Behaviour/Scene as unsaved automatically. Otherwise you would have to tak care of that manually (... don't ^^).