Как я могу добавить глобальный флаг bool для управления всеми состояниями блокировки дверей?

#c# #unity3d

#c# #unity-game-engine

Вопрос:

Теперь он отлично работает в редакторе и во время выполнения для каждой двери. Но я хочу добавить глобальный общедоступный флаг, который будет управлять всеми дверями одновременно в редакторе и во время выполнения. Если я изменю глобальный флаг на true, все двери будут заблокированы, и то же самое, если установлено значение false.

Скрипт DoorsLockManager:

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

[ExecuteInEditMode]
public class DoorsLockManager : MonoBehaviour
{
    [HideInInspector]
    public List<HoriDoorManager> Doors = new List<HoriDoorManager>();

    private void Awake()
    {
        var doors = GameObject.FindGameObjectsWithTag("Door");
        Doors = new HoriDoorManager[doors.Length].ToList();

        for (int i = 0; i < doors.Length; i  )
        {
            Doors[i] = doors[i].GetComponent<HoriDoorManager>();
        }
    }
}
  

И сценарий редактора:

 using UnityEditor;
using UnityEngine;

[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 only 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();
        }
    }
}
  

Ответ №1:

Ну, вы могли бы просто добавить его в DoorsLockManager

 [ExecuteInEditMode]
public class DoorsLockManager : MonoBehaviour
{
    [HideInInspector]
    public List<HoriDoorManager> Doors = new List<HoriDoorManager>();

    // The global state
    [SerializeField] private bool _globalLockState;

    // During runtime use a property instead
    public bool GlobalLockState
    {
        get { return _globalLockState; }
        set 
        {
            _globalLocakState = value;            

            // apply it to all doors
            foreach(var door in Doors)
            {
                // now you would need it public again
                // or use the public property you had there
                Door.doorLockState = _globalLocakState;
            }
        }
    }

    private void Awake()
    {
        var doors = GameObject.FindGameObjectsWithTag("Door");
        Doors = new HoriDoorManager[doors.Length].ToList();

        for (int i = 0; i < doors.Length; i  )
        {
            Doors[i] = doors[i].GetComponent<HoriDoorManager>();
        }
    }
}
  

и в редакторе заставьте его «перезаписать» все остальные флаги, если они изменены:

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

    private bool shouldOverwrite;

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

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

        serializedObject.Update();

        shouldOverwrite = false;

        // Begin a change check here
        EditorGUI.BeginChangeCheck();
        EditorGUILayout.PropertyField(_globalLockState);
        if(EditorGUI.EndChangeCheck())
        {
            // overwrite only once if changed
            shouldOverwrite = true;
        }

        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;

            var serializedDoor = new SerializedObject(door.objectReferenceValue);

            var lockState = serializedDoor.FindProperty("doorLockState");

            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;
            }

            // HERE OVERWRITE
            if(shouldOverwrite)
            {
                lockState.boolValue = _globalLockState.boolValue;
            }
            else
            {
                EditorGUILayout.PropertyField(lockState, new GUIContent("Door "   i   " Lockstate"));
            }

            serializedDoor.ApplyModifiedProperties();
        }

        serializedObject.ApplyModifiedProperties();
    }
}
  

Комментарии:

1. Это позволяет мне только установить все двери глобально заблокированными, но тогда глобальное состояние не проверяется и не может изменить разблокировку дверей. Я имею в виду, что либо в редакторе, либо во время выполнения, если все двери разблокированы, и я проверяю глобальное состояние, это заблокирует их, но затем глобальное состояние не проверяется, поэтому его больше нельзя изменить на разблокированный, только один за другим.

2. О да, я забыл добавить serializedObject.Update(); и serializedObject.ApplyModifiedProperties(); снова

Ответ №2:

Вы могли бы создать статический класс с этим флагом в нем, а затем привязать к нему состояние двери «активировано». Пример:

 public static class GlobalVariables()
{
    public static bool DoorsLocked = true; //Doors would be locked dependent on logic
}
  

Затем вы просто устанавливаете состояние дверей в глобальную переменную в коде. Когда вы меняете глобальный bool, меняются двери.

 GlobalVariables.DoorsLocked = false; //unlocks all doors reading the global bool
  

Надеюсь, это поможет!