<meta charset="utf-8">
快速开始
原理实现步骤
- 逻辑实体
逻辑实体拥有完整的Character所有逻辑。只是SkeletonMesh里面的角色是一个只有骨架没有网格Mesh的角色。

image.png
2.时装蓝图。只有一个SkeletonMesh

image.png
- 点击换装的时候,通过LyraPawnComponent_CharacterParts来Spawn出来一个BP_Actor。并且Attachment到该角色Actor下面。

image.png
- 通过IKRig和IK Retargeting进行两个SkeletonMesh的动画同步

image.png
核心实现代码
bool FLyraCharacterPartList::SpawnActorForEntry(FLyraAppliedCharacterPartEntry& Entry)
{
bool bCreatedAnyActors = false;
if (ensure(OwnerComponent) && !OwnerComponent->IsNetMode(NM_DedicatedServer))
{
if (Entry.Part.PartClass != nullptr)
{
UWorld* World = OwnerComponent->GetWorld();
if (USceneComponent* ComponentToAttachTo = OwnerComponent->GetSceneComponentToAttachTo())
{
const FTransform SpawnTransform = ComponentToAttachTo->GetSocketTransform(Entry.Part.SocketName);
UChildActorComponent* PartComponent = NewObject<UChildActorComponent>(OwnerComponent->GetOwner());
PartComponent->SetupAttachment(ComponentToAttachTo, Entry.Part.SocketName);
PartComponent->SetChildActorClass(Entry.Part.PartClass);
PartComponent->RegisterComponent();
if (AActor* SpawnedActor = PartComponent->GetChildActor())
{
switch (Entry.Part.CollisionMode)
{
case ECharacterCustomizationCollisionMode::UseCollisionFromCharacterPart:
// Do nothing
break;
case ECharacterCustomizationCollisionMode::NoCollision:
SpawnedActor->SetActorEnableCollision(false);
break;
}
// Set up a direct tick dependency to work around the child actor component not providing one
if (USceneComponent* SpawnedRootComponent = SpawnedActor->GetRootComponent())
{
SpawnedRootComponent->AddTickPrerequisiteComponent(ComponentToAttachTo);
}
}
Entry.SpawnedComponent = PartComponent;
bCreatedAnyActors = true;
}
}
}
return bCreatedAnyActors;
}
GM相关开发
UCheatManager
GM指令调用换装逻辑。查看源代码的时候看到这个类很有意思,用来实现UE的GM指令相关的逻辑类。
https://zhuanlan.zhihu.com/p/553937135
/** Cheats related to bots */
UCLASS(NotBlueprintable)
class ULyraCosmeticCheats final : public UCheatManagerExtension
{
GENERATED_BODY()
public:
ULyraCosmeticCheats();
// Adds a character part
UFUNCTION(Exec, BlueprintAuthorityOnly)
void AddCharacterPart(const FString& AssetName, bool bSuppressNaturalParts = true);
// Replaces previous cheat parts with a new one
UFUNCTION(Exec, BlueprintAuthorityOnly)
void ReplaceCharacterPart(const FString& AssetName, bool bSuppressNaturalParts = true);
// Clears any existing cheats
UFUNCTION(Exec, BlueprintAuthorityOnly)
void ClearCharacterPartOverrides();
private:
ULyraControllerComponent_CharacterParts* GetCosmeticComponent() const;
};