how to configurate nested object in Unity's inspector - c#

I tried to create nested object in C# script in Unity and change the values in it.
Class of nested object
using UnityEngine;
public class Replica : ScriptableObject
{
public string Text;
public int Speed = 1;
public int PersonaID = 0;
}
Class with nested object
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
[CreateAssetMenu(fileName = "Data", menuName = "ScriptableObjects/DialogContainer", order = 1)]
public class DialogContainer : ScriptableObject {
public Replica[] Replicas; // Here is nested object
public Sprite[] Avatars;
}
And when I created the ScriptableObject I saw that:
Photo of ScriptableObject interface
Here I can only put an instance of the class there, but I cannot configure it.
But i want to change values right in inspector without creating and inserting object of class "Replica" like in InputManager where I can create one more obj in axis, open it and change values in inspector like that.
Photo of interface I want to see

If u want configurate your class in inspector, that class must be not derived from MonoBehaviour or ScriptableObject and be with tag [System.Serializable] than u can serialize it in nested class without creating instance.
Replica:
[System.Serializable]
public class Replica
{
public string Text;
public int Speed = 1;
public int PersonaID = 0;
}
Dialog Container:
using System.Collections.Generic;
using UnityEngine;
public class DialogContainer : MonoBehaviour{
public List<Replica> Replicas;
public Sprite[] Avatars;
}
Than I saw that:Inspector screenshot

Unless you make a custom editor for DialogContainer, you can't do what you want to do. If you have a parent ScriptableObject that contains a child list of ScriptableObjects, you can't edit each child's fields when you have the parent selected in your Project View.
You will have to edit each child Replica by selecting the Replica instance in the Project View. Then the Inspector will let you edit that Replica.

Related

How do I use a script that is in my ScriptableObject?

I'm creating a system where I have my cards in ScriptableObject, but each card has a unique function. What I thought of was creating a new class for each skill that inherits from "skills". So I put this script for each letter in my ScriptableObject, but now I'm trying to access it and I can't, because I couldn't use the AddComponent.
public class Cards : ScriptableObject
{
public Sprite cardSprite;
public int cardAttack;
public int cardHealth;
public int cardCost;
public Object cardAbility;
}
As you realized, you can't use AddComponent with ScriptableObjects, because it can only be used to attach components to GameObjects.
ScriptableObjects however do support a somewhat similar concept of main assets and sub assets.
You can use AssetDatabase.AddObjectToAsset to add new sub assets, and they'll appear as nested children below the main asset in the Project view.
public abstract class Skill : ScriptableObject
{
#if UNITY_EDITOR
protected static void AddToCard<TSkill>(MenuCommand command) where TSkill : Skill
{
Card card = (Card)command.context;
TSkill skill = CreateInstance<TSkill>();
skill.name = skill.GetType().Name;
card.skill = skill;
string path = AssetDatabase.GetAssetPath(card);
AssetDatabase.AddObjectToAsset(skill, path);
AssetDatabase.ImportAsset(path);
}
[MenuItem("CONTEXT/Card/Remove Skill")]
protected static void RemoveSkill(MenuCommand command)
{
Card card = (Card)command.context;
string path = AssetDatabase.GetAssetPath(card);
Skill skill = AssetDatabase.LoadAssetAtPath<Skill>(path);
AssetDatabase.RemoveObjectFromAsset(skill);
AssetDatabase.ImportAsset(path);
}
#endif
}
using UnityEditor;
using UnityEngine;
[CreateAssetMenu]
public class ExampleSkill : Skill
{
#if UNITY_EDITOR
[MenuItem("CONTEXT/Card/Add Skill/Example Skill")]
private static void AddToCard(MenuCommand command) => AddToCard<ExampleSkill>(command);
#endif
}
using UnityEngine;
[CreateAssetMenu]
public class Card : ScriptableObject
{
public Skill skill;
}
A scriptable object is a data container. A material or lighting settings are scriptable objects. You can save them as assets and use them as settings for other scripts, but not attach them to GameObjects. Read more here.
To create ScriptableObjects in the asset browser, you need to add the following before the start of your class.
[CreateAssetMenu(fileName = "DefaultFileName", menuName = "ScriptableObjects/NameOfYourObjects", order = 1)]
To add them to a script, you use public Cards cards; then you can drag them from your asset browser to the script in the inspector.
The only things you can add to a GameObject are MonoBehaviors

What is the best way to encapsulate inherited fields in Unity 2D?

I'm currently designing a Unity game, and I want to ensure I'm implementing proper encapsulation. However, I'm stuck on this problem:
Suppose I have a class Item with the field: private string itemName. I also have a class Strawberry that is a subclass of Item. I want to write a method in class Strawberry that returns its itemName.
These scripts are MonoBehaviour, so they cannot contain constructors that would solve the problem above using base in its constructor.
What strategy should I use to set the Strawberry class' name?
Thanks!
You need to make your itemName string protected. That means it can be accessed by child classes.
Item base class:
using UnityEngine;
public class Item : MonoBehaviour
{
protected string itemName = "itemBase";
}
Strawberry class:
using UnityEngine;
public class Strawberry : Item
{
// Start is called before the first frame update
void Start()
{
Debug.Log($"my mane is: {GetItemName()}");
}
string GetItemName() => this.itemName;
}

Creating a list of <T> class on an object

This is a common question and I assure you I have done my research first. I simply cannot get a list of all of the instances of a script type on the game object.
I have tried making an array of the types and looping the contents into a list. This gives me conversion errors.
I have tried directly adding the array to the list with .AddRange. Conversion errors.
I have tried the different formats of GetComponents, and casting the output of the array into every applicable type I can think of, with no success.
I have also tried initising the list first and then running GetComponent in start.
I have tried using CharEquipGenre as both monobehaviour and non-monobehaviour.
What am I doing wrong?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
public class CharEquipment : MonoBehaviour
{
public List<CharEquipGenre> equipment_genres = GetComponents <CharEquipGenre>(); // I am trying to do something like this
public CharEquipGenre attack;
public CharEquipGenre defend;
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CharEquipGenre
{
public List<BlockScriptableObject> equipped = new List<BlockScriptableObject>();
}
// Additional code via request:
public class CharEquipment : MonoBehaviour
{
void Start()
{
equipment_genres = GetComponents<CharEquipGenre>();
}
public List<CharEquipGenre> equipment_genres = new System.Collections.Generic.List<CharEquipGenre>();
public CharEquipGenre attack;
public CharEquipGenre defend;
Well, CharEquipGenre is not a MonoBehaviour, so there are no "components" of this class.
You can find all components of a specific type with GetComponents (read more here) but it needs to be a MonoBehaviour that's attached to the GameObject
I don't know unity3d but it seems as though you are trying to initialize equipment_genres when defining it and for that the compiler would need access to something that is available at compile time.
If GetComponents is a method on the class then this will not work as the method is not static. You could use the instance by perhaps going with a method or expression body:
public List<CharEquipGenre> equipment_genres => new List<CharEquipGenre>(GetComponents<CharEquipGenre>());
Although a slightly "better" design would be exposing IEnumerable:
public IEnumerable<CharEquipGenre> EquipmentGenres => ...
// or
public IEnumerable<CharEquipGenre> GetEquipmentGenres() => ...
First of all make your class [Serializable] so you can see it in the inspector and save it.
[Serializable]
public class CharEquipGenre
{
public List<BlockScriptableObject> equipped = new List<BlockScriptableObject>();
}
And then simply instantiate a new list like
public List<CharEquipGenre> equipment_genres = new List<CharEquipGenre>();
Note that both of those will be overruled by whatever you do to them in the Inspector later. Once you added elements any change to the initialization lines is useless (unless you hit Reset in the MonoBehaviour's context menu)

C# / new keyword / class member reference

So i have 2 classes (Sound class, AudioManager class)
I dont understand why we can access Sound's field without creating instance of Sound class. ()
Like what are the differences between
public Sound test1;
AND
Sound test2 = new Sound();
Its weird to me cause when i was learning c# tutorials / u could only access variables from another class only by creating instance of that class but in Unity u cas access it just by typing public Sound test1;
Sound Class
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Audio;
[System.Serializable]
public class Sound
{
public AudioClip clip;
public string name;
public float volume;
public float pitch;
public AudioSource soundclass_source;
}
AudioManager class
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Audio;
public class AudioManager : MonoBehaviour
{
public Sound test1;
Sound test2 = new Sound();
test1.name = "test1"; // without new
test2.name = "test2"; // with new
}
Since it is [Serializable] and the Unity Inspector of MonoBehaviour and ScriptableObject (etc) classes automatically initializes serialized fields with a default instance there is no difference between
public Sound test1;
and
public Sound test1 = new Sound();
(public fields are serialized by default if the type is serializable)
Also note that once you created an instance in Unity and the values are set via the Inspector those serialized values will always overrule the ones you hardcode here. If you later e.g. decide to pre-initliaze some values and change it to
public Sound test1 = new Sound() { name = "test1" };
it will have no effect since the value configured in the Inspector is taken instead. This coded value only is used for the very forst time the instance is created or if you hit Reset in the components context menu.
If you want later that the coded value is used instead you have to assign the new value in a method instead (see below).
No accesibility definition for a field (/class/method/property/etc) like public, protected or private in c# always means private.
If a field is private like in your case you have to initialize it "manually" otherwise the default value for classes is always null (== no instance reference).
You can do this like you did staticly in the class
private Sound test2 = new Sound();
this means the instance is already created as soon as an instance of your class is created or can do it laso later in a method like
private Sound test2;
private void Awake()
{
Sound test2 = new Sound();
}
Finally you can/should make as private as possible for capsulation but can still make them serialized => displayed in the Inspector and stored using the [SerializeField] attribute:
[SerilaizeField] private Sound test2;
You can read more about Unity's serialization here

Unity: Inspector can't find field of ScriptableObject

I'm having trouble showing a public field of a ScriptableObject which is a child of the component I'm inspecting. While I can easily do this in another way, I need this method to work for other variables. (ReorderableLists)
I simplified the problem, maybe I was just doing something obvious wrong, but I can't see what I'm doing wrong.
Code + error:
http://answers.unity3d.com/storage/temp/70243-error.png
class SomeComponent : MonoBehaviour{
public MyScriptable scriptable; //instantiated and saved as asset
}
[Serializable] class MyScriptable : ScriptableObject{
[SerializeField] public float value = 0.1f;
}
[CustomEditor(typeof(SomeComponent))] class SomeComponentEditor : Editor{
public override void OnInspectorGUI() {
if((target as SomeComponent).scriptable==null) (target as SomeComponent).scriptable = ScriptableObject.CreateInstance(typeof(MyScriptable)) as MyScriptable;
EditorGUILayout.PropertyField(serializedObject.FindProperty("scriptable"));
//shows the asset
EditorGUILayout.PropertyField(serializedObject.FindProperty("scriptable").FindPropertyRelative("value"));
//error
}
}
To fix your code you can do this:
using UnityEditor;
using UnityEngine;
class SomeComponent : MonoBehaviour
{
public MyScriptable myScriptable;
}
class MyScriptable : ScriptableObject
{
public float myChildValue = 0.1f;
}
[CustomEditor(typeof(SomeComponent))]
class SomeComponentEditor : Editor
{
public override void OnInspectorGUI()
{
SomeComponent someComponent = target as SomeComponent;
if (someComponent.myScriptable == null)
someComponent.myScriptable = CreateInstance<MyScriptable>();
SerializedProperty myScriptableProp = serializedObject.FindProperty("myScriptable");
EditorGUILayout.PropertyField(myScriptableProp);
SerializedObject child = new SerializedObject(myScriptableProp.objectReferenceValue);
SerializedProperty myChildValueProp = child.FindProperty("myChildValue");
EditorGUILayout.PropertyField(myChildValueProp);
child.ApplyModifiedProperties();
}
}
To inspect a child object, you first need to create a SerializedObject version of it, which then can be searched for properties as usual.
Also, the Serializable attribute is not needed on classes which derive from ScriptableObject and the SerializeField attribute is only needed when serializing private fields; public fields are serialized by default in Unity.
Without knowing the original context of your code, your approach seems a little peculiar to me. ScriptableObject instances are meant to be used as asset files in your Unity project. Have you used the CreateAssetMenu attribute yet? Usually, you would create your assets manually via the menu and then plug them into your components. The way you are doing it, it won't be written to disk, so why use ScriptableObject and not just a normal class? But maybe it all makes sense in your actual context, so never mind if I'm wrong.
Hack solution found:
SerializedObject newserobj = new SerializedObject(serializedObject.FindProperty("scriptable").objectReferenceValue );
EditorGUILayout.PropertyField(newserobj.FindProperty("value"));
newserobj.ApplyModifiedProperties();

Categories