【问题标题】:Unity Custom Drop Down selection for Arrays C#数组 C# 的 Unity 自定义下拉选择
【发布时间】:2020-07-08 13:27:56
【问题描述】:

我正在尝试简化 wave 系统中预制件的选择。

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

public class WaveList : MonoBehaviour
{
    [System.Serializable]
    public class Wave
    {
        public int waveCount;
        public string[] enemies;
        public int[] enemyLVL;
        public float[] delay;
    }

    public Wave[] waves;
}

我试图在 Unity 编辑器中有一个下拉菜单来选择每个敌人的值 “敌人”通过下拉菜单。

非常感谢所有帮助。

干杯

【问题讨论】:

  • 这需要为您的班级提供Custom PropertyDrawer ...您从哪里获得可用值?
  • 我想要一个可以随时间添加的数组,将根据解决方案进行设置

标签: c# unity3d dropdown unity-editor unity3d-editor


【解决方案1】:

通常你的问题很宽泛,需要很多时间。

不过,我有点喜欢 Unity 编辑器脚本,老实说,它非常复杂,而且有点难以入门,所以考虑一下这是一个进入编辑器脚本的小起点 ;)


你想要达到的目标需要四件事:

编辑器脚本可以随心所欲地变得复杂:D 所以我只会实现一个非常基本的脚本。我希望这是满足您需求的良好起点:

using System;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif

public class WaveList : MonoBehaviour
{
    [Serializable]
    public class Wave
    {
        public int waveCount;
        public string[] enemies;
        // Additional field for storing the currently selected enemy index
        public int selectedEnemy;
        public int[] enemyLVL;
        public float[] delay;

#if UNITY_EDITOR
        [CustomPropertyDrawer(typeof(Wave))]
        private class WavePropertyDrawer : PropertyDrawer
        {
            // This will be generated during the OnGUI call
            // Maybe not the cleanest way but it works for now ;)
            private float height;
            private string[] availableEnemies;

            // This method is required so other property drawers (like the Wave[] waves)
            // know how much space to reserve for drawing this property
            public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
            {
                return height + EditorGUIUtility.singleLineHeight;
            }

            // Draw the property inside the given rect
            public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
            {
                // reset the height
                height = 0;

                // Using BeginProperty / EndProperty on the parent property means that    
                // prefab override logic works on the entire property.
                EditorGUI.BeginProperty(position, label, property);
                {
                    // Get the rect for where to draw the label/foldout
                    var labelRect = new Rect(position.x, position.y, position.width, EditorGUIUtility.singleLineHeight);
                    position.y += EditorGUIUtility.singleLineHeight;
                    height += EditorGUIUtility.singleLineHeight;

                    // Draw the foldout
                    property.isExpanded = EditorGUI.Foldout(labelRect, property.isExpanded, property.displayName);
                    if (property.isExpanded)
                    {
                        // indent children for better readability
                        EditorGUI.indentLevel++;
                        {
                            // Get serialized Properties
                            var serializedWaveCount = property.FindPropertyRelative(nameof(waveCount));
                            var serializedEnemies = property.FindPropertyRelative(nameof(enemies));
                            var serializedSelectedEnemy = property.FindPropertyRelative(nameof(selectedEnemy));
                            var serializedEnemyLVL = property.FindPropertyRelative(nameof(enemyLVL));
                            var serializedDelay = property.FindPropertyRelative(nameof(delay));

                            // Calculate rects
                            var waveCountHeight = EditorGUI.GetPropertyHeight(serializedWaveCount);
                            var waveCountRect = new Rect(position.x, position.y, position.width, waveCountHeight);
                            position.y += waveCountHeight;
                            height += waveCountHeight;

                            var enemiesHeight = EditorGUI.GetPropertyHeight(serializedEnemies, true);
                            var enemiesRect = new Rect(position.x, position.y, position.width, enemiesHeight);
                            position.y += enemiesHeight;
                            height += enemiesHeight;

                            var selectedEnemyRect = new Rect(position.x, position.y, position.width, EditorGUIUtility.singleLineHeight);
                            position.y += EditorGUIUtility.singleLineHeight;
                            height += EditorGUIUtility.singleLineHeight;

                            var enemeiesLevelHeight = EditorGUI.GetPropertyHeight(serializedEnemyLVL, true);
                            var enemeiesLVLRect = new Rect(position.x, position.y, position.width, enemeiesLevelHeight);
                            position.y += enemeiesLevelHeight;
                            height += enemeiesLevelHeight;

                            var delayHeight = EditorGUI.GetPropertyHeight(serializedDelay, true);
                            var delayRect = new Rect(position.x, position.y, position.width, delayHeight);
                            position.y += delayHeight;
                            height += delayHeight;

                            // Draw Fields
                            availableEnemies = new string[serializedEnemies.arraySize];
                            for (var i = 0; i < serializedEnemies.arraySize; i++)
                            {
                                availableEnemies[i] = serializedEnemies.GetArrayElementAtIndex(i).stringValue;
                            }

                            EditorGUI.PropertyField(waveCountRect, serializedWaveCount);
                            EditorGUI.PropertyField(enemiesRect, serializedEnemies, true);
                            if (serializedEnemies.arraySize == 0)
                            {
                                serializedSelectedEnemy.intValue = -1;
                            }

                            serializedSelectedEnemy.intValue = EditorGUI.Popup(selectedEnemyRect, serializedSelectedEnemy.displayName, serializedSelectedEnemy.intValue, availableEnemies);
                            EditorGUI.PropertyField(enemeiesLVLRect, serializedEnemyLVL, true);
                            EditorGUI.PropertyField(delayRect, serializedDelay, true);
                        }
                        EditorGUI.indentLevel--;
                    }
                }
                EditorGUI.EndProperty();
            }
        }
#endif
    }

    public Wave[] waves;
}

所以稍后为了得到你要访问的实际选定的敌人

var enemy = someWave.enemies[someWave.selectedEnemy];

现在您可以添加、编辑和删除enemies,然后通过下拉菜单选择特定的


关于如何为所有波全局定义可用的enemies 的问题:

您可能应该将可用数组移动到 WaveList 类中,并且只在其中定义一次。

同样,这里现在可以工作,但很脏

public class WaveList : MonoBehaviour
{
    public string[] availableEnemies;
}

然后在Wave中只存储选中的index like

[Serializable]
public class Wave
{
    public int waveCount;
    public int selectedEnemy;
    public int[] enemyLVL;
    public float[] delay;

    ...

然后在属性抽屉中而不是

var serializedEnemies = property.FindPropertyRelative(nameof(enemies));

您宁愿直接从WaveList 类使用

var serializedEnemies = property.serializedObject.FindProperty(nameof(WaveList.availableEnemies));

注意:在手机上打字,但我希望思路清晰

【讨论】:

  • 太棒了。谢谢你的时间。是否可以让 Enemies[] 中的每个元素成为所有波都取自一个数组的下拉选择?基本上,我试图让在每一波中选择敌人变得容易和快速。当您有 100 多个波浪时,拖放到数组中将成为一场噩梦
  • @CodingNoob_Help 如前所述,您将需要一种填写可用敌人值的方法……从哪里获取这些值取决于您。你可以例如将数组本身放在WaveList 类中,并为此实现一个CustomEditor,您可以将数组注入Waves 中的所有项目...
猜你喜欢
  • 1970-01-01
  • 2023-03-09
  • 2023-03-06
  • 1970-01-01
  • 1970-01-01
  • 2013-08-17
  • 2015-11-17
  • 2015-12-04
  • 1970-01-01
相关资源
最近更新 更多