0
votes

It's working fine when the game is already running. I can change each door lock state. But before running the game is I set one or more of the doors to be locked( Checked set the flag to true ) and then running the game it's changing all the doors back to false unlocked.

I want that if I set the doors lock state also before running the game that it will keep this changes when the game is running.

The first script is with one line only:

using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

public class DoorsLockManager : MonoBehaviour
{
    public List<HoriDoorManager> Doors = new List<HoriDoorManager>();
}

The second script control the doors:

using UnityEditor;

[CustomEditor(typeof(DoorsLockManager))]
public class DoorsLockManagerEditor : Editor
{
    DoorsLockManager _manager;

    public override void OnInspectorGUI()
    {
        _manager = (DoorsLockManager)target;

        base.OnInspectorGUI();

        if (_manager.Doors.Count <= 0)
            return;
        for (var i = 0; i < _manager.Doors.Count; i++)
        {
            if (_manager.Doors[i] == null)
                continue;

            _manager.Doors[i].GetLockState = EditorGUILayout.Toggle("Door " + i + " Lockstate", _manager.Doors[i].GetLockState);
        }
    }
}

The last script is attached to each door:

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System;

public class HoriDoorManager : MonoBehaviour
{
    public List<DoorHori> doors = new List<DoorHori>(); 
    public bool doorLockState;

    private void Awake()
    {
        if (transform.parent != null)
        {
            Transform parent = transform.parent;
            var children = parent.GetComponentsInChildren<Transform>();

            if (children != null)
            {
                foreach (Transform door in children)
                {
                    if (door.name == "Door_Left" || door.name == "Door_Right")
                        doors.Add(door.GetComponent<DoorHori>());
                }
            }
            //ColorDoors(Color.red, Color.green, doorLockState);
        }
    }

    void OnTriggerEnter()
    {
        if (doorLockState == false)
        {
            if (doors != null)
            {
               for(int i =0; i < doors.Count; i++)
                {
                    doors[i].OpenDoor();
                }
            }
        }
    }

    private void ColorDoors(Color red, Color green, bool state)
    {
        List<Transform> children = new List<Transform>();

        for (int i = 0; i < doors.Count; i++)
        {
            foreach (Transform child in doors[i].GetComponentsInChildren<Transform>())
            {
                if (child == doors[i].transform)
                    continue;

                var renderer = child.GetComponent<Renderer>();
                renderer.material.shader = Shader.Find("Unlit/ShieldFX");

                if(state == true)
                {
                    renderer.material.SetColor("_MainColor", red);
                }
                else
                {
                    renderer.material.SetColor("_MainColor", green);
                }
            }
        }
    }

    public bool GetLockState
    {
        get { return doorLockState; }
        set { doorLockState = value; }
    }
}
2

2 Answers

2
votes

properties are not serializable!

those you should never change in editor scripts at all since they are not serialized => never saved and will allways have the default value.

it will not work if it is a property like

public bool GetLockState
{
    get { return doorLockState; }
    set { doorLockState = value; }
}

why even use this if your doorLockState is public anyway??

The problem is that this doesn't mark the changed field doorLockState as "dirty".

However you should not use EditorUtility.SetDirty as suggested by the other answer due to:

From 5.3 onwards, with the introduction of Multi-Scene Editing, this function should no-longer be used for modifying objects in scenes. Instead, you should use Undo.RecordObject prior to making changes to the object. This will mark the object's Scene as dirty and provide an undo entry in the editor.'


Instead in your editor script you have to change the value of doorLockState.

If it where private you would have to add [SerializeField]

[SerializeField] private bool doorLockState;

But in your case it seems to be public anyway so it is automatically serialized.

Than allways use proper SerializedProperty and PropertyField instead of directly manipulate values in editor scripting!

It looks more complex at the beginning but this handles stuff like Undo/Redo and marking stuff dirty (thus saving changes) automatically so you don't have to take care of anything else:

[CustomEditor(typeof(DoorsLockManager))]
public class DoorsLockManagerEditor : Editor
{
    private SerializedProperty _doors;

    private void OnEnable()
    {
        _doors = serializedObject.FindProperty("Doors");
    }

    public override void OnInspectorGUI()
    {
        base.OnInspectorGUI();

        for (int i = 0; i < _doors.arraySize; i++)
        {
            var door = _doors.GetArrayElementAtIndex(i);

            // if door == null the script itself has an error since it can't even find the SerializedProperty
            if (door == null)
            {
                EditorGUILayout.HelpBox("There was an error in the editor script!\nPlease check the log", MessageType.Error);
                Debug.LogError("Couldn't get door property", target);
                return;
            }

            if (door.objectReferenceValue == null) continue;

            // FindPropertyRelative seems not to work for MonoBehaviour classes
            // so we have to use this hack around
            var serializedDoor = new SerializedObject(door.objectReferenceValue);

            // If it's public no worry anyway
            // If it's private still works since we made it a SerializeField
            var lockState = serializedDoor.FindProperty("doorLockState");

            // Fetch current values into the serialized "copy"
            serializedDoor.Update();

            if (lockState == null)
            {
                EditorGUILayout.HelpBox("There was an error in the editor script!\nPlease check the log", MessageType.Error);
                Debug.LogError("Couldn't get lockState property", target);
                return;
            }

            // for the PropertyField there is 
            // no return value since it automatically uses
            // the correct drawer for the according property
            // and directly changes it's value
            EditorGUILayout.PropertyField(lockState, new GUIContent("Door " + i + " Lockstate"));

            // or alternatively
            //lockState.boolValue = EditorGUILayout.Toggle("Door " + i + " Lockstate", lockState.boolValue);

            // Write back changes, mark as dirty if changed
            // and add a Undo history entry
            serializedDoor.ApplyModifiedProperties();
        }
    }
}

enter image description here

(Ofcourse the value is only updated OnInspectorGUI so in case you have multiple Inspector tabs open it is only updated if hovered)

0
votes

Have you tried EditorUtility.SetDirty in DoorsLockManagerEditor ?

It seems like editor doesn't keep the initial value.