CodeyChaos
New member
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: