Unity Dropdown UI Prototype

CodeyChaos

New member
Messages
13
Reactions
2
Points
3
Demo.jpg

I’ve been working on a Unity dropdown menu system for use in VaM, mainly as a clothing selector UI, but with the idea to mod it to use for selecting animations or whatever else. I don’t intend on releasing this as a plugin since the script more resembles the story of a love/hate relationship between an inexperienced coder and his amnesia suffering AI associate, Chat GPT. However, I do appreciate all of the information and assistance available here, so I would like to leave some notes and basic bits of code here in case it's of use to anyone interested in doing something similar, and hopefully better. Or in case anyone would like to tell me to bin this trash and make it properly, or use something better that I may not have found yet.

DISCLAIMER. I DON’T KNOW WHAT I’M DOING

Everything following is most probably a guide on how to do things the hardest and most inefficient way possible, and this is merely a proof of concept prototype. it’s not something I’d suggest copying, but maybe there’s something in here that can save someone a bit of trial and error, or show which paths to avoid or improve on. I'm only providing some of the basic bits of code that took me a while to track down in case it helps any beginners.

USE CASE / WHAT IT DOES

The main use case is for grouping clothing items into categories of items which only 1 should be active at a time. Each dropdown pictured is a separate Custom Unity Asset atom with its own instance of the plugin. The plugin UI gives you a string chooser for each dropdown option. The choosers are only needed for setting up or editing the dropdown. Each chooser is populated with every clothing item. When an item is selected in a chooser, its name is updated to the corresponding dropdown option. It can then be selected in the dropdown to activate that item on the person atom, while deactivating any other item from that same dropdown (items in other dropdowns are not affected). Each dropdown also has a toggle so that the selected dropdown option can be deactivated, essentially deactivating that category.

BUILD PROCESS

It would’ve been a lot simpler but I stupidly decided to add the ability to select the quantity of options which would be available for the dropdown. This lead me round in circles trying to get the choosers selections to save/load with a scene if they'd been recreated by setting a new quantity. After a few dead ends an a catch 22, I resorted to a compromise of setting a max number of choosers to start with, then giving the option in the plugin UI to reduce the amount of options. This means that some can be destroyed and none will be recreated. Downside is that to you'd have to reset this instance of the plugin to get the full amount of options back, which means reselecting the option for each chooser in this dropdown.
Code:
        JSONStorableStringChooser itemChooser = new JSONStorableStringChooser(chooserName, new List<string>(), "Empty", chooserName);
            CreateFilterablePopup(itemChooser, true);
            RegisterStringChooser(itemChooser);
            itemChoosers[i] = itemChooser;

I found it necessary to create the choosers immediately, then populate them later, after the dropdown options and toggles are set up. Otherwise they either wont populate or wont save with a scene.
Code:
          var storables = person.GetStorableIDs()

              .Select(id => person.GetStorableByID(id))

              .Where(storable => storable.storeId == "geometry");

The Custom Unity Asset is a standard Unity Dropdown UI, but I redid the sprites and font so it can be a decent size in VR without bad pixilation.

When loading a scene from save, it wasn’t loading my CUA in time to be found by the plugin, so I just added a 1 sec delay at the start before it starts looking for it.

I’ve had issues trying to find components in atoms if the atom is parented in VaM. The most reliable way I could find to get my CUA components was to get its atom and specify the location of the component.
Code:
      Transform toggleTransform = parentObject.transform.Find("object/rescaleObject/UnityDropdown(Clone)/Toggle");

The location where the CUA ends up in VaM was not expected, and I only found it by getting Chat GPT to write a script to log the hierarchy of a VaM scene or atom.

Once an existing toggle and dropdown option are found it duplicates enough of them to match the number of choosers. What looks like 1 toggle for each dropdown is actually a toggle for each dropdown option. The dropdown switches the active states of its toggles so that you only see the toggle associated with the currently selected dropdown option. A lot of the function is in an OnToggleChange method. The OnDropdownOptionChanged method mainly just sets the toggles isOn and bool.
Code:
JSONStorableBool boolParam = personAtom.GetStorableByID("geometry").GetBoolJSONParam(fullName);
                          if (boolParam != null)
                          {
                              boolParam.val = isVisible && (i == chooserIndex);
                          }

Regardless of how the dropdown was edited in Unity, It wouldn’t import the same render order for the dropdown list. The main dropdown area rendered fine, but not the options that drop below. The only solution I could find was a method to register the dropdown list canvas when the dropdown is clicked. You could also set the render order here too
Code:
SuperController.singleton.AddCanvas(dropdownListCanvas);
//dropdownListCanvas.sortingOrder = 10;

The problem with that is that although you can get an onValueChanged event from a dropdown, that happens too late to run the canvas method, and I couldn’t find a way to call a method immediately on dropdown clicked. I resorted to duct tape and zip ties by rebuilding it in Unity with an invisible button over the main dropdown area that handles the dropdown function. I could then use the button onClick in VaM.

There’s a method for OnChooserChange, which is mostly for the scenario where the user is selecting an item for a dropdown option that is currently active. In this case the item will be activated immediately, while deactivating a previously selected option. It updates a newOption string and previousOption string so it knows what to deactivate. This method also handles updating the text for the dropdown options to match the option shown in its associated chooser. This is done via a dictionary that holds shortened and full names for each item. It displays the short names in the choosers and dropdown options, and uses the full names to set the active state.
Code:
  private string GetShortNameFromFull(string fullName)
  {
      // Extract everything after the last '/'
      int lastSlashIndex = fullName.LastIndexOf('/');

      if (lastSlashIndex >= 0 && lastSlashIndex < fullName.Length - 1)
      {
          fullName = fullName.Substring(lastSlashIndex + 1);
      }

      // Check if the name ends with ".vam" and remove it
      if (fullName.EndsWith(".vam"))
      {
          fullName = fullName.Substring(0, fullName.Length - 4);
      }

      // If there's no '/', check for 'clothing:' and remove it
      if (fullName.StartsWith("clothing:"))
      {
          return fullName.Substring("clothing:".Length);
      }

      // If no '/' and no 'clothing:', return the full name as is
      return fullName;
  }

The chooser selections save fine with a scene now, but I failed to get a scene save to store the dropdown.value, so it will always load the 1st dropdown option. That issue led me to building a separate basic plugin for saving clothing presets, specific to this UI system. If finds all the CUA atoms by UID then saves the dropdown.value of each one. It also finds the toggles associated with those selected dropdown options and saves the bool of those toggles
Code:
// Open a file dialog to select a location to save the JSON data
      SuperController.singleton.GetMediaPathDialog(UISaveJSON, FILE_EXTENSION, BASE_DIRECTORY, false, true, false, null, false, null, false, false);
      SuperController.singleton.mediaFileBrowserUI.SetTextEntry(true);

      if (SuperController.singleton.mediaFileBrowserUI.fileEntryField != null)
      {
          string filename = ((int)(DateTime.UtcNow - new DateTime(2023, 1, 1)).TotalSeconds).ToString();
          SuperController.singleton.mediaFileBrowserUI.fileEntryField.text = filename + "." + FILE_EXTENSION;
          SuperController.singleton.mediaFileBrowserUI.ActivateFileNameField();

TO DO LIST

Still to figure out, is how to add a screenshot when saving my presets.
^
UPDATE: Solved here- https://hub.virtamate.com/threads/script-screenshot-for-plugin-save-load.43085/

I’d also like to add 3 little buttons to the side of each dropdown option to change the material settings of each item via the 3 save slots in the clothing's material settings. These save slots can be accessed via a VaM button with- ReceiverAtom- Person, Receiver- [clothing item]Material[material name], ReceiverTarget- RestoreAllFromStore1, but I don't know how to call this method in code.
^
UPDATE: Solved here- https://hub.virtamate.com/threads/solved-how-to-code-restoreallfromstore1-material-presets.43137/

I do have an issue loading double items like some shoes that have a separate left and right item. Rather than make even more of a mess of the dropdown plugin, I think I’ll just make another script for those items to turn one on if the other is on.

CREDITS

Thanks to anyone that helped me (knowingly or not) make a somewhat functioning plugin.

@MacGruber
@Acid Bubbles
@JayJayWon
@Stopper
@8n2ybclyk982

...
 
Last edited:
I like the idea from a UI and usability perspective.
There's a severe lack of documentation around the VaM api in general and the best option to get anywhere is something like ILSpy.
That being said, you might want to look at prefabs and their factories to create UI elements because I don't see them mentioned in your snippets.
 
I hadn't heard of factory patterns before. I'm looking into it now.
Thanks for the lead, and thanks again for showing me how to add clothing in code.
 
Neat! I've thought about doing something similar, but categorizing clothing based on the relative percentage of each bodypart that they cover. If that sounds like something you'd want to try, let me know and I can share how I thought of doing it. The short of it is that every clothing vert is mapped to a skin triangle, and every skin triangle has a material (which roughly corresponds to the relevant area of the body)
 
Neat! I've thought about doing something similar, but categorizing clothing based on the relative percentage of each bodypart that they cover. If that sounds like something you'd want to try, let me know and I can share how I thought of doing it. The short of it is that every clothing vert is mapped to a skin triangle, and every skin triangle has a material (which roughly corresponds to the relevant area of the body)
Cool concept! I'll let you know if I decide to give it a go. I appreciate the offer!
 
Neat! I've thought about doing something similar, but categorizing clothing based on the relative percentage of each bodypart that they cover. If that sounds like something you'd want to try, let me know and I can share how I thought of doing it. The short of it is that every clothing vert is mapped to a skin triangle, and every skin triangle has a material (which roughly corresponds to the relevant area of the body)
Sounds cool, we could "paint on" clothing. Highlight the area we want covered and the plugin finds and enables matching clothing.
You'd have to take alpha maps into consideration, wouldn't you though?
 
Sounds cool, we could "paint on" clothing. Highlight the area we want covered and the plugin finds and enables matching clothing.
You'd have to take alpha maps into consideration, wouldn't you though?

I reckon it'd work well enough even if you didn't, but yeah.
 
Neat! I've thought about doing something similar, but categorizing clothing based on the relative percentage of each bodypart that they cover. If that sounds like something you'd want to try, let me know and I can share how I thought of doing it. The short of it is that every clothing vert is mapped to a skin triangle, and every skin triangle has a material (which roughly corresponds to the relevant area of the body)
Would it be possible/practical to also get the distance of the clothing from the skin? So that items could be grouped separately if they cover the same area but fit well over each other. For example, separate categories for long sleeve shirts and jackets, even if they had the same coverage.
 
Would it be possible/practical to also get the distance of the clothing from the skin? So that items could be grouped separately if they cover the same area but fit well over each other. For example, separate categories for long sleeve shirts and jackets, even if they had the same coverage.

Yup. Best I can tell, VAM recalculates the vertices of clothing meshes on each frame based off the model's skin verts. There's a wrap object in the clothing that contains all the offsets and other data needed to do that. You should be able to tell whether one piece of clothing is closer to the body than another for each body vertex. It'll be a little slow though.

Then again, you probably don't need to check this for every body vertex. A handful of representative ones should probably be enough.
 
It'll be a little slow though.

Then again, you probably don't need to check this for every body vertex. A handful of representative ones should probably be enough.
Now I'm wondering if it would map all the clothing items to the UI on every load or if it would save the mapping so it only has to do it once for a saved scene (with an option to remap if new items have been added). The speed wouldn't matter much if it's a one time process.

I like the full control of my UI system, but manually setting every option takes a while, so it'd be very cool to see that process automated to any degree. My ideal scenario would be to use your system to automate the setup of mapping items to UI and add something like my system to give the ability to make any changes manually if preferred. The implementation of your concept sounds over my head to be honest, but I'd love to see any part of it working if you get started on it at some point. Not sure if I'd be able to help in any way, but I would be willing to.
 
Now I'm wondering if it would map all the clothing items to the UI on every load or if it would save the mapping so it only has to do it once for a saved scene (with an option to remap if new items have been added). The speed wouldn't matter much if it's a one time process.

I like the full control of my UI system, but manually setting every option takes a while, so it'd be very cool to see that process automated to any degree. My ideal scenario would be to use your system to automate the setup of mapping items to UI and add something like my system to give the ability to make any changes manually if preferred. The implementation of your concept sounds over my head to be honest, but I'd love to see any part of it working if you get started on it at some point. Not sure if I'd be able to help in any way, but I would be willing to.

I've got a bunch of plugins that need love so I'm probably not going to have time to get this to any level of completeness. Best I can do is throw together a prototype that you or someone else can turn into a product.
 
Back
Top Bottom