에너미 약점과 근접 에너미 무기 공격 충돌 만들기
현재 에너미는 어느 부위를 맞아도 체력이 똑같이 감소한다.
하지만 나는 특정 부위를 공격하면 대미지가 더 들어가서 체력이 더 많이 깎이게 만들고 싶다.
먼저 근접 에너미의 어느 부분을 약점으로 할 것인지 블루프린트에서 미리 설정을 해보았다.
머리를 약점으로 잡았으며 메쉬 아래에 속하게 만들고 머리로 볼 수 있는 위치를 설정하였다.
그리고 콜리전을 만들어서 약점이라는 EnemyWeak와 EnemyWeak 프리셋을 만들었다.
이제 설정들을 메모해서 코드로 작성을 한다.
MeleeEnemy.h
UPROPERTY(EditAnywhere, BlueprintReadWrite)
class USphereComponent* HeadCollision;
UFUNCTION()
void OnHeadOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
헤더에서 머리를 탐지하는 콜리전과 해당 콜리전이 불렛과 충돌되었을 때 불릴 함수를 작성하였다.
MeleeEnemy.cpp
AMeleeEnemy::AMeleeEnemy()
{
//약점 부분이 되는 콜리전을 먼저 생성
HeadCollision = CreateDefaultSubobject<USphereComponent>(TEXT("Head"));
//약점 콜리전을 캐릭터 메쉬 아래에 속하게 만듦
HeadCollision->SetupAttachment(GetMesh());
//해당 콜리전의 위치를 조정함
HeadCollision->SetRelativeLocation(FVector(-15.0f, 45.0f, 175.0f));
//해당 콜리전의 프리셋을 지정해줌
HeadCollision->SetCollisionProfileName(FName("EnemyWeak"));
//해당 콜리전이 충돌 감지가 되었을 때 무슨 함수를 불러올 것인지 지정
HeadCollision->OnComponentBeginOverlap.AddDynamic(this, &AMeleeEnemy::OnHeadOverlap);
}
생성자에서는 먼저 콜리전을 생성하고 메쉬의 자식으로 속하게 만들었다.
그리고 위치 조정과 프리셋, 충돌시 함수가 불려오도록 코드를 작성하였다.
MeleeEnemy.cpp
void AMeleeEnemy::OnHeadOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
ADestiny2Projectile* bullet = Cast<ADestiny2Projectile>(OtherActor);
//만약 플레이어 불렛일 경우
if (bullet)
{
//대미지를 입고
GetDamage(40.0f);
//불렛을 파괴
bullet->Destroy();
}
}
충돌시 불려오는 함수의 내용은 이렇다. 앞서 작성한 에너미와 불렛 충돌 때의 내용과 다른 점은 크게 없다. 그냥 대미지 수치만 더 크게 만들었을 뿐이다.
다시 언리얼 엔진을 실행하니 콜리전이 코드에서 설정한대로 생성되었다.
원거리 에너미에게도 똑같은 작업을 하여서 약점을 부여해주자.
이번에는 근접 에너미한테 무기를 휘둘렀을 때 플레이어를 공격할 수 있도록 만들 예정이다.
똑같이 근접 에너미의 무기를 사용할 수 있도록 오브젝트 타입과 프리셋을 새로 하나 만들었다.
이제 에너미 무기에 부착을 할 예정인데, 에너미는 애니메이션에 따라 무기의 모델링이 계속하여 변경된다.
콜리전을 무기에 딱 붙여줘야하는데 여기에 사용할 수 있는 것이 소켓이라는 것이다.
먼저 캐릭터의 메쉬에서 스켈레탈 메쉬를 더블 클릭하여 스켈레탈 메쉬로 넘어가준다.
그러면 스켈레탈 메쉬가 나온다. 오른쪽 창을 보면 뼈 아이콘과 비스듬한 동그라미 아이콘이 보이는데 뼈 아이콘은 본, 비스듬한 동그라미 아이콘은 소켓이다.
오른쪽 창에서 자신이 원하는 위치의 소켓 위치를 찾으면된다. 자신이 원하는 위치의 소켓을 찾았으면 소켓의 이름을 메모장이나 다른 곳에 메모를 해두자.
이제 코드에서 콜리전을 생성하여 소켓에 부착을 해보자.
MeleeEnemy.h
UPROPERTY(EditAnywhere,BlueprintReadWrite)
class UBoxComponent* WeaponCollision;
UFUNCTION()
void OnWeaponOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
헤더에는 무기 부분에 부착될 콜리전과 해당 콜리전이 플레이어와 충돌할 경우 불려울 함수를 작성하였다.
MeleeEnemy.cpp
AMeleeEnemy::AMeleeEnemy()
{
//무기 콜리전을 생성
WeaponCollision = CreateDefaultSubobject<UBoxComponent>(TEXT("WeaponCollision"));
//콜리전을 캐릭터 메쉬의 자식으로 부착
WeaponCollision->SetupAttachment(GetMesh());
//해당 콜리전을 이름이 같은 소켓에게 붙인다
WeaponCollision->AttachToComponent(GetMesh(), FAttachmentTransformRules::SnapToTargetIncludingScale, TEXT("FX_Trail_R_01"));
//콜리전의 프리셋을 지정
WeaponCollision->SetCollisionProfileName(FName("EnemyWeapon"));
//콜리전 충돌이 일어날 경우 어떤 함수가 불려올지 설정
WeaponCollision->OnComponentBeginOverlap.AddDynamic(this, &AMeleeEnemy::OnWeaponOverlap);
}
생성자에서 콜리전을 만든 다음, 해당 콜리전을 캐릭터 메쉬의 자식으로 부착.
AttachToComponent(GetMesh(), FAttachmentTransformRules::SnapToTargetIncludingScale, TEXT("FX_Trail_R_01"))를 통해 해당 이름의 소켓에 부착해준다.
그 후 프리셋과 충돌이 일어날 경우 어떤 함수가 불려올지 설정한다.
MeleeEnemy.cpp
void AMeleeEnemy::OnWeaponOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
ADestiny2Character* player = Cast<ADestiny2Character>(OtherActor);
if (player)
{
GEngine->AddOnScreenDebugMessage(20, 3.0f, FColor::Red, TEXT("Melee Enemy Attack"));
}
}
플레이어와 무기가 충돌이 일어났을 때 불려오는 함수의 내용이다.
플레이어와 부딪혔을 경우 현재는 화면에서 텍스트가 보이도록 만들었다.
언리얼을 실행해보니 콜리전의 크기와 위치가 마음에 들지 않는다. 코드에서 위치와 크기를 설정해주지 않았기에 이렇게 나왔다.
먼저 블루프린트에서 위치와 크기를 설정해봐서 자신이 원하는 값을 찾은 다음 메모해두자.
그 다음 다시 코드를 작성한다.
MeleeEnemy.cpp
AMeleeEnemy::AMeleeEnemy()
{
//무기 콜리전을 생성
WeaponCollision = CreateDefaultSubobject<UBoxComponent>(TEXT("WeaponCollision"));
//콜리전을 캐릭터 메쉬의 자식으로 부착
WeaponCollision->SetupAttachment(GetMesh());
//해당 콜리전을 이름이 같은 소켓에게 붙인다
WeaponCollision->AttachToComponent(GetMesh(), FAttachmentTransformRules::SnapToTargetIncludingScale, TEXT("FX_Trail_R_01"));
//콜리전의 크기와 위치를 설정
WeaponCollision->SetRelativeLocation(FVector(0.0f, 0.0f, -65.0f));
WeaponCollision->SetRelativeScale3D(FVector(1.0f, 1.0f, 2.0f));
//콜리전의 프리셋을 지정
WeaponCollision->SetCollisionProfileName(FName("EnemyWeapon"));
//콜리전 충돌이 일어날 경우 어떤 함수가 불려올지 설정
WeaponCollision->OnComponentBeginOverlap.AddDynamic(this, &AMeleeEnemy::OnWeaponOverlap);
}
원래 콜리전을 생성하는 부분에서 크기와 위치만 설정하는 SetRelativeLocation()과 SetRelativeScale3D()만 추가되었다.
해당 값들은 자신이 미리 메모해둔 값을 적어주도록 하자.
다시 실행해보니 콜리전의 위치가 설정대로 되었다.
하지만 문제점이 하나 있다. 해당 콜리전은 계속해서 충돌을 감지하는 것이 아니라 공격 애니메이션이 재생될 때만 잠시 충돌을 감지하는 형식이다.
현재는 충돌을 계속해서 감지한다. 이를 애니메이션 Notify와 코드를 활용해
1. 처음에는 콜리전이 꺼져있는 상태
2. 공격 애니메이션이 재생될 때 콜리전을 활성화해서 충돌을 감지하게 만들기
3. 공격이 끝나는 애니메이션 부분에는 콜리전을 다시 비활성화
와 같은 방식으로 이루어지게 만들고 싶다.
먼저 공격 애니메이션에서 노티파이를 새로 만들었다. OnCollision은 무기 콜리전을 활성화한다. 반대로 OffCollision은 무기 콜리전을 비활성화하게 만들 예정이다.
이제 코드를 작성하자.
EAnimInstance.h
UFUNCTION()
void AnimNotify_OnCollision();
UFUNCTION()
void AnimNotify_OffCollision();
헤더에서 노티파이 이름을 똑같게 해서 함수를 만들었다.
EAnimInstance.cpp
void UMEAnimInstance::AnimNotify_OnCollision()
{
Enemy->OnWeaponCollision();
}
void UMEAnimInstance::AnimNotify_OffCollision()
{
Enemy->OffWeaponCollision();
}
함수에서는 Enemy(MeleeEnemy를 뜻함)의 OnWeaponCollision과 OffWeaponCollision이 불리도록 하였다.
이렇게 작성을 해서 애니메이션이 재생되어 특정 모션이 재생될 때 콜리전이 켜지고, 또 다시 특정 모션이 재생될 때 콜리전이 다시 꺼지게 할 것이다.
MeleeEnemy.h
void OnWeaponCollision();
void OffWeaponCollision();
MeleeEnemy 헤더에서는 콜리전을 켜주는 함수와 콜리전을 꺼주는 함수를 작성한다.
MeleeEnemy.cpp
AMeleeEnemy::AMeleeEnemy()
{
WeaponCollision->SetCollisionEnabled(ECollisionEnabled::NoCollision);
}
그리고 cpp에서 생성자 부분에서 무기의 콜리전은 처음에는 충돌 처리가 안되도록 NoCollision으로 지정해준다.
MeleeEnemy.cpp
void AMeleeEnemy::OnWeaponCollision()
{
WeaponCollision->SetCollisionEnabled(ECollisionEnabled::QueryOnly);
}
void AMeleeEnemy::OffWeaponCollision()
{
WeaponCollision->SetCollisionEnabled(ECollisionEnabled::NoCollision);
}
OnWeaponCollision()이 불릴 때에는 콜리전을 Query Only로 콜리전을 활성화해준다. Query Only로 활성화하면 물리 작용은 이루어지지 않으며 충돌 감지만 이루어진다.
OffWeaponCollision()이 불릴 때에는 콜리전을 다시 NoCollision으로 바꿔준다.
이렇게 하여 에너미의 약점 콜리전을 부착, 무기 소켓에 콜리전을 부착, 그리고 애니메이션에 따라 콜리전을 활성화, 비활성화를 하였다.