Scripting and Automating the Unreal Editor

Use Blueprints and Python in an automated wa.

DAZ Studio Matrix PostgreSQL install setup on Linux

Introduction to using Blueprints and Python to control the Unreal Editor programmatically.

In the Unreal Editor user interface, you have access to a vast range of visual tools for setting up your Project, designing and building Levels, creating gameplay interactions, and more. But sometimes, once you’ve figured out what you need the Editor to do, you might want to invoke its tools and commands programmatically — either in a reusable script, or by constructing your own interfaces to drive the Editor.

This can help you:

  • Minimize or eliminate the need to repeat the same series of tasks over and over,
  • Automate or randomize the placement, layout and settings of Actors in your Levels,
  • Create your own Asset import and management pipelines,
  • Interoperate with other third-party applications and pipeline scripts,
  • Extend the Editor with additional tools, and even UIs, that you customize specifically for the needs of your Project or content.

The pages in this section show how you can run these kinds of in-Editor scripts using BlueprintsPython, and remote control ol.

Installing the Editor Scripting Utilities Plugin

If you’re planning to do any Editor automation, regardless of the language or system you plan to use, you will almost certainly want to install the Editor Scripting Utilities plugin. This plugin simplifies many of the most common operations you’ll need to do in the Editor, handling uncommon edge cases so that you don’t have to understand all the internal details of how the Editor works in order to do something conceptually simple.

To install the plug-in:

  1. From the main menu, choose Editor > Plugins to open the Plugins window.
  2. Under the Scripting category, find the entry for the Editor Scripting Utilities and check its Enabled box.
  3. Restart the Editor and reload your Project.

Redirectors

Objects that are placed in packages to redirect references to moved assets to their current locations.

Moving or renaming an asset in UE4 leaves a Redirector in its old location. This is so that packages that are not currently loaded, but reference this asset, will know where to find it in its new location. Choosing a naming system early on and sticking with it will avoid many of the problems experienced with Redirectors.

Fixup Redirector From Editor

To see redirectors in the editor enable the Show Redirectors filter in the content browser. Then, right click a redirector and select Fixup. This will resave all packages that point to the redirector, and will delete the redirector if it was able to resave all things referencing it.

ResavePackages Commandlet

The ResavePackages commandlet can be run with the -FixupRedirectors option to try and fixup all redirectors in your project. Here’s an example command line:

UE4Editor.exe <GameName or uproject> -run=ResavePackages -fixupredirects -autocheckout -projectonly -unattended

This version of the command line would be run by a user on their local machine. It will check out all files that need to be fixed up, and the user needs to submit them. -autocheckin can be used by an automated process and it will also check the files in for you.

Gotchas

Renaming

If you create an object, rename it, and then create a new object with the same name as the original, an error will occur. This is because a Redirector was created when the first object was renamed, and a Redirector and a resource cannot have the same name.

Dangling Redirector

There are a few known issues with Redirectors that can be reproduced as follows:

Scenario 1

  • Rename object A to B.
  • Delete B.
  • An error message will say that B could not be deleted because it is in use. This happens because the Redirector created during the rename will still point to B.

Scenario 2

  • Rename object A to B.
  • Rename object B back to A.
  • Delete A.
  • The redirector that was created for the first rename will be destroyed, but a new one will be created at B. As a result, A will not be able to be deleted because it is being referenced.

Fixing redirectors from the editor or SavePackages before deleting should solve these problems.



Working with Modular Characters

Describes the different methods you can use to create modular characters comprised of multiple Skeletal Meshes.

ModularBanner.png

When creating systems that enable players to customize their characters and swap out different parts, for example different heads or body types, clothing or other options, you’ll want to consider constructing your characters in a modular way. Rather than importing a Skeletal Mesh for the full character, breaking out your Skeletal Mesh into sections, such as the torso, legs, and head, and importing them into the Engine; you can then use some of the methods described on this page for assembling those pieces and animating them. Not only will this give you increased flexibility in generating different characters, it will also be more performant. 

Wenn Sie Systeme erstellen, die es Spielern ermöglichen, ihre Charaktere anzupassen und verschiedene Teile auszutauschen, z. B. verschiedene Köpfe oder Körpertypen, Kleidung oder andere Optionen, sollten Sie erwägen, Ihre Charaktere modular zu konstruieren. Anstatt ein Skeletal Mesh für den vollständigen Charakter zu importieren, zerlegen Sie Ihr Skeletal Mesh in Abschnitte wie Torso, Beine und Kopf und importieren Sie diese in die Engine; Sie können dann einige der auf dieser Seite beschriebenen Methoden verwenden, um diese Teile zusammenzusetzen und zu animieren. Dies gibt Ihnen nicht nur mehr Flexibilität beim Generieren verschiedener Zeichen, sondern ist auch leistungsfähiger.

Master Pose Component

Master-Pose-Komponente

The Master Pose Component is a Blueprint callable function that enables you to set a Skinned Mesh Component Object (or Skinned Mesh Component Objects) as children to another Skinned Mesh Component Object considered to be the master. For example, you can define the Torso as the Master Pose Component, assign an animation to the Torso, then add Feet, Legs, Hands and a Head as children which will follow the animation assigned to the Torso. 

Behind the scenes, the children do not use any Bone Transform Buffer and won’t run any animations even if you set them on the child, it only uses the Torso’s Bone Transform Buffer when rendered which makes it a very lightweight attachment system. The only Component that has to run animation is the Torso, and all attached Components will use the Torso’s Bone Transform. Below is an example setup where we have assigned an animation to the Torso. We use the Construction Script inside a Blueprint to set the Torso Skeletal Mesh as the Master Bone Component and the other aspects of our modular character as children. 

Die Master-Pose-Komponente ist eine aufrufbare Blueprint-Funktion, mit der Sie ein Skinned-Mesh-Komponentenobjekt (oder Skinned-Mesh-Komponentenobjekte) als Kinder eines anderen Skinned-Mesh-Komponentenobjekts festlegen können, das als Master betrachtet wird. Beispielsweise können Sie den Torso als Master-Pose-Komponente definieren, dem Torso eine Animation zuweisen und dann Füße, Beine, Hände und einen Kopf als Kinder hinzufügen, die der dem Torso zugewiesenen Animation folgen.

Hinter den Kulissen verwenden die Kinder keinen Bone Transform Buffer und führen keine Animationen aus, selbst wenn Sie sie auf das Kind einstellen. Es verwendet nur den Bone Transform Buffer des Torsos, wenn es gerendert wird, was es zu einem sehr leichten Befestigungssystem macht. Die einzige Komponente, die eine Animation ausführen muss, ist der Torso, und alle angehängten Komponenten verwenden die Knochentransformation des Torsos. Unten sehen Sie ein Beispiel-Setup, bei dem wir dem Torso eine Animation zugewiesen haben. Wir verwenden das Konstruktionsskript innerhalb einer Blaupause, um das Torso-Skelettnetz als Hauptknochenkomponente (Master Bone Component) und die anderen Aspekte unseres modularen Charakters als Kinder festzulegen.

SetMasterPoseComponentImage.png

Above, we use the Construction Script inside a Blueprint to set the Torso Skeletal Mesh as the Master Bone Component and the other aspects of our modular character as children.

The Set Master Pose Component function has a second parameter of the boolean type called Force Update. If Force Update is disabled, it will skip updating all runtime info if that info is the same as the Master Component. If Force Update is enabled, it will force the updating of the runtime info. This only applies to the registration process as that can be serialized, at which point it will need to refresh all runtime data.

Each aspect of our character is a Skeletal Mesh that we can change out with another Skeletal Mesh Component. Below, we have toggled the display of the Torso and the Feet (we could change these out with different Skeletal Meshes that follow the same Skeletal hierarchy). 

Oben verwenden wir das Konstruktionsskript in einem Blueprint, um das Torso-Skelettnetz als Hauptknochenkomponente und die anderen Aspekte unseres modularen Charakters als Kinder festzulegen.

Die Funktion Set Master Pose Component hat einen zweiten Parameter des booleschen Typs namens Force Update. Wenn Aktualisierung erzwingen deaktiviert ist, wird die Aktualisierung aller Laufzeitinformationen übersprungen, wenn diese Informationen mit denen der Master-Komponente übereinstimmen. Wenn Update erzwingen aktiviert ist, wird die Aktualisierung der Laufzeitinformationen erzwungen. Dies gilt nur für den Registrierungsprozess, da dieser serialisiert werden kann und an diesem Punkt alle Laufzeitdaten aktualisiert werden müssen.

Jeder Aspekt unseres Charakters ist ein Skelettnetz, das wir durch eine andere Skelettnetzkomponente ersetzen können. Unten haben wir die Anzeige des Torsos und der Füße umgeschaltet (wir könnten diese durch andere Skelettnetze ersetzen, die derselben Skeletthierarchie folgen).

HiddenComponents.png

One issue to consider however, is that when using Master Pose Component, although it does reduce the game thread cost, it does not reduce the render cost. You will still render the same number of Components separately, keeping in mind that additional sections per Component will incur more draw calls. 

There is also the limitation that any children of the Master Bone has to be a subset with the exact matching structure, you cannot have any other extra joints or skip any joints. Since there are no Bone Buffer data for extra joints, it will render using the reference pose. Also, you cannot run any other animations or physics on any children.

Copy Pose From Mesh

Copy Pose From Mesh is an AnimGraph node you can use on the Animation Blueprint of the child that enables you to copy the animation pose from any Skeletal Mesh Component. Copy Pose From Mesh will only copy matching bones, and everything else will use the reference pose. However, you can play the animation on top of the copied transforms as illustrated below.

Ein zu berücksichtigendes Problem ist jedoch, dass bei Verwendung der Master Pose-Komponente zwar die Thread-Kosten des Spiels reduziert werden, die Renderkosten jedoch nicht. Sie werden immer noch die gleiche Anzahl von Komponenten separat rendern, wobei zu beachten ist, dass zusätzliche Abschnitte pro Komponente mehr Zeichenaufrufe verursachen.

Es gibt auch die Einschränkung, dass alle Kinder des Master Bone eine Teilmenge mit der genau passenden Struktur sein müssen, Sie können keine anderen zusätzlichen Gelenke haben oder Gelenke überspringen. Da es keine Bone Buffer-Daten für zusätzliche Gelenke gibt, wird es mit der Referenzpose gerendert. Außerdem können Sie keine anderen Animationen oder Physik auf Kindern ausführen.

Pose aus Netz kopieren
Copy Pose From Mesh ist ein AnimGraph-Knoten, den Sie auf dem Animations-Blueprint des Kindes verwenden können, der es Ihnen ermöglicht, die Animationspose aus jeder Skelett-Mesh-Komponente zu kopieren. „Pose aus Netz kopieren“ kopiert nur übereinstimmende Bones, und alles andere verwendet die Referenzpose. Sie können die Animation jedoch wie unten dargestellt über den kopierten Transformationen abspielen.

CopyPoseFromMesh.png

Click image for full view.
Bild anklicken für Vollansicht.

When using Copy Pose From Mesh, you will want to make sure that the Skeletal Mesh Component you copy from had already ticked, otherwise you will copy the last frame’s animation (for example, if you are copying from the Body and the Head is the Child). To ensure the Body has ticked, you can attach the Head to the Body, which will ensure the parent ticks first before the child. 

You could also set the relationship in code. If you set it as a prerequisite, it will ensure ticking them prior to the current component. See the Tick Dependency page for more information.

Some elements to consider when using Copy Pose From Mesh is that it is more expensive than Master Pose Component because this runs the animation on each child. Additionally, if you want to use physics on the child, you may want to use the Rigid Body or AnimDynamics skeletal control nodes instead.

When previewing an animation in the Animation Editor, you can assign additional meshes that will automatically use Copy Pose From Mesh. You can also create a custom Preview Mesh Collection that you can use to build collections of related Skeletal Meshes that are animated together (such as the components of a character). Below we illustrate how to change and assign different Skeletal Meshes to our preview enabling us to swap out different heads on our character. 

Wenn Sie „Pose aus Mesh kopieren“ verwenden, sollten Sie sicherstellen, dass die Skelett-Mesh-Komponente, von der Sie kopieren, bereits aktiviert war, da Sie sonst die Animation des letzten Frames kopieren (wenn Sie beispielsweise aus dem Körper kopieren und der Kopf das Kind ist ). Um sicherzustellen, dass der Körper getickt hat, können Sie den Kopf am Körper befestigen, wodurch sichergestellt wird, dass die Eltern zuerst vor dem Kind ticken.

Sie können die Beziehung auch im Code festlegen. Wenn Sie es als Voraussetzung festlegen, wird sichergestellt, dass sie vor der aktuellen Komponente angekreuzt werden. Weitere Informationen finden Sie auf der Seite Tick-Abhängigkeit.

Einige Elemente, die bei der Verwendung von Copy Pose From Mesh zu berücksichtigen sind, sind, dass es teurer ist als Master Pose Component, da dies die Animation auf jedem Kind ausführt. Wenn Sie außerdem Physik auf das Kind anwenden möchten, können Sie stattdessen die Skelett-Steuerknoten Starrer Körper oder AnimDynamics verwenden.

Bei der Vorschau einer Animation im Animationseditor können Sie zusätzliche Netze zuweisen, die automatisch Pose aus Netz kopieren verwenden. Sie können auch eine benutzerdefinierte Vorschau-Mesh-Sammlung erstellen, die Sie verwenden können, um Sammlungen verwandter Skeletal Meshes zu erstellen, die zusammen animiert werden (z. B. die Komponenten einer Figur). Unten veranschaulichen wir, wie Sie unserer Vorschau verschiedene Skelettnetze ändern und zuweisen können, sodass wir verschiedene Köpfe unseres Charakters austauschen können.

Video thumbnail

Skeletal Mesh Merge

At runtime you can merge multiple Skeletal Meshes into a single Skeletal Mesh through code with  FSkeletalMeshMerge . While this has a high initial cost of creating the Skeletal Mesh, the rendering cost is cheaper since you are rendering a single Skeletal Mesh instead of multiple meshes. For example, if you have a character comprised of three Components (head, body and legs) and you have 50 characters on screen, this would result in 50 draw calls. Without Skeletal Mesh Merge, each Component has its own draw call resulting in three calls per character or a total of 150 draw calls

When using  FSkeletalMeshMerge , your main «body» has to contain all the animations because the merged mesh will only use the Skeleton that’s set and it has to contain all the joints you’d need to animate. If you have extra joints for certain body parts, you still have to have all the animations on the body. Other things to consider, you can only run one animation on the merged mesh and transferring Morph Targets to the merged mesh is not supported. If however, you look at  FSkeletalMeshMerge::GenerateLODModel , once you have your Skeletal Mesh you can create your Morph Targets by calculating the  FMorphTargetDelta  between your base mesh and any morphs. 

Additionally when using  FSkeletalMeshMerge  you will likely need to build your content in a specific way from the start. You’ll want to use one common Material and decide on an atlas for your Textures (for example, boots go in this region while gloves go in this region and so on) so you can cut up and put together your textures to make new ones and render your whole character as one section. 

Mesh Merge Example

In the example below, we use the Mesh Merge code to assemble several Skeletal Meshes at runtime. 

Individual_Meshes.png

Above, we have several Skeletal Meshes that we want to join together into a single Skeletal Mesh at runtime. For this example, we create a Blueprint callable function through code called Mesh Merge that will enable us to define the meshes that we want to merge together. The first thing we would need to do is to create our C++ class based on a Blueprint Function Library that will enable us to call the function from any Blueprint and we call it MeshMergeFunctionLibrary

Blueprint_FunctionLibrary.png

Below we have provided the sample code blocks you can use inside your header and source files: 

.h code sample

// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "UObject/NoExportTypes.h"
#include "MeshMergeFunctionLibrary.generated.h"
/**
* Blueprint equivalent of FSkeleMeshMergeSectionMapping
* Info to map all the sections from a single source skeletal mesh to
* a final section entry in the merged skeletal mesh.
*/
USTRUCT(BlueprintType)
struct PROJECTNAME_API FSkelMeshMergeSectionMapping_BP
{
GENERATED_BODY()
/** Indices to final section entries of the merged skeletal mesh */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Mesh Merge Params")
TArray < int32 > SectionIDs;
};
/**
* Used to wrap a set of UV Transforms for one mesh.
*/
USTRUCT(BlueprintType)
struct PROJECTNAME_API FSkelMeshMergeUVTransform
{
GENERATED_BODY()
/** A list of how UVs should be transformed on a given mesh, where index represents a specific UV channel. */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Mesh Merge Params")
TArray < FTransform > UVTransforms;
};
/**
* Blueprint equivalent of FSkelMeshMergeUVTransforms
* Info to map all the sections about how to transform their UVs
*/
USTRUCT(BlueprintType)
struct PROJECTNAME_API FSkelMeshMergeUVTransformMapping
{
GENERATED_BODY()
/** For each UV channel on each mesh, how the UVS should be transformed. */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Mesh Merge Params")
TArray < FSkelMeshMergeUVTransform > UVTransformsPerMesh;
};
/**
* Struct containing all parameters used to perform a Skeletal Mesh merge.
*/
USTRUCT(BlueprintType)
struct PROJECTNAME_API FSkeletalMeshMergeParams
{
GENERATED_BODY()
FSkeletalMeshMergeParams()
{
StripTopLODS = 0;
bNeedsCpuAccess = false;
bSkeletonBefore = false;
Skeleton = nullptr;
}
// An optional array to map sections from the source meshes to merged section entries
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TArray < FSkelMeshMergeSectionMapping_BP > MeshSectionMappings;
// An optional array to transform the UVs in each mesh
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TArray < FSkelMeshMergeUVTransformMapping > UVTransformsPerMesh;
// The list of skeletal meshes to merge.
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TArray < USkeletalMesh* > MeshesToMerge;
// The number of high LODs to remove from input meshes
UPROPERTY(EditAnywhere, BlueprintReadWrite)
int32 StripTopLODS;
// Whether or not the resulting mesh needs to be accessed by the CPU for any reason (e.g. for spawning particle effects).
UPROPERTY(EditAnywhere, BlueprintReadWrite)
uint32 bNeedsCpuAccess : 1;
// Update skeleton before merge. Otherwise, update after.
// Skeleton must also be provided.
UPROPERTY(EditAnywhere, BlueprintReadWrite)
uint32 bSkeletonBefore : 1;
// Skeleton that will be used for the merged mesh.
// Leave empty if the generated skeleton is OK.
UPROPERTY(EditAnywhere, BlueprintReadOnly)
class USkeleton* Skeleton;
};
/**
*
*/
UCLASS()
class PROJECTNAME_API UMeshMergeFunctionLibrary : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
public:
/**
* Merges the given meshes into a single mesh.
* @return The merged mesh (will be invalid if the merge failed).
*/
UFUNCTION(BlueprintCallable, Category = "Mesh Merge", meta = (UnsafeDuringActorConstruction = "true"))
static class USkeletalMesh* MergeMeshes(const FSkeletalMeshMergeParams& Params);
};

Inside the header, you need to change out all the PROJECTNAME_API references to the name of your actual project. For example, if your project is called «MyProject», you will need to instead use MYPROJECT_API in all those instances for the code to work. 

.cpp code sample

// Fill out your copyright notice in the Description page of Project Settings.
#include "MeshMergeFunctionLibrary.h"
#include "SkeletalMeshMerge.h"
#include "Engine/SkeletalMeshSocket.h"
#include "Engine/SkeletalMesh.h"
#include "Animation/Skeleton.h"
static void ToMergeParams(const TArray<FSkelMeshMergeSectionMapping_BP>& InSectionMappings, TArray<FSkelMeshMergeSectionMapping>& OutSectionMappings)
{
if (InSectionMappings.Num() > 0)
{
OutSectionMappings.AddUninitialized(InSectionMappings.Num());
for (int32 i = 0; i < InSectionMappings.Num(); ++i)
{
OutSectionMappings[i].SectionIDs = InSectionMappings[i].SectionIDs;
}
}
};
static void ToMergeParams(const TArray<FSkelMeshMergeUVTransformMapping>& InUVTransformsPerMesh, TArray<FSkelMeshMergeUVTransforms>& OutUVTransformsPerMesh)
{
if (InUVTransformsPerMesh.Num() > 0)
{
OutUVTransformsPerMesh.Empty();
OutUVTransformsPerMesh.AddUninitialized(InUVTransformsPerMesh.Num());
for (int32 i = 0; i < InUVTransformsPerMesh.Num(); ++i)
{
TArray<TArray<FTransform>>& OutUVTransforms = OutUVTransformsPerMesh[i].UVTransformsPerMesh;
const TArray<FSkelMeshMergeUVTransform>& InUVTransforms = InUVTransformsPerMesh[i].UVTransformsPerMesh;
if (InUVTransforms.Num() > 0)
{
OutUVTransforms.Empty();
OutUVTransforms.AddUninitialized(InUVTransforms.Num());
for (int32 j = 0; j < InUVTransforms.Num(); j++)
{
OutUVTransforms[i] = InUVTransforms[i].UVTransforms;
}
}
}
}
};
USkeletalMesh* UMeshMergeFunctionLibrary::MergeMeshes(const FSkeletalMeshMergeParams& Params)
{
TArray<USkeletalMesh*> MeshesToMergeCopy = Params.MeshesToMerge;
MeshesToMergeCopy.RemoveAll([](USkeletalMesh* InMesh)
{
return InMesh == nullptr;
});
if (MeshesToMergeCopy.Num() <= 1)
{
UE_LOG(LogTemp, Warning, TEXT("Must provide multiple valid Skeletal Meshes in order to perform a merge."));
return nullptr;
}
EMeshBufferAccess BufferAccess = Params.bNeedsCpuAccess ?
EMeshBufferAccess::ForceCPUAndGPU :
EMeshBufferAccess::Default;
TArray<FSkelMeshMergeSectionMapping> SectionMappings;
TArray<FSkelMeshMergeUVTransforms> UvTransforms;
ToMergeParams(Params.MeshSectionMappings, SectionMappings);
ToMergeParams(Params.UVTransformsPerMesh, UvTransforms);
bool bRunDuplicateCheck = false;
USkeletalMesh* BaseMesh = NewObject<USkeletalMesh>();
if (Params.Skeleton && Params.bSkeletonBefore)
{
BaseMesh->Skeleton = Params.Skeleton;
bRunDuplicateCheck = true;
for (USkeletalMeshSocket* Socket : BaseMesh->GetMeshOnlySocketList())
{
if (Socket)
{
UE_LOG(LogTemp, Warning, TEXT("SkelMeshSocket: %s"), *(Socket->SocketName.ToString()));
}
}
for (USkeletalMeshSocket* Socket : BaseMesh->Skeleton->Sockets)
{
if (Socket)
{
UE_LOG(LogTemp, Warning, TEXT("SkelSocket: %s"), *(Socket->SocketName.ToString()));
}
}
}
FSkeletalMeshMerge Merger(BaseMesh, MeshesToMergeCopy, SectionMappings, Params.StripTopLODS, BufferAccess, UvTransforms.GetData());
if (!Merger.DoMerge())
{
UE_LOG(LogTemp, Warning, TEXT("Merge failed!"));
return nullptr;
}
if (Params.Skeleton && !Params.bSkeletonBefore)
{
BaseMesh->Skeleton = Params.Skeleton;
}
if (bRunDuplicateCheck)
{
TArray<FName> SkelMeshSockets;
TArray<FName> SkelSockets;
for (USkeletalMeshSocket* Socket : BaseMesh->GetMeshOnlySocketList())
{
if (Socket)
{
SkelMeshSockets.Add(Socket->GetFName());
UE_LOG(LogTemp, Warning, TEXT("SkelMeshSocket: %s"), *(Socket->SocketName.ToString()));
}
}
for (USkeletalMeshSocket* Socket : BaseMesh->Skeleton->Sockets)
{
if (Socket)
{
SkelSockets.Add(Socket->GetFName());
UE_LOG(LogTemp, Warning, TEXT("SkelSocket: %s"), *(Socket->SocketName.ToString()));
}
}
TSet<FName> UniqueSkelMeshSockets;
TSet<FName> UniqueSkelSockets;
UniqueSkelMeshSockets.Append(SkelMeshSockets);
UniqueSkelSockets.Append(SkelSockets);
int32 Total = SkelSockets.Num() + SkelMeshSockets.Num();
int32 UniqueTotal = UniqueSkelMeshSockets.Num() + UniqueSkelSockets.Num();
UE_LOG(LogTemp, Warning, TEXT("SkelMeshSocketCount: %d | SkelSocketCount: %d | Combined: %d"), SkelMeshSockets.Num(), SkelSockets.Num(), Total);
UE_LOG(LogTemp, Warning, TEXT("SkelMeshSocketCount: %d | SkelSocketCount: %d | Combined: %d"), UniqueSkelMeshSockets.Num(), UniqueSkelSockets.Num(), UniqueTotal);
UE_LOG(LogTemp, Warning, TEXT("Found Duplicates: %s"), *((Total != UniqueTotal) ? FString("True") : FString("False")));
}
return BaseMesh;
}

Once you compile the code inside the Editor, you can create an ActorBlueprint with a Skeletal Mesh Component and an exposed variable of the Skeletal Mesh Parameters type. This variable will provide you with several properties to define not only the Skeletal Meshes to merge, but how those meshes are merged and additional options.

SkelMeshParams.png

Below are the options you can use to define how your Skeletal Meshes are merged: 

PropertyDescription
Mesh Section MappingsThis is an optional array to map sections from the source meshes to merged section entries.
UVTransforms Per MeshThis is an optional array used to transform the UVs in each mesh.
Meshes to MergeThese are the Skeletal Meshes that will be merged together.
Strip Top LODsThe number of top LODs to remove from input meshes.
Needs Cpu AccessWhether or not the resulting mesh needs to be accessed by the CPU for any reason (For example, spawning particle effects).
Skeleton BeforeWhether to update the Skeleton before the merge or after (Skeleton must also be provided).
SkeletonThis is the Skeleton that will be used for the merged mesh. You can leave this empty if the generated Skeleton is okay.

Inside your Event Graph, upon Event Begin Play, use the node network below. 

MeshMergeBPScript.png

Click image for full view.

You can use your new Blueprint Function Merge Meshes to return a Skeletal Mesh object reference while passing in the Mesh Merge Parameters. The Skeletal Mesh Component that is added to the Blueprint can then be used as the target for setting a new Skeletal Mesh to use, which you can point to the return value of the Merge Meshes function call. In the above example, we are also assigning an Idleanimation for the Skeletal Mesh to play once all of the individual meshes have been merged. 

After you add the Mesh Merge Blueprint to the Level, inside the Details panel, you can define the Mesh Merge Parameters including the Meshes to Use and Skeleton asset to use. 

AssignedDetails-1.png

At runtime, the Mesh Merge function will execute and assemble the Skeletal Mesh based on the defined meshes.

Video thumbnail

Comparison Chart

Whether you are using Master Pose ComponentCopy Pose from Mesh, or Skeletal Mesh Merge there are advantages and disadvantages with each method. The table below outlines the setup and performance cost associated with each, as well as additional features that are (or are not) supported.

MasterCopy PoseMesh Merge
Setup CostMinMediumHigh
Game Thread CostMinHighMedium
Render Thread CostHighHighLow
PhysicsNoAnimDynamics or RigidBodyYes
Morph TargetYesYesNo

Was this helpful?

0 / 0

Schreibe einen Kommentar 0

Your email address will not be published. Required fields are marked *


WeltenSchmid Knowledge Base
Submit

    Type your search string. Minimum 4 characters are required.

    X
    wpChatIcon
    Skip to content