Hi there,
I’m currently stuck while trying to create a custom forest generator tool. I want to create a list of tree types that I can add and remove using UI Elements, that I can then bind to and get the data from when I come to generate the forest.
I’m currently trying to bind Scroll View Items that I create on button press to values inside an list of structs, these struct instances are getting created as I’m binding. I’m trying to get the path using:
FindProperty()
&
BindProperty()
Every time I try to find the property it tells me that the property in question is null. to get the list of structs I return the list after I’ve added a list value in the monoBehaviour class (I call this function from the editor as seen below)
I’m adding an empty instance of a scriptable object of the tree type class each time I click “add” before returning the full array. I then create a serialized object from the relevant item in the list, which is what I get each property from.
I have two scripts here. One for the monoBehaviour class and one for the Editor Class. This is how I’m adding the scroll view items, you can see the first time I create a list item, i try to use BindingPath(), this also return null:
Editor Class:
[CustomEditor(typeof(GenerationTool))]
[CanEditMultipleObjects]
public class GenerationToolUI : Editor
{
[SerializeField] private GenerationTool m_generationScript;
[SerializeField] private GameObject m_ForestSpawnObject;
[SerializeField] private int m_ListSize = 1;
[SerializeField] private List<TreeType> m_TreeTypesEditor;
private VisualElement root;
public void OnEnable()
{
m_generationScript = GameObject.FindGameObjectWithTag("Generator").GetComponent<GenerationTool>();
m_ForestSpawnObject = GameObject.FindGameObjectWithTag("GeneratorObject");
}
public override VisualElement CreateInspectorGUI()
{
VisualElement rootInspector = new VisualElement();
var addButton = new Button { text = "+"};
addButton.RegisterCallback<ClickEvent>(AddTreeItem);
rootInspector.Add(addButton);
/* ---------------------------------------- */
/* ADDING SCROLL VIEW AND FIRST SCROLL ITEM */
/* ---------------------------------------- */
//m_generationScript.TreeSelectionUpdate(m_ListSize);
m_TreeTypesEditor = m_generationScript.TreeSelectionUpdate(m_ListSize);
var scrollContainer = new ScrollView();
scrollContainer.name = "scroll";
scrollContainer.style.width = 350;
scrollContainer.style.height = 400;
var Treename = new TextField();
Treename.label = "Tree Name:";
Treename.value = "Test";
Treename.bindingPath = $"m_TreeTypes.Array.Data[{m_ListSize - 1}].name";
Debug.Log(Treename.bindingPath);
Treename.multiline = false;
Treename.maxLength = 140;
scrollContainer.Add(Treename);
var prefab = new ObjectField();
prefab.label = "Tree Prefab:";
prefab.objectType = typeof(GameObject);
scrollContainer.Add(prefab);
var spawnFrequency = new Slider();
spawnFrequency.label = "Spawn frequency:";
spawnFrequency.lowValue = 0.0f;
spawnFrequency.highValue = 1.0f;
spawnFrequency.value = 0.5f;
scrollContainer.Add(spawnFrequency);
var treeSpacing = new FloatField();
treeSpacing.label = "Spawn frequency:";
treeSpacing.maxLength = 1;
scrollContainer.Add(treeSpacing);
rootInspector.Add(scrollContainer);
VisualTreeAsset asset = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>("Assets/UI/TreeGenerator.uxml");
asset.CloneTree(rootInspector);
root = rootInspector;
StyleSheet sheet = AssetDatabase.LoadAssetAtPath<StyleSheet>("Assets/UI/Stylesheets/ToolStylesheet.uss");
rootInspector.styleSheets.Add(sheet);
return rootInspector;
}
public void AddTreeItem(ClickEvent eventData)
{
if (m_ListSize < 25)
{
m_ListSize++;
m_TreeTypesEditor = m_generationScript.TreeSelectionUpdate(m_ListSize);
var serializedObject = new SerializedObject(m_TreeTypesEditor[m_ListSize - 1]);
serializedObject.Update();
var property = serializedObject.FindProperty($"m_TreeTypes.Array.data[{m_ListSize}].name");
Debug.Log(property);
var Treename = new TextField();
Treename.label = "Tree Name:";
Treename.multiline = false;
Treename.maxLength = 140;
Treename.BindProperty(property);
root.Q<ScrollView>(name: "scroll").Add(Treename);
var prefab = new ObjectField();
prefab.label = "Tree Prefab:";
prefab.objectType = typeof(GameObject);
root.Q<ScrollView>(name: "scroll").Add(prefab);
var spawnFrequency = new Slider();
spawnFrequency.label = "Spawn frequency:";
spawnFrequency.lowValue = 0.0f;
spawnFrequency.highValue = 1.0f;
spawnFrequency.value = 0.5f;
root.Q<ScrollView>(name: "scroll").Add(spawnFrequency);
//scrollContainer.Add(new Label(spawnFrequency.value.ToString()));
var treeSpacing = new FloatField();
treeSpacing.label = "Tree Spacing:";
treeSpacing.maxLength = 1;
root.Q<ScrollView>(name: "scroll").Add(treeSpacing);
}
}
Monobehaviour Script and Scriptable Obj:
[Header("Tree Type Variables")]
[Space]
public List<TreeType> m_TreeTypes;
[SerializeField] private TreeType m_EmptyTreeType;
public void OnEnable()
{
m_EmptyTreeType = ScriptableObject.CreateInstance<TreeType>();
}
public List<TreeType> TreeSelectionUpdate(int listSize)
{
m_TreeTypes = new List<TreeType>();
for (int i = 0; i < listSize; i++)
{
m_TreeTypes.Add(m_EmptyTreeType);
/* Can't get the values of the m_TreeTypes array to bind to the scroll list variables */
}
Debug.Log(m_TreeTypes[0]);
return m_TreeTypes;
}
public class TreeType : ScriptableObject
{
public string name;
public GameObject prefab;
public float spacing;
public float frequency;
}
I would greatly appreciate any ideas on how to do bind to a list of objects, it is mainly this part of my tool code that I think is wrong:
m_TreeTypesEditor = m_generationScript.TreeSelectionUpdate(m_ListSize);
var serializedObject = new SerializedObject(m_TreeTypesEditor[m_ListSize - 1]);
serializedObject.Update();
var property = serializedObject.FindProperty($"m_TreeTypes.Array.data[{m_ListSize}].name");
Debug.Log(property);
I think i’m not pointing to the proper data here. I don’t know if this is because the m_TreeTypes array is on another class and the FindProperty() class can’t find it. can anybody help me?
