- Home /
Editing a List Propertyfield in Editor Extension overwrites previously saved entries. How to fix?
I have an editor window that I am using to create new rules for a rulebook. I create rules by selecting some options, filling in some fields, etc, and then I click Save Rule.
One of those options is a Propertyfield List.
[SerializeField]
List<Criterion> conditionsToBeMet = new List<Criterion>();
The whole system works fine until I try to save more than one rule. What seems to happen is that editing the options for the List changes those options for the previously saved rules instead of just for the new rule I'm making.
As a result, the List part of all the rules is constantly being overwritten whenever I change any part of the List.
Any ideas why this is happening and how to fix it?
Here's my code for the editor window:
using UnityEngine;
using UnityEditor;
using System.Collections;
using System.Collections.Generic;
public class RuleEditor : EditorWindow {
[SerializeField]
List<Criterion> conditionsToBeMet = new List<Criterion>();
string newWhoID = "someone";
Concepts newConcept = 0;
string combinedCriteria = "no other conditions are met";
Responses newResponse = 0;
string newActionValue = "some value";
[MenuItem ("Window/Rulebook")]
public static void ShowWindow () {
EditorWindow.GetWindow(typeof(RuleEditor));
}
void OnGUI()
{
//Add a Rule
GUILayout.Label ("Add New Rule", EditorStyles.boldLabel);
//Setup
if(conditionsToBeMet.Count == 0)
{ //If we have no criteria at all, fill in the criteria in the visualized rule display with filler text
combinedCriteria = "no other conditions are met";
}
else
{
int index = 0;
foreach(Criterion c in conditionsToBeMet)
{
if(index != 0)
{ //Here we add an understandable language to show these conditions are being concatenated and thus must all be true to met the requirements for the rule
combinedCriteria += " and also " + c;
}
else
{
combinedCriteria = c.ToString();
}
//Make sure that we don't do anything illegal in Criterion construction. In other words, always make sure a min value is never greater than max.
if(c.minValToCompare > c.maxValToCompare)
{
c.minValToCompare = c.maxValToCompare;
}
//Finally, move on to the next criterion
index++;
}
}
//Make sure we can see in plain text what our new Rule will look like
GUILayout.Label ("New Rule Logic: "+"When "+newWhoID+" "+newConcept+" and "+combinedCriteria+", then "+newResponse+" by "+newActionValue, EditorStyles.label);
//Start the Rule editable fields
newWhoID = EditorGUILayout.TextField("Subject:", newWhoID);
newConcept = (Concepts)EditorGUILayout.EnumPopup ("Condition Verb:", newConcept);
//Necessary serializable stuff for PropertyField to work correctly
ScriptableObject target = this;
SerializedObject so = new SerializedObject(target);
SerializedProperty criteriaProperty = so.FindProperty("conditionsToBeMet");
EditorGUILayout.PropertyField (criteriaProperty, true);
so.ApplyModifiedProperties();
//Finish up the Rule editable fields
newResponse = (Responses)EditorGUILayout.EnumPopup ("Response Verb:", newResponse);
newActionValue = EditorGUILayout.TextField("Amount of Response:", newActionValue);
//A button to allow the new Rule to be added to the list
if(GUILayout.Button("Save Rule"))
{
//TODO: There is currently a bug where new rules will have the same Criteria list as the old rules
List<Criterion> newConditions = new List<Criterion>(conditionsToBeMet);
//Turn the WhoID string into an actual WhoID reference and create a new Rule from the data
Rule newRule = new Rule (RuleLogic.GetWhoID(newWhoID), newConcept, newConditions, newResponse, newActionValue);
}
//Rule List
GUILayout.Label ("Rulebook", EditorStyles.boldLabel);
//Setup
int selected = 0;
List<string> optionsList = new List<string> ();
//Show the Rule list as a pop-out drop down
foreach(Rule rule in GlobalLists.ruleBook)
{
string ruleString = rule.ToString ();
optionsList.Add (ruleString);
}
string[] options = optionsList.ToArray ();
selected = EditorGUILayout.Popup("Rule List:", selected, options);
}
}
I don't understand the purpose of this line
List<Criterion> newConditions = new List<Criterion>(conditionsToBe$$anonymous$$et);
Shouldn't you just send conditionsToBe$$anonymous$$et, since it's already a List?
Rule newRule = new Rule (RuleLogic.GetWhoID(newWhoID), newConcept, conditionsToBe$$anonymous$$et, newResponse, newActionValue);
Earlier, I thought the problem was that, since Arrays and Lists are Reference types ins$$anonymous$$d of Value types, that maybe the fact that the new rules were using the same list instance would be the cause of why changing that instance would also change previous rules.
Unfortunately, that didn't solve the bug, but I left that there in the code in case someone suggested I use a middleman list for the data being saved to the new rule.
Answer by Bunny83 · Jul 15, 2016 at 12:56 AM
Well, the problem is pretty simple. You have one list instance inside your editor window "conditionsToBeMet". This list is edited by your propertyfield GUI. In there by changing the list count you will create new instances of your "Criterion" class. When you "save" / create your rule you copy the elements from your "conditionsToBeMet" list into a new lsit which is passed to the rule instance and probably saved inside the rule instance. However both lists will reference the same "Criterion" class instances. So using one list to access a Criterion instance and applying changes to it would be visible from the second List as well since they both have the same elements.
You should not copy the List into a new list but simply pass the current list to your rule and then create a new empty list:
if(GUILayout.Button("Save Rule"))
{
Rule newRule = new Rule (RuleLogic.GetWhoID(newWhoID), newConcept, conditionsToBeMet, newResponse, newActionValue);
// create new empty list
conditionsToBeMet = new List<Criterion>();
}
That way the old list and it's Criterion instances now belongs to your newRule instance and the "conditionsToBeMet" variable gets a new list for the next rule. You also might want to "reset" the other temp variables as well (newWhoID, newConcept, newResponse, newActionValue) unless you specifically want to keep them for the next rule
Yes! Thank you! That solved it.
It's really simple, like you said. I just needed to replace the list with an empty one after the rule is saved.
You really saved my bacon.
Your answer
Follow this Question
Related Questions
Is there a way to get all editable fields of a component in a script? 2 Answers
Is there any way to make a timeline feature in editor scripting? 1 Answer
Editor GUI text becoming misaligned 0 Answers
Error sound on key input EditorWindow OnGUI() 2 Answers
How to get PropertyDrawers in a custom EditorWindow 0 Answers