UE4 C++实现获取指定目录的所有蓝图类UBlueprint和UBlueprintGeneratedClass

本文介绍了如何通过FString路径获取所有蓝图文件的UBlueprintGeneratedClass,并演示了两种创建对象的方法:StaticLoadObject与FStringAssetReference,重点在于蓝图文件的动态加载和对象实例化过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

获取指定目录的所有蓝图文件的UBlueprintGeneratedClass

TArray<UClass*> UFileBFLibrary::getAllClassFromFolder(FString* path)
{
    FString folderpath(*path);
    UObjectLibrary* lib = UObjectLibrary::CreateLibrary(UObject::StaticClass(), false, GIsEditor);
    lib->AddToRoot();
    lib->LoadAssetDataFromPath(folderpath);
    lib->LoadAssetsFromAssetData();
    
    TArray<FAssetData> asset_data;
    lib->GetAssetDataList(asset_data);

    TArray<UClass*> result;
    for (int32 i = 0; i < asset_data.Num(); i++)
    {
        FString filepath(asset_data[i].GetExportTextName());  
        FStringAssetReference asset_stream_ref(filepath);
        TAssetPtr<UBlueprint> asset(asset_stream_ref);
        UBlueprint* bp = asset.LoadSynchronous();
        if(bp)
        {
            result.Add(bp->GeneratedClass);
        }
    }
    return result;
}

通过蓝图文件路径创建物体

void ABaseActor::spawnActorByPath(const TCHAR* path, FVector vector, FRotator rotator)
{
	UObject* SpawnActor = Cast<UObject>(StaticLoadObject(UObject::StaticClass(), NULL, path));

	UBlueprint* GeneratedBP = Cast<UBlueprint>(SpawnActor);
	if (!SpawnActor)
	{
		GEngine->AddOnScreenDebugMessage(-1, 1.f, FColor::Red, FString::Printf(TEXT("CANT FIND OBJECT TO SPAWN")));
		return;
	}

	UClass* SpawnClass = SpawnActor->StaticClass();
	if (SpawnClass == NULL)
	{
		GEngine->AddOnScreenDebugMessage(-1, 1.f, FColor::Red, FString::Printf(TEXT("CLASS == NULL")));
		return;
	}

	UWorld* World = GetWorld();
	FActorSpawnParameters SpawnParams;
	SpawnParams.Owner = this;
	SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
	World->SpawnActor<AActor>(GeneratedBP->GeneratedClass, vector, rotator, SpawnParams);
}

动态加载文件的两种方法

* 使用StaticLoadClass
UObject* SpawnActor = Cast<UObject>(StaticLoadObject(UObject::StaticClass(), NULL, path));
UBlueprint* GeneratedBP = Cast<UBlueprint>(SpawnActor);


* 使用 FStringAssetReference 
FString filepath("Blueprint'/Game/RoomPart/BaseMoveActor_Child4.BaseMoveActor_Child4'");  
FStringAssetReference asset_stream_ref(filepath);
TAssetPtr<UBlueprint> asset(asset_stream_ref);
UBlueprint* bp = asset.LoadSynchronous();
<think>我们正在讨论的是在编辑器模式下修改资产(Asset),而不是运行时修改实例。在Unreal Engine中,编辑器模式下的资产修改通常通过以下方式之一: 1. 使用C++编写编辑器模块(Editor Module)工具,直接修改资产数据。 2. 在资产中重写特定函数,以便在编辑器初始化时设置。 但是,需要注意的是,直接修改资产(例如一个蓝图资产或一个静态网格资产)的默认状态,通常是在该资产的构造函数或序列化过程中设置的。然而,对于已经存在的资产,我们可能需要通过编辑器脚本来批量修改,或者通过自定义细节面板(Detail Customization)来提供编辑能力。 不过,根据问题,你希望用C++代码修改某个Component的位置,然后编译,使得在编辑器模式下该资产(比如一个蓝图)的默认组件位置被修改。这里的关键是:这个修改应该影响到资产的默认对象(CDO),并且是在编辑器加载该资产时就能看到变化。 实现方法: 1. 如果你是在创建新的资产C++),那么直接在的构造函数中设置组件位置(如之前所述),然后编译即可。这样,当你在编辑器中基于这个C++创建蓝图时,默认位置就会是你设置的位置。 2. 如果你想要修改一个已经存在的蓝图资产(比如一个已经保存的.uasset文件)中的组件位置,那么你需要通过编辑器脚本来实现。这涉及到使用Unreal Editor的脚本功能(如Python脚本)或者编写一个编辑器模块(C++)来加载并修改资产。 由于问题中提到“用C++代码修改然后编译”,我们假设你是在修改一个C++的代码,然后重新编译引擎模块,从而影响所有基于该的资产(包括蓝图)的默认对象。 但是,如果这个资产是一个蓝图资产(不是C++),那么你不能直接通过修改C++代码来改变它的默认值,因为蓝图资产是独立于C++的实例数据。这时,你需要通过编辑器扩展来修改。 因此,这里我们主要讨论第一种情况:修改C++的构造函数,从而影响该的默认对象(包括蓝图)。 示例:修改一个C++ Actor中的组件位置 在C++的构造函数中设置组件的位置,这样当你在编辑器中创建一个该的实例,或者创建一个基于该蓝图时,组件的默认位置就会是你设置的位置。 代码示例(同上一个回答似,但这里强调编辑器模式): ```cpp // MyActor.cpp AMyActor::AMyActor() { // 创建根组件 USceneComponent* Root = CreateDefaultSubobject<USceneComponent>(TEXT("RootComponent")); RootComponent = Root; // 创建一个静态网格组件 UStaticMeshComponent* MeshComp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh")); // 设置相对位置 MeshComp->SetRelativeLocation(FVector(100.0f, 0.0f, 0.0f)); MeshComp->SetupAttachment(RootComponent); } ``` 重新编译项目后,在编辑器中拖放这个Actor,就会看到网格组件的位置在根组件的位置基础上偏移了(100,0,0)。 但是,如果用户想要修改一个已经存在的蓝图资产(这个蓝图可能是基于这个C++或者原生UObject)的默认组件位置,那么就需要通过编辑器工具来修改。由于问题要求用C++代码修改然后编译,我们假设用户是在修改C++。 注意:在编辑器模式下,修改C++的构造函数只会影响新创建的实例(包括蓝图中未覆盖的部分),对于已经放置在场景中的实例,需要手动更新(或者重新放置)。 如果问题中的资产指的是一个具体的蓝图资产(.uasset文件),那么我们需要使用不同的方法,例如: 编写一个编辑器工具(Editor Utility),使用Unreal Engine的AssetEditorSubsystem来打开修改资产。但这种方法通常不需要重新编译整个项目,而是运行一个编辑器命令。 鉴于问题要求用C++代码修改然后编译,我们推断用户希望修改的是C++,从而影响所有派生蓝图。 因此,回答重点放在修改C++的构造函数上。 但是,如果用户确实需要修改一个具体的蓝图资产(非C++),那么我们可以这样: 1. 使用C++编写一个编辑器模块(Editor Module),在模块启动时执行修改。 2. 在模块中,通过UBlueprintGeneratedClass获取蓝图的默认对象(CDO),然后修改其组件的相对位置。 3. 注意:修改后需要标记资产为脏(MarkPackageDirty)并保存。 然而,这种方法通常是在编辑器启动时运行一次,或者通过一个按钮触发。而且,修改蓝图资产的CDO需要非常小心,因为可能破坏蓝图。 由于问题描述比较模糊,这里我们提供两种情况的说明,但主要代码示例针对第一种情况(修改C++)。 如果用户需要修改蓝图资产的代码示例(在编辑器模式下运行): ```cpp // 在编辑器模块中(例如一个菜单命令的响应函数中) void ModifyBlueprintAsset() { // 加载蓝图资产 UBlueprint* Blueprint = ...; // 通过路径加载或者选择 if (Blueprint && Blueprint->GeneratedClass) { // 获取CDO AActor* CDO = Cast<AActor>(Blueprint->GeneratedClass->GetDefaultObject()); if (CDO) { // 找到组件 USceneComponent* TargetComp = CDO->FindComponentByClass<USceneComponent>(); if (TargetComp) { // 修改位置 TargetComp->SetRelativeLocation(FVector(0,0,200)); // 注意:需要重新注册组件以更新编辑器视口 TargetComp->ReregisterComponent(); // 标记蓝图资产需要保存 Blueprint->MarkPackageDirty(); } } } } ``` 但是,这种方法需要在编辑器模式下运行(比如通过一个工具栏按钮),并且需要重新加载蓝图编辑器才能看到变化。 考虑到问题要求“编译”,我们推断用户可能是想通过修改代码并编译来影响资产,所以第一种情况(修改C++)更符合。 因此,我们主要回答第一种情况,但简要提及第二种情况。 总结回答: 如果你想修改一个C++的默认组件位置,在的构造函数中设置即可,然后重新编译项目。这样,所有基于该蓝图实例都会使用新的默认位置。 如果你需要修改一个已经存在的蓝图资产(.uasset)的默认组件位置,则需要编写编辑器工具(C++或Python)来直接修改该资产的默认对象,但这不需要重新编译项目(但需要重启编辑器或重新加载资产),或者通过一个编辑器命令来执行。 由于问题要求用C++代码修改然后编译,我们给出第一种情况的代码示例(同前),并强调这是影响默认对象的方法。 因此,回答如下:</think>在Unreal Engine编辑器模式下直接修改资产(如蓝图)的组件位置,需要通过修改资产的**默认对象(CDO)**实现。以下是具体操作步骤C++代码示例: ### 解决方案 1. **定位资产**:继承自`UBlueprintGeneratedClass`或特定资产 2. **重写PostLoad函数**:在资产加载后修改默认对象 3. **获取并修改组件**:通过组件名称或型定位组件并设置位置 4. **标记资产为脏**:确保修改能被保存 ### 代码示例(编辑器模块中) ```cpp // MyAssetEditorModule.cpp #include "AssetToolsModule.h" #include "Engine/Blueprint.h" void ModifyBlueprintAsset(UBlueprint* BlueprintAsset) { if (!IsValid(BlueprintAsset)) return; // 获取默认对象(CDO) UObject* CDO = BlueprintAsset->GeneratedClass->GetDefaultObject(); AActor* ActorCDO = Cast<AActor>(CDO); if (ActorCDO) { // 查找目标组件 (按名称) UActorComponent* TargetComp = ActorCDO->FindComponentByTag( USceneComponent::StaticClass(), FName("YourComponentTag") // 组件标签 ); // 或按型查找 // UStaticMeshComponent* MeshComp = ActorCDO->FindComponentByClass<UStaticMeshComponent>(); if (USceneComponent* SceneComp = Cast<USceneComponent>(TargetComp)) { // 修改相对位置 (X=150, Y=0, Z=50) SceneComp->SetRelativeLocation(FVector(150.0f, 0.0f, 50.0f)); // 标记资产需要保存 BlueprintAsset->MarkPackageDirty(); // 刷新编辑器预览 GEditor->RedrawAllViewports(); } } } // 调用示例 (在资产打开时自动执行) void FMyAssetEditorModule::StartupModule() { FAssetToolsModule::GetModule().Get().GetOnAssetOpened().AddLambda( [](UObject* Asset) { if (UBlueprint* BP = Cast<UBlueprint>(Asset)) { ModifyBlueprintAsset(BP); } } ); } ``` ### 关键注意事项 1. **编辑器模块要求**: - 代码必须放在`Editor`模块中(在`.Build.cs`中添加`UnrealEd`依赖) - 使用`WITH_EDITOR`宏包裹编辑器专用代码 2. **位置修改时机**: - `PostLoad`:资产加载完成后 - `PostEditChangeProperty`:属性修改后 - 自定义编辑器按钮/菜单命令 3. **组件访问方式**: - `FindComponentByTag()`:通过组件标签定位 - `GetComponentByClass()`:通过组件型定位 - `GetComponents()`:遍历所有组件 4. **刷新机制**: - `MarkPackageDirty()`:标记资产需要保存 - `RedrawAllViewports()`:刷新编辑器视口 - `ReregisterComponent()`:重新注册组件
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DvLee1024

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值