Character Customization Via Morphs + Dynamic Collision?

#pragma once

#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "CustomizeFunctionLibrary.generated.h"

UENUM(BlueprintType)
enum class ECollisionShapes : uint8
{	
	FKSphereElem  UMETA(DisplayName = "Sphere"),
	FKBoxElem UMETA(DisplayName = "Box"),
	FKSphylElem UMETA(DisplayName = "Sphyl"),
	FKConvexElem UMETA(DisplayName = "Convex"),
	FKTaperedCapsuleElem UMETA(DisplayName = "Tapered Capsule")
};

UCLASS()
class YOUR_API UCustomizeFunctionLibrary : public UBlueprintFunctionLibrary
{
	GENERATED_BODY()
	
	UFUNCTION(BlueprintCallable)
	static void SetCollider(USkeletalMeshComponent* Mesh, FName BoneName, ECollisionShapes Shape, float Radius, float Radius2, float Height, FTransform PassedInTransform, int32 PrimitiveIndex = 0);

};

.cpp

#include "CustomizeFunctionLibrary.h"
#include "PhysicsEngine/PhysicsAsset.h"

void UCustomizeFunctionLibrary::SetCollider(USkeletalMeshComponent* Mesh, FName BoneName, ECollisionShapes Shape, float Radius, float Radius2, float Height, FTransform PassedInTransform, int32 PrimitiveIndex)
{
	UPhysicsAsset* PhysAsset = Mesh->GetPhysicsAsset();

	if (PhysAsset != nullptr) {

		bool bPassPACheck = false;
		bPassPACheck = (PhysAsset->BodySetupIndexMap.Find(BoneName) != nullptr);

		int32* boneIndex = PhysAsset->BodySetupIndexMap.Find(BoneName);

		if (bPassPACheck) {

			FKAggregateGeom aggGeo = Mesh->GetPhysicsAsset()->SkeletalBodySetups[*boneIndex].Get()->AggGeom;
			
			if (Shape == ECollisionShapes::FKSphylElem) {				
				if (aggGeo.SphylElems.IsValidIndex(PrimitiveIndex)) {
					aggGeo.SphylElems[PrimitiveIndex].Radius = Radius;
					aggGeo.SphylElems[PrimitiveIndex].SetTransform(PassedInTransform);
				}
			}

			if (Shape == ECollisionShapes::FKSphereElem) {
				if (aggGeo.SphereElems.IsValidIndex(PrimitiveIndex)) {
					aggGeo.SphereElems[PrimitiveIndex].Radius = Radius;
					aggGeo.SphylElems[PrimitiveIndex].SetTransform(PassedInTransform);
				}
			}

			if (Shape == ECollisionShapes::FKBoxElem) {					
				if (aggGeo.BoxElems.IsValidIndex(PrimitiveIndex)) {
					aggGeo.BoxElems[PrimitiveIndex].SetTransform(PassedInTransform);
				}
			}
			if (Shape == ECollisionShapes::FKConvexElem) {	
				if (aggGeo.ConvexElems.IsValidIndex(PrimitiveIndex)) {				
					aggGeo.ConvexElems[PrimitiveIndex].SetTransform(PassedInTransform);
				}
				//aggGeo.ConvexElems[0].SetConvexMeshObject();
			}

			if (Shape == ECollisionShapes::FKTaperedCapsuleElem) {
				if (aggGeo.TaperedCapsuleElems.IsValidIndex(PrimitiveIndex)) {
					aggGeo.TaperedCapsuleElems[PrimitiveIndex].SetTransform(PassedInTransform);
					aggGeo.TaperedCapsuleElems[PrimitiveIndex].Radius0 = Radius;
					aggGeo.TaperedCapsuleElems[PrimitiveIndex].Radius1 = Radius;
					aggGeo.TaperedCapsuleElems[PrimitiveIndex].Length = Height;
				}
			}

			Mesh->GetPhysicsAsset()->SkeletalBodySetups[*boneIndex].Get()->AggGeom = aggGeo;
		}
		Mesh->SetPhysicsAsset(PhysAsset);
	}
}

The convex mesh would need more work but it is possible.
You would need to extract the passed in shape data, extract the vertex data from the render data and then reconstruct the mesh to the convex mesh.


collider2

If you would want to add and delete them dynamically you can access the array from the aggGeo index array element to remove parts, or to add just access the while array aggGeo.SpecificShape.Add()

example

FKSphylElem el;
el.Radius = 5;
el.Length = 10;
aggGeo.SphylElems.Add(el);

if collisions don’t register then maybe it would require
Mesh->UpdateBoneBodyMapping(); or Mesh->RecreatePhysicsState();

You can also use the physics asset override if the mesh is known. (Runtime phys asset swap)