This component contains the core logic of your characters visual appearance and handles different things. It is built around the Anatomy Profiles data table and the Customization Profile structure.
Since the Replication Update, our product has basic replication support for applying whole customization profiles and setting individual properties over network. Do not worry, if your game has nothing to do with replication, you can use the Character Editor in a standalone (non-network) game as before the update. We tried to refactor the core logic as little as possible but had to update quite a few places to make everything work smoothly but as well keeping the old standalone behavior.
In general, BP_CharacterCustomization is the source of truth for replication logic and got a new initialize function specific to BP_Character Begin Play and lots of new events in the Event Graph handling the initialization for players and NPCs, applying their customization profiles after initialization and from within the Character Editor, as well as setting individual values of your customization.
Every functionality that potentially can be replicated has a corresponding event with a “(Replicated)” suffix, where we decide to either replicate or not, depending on the “Replicate” and related variables.
The variable “Replicate” must not be disabled manually, if you want to play in a standalone game, because we also check for “Is Standalone” in the “Replicate” branch.
This update does not contain any content for setting up a database to store your Customization Profiles. It is only about replication itself. This cannot be provided as of now because it would depend on Unreal Engine Plugins, which dependencies we are not allowed to submit to Marketplace. If you want to store profiles in a database, you must create your own implementation. We recommend using MongoDB or MySQL.
We will address the wall of nodes from above in the Event Graph in the next chapters.
As a little foreword, the initialization is split into two paths:
Both paths are called at Character Begin Play, explained in the following chapter. There is also a third larger Event: Apply Customization Profile (Replicable) (3) which is called on server-side at the end of initialization.
For specific character instances the additional Event: Request Customization Profile For Other Characters (4) is called in Character Begin Play.
There will be also an example right after explaining these main events.
This function is the entry point from within BP_Character
or your own character Blueprint. Thus, think back to chapter BP_Character > Begin Play, where you saw when it was called.
[V7] Since the Customer Update, at first the Package Registry gets initialized. We search the level for an already placed Package Registry.
Then, in case the Package Registry needs a reload, it will be triggered. (See Package Registry for details.)
Afterwards, as described in the previous chapters, on server / standalone / not-replicated characters we bind to OnInitialized and call Event: Initialize Replicable on client characters we call Event: Initialize Local Uncontrolled to prepare them for receiving customization from sever / standalone / not-replicated character’s request for other character’s customization. Additionally, all not-replicating player characters (no NPCs) are calling exactly that logic: Event: Request Customization Profile For Other Characters right here.
Server/standalone/not-replicated characters also call the latter event at the end of their initialization.
Intended server-side, standalone and not-replicating players, and NPCs, it first checks if bReplicate is enabled.
On this event, we hide the player or NPC on server-side, which as well internally replicates to its owning client, so a human player does not see his character, while we are initializing everything. Optionally, you could add a progress bar, particle animation etc. during this time.
In case the event is called on an NPC, the owning “client” is always the server, thus replicating to its owning client will not hide the NPC for human players on their clients. A multicast event is not suitable in this situation, because lately joining players would not receive that multicast, thus Event: Initialize Local Uncontrolled is used in BP_Character > Begin Play as well (see chapter for details).
Furthermore, in Server_Initialize
there is a logic (see Wait Until Player Is Fully Connected right below) to delay initialization if necessary. (This replaces the 2-seconds-delay from [V6 removed] Replication Update)
Right after this preparation we call Function: Initialize, whereafter we call event Multicast_Initialize to initialize all network instances of the BP_CharacterCustomization component as well (this also happens for the NPCs that already have been initialized locally due to Event: Initialize Local Uncontrolled). This way all characters are prepared for receiving the customization profile.
Afterwards we call a second initialization with Function: Initialize Customization Profile and unhide the character, before calling “On Initialized” and Event: Request Customization Profile For Other Characters.
This functionality replaces the 2-seconds-delay from [V6 removed] Replication Update.
It is necessary for joining players, if this component is initialized directly at character BeginPlay (as in our BP_Character
example). Since the server calls BeginPlay instantly, the player connection may not be established completely, thus the following server-client events would not be sent correctly.
Unfortunately, there is no specific event in Blueprints, we could wait for, without using a custom Game Mode Blueprint. Removing this delay would result in incorrect initialization.
Establishing the connection usually takes 3 attempts (less than 1 second by default), but may depend on network conditions. Please, expect differences between local and remote server execution.
With DebugMode
set to "Verbose Information", you can see the following lines printed in the server log.
Wait for player connection - attempt {X}/{MaxAttempts} - next attempt in {Delay} seconds
(repeated)Player #{PlayerId} is connected!
If the character stays invisible after connecting to the server, all connection check attempts have been used, showing the following message:
Player connection took an unexpectedly long time. Cancel initialization.
If the latter happens, try to adjust variables Initialization_MaxAttempts
(default: 20
) or Initialization_Delay
(default: 0.05
). The defaults result in a roughly 10 seconds total check time as shown in the following table.
Attempt X |
Next attempt in Delay seconds |
Total time passed after attempt |
---|---|---|
1 | 0.00 | 0.00 |
2 | 0.05 | 0.05 |
3 | 0.10 | 0.15 |
4 | 0.15 | 0.30 |
5 | 0.20 | 0.50 |
6 | 0.25 | 0.75 |
7 | 0.30 | 1.05 |
8 | 0.35 | 1.40 |
9 | 0.40 | 1.80 |
10 | 0.45 | 2.25 |
11 | 0.50 | 2.75 |
12 | 0.55 | 3.30 |
13 | 0.60 | 3.90 |
14 | 0.65 | 4.55 |
15 | 0.70 | 5.25 |
16 | 0.75 | 6.00 |
17 | 0.80 | 6.80 |
18 | 0.85 | 7.65 |
19 | 0.90 | 8.55 |
20 | last attempt | - |
Calls Function: Initialize on all client instances.
Running only on the server, it calls Function: Initialize Customization Profile which relies on properties set by Function: Initialize, that's why the latter always needs to be called first. Do not change the order of these functions.
Running only on the server, it only works for player-controlled characters (no NPCs) and for
This event iterates over all characters in the level with an implemented Character Customization and let them all call Server_SendRequestedCustomizationProfile
.
This event works if the specific character replicates and calls Multicast_ApplyRequestedCustomizationProfile
with its current replicable customization profile and Player ID as parameters. This will exclude the current character from re-applying its own Customization Profile twice, by checking for its Player ID – explained in the next event.
(See Replicable Customization Profile and Function: Apply Customization Profile for details on profile conversion.)
Since a multicast gets send to all clients and by default Unreal Engine has no replication type for sending to “a requesting client”, we compare the given Player ID with the current Player ID, thus only execute the following code on the requesting players’ client to avoid unwanted duplicated application on other clients.
Replication mode “owning client” is not suitable, because a newly joining player is simply not the owning client of all the characters in his world. For other players these are the other players’ clients and for NPCs the “owning client” is the server.
Once successfully checked for the Player ID, the event converts the replicable customization profile back to a normal customization profile and applies the character customization profile on client characters of the requesting player. After application, the characters get visible again, thus also reverting their hiding that was made on client-side in Event: Initialize Local Uncontrolled.
(See Replicable Customization Profile and Function: Apply Customization Profile for details on profile conversion.)
Intended for calling on a client-side uncontrolled character. It starts by checking if bReplicate is enabled.
The initialization is done by calling Function: Initialize to be ready for the above-described Multicast_ApplyRequestedCustomizationProfile, which is called by the client player in its own initialization process and at the end of which the current character will be made visible again.
“On Initialized” is currently not bound to before InitializeLocalUncontrolled is called and just there for you to bind to yourself, if desired.
Intended for calling on a server-side during initialization and on client-side from within WBP_ProfileManager.
… after this, we convert the current customization profile to a Replicable Customization Profile one and call Server_ApplyCustomizationProfile.
(See Replicable Customization Profile and Function: Apply Customization Profile for details on profile conversion.)
You can also call this event from your own custom logic, e.g., applying a profile on entering a Box Volume. Calling this event either on server-side or client-side depends on if you want to have direct visual feedback on that application (client-side is instant, while server-side has network delay).
This event takes the converted Replicable Customization Profile and converts it back to a normal one to be passed into Function: Apply Customization Profile on server-side, after which it gets replicated on multicast or on owning client, depending on variable bMulticastProfileApplication, to apply the same customization profile on the client(s).
(See Replicable Customization Profile and Function: Apply Customization Profile for details on profile conversion.)
These events work identical and convert the replicable customization profile to a normal one to be passed into Function: Apply Customization Profile, this time on client-side.
(See Replicable Customization Profile and Function: Apply Customization Profile for details on profile conversion.)
With all the theory from above, we hope to make it more tangible with the following example, while reviewing the given screenshot from the initialization chapter:
Let’s say Player A is hosting a Listening Server and owns his character. There are 2 more NPCs in the level.
All 3 characters call Function: Initialize From Character Begin Play. As they belong to the listening server, all of them go the server-side path, thus they all bind to the dispatcher “On Initialized” and call Event: Initialize (Replicable). All of them get hidden and call their initialization functions on server-side as well as their client instances via multicast. The latter simply calls again on listening server for all 3 characters, since there is no other client, yet. Going on with Event: Initialize Customization Profile a specific customization profile gets chosen on their server-sides, that are passed to Event: Apply Customization Profile (Replicable) which calls on server-side and multicasts to clients as well. Now, all 3 characters have their profiles applied. At the very end, Player A skips the Event: Request Customization Profile For Other Characters, as he is the host.
Now, Player B joins the game, which owns his server-side and client-side character instance.
All 3 previous characters have already been initialized and applied. On client-side of Player B, all 4 characters (Player A, Player B, and the 2 NPCs) call Function: Initialize From Character Begin Play. As they belong to the client, all of them go the client-side path. The character of Player B stops at the branch as it is locally controlled by Player B. All other characters (Player A and NPCs) go through and call Event: Initialize Local Uncontrolled, thus hiding and initializing themselves locally.
On server-side there is only Player B that calls Function: Initialize From Character Begin Play, because as mentioned the others have already been initialized. Player B goes the server-side path as the previous 3 characters and binds to the dispatcher “On Initialized”, calls Event: Initialize (Replicable) and all the related functions as Player A and the 2 NPCs did on server-side, already.
Player B also calls Event: Request Customization Profile For Other Characters at the very end of the server-side path, as he is a normal client player. The logic iterates over all characters (Player A, Player B and the 2 NPCs) and skips Player B as it is the requesting player. Further, all customization profiles of Player A and the NPCs get multicast to all players (Player A and Player B), including the Player ID of the requesting Player B.
Since Player A does not have the same Player ID as Player B, the application is skipped on his client. On the other hand, all the client characters on Player B’s client (Player A and the 2 NPCs) get all their replicated customization profiles applied. Afterwards they get visible again.
This process repeats for Player C, D, E etc.
On a Dedicated Server environment the server simply just initializes all NPCs that are in the level, as there are no players yet, if a Dedicated Server starts. Then Player A will go through the same process as Player B in this example.
In case the character was not replicating, it would have also taken the Sequence Then 1 path in* Function: Initialize From Character Begin Play (see screenshot above), *thus requesting other characters’ customization to stay in sync.
Initialize itself just sets the current Initialization Behavior and Profile to Load. It validates the character and sets the Character variable for validated access later.
It calls also UpdateAvailableAnatomyProfiles, UpdateSavedCustomizationProfiles and UpdatePresetCustomizationProfiles.
Initialization Behavior
BP_Character
> GetCustomizationProfile
).Profile to Load is the profile name for Initialization Behavior “Use Profile to Load”.
[V2] UpdateAvailableAnatomyProfiles registers AvailableAnatomyProfiles by validating the Anatomy Profiles from DT_AnatomyProfiles. They are used in the update functions later. Anatomy Profiles are valid, if at least their body mesh and Anim Blueprint are valid.
[V6] UpdateSavedCustomizationProfiles registers SavedCustomizationProfiles by reading all profiles names from the save game file. They are used in Function: Initialize Customization Profile to find out if “Profile to Load” is a saved profile, a preset profile or none of them.
[V6] UpdatePresetCustomizationProfiles registers PresetCustomizationProfiles by reading all profile names from the PresetDataTable, which is “DT_PresetCustomizationProfiles_V4” by default in BP_Character. They are used in Function: Initialize Customization Profile to find out if “Profile to Load” is a saved profile, a preset profile or none of them.
This function selects the desired way to load the customization profile based on the Initialization Behavior.
Load Current Customization Profile takes the Customization Profile structure from BP_Character > GetCustomizationProfile and passes it to Function: Apply Customization Profile.
Load Saved Customization Profile loads the Customization Profile structure from save game file which matches “Profile Name” and passes it to Function: Apply Customization Profile.
Load Preset Customization Profile loads the Customization Profile structure from PresetDataTable which matches “Profile Name” and passes it to Function: Apply Customization Profile.
[V5 removed] Before the Replication Update, this loading selection previously was part of Function: Initialize, but had to be separated, since the internally called Apply Customization Profile had to be made replicable itself to apply profiles to all clients, even from in-game Character Editor. If we had left that logic in the former function we had caused to call Function: Apply Customization Profile twice on client:
That was not desired, thus we split the logic and now only apply from with InitializeCustomizationProfile.
ApplyCustomizationProfile takes a Customization Profile structure and stores it in CurrentCustomizationProfile
.
It can be called any time once the BP_CharacterCustomization
component has been initialized (e.g., for applying a profile on runtime like the load functionality of the Character Editor does).
[V6] Since the Replication Update, in WBP_CharacterEditor
this function has been replaced with its corresponding event “ApplyCustomizationProfile (Replicable)”. The latter is necessary for you to call instead of the function if you want to use replication as well.
The function itself calls all the main update functions described below.
Now the customization profile is completely applied to the character.
Updates body and head components depending on the Anatomy Profiles and CurrentCustomizationProfile
> Basebody (Profile) by the following functionality and functions.
Anim Instance Class
Sepcialties about this function:
- Can be called on its own to update the whole basebody.
- [V6] Does not replicate on its own, use Event: Apply Customization Profile (Replicable) instead.
- [V8] Calls Function: Update LODSync Component.
Sets the body skeletal mesh.
Sepcialties about this function:
- Can be called on its own to update the body mesh.
- [V6] Does not replicate on its own, use Event: Apply Customization Profile (Replicable) instead.
- [V8] Calls Function: Update LODSync Component.
Sets the head skeletal mesh, if a head component has been created.
Sepcialties about this function:
- Can be called on its own to update the head mesh.
- [V6] Does not replicate on its own, use Event: Apply Customization Profile (Replicable) instead.
- [V8] Calls Function: Update LODSync Component.
Refreshes the morph targets on all skeletal meshes.
Updating only the Basebody Body component and letting Master Pose pass it around is disabled, because it seems that initializing the character customization with weights >
0.0
let further calls ignore the Master Pose Component inheritance. Maybe it is an engine bug, starting in UE4.27.E.g. loading a profile with Body Weight =
1.0
, then changing the weight in the editor only changes the base body morph instead of applying the moprh value also to the head and the apparels etc.
Sepcialties about this function:
- Can be called on its own to update the morph targets.
- [V6] Does not replicate on its own, use Event: Apply Customization Profile (Replicable) instead.
- [V8] Calls Function: Update LODSync Component.
Creates MaterialInstanceDynamics
for the body and head components on the material slots defined in Anatomy Profile > Eyes Material Sets ([V7 removed] previously on hardcoded slots "Body" and "Head").
It then applies the Scalar Parameters
and HDR Vector Parameters
from the CurrentCustomizationProfile
> Basebody (Profile) > Skin (Profile)
.
Sepcialties about this function:
- Can be called on its own to update the skin materials.
- [V6] Does not replicate on its own, use Event: Apply Customization Profile (Replicable) instead.
- [V8] Calls Function: Update LODSync Component.
Creates MaterialInstanceDynamics
for the head compoment (or body component, if no head is used) on the material slots defined in Anatomy Profile > Eyes Material Sets ([V7 removed] previously on hardcoded slots "Eyes" and "Eyelashes").
It then applies the Scalar Parameters
and HDR Vector Parameters
from the CurrentCustomizationProfile
> Basebody (Profile) > Eyes (Profile)
.
Sepcialties about this function:
- Can be called on its own to update the eye materials.
- [V6] Does not replicate on its own, use Event: Apply Customization Profile (Replicable) instead.
- [V8] Calls Function: Update LODSync Component.
Refreshes the skeletal Customization Data Assets for apparels, equipment and hairstyles.
These are logically more complex, since in these functions happens some cleanup initially. In fact, each call of them removes all current data assets and applies all remaining CDAs again to reset any modification done by a previously assigned CDA if it is not assigned anymore (e.g., change from male to female).
The applied CDAs come from the following profiles in CurrentCustomizationProfile
:
The update logic of this function works as follows:
Coming with [V4], this is our new animation workflow. Each Skeletal CDA can have an optional custom animation. (See tutorial: Add your own CDA animations)
For Hairstyles the Material Variant Index is always -1. If you desire, look at Apparel Profile and Update Apparel to copy that functionality over.
In our preset only feet and accessories make use of type-specific properties as an example. Feet set foot transforms for high heels and glasses set translucency sorting. Feel free to add more functionality.
In case of Apparel, after each CDA:
Calls Update Apparel Basebody Masks and optionally, applies the alternative skin textures.
Sepcialties about this function:
- Can be called on their own to update the corresponding CDAs.
- [V6] Do not replicate on their own, use Event: Apply Customization Profile (Replicable) instead.
- [V8] Call Function: Update LODSync Component.
Takes a basebody mask texture from each dressed apparel to pass it to the assigned Skin Material. These textures hide specified parts of the basebody which would stick through the apparels otherwise. (See BP_CDA_Apparel > Mesh
> Basebody Mask
and Anatomy Profile
> Basebody Mask Textures for details.)
Sepcialties about this function:
- Can be called on its own to update the basebody masks.
- [V6] Does not replicate on its own, use Event: Apply Customization Profile (Replicable) instead.
- [V8] Calls Function: Update LODSync Component.
Sepcialties about this function:
- Can be called on its own to update the corresponding CDAs.
- [V6] Does not replicate on its own, use Event: Apply Customization Profile (Replicable) instead.
- [V8] Calls Function: Update LODSync Component.
Refreshes the static Customization Data Assets for attachments.
Works like its skeletal counterparts (apparel, etc.). Though in contrast to these functions, all static CDAs like the attachments get attached to a specific socket with a relative transform to set their position on the character.
The applied BP_CDA_Attachment CDAs come from the Attachments (Profile) in CurrentCustomizationProfile
.
The update logic of this function works as follows:
For Attachments the
Material Variant Index
is always-1
. If you desire, look at Apparel Profile and Update Apparel to copy that functionality over.
Sepcialties about this function:
- Can be called on its own to update the corresponding CDAs.
- [V6] Does not replicate on its own, use Event: Apply Customization Profile (Replicable) instead.
- [V8] Calls Function: Update LODSync Component.
This is necessary, since the master pose component (the “Mesh” component of the character) does not propagate its morph targets properly to the apparels and hairstyle in Construction Script. When playing in editor (during runtime) the morph targets work properly.
Sepcialties about this function:
- Can be called on its own to update the morph targets on all meshes.
- [V6] Does not replicate on its own, use Event: Apply Customization Profile (Replicable) instead.
BETA feature: This is meant for testing and not recommended for production.
Alongside Function: Update LODSync Component, this function shall as well increase performance by merging various skeletal mesh components into one component. Currently, it only supports merging of the Basebody Body mesh with all applied Apparel CDAs - separate Head meshes and other CDAs are not supported.
Using it with the in-game Character Editor is also not supported, thus feel free to use it as a testing feature in the Unreal Editor viewport and as a starting point for your own implementation.
Furthermore, with its current plugin implementation, the Skeletal Merging feature comes with the big caveat of having no Morph Targets, which our Character Editor heavily relies on depending on the CDAs you dress your character with. E.g., weight, muscles, hair, and clothing morph targets are all stripped off, so please expect unwanted clipping, depending on the combination of CDAs.
See this Unreal Engine documentation for details, especially the Comparison Chart at the very end of the page. Nevertheless, we are investigating further possibilities for future performance improvements in this regard.
To activate this feature, please read tutorial Activate Skeletal Merging Plugin first.
BP_Character has a Use Skeletal Merging
checkbox, whoes value is passed to the BP_CharacterCustomization
> bUseSkeletalMerging
in Construction Script. Obseve the BP_Character details panel when changing the variable to see its effect as shown in the screenshots below. You can open the merged meshes as normal meshes from the Content Browser.
With bUseSkeletalMerging = false
the function does nothing, (1) each Apparel CDA gets its own component as usual and the "Mesh" component is populated with the (2) Body mesh from the Anatomy profile.
Whit bUseSkeletalMerging = true
the body and apparel meshes are merged. There are (1) no more individual components and the "Mesh" component is populated with the (2) merged skeletal mesh. You can also see, that all the material slots have been added to it.
This feature also works with Blueprint randomization from within the Character Editor Tools, thus each randomization generates a new merged skeletal mesh.
Sepcialties about this function:
- Can be called on its own to update the skeletal merging.
- [V6] Does not replicate on its own, use Event: Apply Customization Profile (Replicable) instead.
Refreshes the optional LODSync component on the Character. See LODSync and this Unreal Engine documentation for details on how the component itself is working.
The update logic of this function works as follows:
Clear ComponentsToSync
and CustomLODMapping
variables from LODSync component.
Add Basebody Body and Head mesh components to ComponentsToSync
with Sync Option = Drive
.
Add all CDA-related components to ComponentsToSync
with Sync Option = Passive
.
If the UseSkeletalMerging = true
, Apparel is excluded. See Update Skeletal Merging for details.
Add Basebody Body, Attachments, Equipment and Apparel CDAs to CustomLODMapping
using 4 LODs, while Basebody Head, Hairstyles and Groom are using their 8 LODs.
If the UseSkeletalMerging = true
, Apparel is excluded. See Update Skeletal Merging for details.
Sepcialties about this function:
- Can be called on its own to update the lodsync component.
- [V6] Does not replicate on its own, use Event: Apply Customization Profile (Replicable) instead.
The largest number of new nodes in the Event Graph is made up by the setter events. Do not get overwhelmed because there is simply much repetition.
In general, each line consisting of three orange comments makes up one setter event line, which is described below. They are intended to replicate even a single value change, like changing the skin color, pupil scale or a single CDA for live interaction between players.
Although this is an unusual scenario for a Character Editor, just imagine that you play in an open world and go to a clothing store, where you could make small fashion show by changing apparels or hairstyles while other players are watching you.
The event “SetXYZ (Replicable)” (where SetXYZ can be found for any replicable setter method of BP_CharacterCustomization component), is the entry point of the Setter Event Line.
Branching on “Replicate Individual Changes”, checks for bReplicateIndividualChanges and bReplicate.
[V6] Since the Replication Update, these events replace most occurrences of setter functions, meaning in Widget Blueprint bindings and similar places, where the client may trigger a replication setter.
We intentionally separated individual changes from application of whole customization profiles in case you want to replicate initialization and saving a profile but do not replicate individual changes until the player has finished with its customization.
This event calls the corresponding setter function on server-side and branches on “Multicast Individual Changes”, thus checking bMulticastIndividualChanges.
These events call the corresponding setter functions on client-side.
Although this is a setter event, this event behaves like Event: Apply Customization Profile (Replicable) as it also calls SetAnatomy locally to bypass the network delay. This way we are able to update the in-game Character Editor properly, since SetAnatomy applies a cached or new Customization Profile on which WBP_ProfileManager relies on.
SetCDA...(Replicable)
events primarily set CDAs or replace them if the given Index
is already set.
[V9] Since the Utility Update these events can also be used to properly clear the CDA by calling Remove CDAs events instead of setting the CDA to None
if an empty profile (with Data Asset = None
) is passed to the function.
SetCDA_ApparelProfile (Replicable)
SetCDA_AttachmentProfile (Replicable)
SetCDA_EquipmentProfile (Replicable)
SetCDA_GroomProfile (Replicable)
SetCDA_HairstyleProfile (Replicable)
Each event can be called with the following input:
Class
: The CDA class to set, replace or removeIndex
: The index within the filtered class array (see info boxes below)CDA_[TYPE]Profile
, an Index = -1
removes all CDAs of the given class.CDA_[TYPE]Profile
: The profile to set, replace or remove. [TYPE]
matches the name of the function.How
Index
is used:Let's assume there are 5
BP_CDA_Apparel
CDAs (including 2 accessories) dressed in the currentCDA_ApparelProfiles
array:[0: UpperBody, 1: LowerBody, 2: Accessory_A, 3: Accessory_B, 4: Hat]
.If we want to replace
Accessory_B
with anotherAccessory_C
we need to callSetCDA_ApparelProfile (Replicable)
forClass = BP_CDA_Accessory
andIndex = 1
. The function filters for the class to get the array[0: Accessory_A, 1: Accessory_B]
where the second item atIndex = 1
is then found in the originalCDA_ApparelProfiles
array at index3
.This index
3
is then replaced withAccessory_C
in the currentCDA_ApparelProfiles
array.
How
Index
is used with base classes:E.g., with the example from above, when inputing the base class
BP_CDA_Apparel
instead of the specific subclassBP_CDA_Accessory
, the function will filter for all apparels.This means that you still have the array:
[0: UpperBody, 1: LowerBody, 2: Accessory_A, 3: Accessory_B, 4: Hat]
and with the givenIndex = 1
the function now replaces theLowerBody
.
There are event dispatchers for each CDA type, which are called at the end of setting or replacing CDA(s). When removing CDA(s), the removal event dispatchers are called, to also account for the case of removing all CDA(s) (see Remove CDAs).
OnSet_CDA_ApparelProfile
OnSet_CDA_AttachmentProfile
OnSet_CDA_EquipmentProfile
OnSet_CDA_GroomProfile
OnSet_CDA_HairstyleProfile
Each event dispatcher passes the following input:
CharacterCustomization
: The calling BP_CharacterCustomization
Class
: The set classClassIndex
: The set, replaced or removed index within the filtered class array (see info box below)CDA_ProfilesIndex
: The set, replaced or removed index within the CDA profile array (see info box below)CDA_[TYPE]Profile
: The set, replaced or removed profile.How
ClassIndex
andCDA_ProfilesIndex
differ:Generally,
ClassIndex
is identical toSetCDA...(Replicable)
inputIndex
, but has another name to explain the difference with the secondCDA_ProfilesIndex
.Given the first example from above in
SetCDA...(Replicable)
withIndex = 1
and replaced index3
in the currentCDA_ApparelProfiles
array, eventually, the event dispatcher is called withClassIndex = 1
andCDA_ProfilesIndex = 3
.If the profile was removed, then
CDA_ProfilesIndex
is-1
.
Since the Utility Update, there is dedicated CDA removal support that properly removes profiles instead of setting empty profiles, also allowing to bind to dedicated removal event dispatchers.
RemoveCDA...(Replicable)
events remove CDAs.
RemoveCDA_ApparelProfile (Replicable)
(replaces [V7 removed] RemoveApparel
)RemoveCDA_AttachmentProfile (Replicable)
RemoveCDA_EquipmentProfile (Replicable)
RemoveCDA_GroomProfile (Replicable)
RemoveCDA_HairstyleProfile (Replicable)
(replaces [V7 removed] RemoveHairstyle
)RemoveCDA_Profile (Replicable)
BP_CustomizationDataAsset
itself)Each event can be called with the following input:
Class
: The CDA class to removeIndex
: The index within the filtered class array (see info boxes in SetCDA...(Replicable)).Index = -1
removes all CDAs of the given class.RemoveAllCDA...(Replicable)
functions call RemoveCDA...(Replicable)
events with fixed values for Class
and Index = -1
.
RemoveAllCDA_ApparelProfiles (Replicable)
(former RemoveAllApparels
)RemoveAllCDA_AttachmentProfiles (Replicable)
RemoveAllCDA_EquipmentProfiles (Replicable)
RemoveAllCDA_GroomProfiles (Replicable)
RemoveAllCDA_HairstyleProfiles (Replicable)
RemoveAllCDA_Profiles (Replicable)
There are event dispatchers for each CDA type.
OnRemoved_CDA_ApparelProfile
OnRemoved_CDA_AttachmentProfile
OnRemoved_CDA_EquipmentProfile
OnRemoved_CDA_GroomProfile
OnRemoved_CDA_HairstyleProfile
Each event dispatcher passes the following input.
The Index
mentioned below, comes from RemoveCDA...(Replicable)
.
CharacterCustomization
: The calling BP_CharacterCustomization
Class
: The removed classClassIndex
: The removed Index
with the filtered class array (see info boxes in SetCDA...(Replicable))CDA_ProfilesIndex
: The removed index within the CDA profile array (see info boxes in SetCDA...(Replicable))
>= 0
, if a CDA was removed-1
, if Index >= 0
but was not in the class array, thus no CDA was removed-1
, if Index = -1
CDA_[TYPE]Profile
: The removed profile
Index >= 0
RemovedCDA_ProfilesCount
: The count of removed elements:
0
, if no CDA was removed1
, if one CDA was removed with Index >= 0
>= 1
, if one or more CDA were removed with Index = -1
How
ClassIndex
andCDA_ProfilesIndex
differ:
See info box in OnSet_CDA Event Dispatchers
Since the Customer Update, our product has Blueprint randomization support for CDAs. Previously, it was only possible to randomize your character in-game with the WBP_CharacterEditor widget.
For that purpose, the new Package Registry got introduced, which gets initialized in Function: Initialize From Character Begin Play. Afterwards, you can call the following built-in functions for our two most prominent CDA base classes BP_Apparel
and BP_Hairstyle
like this.
SetRandomCDA...(Replicable)
randomize CDAs.
SetRandomCDA_ApparelProfile (Replicable)
(replaces [V7 removed] SetRandomCDA_ApparelProfile
)SetRandomCDA_AttachmentProfile (Replicable)
SetRandomCDA_EquipmentProfile (Replicable)
SetRandomCDA_GroomProfile (Replicable)
SetRandomCDA_HairstyleProfile (Replicable)
(replaces [V7 removed] SetRandomCDA_HairstyleProfile
)SetRandomCDA_Profile (Replicable)
BP_CustomizationDataAsset
itself)Each function can be called with the following input:
Class
: The CDA class to randomizeIndex
: The index within the filtered class array (see info boxes in SetCDA...(Replicable))Collection
: The collection to further narrow down the possible CDAsAcceptDebugAssets
: If CDAs marked as DebugAsset = true
are included in possible CDAsRandomize...(Replicable)
helper functions call SetRandomCDA...(Replicable)
functions for every available subclass with fixed input for Class
, except RandomizeCDAs (Replicable)
which accepts a Class
.
The new logic replaces the hard-coded implementation of [V7 removed] RandomizeApparel
and [V7 removed] RandomizeHairstyle
by using the Package Registry to get available classes dynamically, allowing you to also randomize any created custom CDA subclasses of Apparel, Attachments, Equipments, Groom, and Hairstyle. (see Tutorial: Add Customization Data Asset Class)
RandomizeCDAs (Replicable)
RandomizeApparel (Replicable)
(replaces [V7 removed] RandomizeApparel
)RandomizeAttachments (Replicable)
RandomizeEquipment (Replicable)
RandomizeGroom (Replicable)
RandomizeHairstyle (Replicable)
(replaces [V7 removed] RandomizeHairstyle
)Each function can be called with the following input:
Class
(only for RandomizeCDAs (Replicable)
): The CDA class to randomizeIndex
: The index within the filtered class array (see info boxes in SetCDA...(Replicable))Collection
: The collection to further narrow down the possible CDAsAcceptDebugAssets
: If CDAs marked as DebugAsset = true
are included in possible CDAsThere are no dedicated event dispatchers for randomization, but the event dispatchers of Set CDAs and Remove CDAs are used depending on the given input.
The logging functionality can help you figure out what BP_CharacterCustomization
component does internally at a certain point in its logic. Setting a specific Debug Mode
on BP_Character
, respectively on the component let you choose what is printed to the Unreal Engine Output Log. There are 6 logging types:
Errors
: Logs if something is wrong in the first place with setting up your custom character, e.g., missing the implementation of the Character Customization Interface.Warnings
: Logs problems you might want to fix, e.g., you are trying to load a profile that could not be found neither in save game file nor in the Preset Data Table.Information
(light blue in screenshot): Logs most important steps like initialization, profile application and as a special setter also for SetAnatomy.Detailed Information
(green in screenshot): Logs subordinated steps of these main steps. Here you see all subordinates of “Initialize Character Customization”.Verbose Information
(yellow in screenshot): Logs function internal information.Replication
(blue in screenshot): If DebugMode is at least set to “Errors, Warnings and Information”, if the game is replicating and if bDebugReplication is true, then above each log with type of “Information” another line is printed about the calling character, who owns the character, and on which target the event will be called. In the case above you see that “Server: BP_Character…” is owned by the player with ID 272 and calls the following “Initialize Character Customization” on server.The target information is useful to track down from which event (Server, Multicast or Client) the following step has been called, since the log printing game instance on the left side “Server:“ can also be “Client 1:”, “Client 2:” etc. but sometimes confuses, because especially multicasts on listening server are printed by “Server:” as well, but actually are happening on the so to say “server’s client”.
If you want to find these logs, simply search for them in Blueprint. As a general hint, “information” and “detailed information” are mostly named by their corresponding function.