이제 잘라보자.
자르는 코드는 어렵지 않다.
void AEnemy::SliceMeshAtBone(FVector SliceNormal, bool bCreateOtherHalf)
{
if (!GetMesh()|| !ProcMeshComponent) {
UE_LOG(LogTemp, Warning, TEXT("SliceMeshAtBone: SkeletalMeshComponent or ProcMeshComponent is null."));
return;
}
FVector BoneLocation = GetMesh()->GetBoneLocation(TargetBoneName);
if (BoneLocation == FVector::ZeroVector) {
UE_LOG(LogTemp, Error, TEXT("SliceMeshAtBone: Failed to get Bone '%s' location. Check if the bone exists in the skeleton."), *TargetBoneName.ToString());
return;
}
UMaterialInterface* ProcMeshMaterial = ProcMeshComponent->GetMaterial(0);
if (!ProcMeshMaterial) {
UE_LOG(LogTemp, Warning, TEXT("SliceMeshAtBone: Procedural mesh has no material assigned."));
}
UProceduralMeshComponent* OtherHalfMesh = nullptr; //잘린 Procedural Mesh가 OtherHalfMesh가 된다.
UKismetProceduralMeshLibrary::SliceProceduralMesh(
ProcMeshComponent,
BoneLocation,
SliceNormal,
bCreateOtherHalf,
OtherHalfMesh,
EProcMeshSliceCapOption::CreateNewSectionForCap,
CapMaterial //절단면 Material
);
if(!OtherHalfMesh){
UE_LOG(LogTemp, Warning, TEXT("SliceMeshAtBone: Failed to slice mesh at bone '%s'."), *TargetBoneName.ToString());
return;
}
//Attach할 Socket 이름을 확인하기 - Socket이 있는지 꼭 확인!!
if (ProceduralMeshAttachSocketName.IsNone() || OtherHalfMeshAttachSocketName.IsNone()) {
UE_LOG(LogTemp, Warning, TEXT("SliceMeshAtBone: One or both Socket Names are invalid!"));
return;
}
ProcMeshComponent->SetSimulatePhysics(false);
OtherHalfMesh->SetSimulatePhysics(false);
UE_LOG(LogTemp, Display, TEXT("Physic Disable"));
//Procedural Mesh를 특정 Socket에 Attach
FAttachmentTransformRules TransformRules(EAttachmentRule::SnapToTarget, true);
ProcMeshComponent->AttachToComponent(GetMesh(), TransformRules, ProceduralMeshAttachSocketName);
OtherHalfMesh->AttachToComponent(GetMesh(), TransformRules, OtherHalfMeshAttachSocketName);
//Ragdoll 적용 & Bone 자름.
GetMesh()->SetCollisionProfileName(TEXT("Ragdoll"));
GetMesh()->BreakConstraint(FVector(1000.f, 1000.f, 1000.f), FVector::ZeroVector, TargetBoneName);
GetMesh()->SetSimulatePhysics(true);
//Procedural Mesh에 물리 적용
//ProcMeshComponent->SetSimulatePhysics(true); -> true 시 따로 움직인다.
ProcMeshComponent->SetCollisionEnabled(ECollisionEnabled::NoCollision);
}
이 부분에선 크게 설명할 부분이 없는 것 같다.
BreakConstraint는 Bone의 연결을 끊는 함수이다. 자세한건 도큐먼트를 참고하면 될 것 같다.
여기까지하면 거의 다 왔다.
여기까지 따라오셨다면 Slice했을 때 기이한 현상이 일어날 것이다.
Skeletal Mesh가 BreakConstraint에 있는 FVector Impulse 파라미터로 인해 Mesh의 vertex들이 예상치 못한 위치로 이동하면서 텍스쳐가 왜곡된 현상이다.
이 현상을 방지하기 위해 다음과 같은 코드를 실행시켜주면 된다.
vertex를 수집할 때 NumVertices를 찾는 코드를 추가해준다.
//GetResourceForRendering - Skeletal Mesh의 렌더링 데이터를 가져오는 함수
const FSkeletalMeshRenderData* RenderData = SkeletalMesh->GetResourceForRendering();
if (!RenderData || !RenderData->LODRenderData.IsValidIndex(LODIndex)){
UE_LOG(LogTemp, Warning, TEXT("CopySkeletalMeshToProcedural: LODRenderData[%d] is not valid."), LODIndex);
return;
}
//vertex의 총 개수를 들고온다.
NumVertices = RenderData->LODRenderData[LODIndex].GetNumVertices();
그리고 해당 함수를 추가해주면 된다.
void AEnemy::ApplyVertexAlphaToSkeletalMesh()
{
if (!GetMesh() || !GetMesh()->GetSkeletalMeshAsset()) return;
TArray<FLinearColor> LinearVertexColors;
LinearVertexColors.Init(FLinearColor(1, 1, 1, 1), NumVertices); // 흰색(보임)
// VertexIndexMap을 활용해 잘린 부분만 색상을 변경
for (const TPair<int32, int32>& Pair : VertexIndexMap) {
int32 ColorChangeIndex = Pair.Key; // 원본 Skeletal Mesh의 버텍스 인덱스
if (ColorChangeIndex >= 0) { //잘못된 Index 방지.
LinearVertexColors[ColorChangeIndex] = FLinearColor(0, 0, 0, 0); // 검은색 = 마스킹 처리
}
}
// Skeletal Mesh에 버텍스 컬러 적용
GetMesh()->SetVertexColorOverride_LinearColor(0, LinearVertexColors);
GetMesh()->MarkRenderStateDirty(); // 렌더 상태 갱신
}
이 함수도 그렇게 어려운건 없어서 그냥 넘어간다.
결과는 위 사진과 같이 깔끔하게 잘린다.
물론 이렇게 하기 위해서 Matrial을 수정할 필요가 있다.
이 마네킹의 경우 아래와 같이 MakeMaterialAttributes노드와 VertexColor Node를 추가해주었다.
Blend Mode는 Masked이다.
전체 노드는 다음과 같다.
'Unreal 게임 개발' 카테고리의 다른 글
Unreal Skeletal Mesh to Procedural Mesh - 일정 범위 복사 (0) | 2025.02.25 |
---|---|
Unreal Skeletal Mesh to Procedural Mesh - 전체 Skeletal Mesh 복사 (0) | 2025.02.24 |