Quick Start Guidance
EAUploader Extension Development Quick Start Guidance
This document explains how to develop extensions for EAUploader. Extensions are added to “Edit tool” on EAUploader’s Edit screen, and can perform any processing on the item selected by the user.
As an example of registration, also see the “Viewpoint Position Editor” extension that comes built into EAUploader.
Its code can be found under Packages/tech.uslog.eauploader/Editor/Resources/UI/TabContents/Setup/Editor/ViewpointPositionEditor.
This document is based on EAUploader v1.6.2.
How Extensions Work
- At startup (when Unity starts or after scripts are recompiled), EAUploader finds static methods with the
EAUPluginattribute and calls them. - In that method, the extension calls
EAUploaderEditorManager.RegisterEditorto register its own information (editor registration information). - EAUploader lists the registered extensions in “Edit tool” on the Edit screen.
- When the user presses an extension’s button, the Unity menu specified in
MenuNameof the editor registration information is executed. The extension opens its own window from that menu.
1. Create a Folder for the Package
Create a folder to store the source code and other files created in the following steps.
Create it inside your project’s Assets folder or directly under Packages. Any name is fine.
2. Add the Source Code
Making EAUploader Recognize the Extension
To make EAUploader recognize the extension, you need to write code like the following. The following code is an excerpt of the registration part of ViewpointPositionEditor.cs. The role of LanguageUtility.T7eFromJsonFile is explained in detail later.
[EAUPlugin] private static void Initialize() { string editorName = LanguageUtility.T7eFromJsonFile("Viewpoint Position Editor", LOCALIZATION_FOLDER_PATH); string description = LanguageUtility.T7eFromJsonFile("A powerful viewpoint position editor for Unity.", LOCALIZATION_FOLDER_PATH); string requirementDescription = LanguageUtility.T7eFromJsonFile("The selected model must have a VRCAvatarDescriptor component.", LOCALIZATION_FOLDER_PATH);
// Create the editor registration information var editorRegistration = new EditorRegistration { // Menu path shown in Unity // Must match the string of the MenuItem attribute described later. MenuName = "EAUploader/ViewpointPositionEditor", // Name of the editor shown in EAUploader EditorName = editorName, // Description of the editor shown in EAUploader Description = description, // Version of the extension Version = "0.1.0", // Author of the extension Author = "USLOG", // A support URL for the extension, if any, can be written here. Url = "https://uslog.tech/eauploader", // Specifies a method that decides whether the extension can be used. // The method receives the path of the selected prefab as its argument // and must return a bool indicating whether the extension can be used. // True: can be used, False: cannot be used Requirement = CheckAvatarHasVRCAvatarDescriptor,
// Message shown to the user when the extension cannot be used. RequirementDescription = requirementDescription };
// Register the editor EAUploaderEditorManager.RegisterEditor(editorRegistration); }
private static bool CheckAvatarHasVRCAvatarDescriptor(string prefabPath) { GameObject gameObject = PrefabManager.GetPrefab(prefabPath); return Utility.CheckAvatarHasVRCAvatarDescriptor(gameObject); }- Add the EAUPlugin attribute to any static method.
- In this sample, the EAUPlugin attribute is added to the Initialize method. Methods with this attribute are called automatically when EAUploader starts.
- The method must be a static method with no parameters. It may be private.
- In the method with the attribute, create the editor registration information and call EAUploaderEditorManager.RegisterEditor with it as the argument.
- Calling EAUploaderEditorManager.RegisterEditor adds the extension to EAUploader.
- For details on the editor registration information, see the comments in the source code above or the table below.
| Property | Type | Required | Contents |
|---|---|---|---|
MenuName | string | ○ | Path of the menu executed when the extension’s button is pressed. Must match the string of the MenuItem attribute. |
EditorName | string | ○ | Name shown in the edit tool list |
Description | string | Description (shown when ▼ is pressed) | |
Version | string | Version (shown when ▼ is pressed) | |
Author | string | Author (shown when ▼ is pressed) | |
Url | string | URL of a support page, etc. When specified, an “Open the link” button is shown. | |
Requirement | Func<string, bool> | Method that decides whether the extension can be used. The path of the selected prefab is passed as the argument. If null, it can always be used. | |
RequirementDescription | string | Message shown when the extension cannot be used |
Implementing the Method Called from EAUploader
Add the MenuItem attribute to any method as shown below. This is the method called when the user presses the extension editor’s button.
[MenuItem("Tools/My Avatar Tool")] public static void Open() { var window = EditorWindow.GetWindow<MyAvatarTool>(); window.titleContent = new GUIContent("My Avatar Tool"); window.minSize = new Vector2(800, 600); }With the steps above, the extension’s button will be shown in EAUploader’s screen.
Getting the Selected Item
The path of the item (prefab) selected by the user in EAUploader can be obtained with EAUploaderCore.selectedPrefabPath (CoreAPI.SelectedPrefabPath in the public API).
The prefab’s GameObject can be obtained with PrefabManager.GetPrefab (PrefabAPI.GetPrefab in the public API).
To detect when the selected item changes, use the CoreAPI.SelectedPrefabPathChanged event.
Modifying the Selected Item
The path of the selected item is the path of a Prefab Variant managed by EAUploader (Assets/EAUploader/Prefabs/<item name>/<item name>.prefab).
EAUploader is designed never to modify the original Prefab imported by the user, and to modify only this Prefab Variant. Please make your extension modify only the Prefab at the selected path as well.
- To modify the Prefab, we recommend opening it with
PrefabUtility.LoadPrefabContentsand saving it to the same path withPrefabUtility.SaveAsPrefabAsset. Changes are saved as overrides on the Prefab Variant. - When you save, EAUploader detects the import and automatically updates the list information and the preview image.
- We recommend saving assets created by your extension, such as textures and meshes, inside the item’s folder (
Assets/EAUploader/Prefabs/<item name>/). When the item is deleted, assets used only by that item are deleted together.
Implement CreateGUI, OnEnable, and Other Methods as Needed
Implement CreateGUI, OnEnable, and other methods as needed. If you want to create UI with the same look as EAUploader, see “UI Components”.
3. Create an Assembly
Right-click the folder created in step 1 and click “Create - Assembly Definition”. An .asmdef file is created in the folder.
Configure the created .asmdef file as follows.
- Uncheck Auto Referenced.
- Add EAUploader.Editor to Assembly Definition References.
- If you use VRChat SDK types such as
VRCAvatarDescriptor, also add the VRChat SDK assemblies (such asVRC.SDK3A).
- If you use VRChat SDK types such as
- Set Platforms according to where the source code is placed. (Since extensions are editor extensions, usually check only
Editor.) - Add Version Defines with the following values. Change the Define and Expression values as appropriate.
- Resource: tech.uslog.eauploader
- Define: EAUploader
- Expression: 1.6.0
Step 4 enables conditional compilation. For example, writing the following enables the code only when EAUploader version 1.6.0 or later is present.
#if EAUploader using EAUploader;#endifIf you want the extension to work in projects without EAUploader, wrap the EAUploader-related code in #if EAUploader … #endif.
Minimal Sample
A minimal extension sample that works as is. It shows the name and viewpoint height of the selected item.
using UnityEditor;using UnityEngine;using UnityEngine.UIElements;#if EAUploaderusing EAUploader;using EAUploader.Editor.API;using EAUploader.Editor.Prefab.API;#endif
namespace MyTool.Editor{ public class MyAvatarTool : EditorWindow { private const string MENU_NAME = "Tools/My Avatar Tool";
#if EAUploader [EAUPlugin] private static void RegisterToEAUploader() { EAUploaderEditorManager.RegisterEditor(new EditorRegistration { MenuName = MENU_NAME, EditorName = "My Avatar Tool", Description = "Shows information about the selected avatar.", Version = "0.1.0", Author = "Your Name", Url = "https://example.com", Requirement = prefabPath => PrefabUtilityAPI.CheckAvatarHasVRCAvatarDescriptor(PrefabAPI.GetPrefab(prefabPath)), RequirementDescription = "Please select an avatar that has a VRCAvatarDescriptor." }); }#endif
[MenuItem(MENU_NAME)] public static void Open() { GetWindow<MyAvatarTool>("My Avatar Tool"); }
public void CreateGUI() {#if EAUploader var prefabPath = CoreAPI.SelectedPrefabPath; var avatar = string.IsNullOrEmpty(prefabPath) ? null : PrefabAPI.GetPrefab(prefabPath); if (avatar == null) { rootVisualElement.Add(new Label("No avatar is selected.")); return; }
rootVisualElement.Add(new Label("Avatar: " + avatar.name)); rootVisualElement.Add(new Label("Viewpoint height: " + PrefabUtilityAPI.GetAvatarHeight(avatar) + "m"));#else rootVisualElement.Add(new Label("EAUploader is required."));#endif } }}Configure the assembly as described in “3. Create an Assembly”.
4. Distribute the Extension
Export the folder you created (source code and .asmdef file) as a .unitypackage, or distribute it as a VPM package.
- For a
.unitypackage, users can import it from the Unity menu “Assets” → “Import Package” → “Custom Package…“. - For a VPM package, you can declare the dependency on EAUploader by specifying
tech.uslog.eauploaderinvpmDependenciesofpackage.json.
Please let us know about extensions you have distributed on Project EAUploader’s Discord server.
EAUploader API Quick Reference
Localization
EAUploader achieves localization by creating JSON files that map English strings to strings in any language, and loading those JSON files at an appropriate time. Strings in any language can always be obtained using English as the key.
JSON File Format
The JSON files must be stored in the following format. You need to add one JSON file per supported language.
- (any folder)
- ja.json
- ko.json
- [language code].json
Each JSON file has content like the following.
{ "name": "日本語", "items": [ { "key": "Apply", "value": "適用" }, { "key": "Close", "value": "閉じる" }, { "key": "Actual View", "value": "実際のビュー" }, { "key": "Viewpoint Position Editor", "value": "ビューポイント位置エディタ" }, { "key": "A powerful viewpoint position editor for Unity.", "value": "Unity用の強力なビューポイント位置エディタ" }, { "key": "The selected model must have a VRCAvatarDescriptor component.", "value": "選択したモデルにはVRCAvatarDescriptorコンポーネントが必要です。" } ]}Applying Directly to rootVisualElement
You can apply the created JSON files directly to rootVisualElement.
To apply them, use the LanguageUtility.LocalizationFromJsonFile method.
Specify the rootVisualElement to apply to as the first argument, and the path of the folder containing the JSON files created above as the second argument.
This method replaces the Text values of all components derived from TextElement (e.g., Label, Button) in rootVisualElement according to the values in the JSON file. For example, text="Actual View" in the following UXML works as if it were loaded as text="実際のビュー" when the language is set to Japanese.
<ui:Label text="Actual View" class="self-center text-lg font-bold" />Note that the explanation above is only an image; the strings in the UXML file are not actually rewritten. LanguageUtility.LocalizationFromJsonFile only replaces the values in rootVisualElement.
public void CreateGUI() { var visualTree = Resources.Load<VisualTreeAsset>("MyAvatarTool"); visualTree.CloneTree(rootVisualElement);
// Localization file path var localizationFolderPath = "Assets/MyAvatarTool/Localization"; LanguageUtility.LocalizationFromJsonFile(rootVisualElement, localizationFolderPath); }In the public API, LanguageAPI.LocalizeFromJsonFile does the same.
Getting a String in Any Language Directly from the JSON File
With the LanguageUtility.T7eFromJsonFile method, you can specify an English string and get the string for the currently set language. In the sample code, it is used in the Initialize method.
[EAUPlugin] private static void Initialize() { string editorName = LanguageUtility.T7eFromJsonFile("Viewpoint Position Editor", LOCALIZATION_FOLDER_PATH); string description = LanguageUtility.T7eFromJsonFile("A powerful viewpoint position editor for Unity.", LOCALIZATION_FOLDER_PATH); string requirementDescription = LanguageUtility.T7eFromJsonFile("The selected model must have a VRCAvatarDescriptor component.", LOCALIZATION_FOLDER_PATH);Specify the English string as the first argument, and the path of the folder containing the JSON files as the second argument. It returns the string for the currently set language. If no corresponding string is found, the English string of the first argument is returned as is.
In the public API, LanguageAPI.GetLocalizedTextFromJsonFile does the same.
Getting the Current Language
Use the LanguageUtility.GetCurrentLanguage method. Example:
string language = LanguageUtility.GetCurrentLanguage();It returns a string representing the current language.
- ja: Japanese is set as the display language.
- ko: Korean is set as the display language.
- en: English is set as the display language.
UI
EAUploader’s custom UI components are under Editor/Resources/UI/Components. See “UI Components” for how to use them.
Other APIs
EAUploader provides public APIs (such as EAUploader.Editor.API) intended for use by external tools.
See “API Reference” for details.