本文对 UE4 的一些功能代码做了汇总,可能具备参考价值。
最近开始做 UE4 相关的需求,实现了一个模型自动化导入插件。要求在导入模型后,自动生成角色蓝图和动画蓝图,并把 Actor 放置到场景中自动播放动画。
整个过程要求一键导入,因此,需要使用代码来实现编辑器的一些操作。
这里不得不吐槽下 UE4 的文档,API 不全还难搜,以至于某些功能还得去翻看源码才知道调用了哪个接口。
所以,打算整理下本次涉及到的一些功能实现。由于 UE4 只是初学,本文只做记录不做讲解,若存在纰漏请见谅。
FBX 文件导入
创建 UFbxFactory ,设置 UAssetImportTask ,调用 ImportObject 方法:
UFbxFactory* fbxFactory = NewObject<UFbxFactory>(); fbxFactory->ImportUI->MeshTypeToImport = FBXIT_StaticMesh; fbxFactory->ImportUI->OriginalImportType = FBXIT_StaticMesh; fbxFactory->ImportUI->StaticMeshImportData->bCombineMeshes = true; fbxFactory->ImportUI->StaticMeshImportData->ImportUniformScale = 1.0f; fbxFactory->ImportUI->bImportMaterials = true; fbxFactory->ImportUI->bImportTextures = true; fbxFactory->ImportUI->bAutoComputeLodDistances = true; fbxFactory->ImportUI->StaticMeshImportData->bAutoGenerateCollision = true; UAssetImportTask* importTask = NewObject<UAssetImportTask>(); importTask->bAutomated = true; importTask->bSave = true; fbxFactory->SetAssetImportTask(importTask); bool&& canceled = false; fbxFactory->ImportObject( UStaticMesh::StaticClass(), folderPackage, *FBXName, EObjectFlags::RF_Transactional | EObjectFlags::RF_Public | EObjectFlags::RF_Standalone, path, nullptr, canceled );
设置导入完成回调,用于执行下一步操作:
FEditorDelegates::OnAssetPostImport.AddUFunction(this, STATIC_FUNCTION_FNAME(TEXT("UFBXLoader::LoadCallback")));
注意 this 必须继承自 UObject 。
文件拷贝
FString pluginsFolder = FPaths::ConvertRelativePathToFull(FPaths::ProjectPluginsDir()); FString contentFolder = FPaths::ConvertRelativePathToFull(FPaths::ProjectContentDir()); const FString bpPath = pluginsFolder + "BlendShape/Resources/BP.uasset"; const FString copyPath = contentFolder + "BP.uasset"; IFileManager::Get().Copy(*copyPath, *bpPath);
注意 Copy 里需要传入绝对路径。
资源加载
通过 LoadObject 加载 UObject 对象:
UBlueprint* sourceBlueprint = LoadObject<UBlueprint>(NULL, *sourceBlueprintPath); USkeletalMesh* mesh = LoadObject<USkeletalMesh>(NULL, *skeletalPath, NULL, LOAD_None, NULL); USkeleton* newSkeleton = LoadObject<USkeleton>(NULL, *newSkeletonPath, NULL, LOAD_None, NULL); UPoseAsset* poseAsset = LoadObject<UPoseAsset>(NULL, *poseAssetPath, NULL, LOAD_None, NULL); UAnimBlueprint* animBP = LoadObject<UAnimBlueprint>(NULL, *animBPPath, NULL, LOAD_None, NULL);
这里的路径基于 Content Browser 的路径。
通过 LoadClass 加载 UClass 对象:
UClass* animClass = LoadClass<UAnimInstance>(NULL, *animClassPath);
这里的路径同样是基于 Content Browser,但是资源的名称需要写成 name.name_C 。
动画蓝图重定向
自动生成动画蓝图的前提是有一份原始的动画蓝图,并且事先连接好事件图表,设置好 PoseAsset,然后调用 RetargetAnimations 方法重定向生成一份新的。
USkeleton* oldSkeleton = LoadObject<USkeleton>(NULL, *oldSkeletonPath, NULL, LOAD_None, NULL); USkeleton* newSkeleton = LoadObject<USkeleton>(NULL, *newSkeletonPath, NULL, LOAD_None, NULL); TArray<FAssetData> assetsToRetarget; assetsToRetarget.Add(FAssetData((UObject*)poseAsset)); assetsToRetarget.Add(FAssetData((UObject*)animBP)); bool bRetargetReferredAssets = true; bool bConvertSpace = true; EditorAnimUtils::FNameDuplicationRule nameRule; nameRule.Prefix = meshName + "_"; nameRule.FolderPath = FString(GameFolder) + "/" + newSkeletonFolder; EditorAnimUtils::RetargetAnimations(oldSkeleton, newSkeleton, assetsToRetarget, bRetargetReferredAssets, &nameRule, bConvertSpace);
角色蓝图生成
复制一个蓝图类不能简单地拷贝文件,正确的操作在编辑器里叫 Duplicate ,这样可以指定新蓝图类的类名。
UEditorAssetLibrary::DuplicateLoadedAsset(sourceBlueprint, blueprintPath);
路径同样是基于 Content Browser ,最终新类名由 blueprintPath 的文件名指定。
角色蓝图设置 SkeletalMesh
SkeletalMesh 需要在 UBlueprint 的 RootComponent 下的 USkeletalMeshComponent 上添加。
但是我没有找到获取 UBlueprint 的 RootComponent 的方式,只找到 AActor 的。
所以这里的做法是,先创建一个空的 AActor,在上面修改 Component,再把 AActor 的 Component 添加到 UBlueprint 上。
FPreviewScene* scene = new FPreviewScene(); FActorSpawnParameters spawnparam; spawnparam.bAllowDuringConstructionScript = true; AActor* tempActor = scene->GetWorld()->SpawnActor<AActor>(AActor::StaticClass(), FTransform::Identity); USceneComponent* rootComponent = NewObject<USceneComponent>(tempActor, FName(ActorRootComponentName)); tempActor->AddOwnedComponent(rootComponent); tempActor->AddInstanceComponent(rootComponent); rootComponent->RegisterComponent(); tempActor->SetRootComponent(rootComponent); FString skeletalPath = FString(GameFolder) + "/" + toFolder + "/" + meshName; USkeletalMesh* mesh = LoadObject<USkeletalMesh>(NULL, *skeletalPath, NULL, LOAD_None, NULL); USkeletalMeshComponent* meshComponent = NewObject<USkeletalMeshComponent>(tempActor, FName(*UKismetSystemLibrary::GetObjectName(mesh))); tempActor->AddOwnedComponent(meshComponent); tempActor->AddInstanceComponent(meshComponent); meshComponent->RegisterComponent(); meshComponent->SetSkeletalMesh(mesh); meshComponent->AttachToComponent(tempActor->GetRootComponent(), FAttachmentTransformRules::KeepRelativeTransform); FKismetEditorUtilities::AddComponentsToBlueprint(blueprint, tempActor->GetComponents().Array()); delete(scene);
角色蓝图关联动画蓝图
读取 UClass 后,通过 SetAnimClass 指定动画蓝图类:
FString animClassName = meshName + "_" + SourceAnimBPName; FString animClassPath = "AnimBlueprint'" + FString(GameFolder) + "/" + toFolder + "/" + animClassName + "." + animClassName + "_C'"; UClass* animClass = LoadClass<UAnimInstance>(NULL, *animClassPath); meshComponent->SetAnimClass(animClass);
Actor 放置到场景
通过 SpawnActor ,可以在场景中添加一个 Actor 对象:
UClass* bpClass = blueprint->StaticClass(); UWorld* world = GEditor->GetEditorWorldContext().World(); AActor *actor = world->SpawnActor<AActor>(blueprint->GeneratedClass, FVector(-250, 0, 100), FRotator(0, 90, 0));
为了后续方便获取到这个 Actor,给 Actor 加上 Tag :
actor->Tags.Add(ActorTag);
修改 Actor 属性
先通过 Tag 获取到场景中的 Actor 对象:
TArray<AActor*> actors; UWorld* world = GWorld->GetWorld(); UGameplayStatics::GetAllActorsWithTag(world, ActorTag, actors); AActor* actor = actors[0];
通过 FindFieldChecked 获取 FProperty:
FArrayProperty* arrayProperty = FindFieldChecked<FArrayProperty>(actor->GetClass(), *name);
通过 ContainerPtrToValuePtr 从 FProperty 里读取属性值:
TArray<FString> arrayOfStrings = *arrayProperty->ContainerPtrToValuePtr<TArray<FString>>(actor);
通过 SetPropertyValue_InContainer 修改 Actor 的属性值:
strProperty->SetPropertyValue_InContainer(actor, path);
Runtime 切换摄像机
我们希望在运行的时候,切换到特定的视角。这里的做法是动态添加 ACameraActor,调整位置,并在 Runtime 时把视角切换到新添加的 ACameraActor 上。
由于是在 Runtime 执行切换,所以要先继承 ACameraActor,在子类的 BeginPlay 方法里实现切换逻辑:
void AFaceCameraActor::BeginPlay() { GetWorld()->RegisterAutoActivateCamera(this, 0); APlayerController* playerController = UGameplayStatics::GetPlayerController(this, 0); playerController->SetViewTarget(this); Super::BeginPlay(); }
参考
UE4 编辑器资源操作篇
深入 Unreal 蓝图开发:理解蓝图技术架构
Unreal 动画实时重定向的源码分析
UE4 C++ 访问蓝图里的变量
离线重定向源码分析
UE4 类型系统、语言修饰符和元数据
UE4 使用蓝图或 C++ 切换摄像机视角
声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有,如涉及侵权,请联系小编邮箱 demi@eetrend.com 进行处理。