首页 > 解决方案 > 为什么门/s颜色标志一直是真/假,并且一直在真假之间变化?

问题描述

此脚本附在门上:

即使变量 doorLockState 设置为 true 在编辑器中启用 true 并且门应该是红色的,门是绿色的,使用断点 ColorDoors 中的状态变量在红色和绿色之间不停变化,一旦状态为真,下一个它是假的,但是在编辑器中,它一直被检查为 true :

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

[ExecuteAlways]
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 Update()
    {
        ColorDoors(Color.red, Color.green, doorLockState);
    }

    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.sharedMaterial.shader = Shader.Find("Unlit/ShieldFX");

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

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

    private void LockState(bool state)
    {
        var collider = gameObject.GetComponent<BoxCollider>();

        if (state == false)
        {
            collider.size = new Vector3(2.3f, 2.736307f, 2.5f);
            collider.center = new Vector3(0, 1.378154f, 0);
            collider.transform.localPosition = new Vector3(-1.57f, 0, -2.98f);
            collider.isTrigger = true;
        }
        else
        {
            collider.size = new Vector3(2.3f, 2.736307f, 3);
            collider.center = new Vector3(0, 1.378154f, 0);
            collider.transform.localPosition = new Vector3(-1.57f, 0, -2.98f);
            collider.isTrigger = false;
        }
    }
}

Hierarchy 中的门构造函数和 Inspector 中的脚本的屏幕截图:

门构造器

对象 Horizo​​ntal_Doors_Kit 标记设置为 Door。

这是应该让我控制门的编辑器脚本,但这里也有一个问题,因为在编辑器中它没有列出所有门:

using UnityEditor;
using UnityEngine;

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

编辑器脚本检查器的屏幕截图:

门编辑器

它显示了两个变量 Global Lock State,但它也应该列出 12 个门。

和脚本 DoorsLockManager :

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

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
        {
            _globalLockState = 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 = _globalLockState;
            }
        }
    }

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

标签: c#unity3d

解决方案


目前,您的Doors列表仅Awake()在您的调用DoorsLockManager(并且无法从检查器访问)时更新,这意味着该列表在运行时之外始终为空。更改此设置应允许列表在编辑时显示在检查器中。

对DoorsLockManager.cs进行以下小改动:

private void Awake()
{
    UpdateDoors();
}

public void UpdateDoors()
{
    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>();
    }
}

并将以下内容添加到DoorsLockManagerEditor.csOnInspectorGUI()方法的顶部:

if (GUILayout.Button("Update Door List"))
{
    ((DoorsLockManager)target).UpdateDoors();
}

应该通过提供一个按钮来在需要时更新列表来实现这一点。

单个和全局锁定状态现在应该可以正常工作,但是两个Global Lock State切换中只有一个可以正常工作,这可以通过base.OnInspectorGUI();从 DoorsLockManagerEditor.cs 中的OnInspectorGUI()方法中删除或在DoorsLockManager.cs添加[HideInInspector]before来修复[SerializeField] private bool _globalLockState;

目前,HoriDoorManager.cs只会在运行时更新它们各自的门,因为该ColorDoors方法仅在Awake()和中调用Update()。每当doorLockStatebool 更改时,可以通过如下方式修改您的GetLockState属性来进行更新:

public bool GetLockState
{
    get { return doorLockState; }
    set
    {
        doorLockState = value;
        ColorDoors(Color.red, Color.green, doorLockState);
    }
}

并分配给此属性,而不是DoorsLockManager.csGlobalLockState属性中的支持变量(将第 28 行:替换为)。door.doorLockState = _globalLockState;door.GetLockState = _globalLockState;


编辑:这是一个快速破解,但将以下内容添加到DoorsLockManager.cs

public void UpdateColors()
{
    foreach (var door in Doors)
    {
        door.GetLockState = door.GetLockState;
    }
}

以及在另一个按钮下方的 DoorsLockManagerEditor.cs 中的OnInspectorGUI()以下内容

    if (GUILayout.Button("Update Door Colors"))
    {
        ((DoorsLockManager)target).UpdateColors();
    }

应该允许您告诉检查员手动更新它们的颜色。


推荐阅读