[UE5] Lena: Dev Diary #4 - 다양한 적 캐릭터 등장과 무기 조준 문제 해결

ChangJin·2024년 6월 16일
0

Unreal Engine5

목록 보기
70/114
post-thumbnail

2024.06.16

깃허브!풀리퀘!
https://github.com/ChangJin-Lee/SimpleShooter https://github.com/ChangJin-Lee/Project-Lena/pull/3

이번 포스팅에서는 적 캐릭터들이 무기를 조준하지 않던 문제를 해결한 과정을 다룹니다. 블루프린트에서 On Changed 함수 노드를 사용하지 않은 것이 원인이었습니다. 이를 해결하고 적 캐릭터가 무기를 제대로 소지하도록 수정했습니다. 그리고 이제 다양한 적 캐릭터가 등장합니다.


진행상황

  • ✅ 적 캐릭터의 이동, 공격 구현
  • ✅ 적 캐릭터의 플레이어 추적 구현
  • ✅ 캐릭터 사망 애니메이션
  • ✅ 더 다양한 캐릭터가 적으로 등장하기
  • ✅ 적 캐릭터가 무기를 들지 않고 있던 문제
  • ✅ 적 캐릭터가 무기를 조준하지 않는 문제
  • ✅ HUD에서 플레이어의 체력이 등장하지 않음



이번 주 개발

  • 적 캐릭터의 무기 관련 오류 해결
  • 적 캐릭터 다양하게 스폰하는 방법 탐구
  • 퍼즐 기믹 추가

이번 주는 퍼즐 기믹 추가를 하지 못했습니다.
다음 주의 개발 계획입니다.

다음 주 개발 계획

  • 퍼즐 기믹 추가
  • 총구 발사 이펙트 바꾸기
  • 총 피격 이펙트 바꾸기
  • 게임 bgm 바꾸기
  • Ammo 만들기 (탄수, 장전)



무기를 조준하지 않던 문제

  • 마지막 개발 상황에서 잘 보면 적 캐릭터가 무기를 조준하지 않습니다.

무기를 조준하고 있지 않는 적 모습

  • 원인은 무엇이고 왜 이런 현상이 발생했던 것일까?

  • 저번 벨로그 내용을 보면 AI Character Weapon 이라는 부분을 캐릭터 블루프린트에 만들었습니다.

저번 벨로그 내용


지난 번에 만든 Base_Character 블루프린트
  • 바로 이 부분이 문제였습니다.
지난 번에 만든 Base_Character 블루프린트
  • alsV4 에서는 Aming이라는 Rotation Mode가 있는데 무기를 들었을 때 이 모드를 Set 해주어야 무기를 올바르게 조준합니다.

  • BPI Set Rotation Mode를 추가해주니 올바르게 총을 조준했습니다.



다양한 적이 등장하기

[UE5] TPS 게임의 적 캐릭터에 다양한 모델 적용하기: ALS V4와 VRoid 리타기팅 완벽 가이드

  • 이전에 포스팅한 벨로그 글을 참고하면 vrm4u를 사용해서 다양한 메시를 alsV4에 적용할 수 있게 되었습니다.
메이드복을 입은 캐릭터단발머리 캐릭터
  • VRoid에서 원하는 캐릭터를 만들고 vrm 확장자 파일로 내보내기한 후 언리얼 프로젝트에서 불러오기를 했습니다.
  • 이후는 벨로그 글을 참고하여 리타기팅을 진행했습니다.

  • 그런데 문제가 발생했습니다.

  • 바로 총이 캐릭터의 손 위치보다 위에서 스폰되고 있었습니다.

  • 원인은 바로 스켈레톤의 크기 차이 였습니다.

  • 지금은 총의 스폰 위치가 alsV4 기본 스켈레톤의 WeaponSocket라는 커스텀 소켓에 장착되고 있습니다. VRoid 캐릭터의 크기가 alsV4 기본 스켈레톤과 다르기 때문에 총이 VRoid에서 가져온 모델보다 위에 있었습니다.

총 스폰 위치
  • 어떻게 해결해야 할까 고민을 많이 했습니다.

  • 해결 방법이 여러 개 떠올랐는데 그 중에서 괜찮았던 것들을 정리해 보겠습니다.




1. 총의 위치를 개별 스켈레탈 메쉬의 소켓에 맞게 조정하기

  • 다음처럼 플레이어, 적 메쉬를 따로 생성해주고 각 캐릭터의 스켈레탈 메쉬에 따라 총의 위치를 동적으로 조정하는 방법입니다.
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);
    }
}
  • 이렇게 하면 플레이어인지 적인지 확인이 필요하기 때문에 그리 좋지 않은 방법입니다.
  • 그리고 소켓 이름이 하드코딩으로 들어가 있어서 캐릭터의 설정이 변경될 때마다 코드를 수정해야합니다.
  • 캐릭터를 추가할 때 코드의 수정은 최소한으로 하고 싶기 때문입니다.


2. FindMeshByName 사용하기

  • 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
profile
게임 프로그래머

0개의 댓글