コンテンツにスキップ

Editor

開発に興味がありますか?

Project EAUploaderでは開発メンバーを常に募集しています!! 話を聞くだけでもOK! 是非当プロジェクトのDiscordサーバーへお越しください。 参加はこちら



本ページでは、EAUploaderの本体であるEAUploader.Editorアセンブリの処理の流れを解説します。 より詳しい仕様は、リポジトリのDocs/フォルダにある仕様書(英語)もご覧ください。

本ドキュメントは、EAUploader v1.6.2 の内容に準拠しています。

起動処理


EAUploaderの起動処理はEAUploaderCore(Editor/Scripts/EAUploaderCore.cs)が担当します。

1. 静的コンストラクタ

EAUploaderCoreには[InitializeOnLoad]属性が付与されており、Unityの起動時やスクリプトの再コンパイル(ドメインリロード)のたびに静的コンストラクタが実行されます。

  1. Application.logMessageReceivedにログ出力処理を登録する。
  2. 出力するログファイル名を決定し、環境情報(日付、ログファイルGUID、EAUploaderのバージョン、Unityのバージョン、プラットフォーム、VPMパッケージの一覧)をログに出力する。
  3. EditorApplication.updateに初期化処理の待機を登録する。

2. 初期化処理

Unityがコンパイル中でもプレイモードへの移行中でもない状態になると、PerformInitializationが一度だけ実行されます。

  1. InitializeEAUploader ― Assets/EAUploaderフォルダとPrefabManager.jsonが無ければ作成する。
  2. AvatarUploader.CleanupLeftoverBuildInstances ― 中断されたマルチプラットフォームビルドでシーンに残った、アップロード用の一時インスタンスを削除する。
  3. EAUploaderEditorManager.OnEditorManagerLoad ― 編集ツールの登録情報(EditorsManage.json)を初期化する。
  4. ShaderChecker.CheckShaders ― シェーダーが不足しているプレハブを確認する。
  5. PrefabManager.Initialize ― 管理フォルダを用意し、旧バージョンからの移行(後述)を行い、不足しているプレビュー画像を生成する。
  6. CheckAvailablePackages ― Packages/packages-lock.jsonを読み込み、HasVRM、HasAAO、HasMAを設定する。
  7. [EAUPlugin]属性が付与されたstaticメソッドをすべてのアセンブリから探し、呼び出す。(編集ツール・拡張機能の登録)
  8. 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.jsonlanguage、isLibraryOpen、showErrorReportWindow、managedPrefabsMigratedConfig
Assets/EAUploader/PrefabManager.json旧バージョンとの互換用(空のファイルを作成する)EAUploaderCore
Assets/EAUploader/EditorsManage.json登録されている編集ツール(起動のたびに作り直す)EAUploaderEditorManager
Assets/EAUploader/tags.jsonタグ名とタググループTagManager
Assets/EAUploader/Prefabs/<アイテム>/<Variant>.prefab管理用Prefab VariantManagedPrefabs
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名>.prefab
  • ManagedPrefabs.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ごとに次の処理を行います。

  1. ShaderChecker.CheckShadersInPrefabs ― シェーダーが不足している場合、ダイアログを表示する。ダイアログの「なぜこれを見ているのですか?」ボタンは、本ドキュメントの「シェーダーが見つからないエラー」(https://eauploader-docs.uslog.tech/faq/missing_shader)を開く。
  2. PrefabManager.ImportPrefab ― 管理用Variantを作成又は更新する。
  3. ジャンルの決定が必要なアバター以外のアイテムをまとめて、次のdelayCallでClothSettingModalを表示する(メインウィンドウが開いている場合のみ)。

PrefabManager.ImportPrefab(path)の動作は次の通りです。

  • 管理されていないPrefab(Assets/の外、Assets/EAUploader/の中、既にVariantがあるもの、SuppressAutoVariantCreationが設定されている間は対象外)
    • Variantを作成し、typeとlastModifiedTicksを設定する。
    • アバター(VRC_AvatarDescriptorを持つ)はgenre = Avatar、genreConfirmed = trueにする。
    • それ以外はgenre = Cloth、genreConfirmed = falseにし、ジャンル選択のモーダルの対象にする。
    • プレビュー画像を保存し、一覧を更新する。
  • 管理されている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)は、ウィンドウ内の削除確認モーダルを開きます。

  1. Variantの元のPrefab、マテリアル、テクスチャを検索する。参照の確認はAssets/以下の.prefab/.unity/.controller/.overridecontroller/.anim/.asset/.matを対象に行い、他のアセットから使われているものは削除の対象外にする。
  2. 削除される内容を、読み取り専用の一覧で表示する。検索が終わると削除を確定できる。
  3. 検索結果は、アセットデータベースが変わらない間は再利用する。アセットが変更された場合は削除の前に検索し直し、対象が変わった場合は再度確認を求める。
  4. 削除後、空になったアイテムフォルダを削除し、必要に応じて選択を解除してcompleted(true)を呼ぶ。キャンセルした場合はcompleted(false)を呼ぶ。

旧バージョンからの移行(LegacyPrefabMigration)

旧バージョンでは、インポートしたPrefabに直接EAUploaderMetaを追加していました。起動時にmanagedPrefabsMigratedが設定されておらず、管理フォルダの外にそのようなPrefabがある場合、ユーザーに移行を確認します。

  1. 元のPrefabからメタデータを読み込む。
  2. 元のPrefabからEAUploaderMetaを削除する(Variantに継承されないよう、Variantの作成前に行う)。
  3. メタデータを持つ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組み立て内容
AvatarListUI.AvatarList.Main.ShowContentアバター一覧
SetupUI.Setup.Main.ShowContent編集画面
UploadUI.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)でビルド用のインスタンスを作成します。

  1. 管理用Prefabを読み込み、Blueprint IDが無ければ割り当ててPrefabに保存する(プラットフォームの切り替えによるドメインリロードをまたいでも紐づけを保つため)。
  2. アクティブなシーンにインスタンス化し、Prefabとの接続を完全に解除する。これにより、Modular AvatarやAvatarOptimizer(NDMF)などの非破壊ツールがビルド時に処理でき、ビルド時の変更がPrefabアセットに書き戻されない。
  3. 名前を<名前> (EAUploader Upload)にする。

インスタンスはビルド後に削除されます。VRChat SDKのマルチプラットフォームビルド中は使い続けるため残し、次回の起動時にCleanupLeftoverBuildInstancesで削除します。

テスト(BuildAndTestAsync)

VRChat SDKのコントロールパネルを開き、builder.BuildAndTestでビルドして、結果をDialogProで表示します。ビルドとアップロードは同時に1つだけ実行できます。

アップロード(UploadAvatarAsync)

  1. ビルドするプラットフォームを決める(Windowsを先頭に、選択されていてビルドサポートが導入されているプラットフォーム)。
  2. ビルド用インスタンスを作成し、Blueprint IDで既存のVRCAvatarを取得する。無ければ新規に作成する。
  3. プラットフォームが1つの場合はbuilder.BuildAndUpload、複数の場合はSDKのマルチプラットフォームのターゲットを設定してbuilder.BuildAndUploadMultiPlatformを呼ぶ(Windows → 追加のプラットフォーム → 元のプラットフォームの順。途中でドメインリロードが発生する場合がある)。
  4. SDKが割り当てたBlueprint IDをPrefabアセットに書き戻す。
  5. 成功・キャンセル・失敗を表示し、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をご覧ください。拡張機能の作り方は「クイックスタートガイダンス」をご覧ください。

ログ出力


ログ出力とエラーレポートについては「ログ出力機能仕様書」をご覧ください。