The Generic Menu – bon appetité – Part 1

(this article is more for myself and for future documentation, but it might be interesting for people struggling with Generic Menus.)

For my Interaction Project one main goal is that you can add Flags easily to an Actionable Object Flag Dependency.

Meaning : If there is a chest which can be opened, then you add a flag to it like „open“. The open and the close action then depend and act on the state of that flag.

For adding a flag dependency to an Actionable Object, I wanted a fast, organized and quick way to choose a flag. For this I use an Generic Menu in the Editor Script for the Flag Dependency Class, a custom Property Drawer.

(Of course, if you have multiple chest this gets a bit confusing when they have the same name, but I will add a menu entry for the flags in the current editied AO. Also there are some more ways to group AOs, like region, area, group, scene…)

My first approach used simply the flag names. But this did not work out. I need a unique identifier for each flag. That is necessary cause Actionable Objects Flag Dependencies can depend on flags from other AOs. Also to be able to clone an Actionable Object easily, for example windows or doors, you need to be able to create the flags with the same name but with another unique identifier.

Also there was one small problem I solved for this to work : Since there can be more than one Action in an AO logically, the menu in each of these has to report to the AO which Action actually used a menu to create a new flag dependency.

I solved this by adding the unique property path to the return value of the selected menu item.

flagNameToAdd = property.propertyPath + "\n" + flagNameToAdd;

Here I still use the flag name instead of the flag uniquie uint identifier.

Then the next „trick“ I used is to place the selected menu item string into an member of the FlagDependencyDrawer. In the next OnGUI the drawer checks if the member variable is empty, if not, it parses the content and creates the Flag Property accordingly. It also makes sure that the string is for this drawer, which seems a bit redundent, but seemed necessary at the time.

if (property.propertyPath == propertyPath)

Custom Editor Struggles in Unity Stories, Beginner, Volume 1

I am doing a world system for Unity, mainly to seperate GameObjects from WorldObjects, but that is another story for another post.

So I have this structure which uses ConventionOverConfiguration and it works nicely.

But I did not want to do the same steps over and over, so I wrote a custom editor that just takes an active GameObject in the scene. Then it should add all the needed scripts, which itself where taken from other existing scripts or just the World Object / WorldGameObject base classes.

That is easy. But I ran into little troubles after I created these scripts.

I could not add them via script as components to the GameObject, which then I wanted to turn into a prefab.

So I write down the solution here, Stand Unity 2019.6.1.

Lets assume you used System.IO to create the new class for the GameObject. Then you want to add the class to the GameObject with inside the custom editor after the „Create World Object“ Button e.g.

sceneObjectToTurnIntoPrefab.AddComponent<"WorldObject sceneObjectToTurnIntoPrefab">();

You only have the name of the new class at this time as string.

Here is where the problems begin.

In this blog entry Unity3D said that AddComponent can not be used with a string anymore. You have to use the type or a generic class identifier. (You can do that with UnityObjects like GameObject or Renderer, though.)

Since the new class is not yet compiled, it is not availabe, so

sceneObjectToTurnIntoPrefab.AddComponent<Type.GetType(WorldObject sceneObjectToTurnIntoPrefab)>();

brings a error message, since this type does not exist in the assembly.

But you can not just give the new class name as string, even if the class would have been compiled. You need to also give the assembly full qualifier !

If you do not use an own namespace for your e.g. world system (which I did first), I just took a compiled class and got the full assembly qualifier so.

"WorldObject sceneObjectToTurnIntoPrefab, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"

First, I did the error not to import the new script as asset. This I did then with

    AssetDatabase.ImportAsset(MakePathRelative(pathAndNameOfFileToCreate), ImportAssetOptions.ForceUpdate);

    AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport);

Now Unity3D knew about it, and started to compile it ! But, the compiling is asynchron ! So when I called

objectToConvertToAsset.AddComponent(Type.GetType(componentClassNameToAdd + ", Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"));

the class script that I wanted to add as component to the GameObject in the scene which I wanted to turn into a prefab was not availabe yet in the assembly.

I digged deep into the compiling process and the AssetManager, but in the end, after learning quite a few things, I found this Stackoverflow Post, which was exactly what I had been looking for and it works perfectly now.

I will write this down in detail later. (To be continued …)