UE创建一个关卡类型的Asset资产

 直接上代码:

void FPutBuildingToLevelModule::CreateNewlevelAsset(FString AssetName,FString PackagePath)
{


	UWorldFactory* Factory = NewObject<UWorldFactory>();
	Factory->bCreateWorldPartition = false;
	Factory->WorldType = EWorldType::Inactive;
		
	FAssetToolsModule& AssetToolsModule = FModuleManager::Get().LoadModuleChecked<FAssetToolsModule>("AssetTools");
	AssetToolsModule.Get().CreateAsset(AssetName,PackagePath,UWorld::StaticClass(),Factory);


}

下面是此需求在数字孪生开发的一些分享,可以直接忽略直接看代码的部分


本人在公司数字孪生项目开发中,有个重复性人工操作,浪费大量的时间。

我目前公司基本上每个数字孪生项目都会有大量摄像头/门禁/消防点位 等设备的点位,

这些点位Actor需要准确的放置到对应楼层的对应位置。

并且使用流关卡的形式加载和卸载,当用户点击A栋B层的时候,在固定位置加载“A栋B层Level”,

并且让GameController 切换控制 固定位置的那个Pawn,使用这个Pawn负责楼栋内的控制 (因为楼栋内和大场景的操作逻辑是不同的,比如缩放速度,旋转速度,等等)

因此有个重复性的需求  :复制一份模型,新建一个关卡实例,讲模型放入到新关卡中

就是要将单层楼栋的模型复制一份到一个新关卡中(这样不影响大场景),并且复制后的模型移动到固定位置,这样就可以实现 上文中说的 当用户点击A栋B层的时候,在固定位置加载“A栋B层Level”。


使用一个球包住楼栋的位置,里面是单层楼栋的模型


这里代码只列出主要的部分

需求:复制一份模型,新建一个关卡实例,讲模型放入到新关卡中

1.拿到目前编辑器中选中的资源

这里我添加了自定义的右击菜单

void FPutBuildingToLevelModule::ShutdownModule()
{

}

void FPutBuildingToLevelModule::InitCBMenuExtention()
{

	//获取Level Editor模块
	FLevelEditorModule& LevelEditorModule = FModuleManager::LoadModuleChecked<FLevelEditorModule>("LevelEditor");
	//获取Viewport菜单上下文拓展项(Viewport中选中物体的右击菜单拓展项),是一个委托数组
	auto& LVCMExtenders = LevelEditorModule.GetAllLevelViewportContextMenuExtenders();
	//在数组中添加一个新的拓展项,当右击选中Actor并弹出菜单时,将会遍历此数组并执行绑定的委托方法
	LVCMExtenders.Add(FLevelEditorModule::FLevelViewportMenuExtender_SelectedActors::CreateRaw(this, &FPutBuildingToLevelModule::CreatMenuExtension));
	
}

TSharedRef<FExtender> FPutBuildingToLevelModule::CreatMenuExtension(const TSharedRef<FUICommandList> CommandList ,
	const TArray<AActor*> Actors)
{

	//创建一个拓展项,与上一章中用法相似,用其拓展菜单项
	TSharedPtr<FExtender> Extender = MakeShareable(new FExtender());
	//设置菜单拓展项显示内容,绑定的委托是上章创建的委托方法
	Extender->AddMenuExtension("GoHere", EExtensionHook::After, CommandList, FMenuExtensionDelegate::CreateRaw(this, &FPutBuildingToLevelModule::AddMenuExtension));
	//auto& LVCMExtenders = LevelEditorModule.GetAllLevelViewportContextMenuExtenders();
	//LVCMExtenders.Pop();          //如果将最后一个(我们刚刚创建的)Extender移除,则将会在第一显示菜单后,下次不会再显示此项
	//返回拓展项,将会被显示在菜单栏


	//当前选中的资源
	SelectActors = Actors;
	
	return Extender.ToSharedRef();
	
}

void FPutBuildingToLevelModule::AddMenuExtension(FMenuBuilder& MenuBuilder)
{
	MenuBuilder.AddMenuEntry
(
	//按钮名称
	FText::FromString(TEXT("移动选择资源至关卡")),
	//提示文本
	FText::FromString(TEXT("移动选择资源至关卡")),
	//按钮图标
	FSlateIcon(FEditorStyle::GetStyleSetName(), "LevelEditor.Tabs.Viewports"),
	//绑定要执行的事件
	FExecuteAction::CreateRaw(this, &FPutBuildingToLevelModule::OnClick),
	"MoveBuilding"
);
	
}

void FPutBuildingToLevelModule::OnClick()
{
	
	//具体实现
	
}

2.遍历拿到的资源,生成对应数量的关卡实例

	//遍历选中的资源
	for (AActor* Actor : SelectActors)
	{
		if (!Actor)
			continue;

		AStaticMeshActor* MeshActor = Cast<AStaticMeshActor>(Actor);
		if (!MeshActor)
			continue;

		if (!MeshActor->GetStaticMeshComponent())
			continue;

		if (!MeshActor->GetStaticMeshComponent()->GetStaticMesh())
			continue;

		//新建关卡 并复制资源移动至新关卡内
		//UKismetSystemLibrary::GetDisplayName(MeshActor);  
		FString DisplayName=MeshActor->GetActorLabel();
		UWorld* World =CreateNewlevelAsset(DisplayName,PackagePath);
UWorld* FPutBuildingToLevelModule::CreateNewlevelAsset(FString AssetName,FString PackagePath)
{
	UWorldFactory* Factory = NewObject<UWorldFactory>();
	Factory->bCreateWorldPartition = false;
	Factory->WorldType = EWorldType::Inactive;
	
	FAssetToolsModule& AssetToolsModule = FModuleManager::Get().LoadModuleChecked<FAssetToolsModule>("AssetTools");
	
	UObject* WorldAsset =AssetToolsModule.Get().CreateAsset(AssetName,PackagePath,UWorld::StaticClass(),Factory);
	if (!IsValid(WorldAsset))
	{
		return  nullptr;
	}
	
	UWorld* World =WorldAsset->GetWorld();
	return World;
}

3.复制一份到新关卡资源中

	//遍历选中的资源
	for (AActor* Actor : SelectActors)
	{
		if (!Actor)
			continue;

		AStaticMeshActor* MeshActor = Cast<AStaticMeshActor>(Actor);
		if (!MeshActor)
			continue;

		if (!MeshActor->GetStaticMeshComponent())
			continue;

		if (!MeshActor->GetStaticMeshComponent()->GetStaticMesh())
			continue;

		//新建关卡 并复制资源移动至新关卡内
		//UKismetSystemLibrary::GetDisplayName(MeshActor);  
		FString DisplayName=MeshActor->GetActorLabel();
		UWorld* World =CreateNewlevelAsset(DisplayName,PackagePath);
		
		if (!IsValid(World))continue;

		//复制一份选中的Mesh
		UEditorActorSubsystem* EditorActorSubsystem = GEditor->GetEditorSubsystem<UEditorActorSubsystem>();
		//会将选中的复制一份到目标世界  

		//设置新位置


			
		//将新建的关卡添加至当前关卡的子关卡
		//这里先复制一个到当前关卡里  生成正确的偏移
		//再二次复制到正确关卡里
		AActor* DuplicateActorA = EditorActorSubsystem->DuplicateActor(Actor,nullptr,MoveLocation);
		//修改世界大纲名称
		FString NewName = Actor->GetActorLabel()+"_New";
		DuplicateActorA->SetActorLabel(NewName);
		
		AActor* DuplicateActorB = EditorActorSubsystem->DuplicateActor(DuplicateActorA,World,FVector(0,0,0));
		DuplicateActorB->SetActorLabel(Actor->GetActorLabel());
		
		//删除中转actor
		EditorActorSubsystem->DestroyActor(DuplicateActorA);
		
	}
AActor* DuplicateActor(
    AActor* ActorToDuplicate, 
    UWorld* ToWorld = nullptr, 
    FVector Offset = FVector::ZeroVector)

DuplicateActor()这个函数有个大坑,当复制到新的World时,函数返回的总是我当前关卡里的actor,而不是我复制出来的那个actor。于是我设置复制出来的名字时总是把选中的actor名字给修改了。同关卡里返回值是新复制出来的,只是不同关卡时出现发现这个问题。没具体深入研究。

于是 - -

我在当前关卡右击A,复制一份B,将B名字修改为带_new字样的,再复制B到新关卡里,这样复制出来的C就带_new。再删掉B

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值