関連ブログ
- [UE4][UE5]開発環境の容量を少しでも減らす 2024.08.14UE
- [UE5] PushModel型のReplicationを使い、ネットワーク最適化を図る 2024.05.29UE
- [UE5]マテリアルでメッシュをスケールする方法 2024.01.17UE
CATEGORY
2018.07.18UE4UE/ Editor拡張
レベルブループリントはレベル全体に渡るグローバルな処理を記述できるブループリントとなっています。
そのため、通常のブループリントとは一部の機能が異なっていたり、ブループリントエディタの開き方も通常のブループリントとは異なります。
公式のレベルブループリントドキュメントはこちら
通常のブループリントの開き方は
などでブループリントエディタを開くことができますが、レベルブループリントは上記の方法で開くとエディタを開くのではなく、そのレベルを読み込んでレベルの編集を行おうとします。
Open Blueprint Class…には候補としても現れません。
レベルプループリントを開くには、現在開いているレベルなら
Toolbar->Blueprints->Open Level Blueprint
サブレベルに設定していれば、
Toolbar->Blueprints->Sub-Levels
から開けます。
もしくはLevelsウィンドウのコントローラーマークをクリックしても開けます。
レベルブループリントのエディタを開くためには前述の通りの方法を取りますが、該当のレベルを開いてPersistent Levelに設定する必要があり、ただレベルブループリントを開きたいだけだとしても開発の規模が大きくなってくると読み込みに非常に多くの時間がかかってしまいます。
読み込み時間を最小限にするために、空のマップを開いて、レベルブループリントのみ編集するといった方針は取れません。
そこで今回はPersistentでもSubでもないレベルをコンテンツブラウザのLevelアセットを右クリックしてレベルブループリントを開けるようにしてしまおうという内容です。
C++によるプラグインでの実装になるので、C++はどうでもいいよという方はこちらでプラグインを配布しています。
Edit->PluginsからPluginsウィンドウを開き、右下のNew Pluginをクリックします。
テンプレートの中のBlankを選択し、プラグインの名称を決めてCreatePluginを行います。説明のためここではHogeと名付けます。
配布プラグインはAddOpenLevelBlueprintMenuPluginと長くなってしまったのでどうかいい名前をつけてください。
CreatePluginをおして、しばらく待つとプラグインのコードがテキストエディタで開きます。
プラグイン名.upluginを開いてModuleのTypeを”Developer”から”Editor”に変更します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
{ "FileVersion": 3, "Version": 1, "VersionName": "1.0", "FriendlyName": "Hoge", "Description": "", "Category": "Other", "CreatedBy": "", "CreatedByURL": "", "DocsURL": "", "MarketplaceURL": "", "SupportURL": "", "CanContainContent": true, "IsBetaVersion": false, "Installed": false, "Modules": [ { "Name": "Hoge", "Type": "Editor", "LoadingPhase": "Default" } ] } |
次にBuild.csを開き、PrivateDependencyModuleNamesに”UnrealEd”を追加します。
1 2 3 4 5 6 7 8 9 10 11 |
PrivateDependencyModuleNames.AddRange( new string[] { "CoreUObject", "Engine", "Slate", "SlateCore", "UnrealEd", // ... add private dependencies that you statically link with here ... } ); |
次にヘッダーを開き関数を追加します。
1 2 3 |
class FHogeModule : public IModuleInterface { public: |
/** IModuleInterface implementation */
virtual void StartupModule() override;
virtual void ShutdownModule() override;
private:
static TSharedRef<FExtender> OnExtendContentBrowserAssetSelectionMenu(const TArray<FAssetData>& SelectedAssets);
static void CreateAssetMenu(FMenuBuilder& MenuBuilder, TArray<FAssetData> SelectedAssets);
static void OpenLevelBlueprint(TArray<FAssetData> SelectedAssets);
};
最後にc++の実装です
1 |
#include "Hoge.h" |
#include “Engine/World.h”
#include “LevelEditor.h”
#include “ContentBrowserModule.h”
#include “IContentBrowserSingleton.h”
#include “Misc/MessageDialog.h”
#include “Engine/LevelScriptBlueprint.h”
#include “Toolkits/AssetEditorManager.h”
#define LOCTEXT_NAMESPACE “FHogeModule”
namespace
{
FContentBrowserMenuExtender_SelectedAssets ContentBrowserExtenderDelegate;
FDelegateHandle ContentBrowserExtenderDelegateHandle;
}
void FHogeModule::StartupModule()
{
if (IsRunningCommandlet()) { return; }
FContentBrowserModule& ContentBrowserModule =
FModuleManager::LoadModuleChecked<FContentBrowserModule>(TEXT(“ContentBrowser”));
// アセット右クリックメニューへのExtender登録
ContentBrowserExtenderDelegate =
FContentBrowserMenuExtender_SelectedAssets::CreateStatic(
&FHogeModule::OnExtendContentBrowserAssetSelectionMenu
);
TArray<FContentBrowserMenuExtender_SelectedAssets>& CBMenuExtenderDelegates =
ContentBrowserModule.GetAllAssetViewContextMenuExtenders();
CBMenuExtenderDelegates.Add(ContentBrowserExtenderDelegate);
ContentBrowserExtenderDelegateHandle = CBMenuExtenderDelegates.Last().GetHandle();
}
void FHogeModule::ShutdownModule()
{
FContentBrowserModule* ContentBrowserModule =
FModuleManager::GetModulePtr<FContentBrowserModule>(TEXT(“ContentBrowser”));
if (nullptr != ContentBrowserModule)
{
TArray<FContentBrowserMenuExtender_SelectedAssets>& CBMenuExtenderDelegates =
ContentBrowserModule->GetAllAssetViewContextMenuExtenders();
CBMenuExtenderDelegates.RemoveAll([](const FContentBrowserMenuExtender_SelectedAssets& Delegate)
{ return Delegate.GetHandle() == ContentBrowserExtenderDelegateHandle; });
}
}
TSharedRef<FExtender> FHogeModule::OnExtendContentBrowserAssetSelectionMenu(const TArray<FAssetData>& SelectedAssets)
{
TSharedRef<FExtender> Extender(new FExtender());
for (auto ItAsset = SelectedAssets.CreateConstIterator(); ItAsset; ++ItAsset)
{
if ((*ItAsset).AssetClass == UWorld::StaticClass()->GetFName())
{
Extender->AddMenuExtension(
TEXT(“CommonAssetActions”),
EExtensionHook::First,
nullptr,
FMenuExtensionDelegate::CreateStatic(&FHogeModule::CreateAssetMenu, SelectedAssets)
);
return Extender;
}
}
return Extender;
}
void FHogeModule::CreateAssetMenu(FMenuBuilder& MenuBuilder, TArray<FAssetData> SelectedAssets)
{
int count = 0;
for (auto ItAsset = SelectedAssets.CreateConstIterator(); ItAsset; ++ItAsset)
{
if ((*ItAsset).AssetClass == UWorld::StaticClass()->GetFName())
{
count++;
}
}
FText toolTip = count == 1 ? LOCTEXT(“Open Level Blueprint”, “Open Level Blueprint”) : LOCTEXT(“Open Level Blueprints”, “Open Level Blueprints”);
MenuBuilder.AddMenuEntry(
toolTip,
LOCTEXT(“OpenLevelBlueprint_Tooltip”, “Open Level Blueprint. Note: This is unofficial”),
FSlateIcon(),
FUIAction(FExecuteAction::CreateStatic(&FHogeModule::OpenLevelBlueprint, SelectedAssets)),
NAME_None,
EUserInterfaceActionType::Button
);
}
void FHogeModule::OpenLevelBlueprint(TArray<FAssetData> SelectedAssets)
{
for (auto ItAsset = SelectedAssets.CreateConstIterator(); ItAsset; ++ItAsset)
{
UWorld* world = Cast<UWorld>((*ItAsset).GetAsset());
if (!world)
{
continue;
}
ULevel* level = world->GetCurrentLevel();
if (level == NULL)
{
return;
}
ULevelScriptBlueprint* LevelScriptBlueprint = level->GetLevelScriptBlueprint();
if (LevelScriptBlueprint)
{
FAssetEditorManager::Get().OpenEditorForAsset(LevelScriptBlueprint);
}
else
{
FMessageDialog::Open(EAppMsgType::Ok, NSLOCTEXT(“UnrealEd”, “UnableToCreateLevelScript”, “Unable to find or create a level blueprint for this level.”));
}
}
}
#undef LOCTEXT_NAMESPACE
IMPLEMENT_MODULE(FHogeModule, Hoge)
以上でレベルを右クリックした時にメニューに追加されました。
エディタ実行時にのみ読み込ませるため、upluginではtypeにEditorを記述しました。
また、UnrealEdモジュールを使用しているため、Build.csにはUnrealEdを追加しています。
コードの流れとしては、アセットを右クリックした時にUWorldクラスのアセットだったら右クリックメニューの中にFHogeModule::OpenLevelBlueprint()を実行するメニューを追加するというものです。
ブループリントエディタを開くのにはFAssetEditorManager::Get().OpenEditorForAsset()を使用しています。
右クリックして選択したアセットはUWorldとなっているのでブループリントの本体ではありません。
FHogeModule::OpenLevelBlueprint()関数内で右クリックして選択したSelectedAssetsから->GetCurrentLevel()->GetLevelScriptBlueprint()としてブループリントを取得し、OpenEditorForAsset()に投げています。
今回実装したOpen Level Blueprintにはアイコンが付いていないのでこのアイコンを付けてみます。
Build.csにEditorStyleを追加します。
1 2 3 4 5 6 7 8 9 10 11 12 |
PrivateDependencyModuleNames.AddRange( new string[] { "CoreUObject", "Engine", "Slate", "SlateCore", "UnrealEd", "EditorStyle", // ... add private dependencies that you statically link with here ... } ); |
FHogeModule::CreateAssetMenu()内のMenuBuilder.AddMenuEntry()でFSlateIcon()の引数を変更します。
1 2 3 4 5 6 7 8 9 10 |
void FHogeModule::CreateAssetMenu(FMenuBuilder& MenuBuilder, TArray<FAssetData> SelectedAssets) { int count = 0; for (auto ItAsset = SelectedAssets.CreateConstIterator(); ItAsset; ++ItAsset) { if ((*ItAsset).AssetClass == UWorld::StaticClass()->GetFName()) { count++; } } |
FText toolTip = count == 1 ? LOCTEXT(“Open Level Blueprint”, “Open Level Blueprint”) : LOCTEXT(“Open Level Blueprints”, “Open Level Blueprints”);
MenuBuilder.AddMenuEntry(
toolTip,
LOCTEXT(“OpenLevelBlueprint_Tooltip”, “Open Level Blueprint. Note: This is unofficial”),
FSlateIcon(FEditorStyle::GetStyleSetName(), “Level.ScriptIcon16x”),
FUIAction(FExecuteAction::CreateStatic(&FHogeModule::OpenLevelBlueprint, SelectedAssets)),
NAME_None,
EUserInterfaceActionType::Button
);
}
以上でアイコンが追加されました。