Editor
開発に興味がありますか?
Project EAUploaderでは開発メンバーを常に募集しています!! 話を聞くだけでもOK! 是非当プロジェクトのDiscordサーバーへお越しください。 参加はこちら
本ページでは、EAUploaderの本体であるEAUploader.Editorアセンブリの処理の流れを解説します。
より詳しい仕様は、リポジトリのDocs/フォルダにある仕様書(英語)もご覧ください。
本ドキュメントは、EAUploader v1.6.2 の内容に準拠しています。
起動処理
EAUploaderの起動処理はEAUploaderCore(Editor/Scripts/EAUploaderCore.cs)が担当します。
1. 静的コンストラクタ
EAUploaderCoreには[InitializeOnLoad]属性が付与されており、Unityの起動時やスクリプトの再コンパイル(ドメインリロード)のたびに静的コンストラクタが実行されます。
Application.logMessageReceivedにログ出力処理を登録する。- 出力するログファイル名を決定し、環境情報(日付、ログファイルGUID、EAUploaderのバージョン、Unityのバージョン、プラットフォーム、VPMパッケージの一覧)をログに出力する。
EditorApplication.updateに初期化処理の待機を登録する。
2. 初期化処理
Unityがコンパイル中でもプレイモードへの移行中でもない状態になると、PerformInitializationが一度だけ実行されます。
InitializeEAUploader―Assets/EAUploaderフォルダとPrefabManager.jsonが無ければ作成する。AvatarUploader.CleanupLeftoverBuildInstances― 中断されたマルチプラットフォームビルドでシーンに残った、アップロード用の一時インスタンスを削除する。EAUploaderEditorManager.OnEditorManagerLoad― 編集ツールの登録情報(EditorsManage.json)を初期化する。ShaderChecker.CheckShaders― シェーダーが不足しているプレハブを確認する。PrefabManager.Initialize― 管理フォルダを用意し、旧バージョンからの移行(後述)を行い、不足しているプレビュー画像を生成する。CheckAvailablePackages―Packages/packages-lock.jsonを読み込み、HasVRM、HasAAO、HasMAを設定する。[EAUPlugin]属性が付与されたstaticメソッドをすべてのアセンブリから探し、呼び出す。(編集ツール・拡張機能の登録)EditorApplication.delayCallでEAUploaderのウィンドウを開く。
初期化中に例外が発生した場合は、EAUploaderの再起動を促すダイアログを表示します。「再起動」を押すと、Packages/tech.uslog.eauploaderを再インポートします。
メニューの「EAUploader」→「Reload」(EAUploaderCore.ReloadSDK)では、選択の解除と、上記の手順3〜6を再度行います。
グローバルな状態
| メンバー | 内容 |
|---|---|
EAUploaderCore.selectedPrefabPath | 選択中のアイテムのパス。変更するとSelectedPrefabPathChangedが発生する。すべての画面と編集ツールはこのアイテムに対して動作する。 |
EAUploaderCore.HasVRM / HasAAO / HasMA | 連携パッケージが導入されているか |
EAUploaderCore.GetVersion() | package.jsonからバージョンを取得する |
データの保存
| パス | 内容 | 担当クラス |
|---|---|---|
Assets/EAUploader/settings.json | language、isLibraryOpen、showErrorReportWindow、managedPrefabsMigrated | Config |
Assets/EAUploader/PrefabManager.json | 旧バージョンとの互換用(空のファイルを作成する) | EAUploaderCore |
Assets/EAUploader/EditorsManage.json | 登録されている編集ツール(起動のたびに作り直す) | EAUploaderEditorManager |
Assets/EAUploader/tags.json | タグ名とタググループ | TagManager |
Assets/EAUploader/Prefabs/<アイテム>/<Variant>.prefab | 管理用Prefab Variant | ManagedPrefabs |
Assets/EAUploader/Prefabs/<アイテム>/Textures/、Materials/ | テクスチャ編集で生成したアセット | テクスチャ編集 |
Assets/EAUploader/PrefabPreviews/<GUID>.png | プレビュー画像(1200×900)。ファイル名はPrefabのGUIDのため、名前を変えても引き継がれる | PrefabPreview |
EAUploaderLog/(プロジェクト直下) | ログファイル(合計100MBまで) | UI.Windows.Logger |
EditorPrefs EAUploader.Theme | テーマ(white / dark / pastel) | ThemeUtility |
EditorPrefs EAUploader.CardWidth | アバター一覧のカードの幅 | アバター一覧 |
アイテムの管理
管理用Prefab Variant
EAUploaderは、ユーザーがインポートしたPrefabを変更しません。インポートしたPrefabごとにPrefab Variantを作成し、そのVariantだけを管理・編集します。
Assets/EAUploader/Prefabs/<アイテム名>/<Variant名>.prefabManagedPrefabs.ROOTはAssets/EAUploader/Prefabsです。インポートしたアイテムごとにフォルダを作ります。- Variantは、元のPrefabを一時的なプレビューシーンでインスタンス化し、
EAUploaderMetaを追加してPrefabUtility.SaveAsPrefabAssetで保存して作成します。開いているシーンには影響しません。 EAUploaderMetaはVariantにだけ追加されます。- 編集ツールなどで生成したアセットは、アイテムのフォルダに保存します。
ManagedPrefabsのメソッド | 内容 |
|---|---|
IsManaged(path) | ROOT/以下のパスか |
IsInEAUploaderFolder(path) | Assets/EAUploader/以下のパスか(インポートの対象外) |
CreateVariant(sourcePath, configure) | 新しいアイテムフォルダにVariantを作成する |
DuplicateVariant(variantPath) | 同じアイテムフォルダ内にVariantを複製する |
GetSourcePath(variantPath) | Variantの元のPrefabを取得する |
RenameItemFolder(variantPath, name) | フォルダ内がそのPrefabだけの場合に、フォルダ名を変更する |
メタデータ(EAUploaderMeta)
Variantのルートに追加されるEAUploaderMetaに、一覧での表示状態(status)、種類(type)、ジャンル(genre)、最終更新日時(lastModifiedTicks)、メモ(memo)、タグ(tags)などを保存します。詳しくは「Runtime」をご覧ください。
PrefabInfoは、管理用Prefabの情報をUI向けにまとめたスナップショット(パス、名前、最終更新日時、種類、状態、ジャンル、タグ、メモ、プレビュー画像)です。
インポート時の処理
AssetImportProcessor(Editor/Scripts/AssetProcessor.cs)が、インポートされた.prefabごとに次の処理を行います。
ShaderChecker.CheckShadersInPrefabs― シェーダーが不足している場合、ダイアログを表示する。ダイアログの「なぜこれを見ているのですか?」ボタンは、本ドキュメントの「シェーダーが見つからないエラー」(https://eauploader-docs.uslog.tech/faq/missing_shader)を開く。PrefabManager.ImportPrefab― 管理用Variantを作成又は更新する。- ジャンルの決定が必要なアバター以外のアイテムをまとめて、次の
delayCallでClothSettingModalを表示する(メインウィンドウが開いている場合のみ)。
PrefabManager.ImportPrefab(path)の動作は次の通りです。
- 管理されていないPrefab(
Assets/の外、Assets/EAUploader/の中、既にVariantがあるもの、SuppressAutoVariantCreationが設定されている間は対象外)- Variantを作成し、
typeとlastModifiedTicksを設定する。 - アバター(
VRC_AvatarDescriptorを持つ)はgenre = Avatar、genreConfirmed = trueにする。 - それ以外は
genre = Cloth、genreConfirmed = falseにし、ジャンル選択のモーダルの対象にする。 - プレビュー画像を保存し、一覧を更新する。
- Variantを作成し、
- 管理されているPrefab
typeを判定し直す。アバターはgenre = Avatarにする。- 記録されている時刻が5秒以上前の場合だけ
lastModifiedTicksを更新する(保存が再びインポート処理を呼んでループしないようにするため)。 - 変更があればプレビュー画像を保存する。
一覧
PrefabManager.GetAllPrefabs()は、EAUploaderMetaを持つ管理用Variantを走査し、結果を2秒間キャッシュします。InvalidateCache()で強制的に再走査します。
GetAllPrefabsWithPreview()は非表示のアイテムを除き、GetAllPrefabsIncludingHidden()は含めます。どちらもピン留めを先頭に、新しい順に並べます。
タグ(TagManager)
- タグ名とグループはプロジェクト全体で
Assets/EAUploader/tags.jsonに保存し、アイテムへの付与はEAUploaderMeta.tagsに保存します。 - 大文字と小文字を区別せずに比較します。大文字・小文字だけが違う新しいタグは、既存のタグの表記を使います。
- タグ名とグループ名は32文字まで(
MAX_TAG_LENGTH)です。 - 1つのタグは1つのグループにだけ所属します。タグを削除するとすべてのPrefabから外れ、グループを削除してもタグは残ります。
削除
PrefabManager.ShowDeletePrefabDialog(path, completed)は、ウィンドウ内の削除確認モーダルを開きます。
- Variantの元のPrefab、マテリアル、テクスチャを検索する。参照の確認は
Assets/以下の.prefab/.unity/.controller/.overridecontroller/.anim/.asset/.matを対象に行い、他のアセットから使われているものは削除の対象外にする。 - 削除される内容を、読み取り専用の一覧で表示する。検索が終わると削除を確定できる。
- 検索結果は、アセットデータベースが変わらない間は再利用する。アセットが変更された場合は削除の前に検索し直し、対象が変わった場合は再度確認を求める。
- 削除後、空になったアイテムフォルダを削除し、必要に応じて選択を解除して
completed(true)を呼ぶ。キャンセルした場合はcompleted(false)を呼ぶ。
旧バージョンからの移行(LegacyPrefabMigration)
旧バージョンでは、インポートしたPrefabに直接EAUploaderMetaを追加していました。起動時にmanagedPrefabsMigratedが設定されておらず、管理フォルダの外にそのようなPrefabがある場合、ユーザーに移行を確認します。
- 元のPrefabからメタデータを読み込む。
- 元のPrefabから
EAUploaderMetaを削除する(Variantに継承されないよう、Variantの作成前に行う)。 - メタデータを持つVariantを作成し、旧形式(名前ベース)のプレビュー画像をGUIDベースのパスに移動する。
作成に失敗した場合は元のPrefabのデータを復元します。すべてのアイテムが成功した場合だけ移行完了とし、「あとで」を選んだ場合は次回の起動時に再度確認します。
UI
EAUploaderのUIは、UI Toolkit(UXML / USS)で作成されています。
- メインウィンドウは
UI.EAUploader(メニュー「EAUploader」→「Open EAUploader」、既定の大きさ 1280×640、最小 1080×640)の1つだけです。 CreateGUI()でUI/styles.ussとUI/tailwind.ussを読み込み、MainWindow.uxml(contentRootのみ)を複製し、共通のModalと通知(ToastManager)を追加し、テーマを適用します。- 画面はタブではなく、
contentRootに1つずつ組み立てます。EAUploader.Navigate(screen)で切り替え、EAUploader.RefreshCurrentScreen()で現在の画面を作り直します。
EAUploaderScreen | 組み立て | 内容 |
|---|---|---|
AvatarList | UI.AvatarList.Main.ShowContent | アバター一覧 |
Setup | UI.Setup.Main.ShowContent | 編集画面 |
Upload | UI.Setup.Main.ShowContent(アップロードのシートを開いた状態) | アップロード |
- アップロードは独立した画面ではなく、編集画面のシートとして
UI.Upload.UploadFormを表示します。 - 独自のUIコンポーネントは
Editor/Resources/UI/Components、モーダルはEditor/Resources/UI/Modalsにあります。UXMLではxmlns:eau="EAUploader.UI.Components"を宣言して使用します。詳しくは「UIコンポーネント」をご覧ください。
多言語対応
メインウィンドウは100msごとにLanguageUtility.Localizationを実行し、ウィンドウ内のTextElementの文字列を翻訳します。USSクラスno-translateを持つ要素(名前やタグなどユーザーが入力した文字列)と、テキスト入力欄の中身は翻訳しません。
UXMLには英語の文字列をそのまま記述し、C#から文字列を設定する場合はT7e.Get("English text")を使用します。詳しくは「多言語対応とリソース」をご覧ください。
アップロード処理
ビルドとアップロードはAvatarUploader(Editor/Scripts/AvatarUploader.cs)が担当します。VRChat SDKのビルダーAPI(IVRCSdkAvatarBuilderApi)を使用します。アップロードできるのはジャンルがAvatarのアイテムだけです。
ビルド用インスタンス
テストとアップロードのどちらもCreateBuildInstance(prefabPath)でビルド用のインスタンスを作成します。
- 管理用Prefabを読み込み、Blueprint IDが無ければ割り当ててPrefabに保存する(プラットフォームの切り替えによるドメインリロードをまたいでも紐づけを保つため)。
- アクティブなシーンにインスタンス化し、Prefabとの接続を完全に解除する。これにより、Modular AvatarやAvatarOptimizer(NDMF)などの非破壊ツールがビルド時に処理でき、ビルド時の変更がPrefabアセットに書き戻されない。
- 名前を
<名前> (EAUploader Upload)にする。
インスタンスはビルド後に削除されます。VRChat SDKのマルチプラットフォームビルド中は使い続けるため残し、次回の起動時にCleanupLeftoverBuildInstancesで削除します。
テスト(BuildAndTestAsync)
VRChat SDKのコントロールパネルを開き、builder.BuildAndTestでビルドして、結果をDialogProで表示します。ビルドとアップロードは同時に1つだけ実行できます。
アップロード(UploadAvatarAsync)
- ビルドするプラットフォームを決める(Windowsを先頭に、選択されていてビルドサポートが導入されているプラットフォーム)。
- ビルド用インスタンスを作成し、Blueprint IDで既存の
VRCAvatarを取得する。無ければ新規に作成する。 - プラットフォームが1つの場合は
builder.BuildAndUpload、複数の場合はSDKのマルチプラットフォームのターゲットを設定してbuilder.BuildAndUploadMultiPlatformを呼ぶ(Windows → 追加のプラットフォーム → 元のプラットフォームの順。途中でドメインリロードが発生する場合がある)。 - SDKが割り当てたBlueprint IDをPrefabアセットに書き戻す。
- 成功・キャンセル・失敗を表示し、
OnCompleteを発生させる。
進捗はSDKのイベント(OnSdkBuildProgress、OnSdkUploadProgress)からAvatarUploader.ProgressChangedに転送されます。CancelUpload()でアップロードを中止します。
アバターパフォーマンス
アップロードのシートのアバターパフォーマンスは、PerformanceInfoComputer(Editor/Scripts/VRCSDK/PerformanceInfo.cs)がVRChat SDKのAvatarPerformanceを使用して計算しています。現在はPC(Windows)用の基準で計算しています。
自動最適化
AvatarOptimizerが導入されている場合(HAS_AAO)、「自動最適化」をオンにすると、アバターのルートにTraceAndOptimizeコンポーネントを追加します。オフにすると削除します。
編集ツール
EAUploaderEditorManagerは、RegisterEditorで登録されたEditorRegistrationを保持し、Assets/EAUploader/EditorsManage.jsonにも書き出します。編集画面の「編集ツール」はGetRegisteredEditorsで取得した一覧をもとに表示し、ボタンを押すとEditorRegistration.MenuNameのメニューをEditorApplication.ExecuteMenuItemで実行します。
標準の編集ツールは、Editor/Resources/UI/TabContents/Setup/Editor/Common/の共通基盤(namespace EAUploader.UI.Setup.EditTools)を使用しています。
| 型 | 役割 |
|---|---|
EditToolSession | 管理用PrefabをPrefabUtility.LoadPrefabContentsで独立したプレビューシーンに開き、変更の有無を管理し、PrefabUtility.SaveAsPrefabAssetで保存(VariantはVariantのまま)、再読み込み(元に戻す)、対象の切り替えを行う。 |
EditToolModal | 標準の編集ツールの抽象基底クラス。ヘッダー(ツール名、アイテム名、「未保存の変更あり」、戻る)、サイドパネル、ライブプレビュー、フッター(元に戻す、適用)の共通レイアウトで、ツールをEAUploaderのウィンドウ内のモーダルとして表示する。同時に開けるツールは1つだけ。 |
ToolModalFrame | ビューポイント位置エディタを含むすべてのツールで共通のモーダルの枠。 |
EditToolUtility | 生成アセットのフォルダ、ファイル名の整形、保存後の更新(OnPrefabSaved)、共通のUI部品、使用条件の判定。 |
- ツールに未保存の変更があると、EAUploaderのウィンドウは
hasUnsavedChangesを報告し、ウィンドウを閉じるときに保存を確認します。 - 保存すると、
EditToolUtility.OnPrefabSavedがPrefabManagerのキャッシュを無効化し、プレビュー画像を作り直し、一覧と編集画面を更新します。 - ツールのプレビューは、共通の
Previewコンポーネントのライブモード(new Preview(container, session.Root))を使用します。
標準の編集ツールの動作の詳細は、リポジトリのDocs/EditTools.mdをご覧ください。拡張機能の作り方は「クイックスタートガイダンス」をご覧ください。
ログ出力
ログ出力とエラーレポートについては「ログ出力機能仕様書」をご覧ください。