質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
列挙型

データ型の一種で、要素・メンバなど名前のある値や、型の列挙子によって構成されます。

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

Q&A

1回答

5048閲覧

[Unity] inspectorで複数のEnum型から型を選択したい&選んだ型のEnumリストから選択できるようにしたい

mushipan0929

総合スコア56

列挙型

データ型の一種で、要素・メンバなど名前のある値や、型の列挙子によって構成されます。

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

0グッド

0クリップ

投稿2021/05/04 04:07

前提

・Inspector内
食べたい料理の国を選択→その国の料理を一つ選択
・class内
選んだ料理の国によってどの国の料理、どの料理を選んだかログに表示させたい

やりたい事

・Insoector内
複数のEnum型から一つ型を選び、その型そのものを格納出来る変数を作りたい
さらに選んだEnun型のリストを表示してその中から一つ格納できる変数を作りたい

試したこと

やり方が分からないので、それぞれの国の料理を入れる型を用意して分岐させました。
ただこのやり方だと対象の国が増える度分岐が大変なことになるので改善策をお聞きしたいです。

FoodMenuクラス内にこんな感じに出来れば...といったコメントを残しました。
Type型変数のようなものがあると思い探しましたが同名のクラスしか見つかりませんでした。

該当のソースコード

C#

1using System.Collections; 2using System.Collections.Generic; 3using UnityEngine; 4using System; 5 6public class JOE : MonoBehaviour 7{ 8 [SerializeField] private FoodMenu breakfast; 9 public Type aa; 10 11 private void Start() 12 { 13 if (breakfast.japaneseFood == JapaneseFood.NONE) 14 { 15 if (breakfast.chineseFood == ChineseFood.NONE) 16 { 17 Debug.Log("今日のご飯無し!!!"); 18 } 19 else 20 { 21 Debug.Log("今日のご飯は中華料理の" + 22 Enum.GetName(typeof(ChineseFood), breakfast.chineseFood) + "です"); 23 } 24 } 25 else 26 { 27 if (breakfast.chineseFood == ChineseFood.NONE) 28 { 29 Debug.Log("今日のご飯は日本料理の" + 30 Enum.GetName(typeof(JapaneseFood), breakfast.japaneseFood) + "です"); 31 } 32 else 33 { 34 Debug.Log("一食一品まで!!!"); 35 } 36 } 37 } 38} 39 40[System.Serializable] 41public class FoodMenu 42{ 43 public JapaneseFood japaneseFood; 44 public ChineseFood chineseFood; 45 46 // 理想 47 // public Type foodType; 48 // public type(foodType) food; 49} 50 51 52// 主食 53public enum JapaneseFood 54{ 55 UDON, 56 SOBA, 57 NONE 58} 59 60public enum ChineseFood 61{ 62 RAMEN, 63 GYOZA, 64 NONE 65}

イメージ図

イメージ説明

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2021/05/04 07:15

なんか自分も昔調べた記憶があります。ネットで漁っただけですが、 言語仕様として間違っているとか、静的言語を否定する事です、みたいな批判的な回答だらけだった記憶があります。 Object型にしてキャストとか、Dynamicで、Reflectionでとか。思っていたのと違う、、、みたいな方向のお話になっていった記憶があります。 知識ないので結局理解出来ませんでした。今も理解出来てません。 なのでいろいろ適当ですが現実的にはこういう系なのか? using UnityEngine; public class Food : MonoBehaviour { public enum Country { JP, CHA, NONE, } string Name(Country country) { return country == Country.JP ? "日本" : country == Country.CHA ? "中国" : "無し"; } IFood food; private void Start() { food = new UDON(); Debug.Log("今日のごはんは" + Name(food.Country) + "料理の" + food.Name + "です"); food = new RAMEN(); Debug.Log("今日のごはんは" + Name(food.Country) + "料理の" + food.Name + "です"); } } public interface IFood { string Name { get; } Food.Country Country { get; } } public class UDON : IFood { string name = "うどん"; public string Name => name; Food.Country country = Food.Country.JP; public Food.Country Country => country; } public class SOBA : IFood { string name = "そば"; public string Name => name; Food.Country country = Food.Country.JP; public Food.Country Country => country; } public class RAMEN : IFood { string name = "ラーメン"; public string Name => name; Food.Country country = Food.Country.CHA; public Food.Country Country => country; } public class GYOZA : IFood { string name = "チャオズ"; public string Name => name; Food.Country country = Food.Country.CHA; public Food.Country Country => country; } public class NONE : IFood { string name = "なし"; public string Name => name; Food.Country country = Food.Country.NONE; public Food.Country Country => country; } 私も詳しい方に教えていただきたいです。やっぱり理解できない気もしますが
guest

回答1

0

少々長くなってしまいすみませんが、一案として下記のようなものはどうでしょうかね?
とはいえ、このやり方だとFoodMenuを使う側が必要に応じてfoodを適切な型にキャストしてやる必要があるでしょう。型安全性の観点からは1570pさんの方針の方が好ましいかもしれません。

lang

1using System; 2using System.Collections.Generic; 3using System.Linq; 4using System.Reflection; 5using UnityEngine; 6#if UNITY_EDITOR 7using UnityEditor; 8#endif 9 10// スクリプトからの使用時は、一例としては下記のようになります 11public class JOE : MonoBehaviour 12{ 13 [SerializeField] private FoodMenu breakfast; 14 [SerializeField] private FoodMenu lunch; 15 [SerializeField] private FoodMenu dinner; 16 17 private void Start() 18 { 19 PrintMeal(breakfast, "朝"); 20 PrintMeal(lunch, "昼"); 21 PrintMeal(dinner, "晩"); 22 } 23 24 private static void PrintMeal(FoodMenu menu, string period) 25 { 26 var foodTypeString = "謎の料理"; 27 if (menu.foodType == typeof(JapaneseFood)) 28 { 29 foodTypeString = "日本料理"; 30 } 31 else if (menu.foodType == typeof(ChineseFood)) 32 { 33 foodTypeString = "中華料理"; 34 } 35 36 var foodString = menu.food.ToString(); 37 if (foodString == "NONE") 38 { 39 Debug.Log($"今日の{period}ご飯無し!!!"); 40 } 41 else 42 { 43 Debug.Log($"今日の{period}ご飯は{foodTypeString}の{foodString}です"); 44 } 45 } 46} 47 48[Serializable] 49public class FoodMenu : ISerializationCallbackReceiver 50{ 51 // インスペクター上のポップアップに表示するのを許可する型をリストにしておき... 52 public static readonly List<Type> FoodTypes = new List<Type> 53 { 54 typeof(JapaneseFood), 55 typeof(ChineseFood) 56 }; 57 58 // その先頭の型をデフォルトの型としました 59 private static readonly Type DefaultType = FoodTypes[0]; 60 61 // また、デフォルトの食事は「NONE」としました 62 // どの国の料理にも共通して含まれる値であることを前提としています 63 private static readonly string DefaultFoodString = "NONE"; 64 65 // foodTypeやfoodを直接シリアライズする代わりに... 66 [NonSerialized] public Type foodType; 67 [NonSerialized] public Enum food; 68 69 // foodTypeの型名とfoodの列挙名を文字列としてシリアライズすることにしました 70 [SerializeField][HideInInspector] private string serializedFoodType; 71 [SerializeField][HideInInspector] private string serializedFood; 72 73 public void OnBeforeSerialize() 74 { 75 // foodTypeがnullならデフォルトの型でシリアライズし... 76 foodType ??= DefaultType; 77 if (!foodType.IsEnum) 78 { 79 Debug.LogError($"{foodType} is not an enum type."); 80 return; 81 } 82 serializedFoodType = foodType.AssemblyQualifiedName; 83 84 // foodがnullならデフォルトの食事でシリアライズしました 85 food ??= (Enum)Enum.Parse(foodType, DefaultFoodString); 86 serializedFood = food.ToString(); 87 } 88 89 public void OnAfterDeserialize() 90 { 91 // デシリアライズ時には文字列からTypeやEnumを復元します 92 // 失敗すればデフォルト値としました 93 var deserializedFoodType = (string.IsNullOrWhiteSpace(serializedFoodType) 94 ? DefaultType 95 : Type.GetType(serializedFoodType, false)) ?? DefaultType; 96 if (!deserializedFoodType.IsEnum) 97 { 98 Debug.LogError($"{deserializedFoodType} is not an enum type."); 99 deserializedFoodType = DefaultType; 100 } 101 102 Enum deserializedFood; 103 try 104 { 105 deserializedFood = (Enum)Enum.Parse( 106 deserializedFoodType, 107 string.IsNullOrWhiteSpace(serializedFood) 108 ? DefaultFoodString 109 : serializedFood); 110 } 111 catch (ArgumentException) 112 { 113 // 型の変換時に失敗するのは異常事態ですのでエラーメッセージを出しましたが、 114 // ある国の料理名が別の国の料理名に含まれないということはありふれているように思い 115 // Debug.LogErrorによるメッセージは出さずにデフォルト値で置き換えることにしました 116 deserializedFood = (Enum)Enum.Parse(foodType, DefaultFoodString); 117 } 118 119 foodType = deserializedFoodType; 120 food = deserializedFood; 121 } 122} 123 124// 主食 125public enum JapaneseFood 126{ 127 UDON, 128 SOBA, 129 NONE 130} 131 132public enum ChineseFood 133{ 134 RAMEN, 135 GYOZA, 136 NONE 137} 138 139// インスペクター上でポップアップを表示させるため、FoodMenu用のPropertyDrawerも作成しました 140#if UNITY_EDITOR 141[CustomPropertyDrawer(typeof(FoodMenu))] 142public class FoodMenuDrawer : PropertyDrawer 143{ 144 // serializedFoodTypeやserializedFoodにアクセスするためのフィールド情報を取得しておきます 145 private static readonly FieldInfo SerializedFoodTypeField = typeof(FoodMenu).GetField( 146 "serializedFoodType", 147 BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.NonPublic); 148 private static readonly FieldInfo SerializedFoodField = typeof(FoodMenu).GetField( 149 "serializedFood", 150 BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.NonPublic); 151 152 // FoodMenuを一つ作っておき、シリアライズされた文字列と 153 // TypeやEnumの相互変換を担当させることにしました 154 private readonly FoodMenu menu = new FoodMenu(); 155 156 // 後述のfoodType用ポップアップの項目名一覧です 157 private string[] cachedFoodTypeStrings; 158 159 // 後述の処理効率化のための前回取得文字列を保持するフィールドです 160 private string cachedSerializedFoodType; 161 private string cachedSerializedFood; 162 163 // 後述のように項目を折りたたみ式で表示するようにしました 164 // そこで項目が折り畳まれているかどうかも考慮し、必要な表示領域の高さを求めるようにします 165 public override float GetPropertyHeight(SerializedProperty property, GUIContent label) 166 { 167 const int itemCount = 2; 168 var lineCount = property.isExpanded ? itemCount + 1 : 1; 169 return (lineCount * EditorGUIUtility.singleLineHeight) + ((lineCount - 1) * EditorGUIUtility.standardVerticalSpacing); 170 } 171 172 public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) 173 { 174 EditorGUI.BeginProperty(position, label, property); 175 176 // 折りたたみ式で表示することにしました 177 position.height = EditorGUIUtility.singleLineHeight; 178 property.isExpanded = EditorGUI.Foldout(position, property.isExpanded, label); 179 if (property.isExpanded) 180 { 181 // 折りたたみが展開されていれば各項目の描画に進みます 182 // まずpropertyからシリアライズされた文字列を取得し... 183 var serializedFoodTypeProperty = property.FindPropertyRelative("serializedFoodType"); 184 var serializedFoodProperty = property.FindPropertyRelative("serializedFood"); 185 var serializedFoodType = serializedFoodTypeProperty.stringValue; 186 var serializedFood = serializedFoodProperty.stringValue; 187 188 // menuに文字列をType、Enumに変換させます 189 // このとき、多少なりとも効率化しようと思い、前回取得時の文字列と 190 // 異なっている時のみ変換を行うことにしました 191 if ((serializedFoodType != cachedSerializedFoodType) || (serializedFood != cachedSerializedFood)) 192 { 193 SerializedFoodTypeField.SetValue(menu, serializedFoodTypeProperty.stringValue); 194 SerializedFoodField.SetValue(menu, serializedFoodProperty.stringValue); 195 menu.OnAfterDeserialize(); 196 cachedSerializedFoodType = serializedFoodType; 197 cachedSerializedFood = serializedFood; 198 } 199 200 // 変換結果からfoodTypeのインデックス(foodTypeがFoodMenu.FoodTypesリスト中の何番目か)と 201 // foodを取得し... 202 var currentFoodTypeIndex = Mathf.Max(FoodMenu.FoodTypes.IndexOf(menu.foodType), 0); 203 var currentFood = menu.food; 204 205 EditorGUI.indentLevel++; 206 207 // 1項目分の垂直移動量を求め、次の項目位置までずらし... 208 var stride = EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; 209 position.y += stride; 210 211 // ラベル部分とポップアップ部分の領域を計算し... 212 const float minValueWidth = 16.0f; 213 var labelPosition = position; 214 var valuePosition = position; 215 labelPosition.width = EditorGUIUtility.labelWidth; 216 valuePosition.width = position.width - labelPosition.width; 217 if (valuePosition.width <= minValueWidth) 218 { 219 labelPosition.width = valuePosition.width = position.width * 0.5f; 220 } 221 valuePosition.x += labelPosition.width; 222 223 // foodType用ラベルとポップアップを描画、選択された項目インデックスを取得し... 224 EditorGUI.LabelField(labelPosition, nameof(menu.foodType)); 225 cachedFoodTypeStrings ??= FoodMenu.FoodTypes.Select(t => t.Name).ToArray(); 226 var newFoodTypeIndex = EditorGUI.Popup(valuePosition, currentFoodTypeIndex, cachedFoodTypeStrings); 227 228 // 次の行へ進み... 229 labelPosition.y += stride; 230 valuePosition.y += stride; 231 232 // food用ラベルとポップアップを描画、選択されたEnumを取得し... 233 EditorGUI.LabelField(labelPosition, nameof(menu.food)); 234 var newFood = EditorGUI.EnumPopup(valuePosition, currentFood); 235 236 // 最後に、インスペクター操作によって値が変化していればプロパティを更新します 237 if ((newFoodTypeIndex != currentFoodTypeIndex) || !Equals(newFood, currentFood)) 238 { 239 menu.foodType = FoodMenu.FoodTypes[newFoodTypeIndex]; 240 menu.food = newFood; 241 menu.OnBeforeSerialize(); 242 serializedFoodTypeProperty.stringValue = SerializedFoodTypeField.GetValue(menu) as string; 243 serializedFoodProperty.stringValue = SerializedFoodField.GetValue(menu) as string; 244 } 245 246 EditorGUI.indentLevel--; 247 } 248 249 EditorGUI.EndProperty(); 250 } 251} 252#endif

図1

図2

投稿2021/05/04 17:26

Bongo

総合スコア10807

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問