- Home /
Accessible collection of Type
I'm using collection of Type
inside my class and wondering how to make it accessible through inspector or editor window.
To be more specific I create some system based on enums, which takes a few Types, checks it is an enum type, gets base name of enum and it's elements etc. For now it's just a list fused inside of a class: public static readonly
List<Type> EnumsToTransation = new List<Type>
{
(typeof(Items)),
(typeof(Dialogs))
};
It won't be modified to often and can be stored that way, but access through the inspector will be wery handy and "the right thing", I guess. Thanks in advance
Just came across your question here. It's not really clear what you mean by "access". The System.Type class isn't a serializable class and even if it was, what should be displayed? Furthermore your can't changed / edit anything on a System.Type object.
$$anonymous$$y guess is that you want to use the Type of an enum for things like EditorGUILayout.EnumPopup. However those methods don't take a Type object as parameter. They use GetType internally to get the type of the used enum. Also when using an enum to select a certain index / mask you usually use a distinct enum. What's the point of picking an enum from a list of enums?
Your example doesn't provide and usecase. "Items" and "Dialogs" doesn't seem to have any relationship that would make sense. If you're still waiting for an answer you should be more precise what you want to do. If you don't have more code as example, try to describe it.
That's the point, th`enter code here`is enums is not have any relations.
Okay, my use-case: I get this Type, check if it is an Enum type, use Enum.GetValues(Type) to get array of values, form values into serializable structures. Strucrure represent the localization entry. This structures may be serialized = imported/exported for easier localization.
As a result I have method to get localized string based on enum
public string Get(Enum enumEntry)
{
var entry = Base.Entries.SingleOrDefault(e => e.UnderlyingGroup.StartsWith(enumEntry.GetType().Name) && e.Name == enumEntry.ToString());
if (entry == null) return string.Empty;
return (CurrentLanguage == LocalizationLanguage.Russian) ? entry.Russian : entry.English;
}
for now it stores only two languages, but can be easily extended.
so now I can get string by enum in a generic way
Get(Items.Sword);
or
Get($$anonymous$$enuItem.NewGame);
Since I'm a fan of enums and looking for a solution to get values by enum and not by string, and group them.
Hope my English is readable :)
So all I need is to dynamically get all Enum values as a strings and Enum Type name as a string on request in Editor... Don't found a better solution that'll handle this better than List of a Type
Answer by Bunny83 · May 17, 2015 at 05:06 PM
Ok, i see what you want to do here. However Enums aren't great when it comes to generalization. Enums are represented by integer values (as you probably know). An enum type represents it's own type. Is a value type (since it's an integer) and in almost all generalizations it has to be boxed into a reference type. The System.Enum class is actually a class, not a struct.
Anyways, since i don't know how your localization structure looks like here are some suggestions:
Generally you should distinguish between how the localization data is serialized and how it's stored / used at runtime.
I would also suggest to not use Unity's serialization system to actually define the localization data. If you change the structure of your classes it can happen quite easily that you loose everything you've entered already. It's best to store the localization in a seperate file (JSON / XML / ...). That also simplifies translation to other languages (which is usually done by external people).
For runtime use i would group the localization information by language, followed by the group (your System.Type) followed by the actual item. See example below.
For storing the information in a file it's best to not use the enum values as it's difficult to remember what number represents what.
The localization file should be read at start. If you grouped the data by language you can also only load the current language entries.
Example of a localization file:
<languages>
<language name="english">
<enumgroup name="Items">
<string name="Sword">sword</string>
<string name="Apple">apple</string>
</enumgroup>
<enumgroup name="MenuItem">
<string name="NewGame">New Game</string>
<string name="Quit">Quit Game</string>
</enumgroup>
</language>
<language name="russian">
<enumgroup name="Items">
<string name="Sword"> ... </string>
<string name="Apple"> ... </string>
</enumgroup>
<enumgroup name="MenuItem">
<string name="NewGame"> ... </string>
<string name="Quit"> ... </string>
</enumgroup>
</language>
</languages>
At runtime you might want to use a Dictionary>
for your enum localization data. When you read in the localization file you could use the enumgroup's name to either find the enum Type with System.Type.GetType or if you use namespaces (which makes it difficult to find the correct type) use your System,Type List you have defined in your question. For each type you create a dedicated dictionary which maps an enum value to a string. You can use Enum.Parse to get the actual integer value for a certain string. With that you can set the string entries in the dictionary.
To query a string value at runtime your method could look like this:
private Dictionary<System.Type, Dictionary<int, string>> localization;
private Dictionary<System.Type, Dictionary<int, string>> fallbackLocalization;
private string GetDefault(System.Enum enumEntry)
{
var type = enumEntry.GetType();
Dictionary<int, string> dict;
if (!fallbackLocalization.TryGetValue(type, out dict))
return "Localization missing";
int val = (int)(object)enumEntry;
string result;
if (dict.TryGetValue(val, out result))
return result;
return "Localization missing";
}
public string Get(System.Enum enumEntry)
{
var type = enumEntry.GetType();
Dictionary<int, string> dict;
if (!localization.TryGetValue(type, out dict))
return GetDefault(enumEntry);
int val = (int)(object)enumEntry;
string result;
if (dict.TryGetValue(val, out result))
return result;
return GetDefault(enumEntry);
}
Usually you have a default language that usually is ensured that it contains a localization string for everything (Doesn't need to be english but usually it's english ^^). The Get method here first tries to get the value from the current localization structure and if it fails it gets the value from the default localization.
If you want to load all languages you could manage them again with a dictionary like this:
private Dictionary<LocalizationLanguage, Dictionary<System.Type, Dictionary<int, string>>> localization;
All that is usually packed into a Localization class which handles all this language switching in one place.
XML is usually nicer to structure the data. However personally i would probably go with JSON as most XML parsers are quite large (in the sense of assembly size).
Another nice thing about an external loc-file is that you can ship an update / patch without rebuilding the game.
Wow, thanks for this answer. Truely, there a dozens of ways to get it done. Personally I use ScriptableObject to store data ingame and EditorWindow to manage and imort/export. Ins$$anonymous$$d of xml I use my custom text format to acheive a bit clearer presentation, since there a wery few of people will use this exported file during development and them won't do anything fancy that'll break the import process :)
What you just shown is how to store and get data. However, the only feature that forced me to use Types first of all is ability to dinamically generate empty items.
For example during development I just need to add new Item. A Banana :) So I add new Item.Banana. I don't even remember about Localization System at the moment, I just need to add new element. But Localization System remember about Item, so next time on base validation it'll show alert: "Hey, bro, there is some Banana localization entry need to be filled".
Any ideas on that? Thanks again, I appreciate your help
Well, that's not that hard if you have your localization system working. If you keep your EnumsToTransation list up-to-date you can simply iterate through all enums, get all names or values (depending how you actually manage your localization data) and simply iterate through all names / values and see if your localization data has the strings for those items setup. If not print a warning. The Enum class has two methods (GetNames and GetValues) which will return the names / values of the enum members for a given enum type.
Yes, I already had all this done in a times when I asked this question :) And question was not how to implement localization system, but how to reference Enum Types via Custom Inspector ins$$anonymous$$d of hardcoded version of EnumsToTransation initialization. I had read your answer today and thought there is got to be a way to get Type from string via reflection. So in custom inspector we may just print name of the type and it'll be validated on spot to ensure that this string represent correct enum type. Don't think I need to go that deep in my case though.
Thanks you for reply
Your answer
Follow this Question
Related Questions
c# enum wont show in inspector 3 Answers
Multiple Cars not working 1 Answer
Array to match Enum in Inspector 1 Answer
if statement inside variables of a class 1 Answer
Restricting enum options in inspector when using a propertydrawer. 0 Answers