2024.06.16
깃허브! | 풀리퀘! |
---|---|
★ https://github.com/ChangJin-Lee/SimpleShooter | ★ https://github.com/ChangJin-Lee/Project-Lena/pull/3 |
이번 포스팅에서는 적 캐릭터들이 무기를 조준하지 않던 문제를 해결한 과정을 다룹니다. 블루프린트에서 On Changed 함수 노드를 사용하지 않은 것이 원인이었습니다. 이를 해결하고 적 캐릭터가 무기를 제대로 소지하도록 수정했습니다. 그리고 이제 다양한 적 캐릭터가 등장합니다.
이번 주는 퍼즐 기믹 추가를 하지 못했습니다.
다음 주의 개발 계획입니다.
무기를 조준하고 있지 않는 적 모습 |
원인은 무엇이고 왜 이런 현상이 발생했던 것일까?
저번 벨로그 내용을 보면 AI Character Weapon 이라는 부분을 캐릭터 블루프린트에 만들었습니다.
지난 번에 만든 Base_Character 블루프린트 |
지난 번에 만든 Base_Character 블루프린트 |
alsV4 에서는 Aming이라는 Rotation Mode가 있는데 무기를 들었을 때 이 모드를 Set 해주어야 무기를 올바르게 조준합니다.
BPI Set Rotation Mode를 추가해주니 올바르게 총을 조준했습니다.
[UE5] TPS 게임의 적 캐릭터에 다양한 모델 적용하기: ALS V4와 VRoid 리타기팅 완벽 가이드
메이드복을 입은 캐릭터 | 단발머리 캐릭터 |
그런데 문제가 발생했습니다.
바로 총이 캐릭터의 손 위치보다 위에서 스폰되고 있었습니다.
원인은 바로 스켈레톤의 크기 차이 였습니다.
지금은 총의 스폰 위치가 alsV4 기본 스켈레톤의 WeaponSocket라는 커스텀 소켓에 장착되고 있습니다. VRoid 캐릭터의 크기가 alsV4 기본 스켈레톤과 다르기 때문에 총이 VRoid에서 가져온 모델보다 위에 있었습니다.
총 스폰 위치 |
어떻게 해결해야 할까 고민을 많이 했습니다.
해결 방법이 여러 개 떠올랐는데 그 중에서 괜찮았던 것들을 정리해 보겠습니다.
public:
AYourCharacter();
protected:
virtual void BeginPlay() override;
private:
UPROPERTY(EditDefaultsOnly, Category = "Weapon")
TSubclassOf<class AGun> GunClass;
AGun* CurrentGun;
UPROPERTY(EditDefaultsOnly, Category = "Weapon")
FName PlayerGunSocketName;
UPROPERTY(EditDefaultsOnly, Category = "Weapon")
FName EnemyGunSocketName;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Mesh", meta = (AllowPrivateAccess = "true"))
USkeletalMeshComponent* PlayerMesh;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Mesh", meta = (AllowPrivateAccess = "true"))
USkeletalMeshComponent* EnemyMesh;
void AttachGunToMesh(USkeletalMeshComponent* Mesh, FName SocketName);
};
AYourCharacter::AYourCharacter()
{
// 기본 메쉬 설정
PlayerMesh = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("PlayerMesh"));
PlayerMesh->SetupAttachment(RootComponent);
// 적 메쉬 설정
EnemyMesh = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("EnemyMesh"));
EnemyMesh->SetupAttachment(RootComponent);
PlayerGunSocketName = TEXT("PlayerHand_R"); // 플레이어 소켓 이름 예시
EnemyGunSocketName = TEXT("EnemyHand_R"); // 적 소켓 이름 예시
}
void AYourCharacter::BeginPlay()
{
Super::BeginPlay();
if (GunClass)
{
CurrentGun = GetWorld()->SpawnActor<AGun>(GunClass);
if (CurrentGun)
{
if (EnemyMesh->IsVisible())
{
AttachGunToMesh(EnemyMesh, EnemyGunSocketName);
}
else
{
AttachGunToMesh(PlayerMesh, PlayerGunSocketName);
}
}
}
}
void AYourCharacter::AttachGunToMesh(USkeletalMeshComponent* Mesh, FName SocketName)
{
if (Mesh && CurrentGun)
{
FAttachmentTransformRules AttachmentRules(EAttachmentRule::SnapToTarget, true);
CurrentGun->AttachGunToSocket(Mesh, SocketName);
}
}
FindMeshByName로 BodyMesh를 찾아서 해당하는 Mesh의 WeaponSocket 소켓 부분에 장착하도록 하는 방법입니다.
기존 코드를 다음처럼 BodyMesh를 가진 Mesh를 찾아서 부착해주도록 수정했습니다.
void ABase_Character::BeginPlay()
{
Super::BeginPlay();
Health = MaxHealth;
if(!ensure(!GunClassArray.IsEmpty())) return;
USkeletalMeshComponent* SpecificMesh = FindMeshByName(TEXT("BodyMesh"));
if (SpecificMesh)
{
UE_LOG(LogTemp, Warning, TEXT("Find VRoidCharacter %s"), *SpecificMesh->GetName());
// 총 부착
for(TSubclassOf<AGun> GunClass : GunClassArray)
{
Gun = GetWorld()->SpawnActor<AGun>(GunClass);
SpecificMesh->HideBoneByName(TEXT("weapon_r"), EPhysBodyOp::PBO_None);
Gun->AttachToComponent(SpecificMesh, FAttachmentTransformRules::KeepRelativeTransform, TEXT("WeaponSocket"));
Gun->SetOwner(this);
Gun->SetActorHiddenInGame(true);
GunArray.Add(Gun);
UE_LOG(LogTemp, Warning, TEXT("Spawn Guns %s"), *Gun->GetName());
}
}
}
이 방법은 매번 BeginPlay에서 모든 USkeletalMeshComponent를 순회하여 찾는 방식이기때문에 성능에 영향을 미칠 수 있습니다. 특히 메쉬가 많은 경우 더욱 그렇습니다.
그리고 이름 충돌이 일어날 경우에 큰 문제가 생길 수도 있습니다.
다음과 같은 이유로 2번의 방법으로 진행하기로 결정했습니다.
더 다양한 캐릭터를 추가할 때 어렵지 않아야한다. 코드가 간단해야한다.
하지만 해결해야하는 문제점이 충분이 있습니다. 예를들어 성능이슈나 복잡성 등이 있습니다. 이후에 자주 사용되는 메쉬 컴포넌트를 캐시해서 필요할 때마다 검색되지 않도록 하는 캐싱 방법으로 고칠 예정입니다.
총 스폰 위치 회전됨 |
원인은 무엇이고 어떻게 해결해야할까.
원인은 VRoid의 캐릭터와 alsV4의 WeaponSocket 회전축이 달라서 였습니다.
alsV4의 기본 스켈레탈 회전축 | VRoid의 소켓 회전축 |
해당 위치를 올바르게 바꿔주고 소켓의 회전과 위치를 복사-붙여넣기로 업데이트 해주었습니다.
올바른 위치에 스폰되었고 총도 정상적으로 발사하는 모습을 볼 수 있습니다.
올바르게 스폰된 모습1 |
올바르게 스폰된 모습2 |