Download

How to create a new actor blueprint with a static mesh component

I’m working on a scripted asset action for right clicking on a static mesh and creating an actor with the static mesh component added as the root. It’s meant as a quick script to speed up work when prototyping a level and we need an actor that contains a static mesh.

So far I have everything working, except for the part where I add the component. I’m trying to create it as if it was added in blueprint, so I want the component to be editable and removeable.

I was doing something like this


UBlueprintFactory* Factory = NewObject<UBlueprintFactory>();
UObject* NewAsset = AssetTools.CreateAsset(Name, FPackageName::GetLongPackagePath(PackagePath), UBlueprint::StaticClass(), Factory);
UBlueprint* NewBlueprint = Cast<UBlueprint>(NewAsset);

AActor* Prefab = GetMutableDefault<AActor>(NewBlueprint->GeneratedClass);

UStaticMeshComponent* NewStaticMeshComponent = NewObject<UStaticMeshComponent>(Prefab, TEXT("StaticMesh"));
NewStaticMeshComponent->SetStaticMesh(StaticMesh);

Prefab->Modify();

But when I open the blueprint, the static mesh isn’t editable at all and I can’t delete the component. Also the static mesh isn’t set.

So now I’m trying to figure out how to add SCS nodes programmatically to the class but it’s not working at all. In code I can see the SCS nodes and everything are created, but when I open the blueprint it’s as if the actor never had any component added at all.

I was missing one function when creating an SCS node:
NewBlueprint->SimpleConstructionScript->AddNode(NewNode);

I can just post my entire function here. This is triggered by an editor utility function.
https://blueprintue.com/blueprint/i33oxapv/



void URDLGEGameplayStatics::CreateSpawnablePrefabActorFromSelectedStaticMeshes(TArray<UObject*> SelectedAssets)
{
   IAssetTools& AssetTools = FModuleManager::LoadModuleChecked<FAssetToolsModule>("AssetTools").Get();
   FContentBrowserModule& ContentBrowserModule = FModuleManager::LoadModuleChecked<FContentBrowserModule>("ContentBrowser");

   TArray<UObject*> ObjectsToSync;
   bool bBulkApplyClass = false;
   TSubclassOf<UObject> BulkPrefabClass = nullptr;

   // Ask if the user wants to bulk apply the same class to all assets
   if (SelectedAssets.Num() > 1)
   {
      const EAppReturnType::Type Result = FMessageDialog::Open(EAppMsgType::YesNoCancel, RDLGE_MULTIPLE_OBJECTS_CLASSDIALOG);

      if (Result == EAppReturnType::Cancel)
      {
         return;
      }

      if (Result == EAppReturnType::Yes)
      {
         bBulkApplyClass = true;
      }
   }

   for (UObject* SelectedAsset : SelectedAssets)
   {
      // Figure out the asset type
      UStaticMesh* StaticMesh = Cast<UStaticMesh>(SelectedAsset);

      if (StaticMesh)
      {
         // Determine an appropriate name
         FString Name;
         FString PackagePath;

         FString AssetName = SelectedAsset->GetOutermost()->GetName();

         // If the asset is a static mesh and ends in __SM, remove that part so it'll automatically append __A
         if (StaticMesh && AssetName.EndsWith("__SM"))
         {
            AssetName.RemoveAt(AssetName.Len() - 4, 4, false);
         }

         AssetTools.CreateUniqueAssetName(AssetName, TEXT("__A"), PackagePath, Name);

         // Create the factory used to generate the asset
         UBlueprintFactory* Factory = NewObject<UBlueprintFactory>();

         Factory->ParentClass = BulkPrefabClass;

         while (true)
         {
            if (!Factory->ParentClass)
            {
               Factory->ConfigureProperties();
            }

            if (Factory->ParentClass->IsChildOf(AActor::StaticClass()))
            {
               break;
            }
            else
            {
               Factory->ParentClass = nullptr;
               FMessageDialog::Open(EAppMsgType::Ok, RDLGE_EXPECTING_ACTOR_CLASS_ERROR);
            }
         }

         // Set this so it won't have to ask again if bulk apply is on
         if (bBulkApplyClass)
         {
            BulkPrefabClass = Factory->ParentClass;
         }

         UObject* NewAsset = AssetTools.CreateAsset(Name, FPackageName::GetLongPackagePath(PackagePath), UBlueprint::StaticClass(), Factory);

         if (NewAsset)
         {
            ObjectsToSync.Add(NewAsset);
         }

         if (UBlueprint* NewBlueprint = Cast<UBlueprint>(NewAsset))
         {
            USCS_Node* NewNode = nullptr;

            // Create a component with a static mesh
            if (StaticMesh)
            {
               NewNode = NewBlueprint->SimpleConstructionScript->CreateNode(UStaticMeshComponent::StaticClass(), TEXT("StaticMesh"));
               NewBlueprint->SimpleConstructionScript->AddNode(NewNode);
               FComponentAssetBrokerage::AssignAssetToComponent(NewNode->ComponentTemplate, StaticMesh);
            }

            NewBlueprint->GeneratedClass->Modify();
            FBlueprintCompilationManager::CompileSynchronously(FBPCompileRequest(NewBlueprint, EBlueprintCompileOptions::None, nullptr));
            NewBlueprint->Modify();

            if (NewNode)
            {
               NewNode->Modify();
            }
         }
      }
   }

   if (ObjectsToSync.Num())
   {
      ContentBrowserModule.Get().SyncBrowserToAssets(ObjectsToSync, true);
   }
}