이번 개발은 저번에 캐릭터 콤보 액션을 구현했으니 진짜로 캐릭터가 누군가를 공격할 수 있게 충돌 처리를 해보자.
내가 애셋으로 받은 캐릭터는 기본적으로 무기가 메시에 같이 붙어있다. 그래서 공격 판정을 위해서는 내가 알기로 두 가지 방법이 있는데
1번은 강의에서 하기도 했고 직접 공격해 무기에 충돌하는 부분만 공격하고 싶어서 2번 방법을 선택했다
앞선 개발일지에서 공격 부분을 LKPlayerCharacter에 구현했었는데 다시 생각해보니까 모든 캐릭터가 공격은 가능하게 해야할 것 같아서 LKPlayerCharacter에 있던 공격 코드들을 LKCharacterBase로 다 옮겼다
무기가 캡슐형이랑 비슷해서 LKCharacterBase에 CapsuleComponent를 하나 만들어주자.
// LKCharacterBase.h
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Collision")
TObjectPtr<UCapsuleComponent> WeaponComponent;
// LKCharacterBase.cpp
ALKCharacterBase::ALKCharacterBase()
{
WeaponComponent = CreateDefaultSubobject<UCapsuleComponent>(TEXT("WeaponCollision"));
WeaponComponent->SetupAttachment(GetMesh(), TEXT("WeaponSocket"));
WeaponComponent->SetCollisionProfileName(TEXT("NoCollision"));
...
}
헤더에 CapsuleComponent 변수를 선언해주고 생성자에서 초기화해준다
CDO 함수로 WeaponComponent를 할당하고 다른 컴포넌트에 붙여야 하는데 캐릭터 메시가 움직일 때 무기도 같이 움직이는 것이 좋으니까 캐릭터 메시에 붙인다. 두 번째 매개변수는 소켓 이름인데 스켈레탈 메시에 존재하는 WeaponSocket라는 이름의 소켓에 캡슐 컴포넌트가 붙여질 것임
소켓은 어디서 확인하냐?

적용하고 있는 스켈레탈 메시를 열어 Skeleton Tree를 본 다음 내가 붙이고 싶은 bone을 우클릭해 소켓이 없다면 소켓을 만들어준다(소켓 표시는 이름 앞에 뼈가 아닌 다른 아이콘이 있음)
이 소켓 이름을 그대로 SetupAttachment 함수의 두 번째 매개변수로 넘겨주면 자동으로 컴포넌트가 그 소켓에 붙여진다. 생성자를 변경했으니 엔진을 끄고 컴파일해주자
언리얼을 키고 나의 캐릭터 클래스 블루프린트를 열어보면 다음과 같이 캡슐 컴포넌트가 기본적으로 생성됐다

기본 값으로 컴포넌트가 생성됐기에 위치, 회전, 크기 같은건 우리가 무기에 맞게 조절해야 한다. 계층에서 Weapon 캡슐 컴포넌트를 클릭한 후 Details에서 Transform과 Capsule Shape를 조절해주면 대충 무기와 비슷한 Collision이 나온다

기존에는 원래 공격을 PlayerController에서 특정 캐릭터 클래스(LKPlayerController) 타입으로 캐스팅해서 함수를 호출했는데 플레이어 컨트롤러에서는 이래도 되긴 하지만 이왕이면 의존성을 떨어뜨리면 좋으니까 공격 부분을 인터페이스로 따로 빼주겠다
UINTERFACE(MinimalAPI)
class ULKAttackInterface : public UInterface
{
GENERATED_BODY()
};
/**
*
*/
class LOSTKINGDOM_API ILKAttackInterface
{
GENERATED_BODY()
public:
virtual void ProcessCombo() = 0;
virtual void AttackStart() = 0; // 추후에 설명
virtual void ComboAttackCheck() = 0;
};
그리고 이 함수들을 LKCharacterBase에서 인터페이스를 상속받아 override해서 재정의했다.
그러면 이제 다음과 같이 캐스팅이 가능하다
ILKAttackInterface* PlayerCharacter = CastChecked<ILKAttackInterface>(GetPawn());
if (PlayerCharacter)
{
PlayerCharacter->ProcessCombo();
}
캐릭터가 공격을 해서 적이 공격을 받으려면 Collision 채널끼리 충돌을 감지해야 한다.
Collision에 관한 자세한 내용은 각자 공부하는 것으로 하자
먼저 Object Channel을 생성해보자. 크게 5개를 만들었는데


블루프린트 클래스로 만든 캡슐 컴포넌트와 무기 컴포넌트에 위에서 만든 프로필을 지정해주자. 플레이어 클래스의 캡슐 컴포넌트에는 LKPlayer 프로필, 무기 컴포넌트에는 LKPlayerAttack 프로필을 지정해줬다
그리고 이제 컴포넌트에 오버랩 이벤트를 붙이자
// LKCharacterBase.h
// 인자를 맞춰줘야 오버랩 이벤트로 등록시킬 수 있음
UFUNCTION()
virtual void OnAttack(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
// LKCharacterBase.cpp
void ALKCharacterBase::PostInitializeComponents()
{
Super::PostInitializeComponents();
...
WeaponComponent->OnComponentBeginOverlap.AddDynamic(this, &ALKCharacterBase::OnAttack);
}
void ALKCharacterBase::OnAttack(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
UE_LOG(LogTemp, Warning, TEXT("OtherActor : %s"), *OtherActor->GetName());
}
무기 컴포넌트가 Overlap 이벤트를 발생시킬 컴포넌트에 오버랩이 시작될 때 OnAttack 함수가 호출된다.
나같은 사람이 있을까봐 적는데 콜리전의 오버랩 이벤트를 저렇게 등록시킬때 뭣도 모르고 생성자에서 이벤트를 추가했다가 호출이 안됐는데 찾아보니까 컴포넌트가 완전히 초기화되고 이벤트를 등록시키는게 안정적이라고 하니 조심하자
맵에 캐릭터를 배치해 임시로 콜리전 프로필을 Enemy로 바꾸고 공격해보면 다음과 같이 로그가 뜬다

그런데 좀 이상한게 있는데 위에 움짤과 같이 한 번 때리고 다른 곳으로 이동하면 괜찮은데 짤에는 없는데 공격을 하면 캐릭터가 검을 휘두르고 다시 원상태로 돌아가는 과정에서 오버랩 이벤트가 한번 더 실행되면서 총 두 번 공격이 된다. 의도와는 다른 행동이므로 고쳐야한다
일단 문제는 무기 컴포넌트의 콜리전이 계속 LKPlayerAttack일 경우 캐릭터가 공격을 안해도 무기 컴포넌트에 닿기만 해도 데미지가 들어간다는 것이다. 그래서 애니메이션 몽타주의 섹션에서 공격을 시작할 때와 끝날때를 구분해서 시작할 때 콜리전을 LKPlayerAttack으로 바꾸고 공격이 끝나면 NoCollision으로 바꿔보자
void ALKCharacterBase::AttackStart()
{
LookAt();
}
void ALKCharacterBase::AttackEnd()
{
WeaponComponent->SetCollisionProfileName(TEXT("NoCollision"));
}
콤보 공격 판정하듯이 Notify를 만들어서 위 두 함수를 호출하게 할 것이다.
ALKCharacterBase에 AttackStart 함수에 프로필을 바꾸는 게 없는데 왜냐면 플레이어랑 적은 서로 다른 프로필 이름을 바꿔야 하니까 자식 클래스에서 오버라이드해 재정의하는게 좋다
void ALKPlayerCharacter::AttackStart()
{
Super::AttackStart();
WeaponComponent->SetCollisionProfileName(TEXT("LKPlayerAttack"));
}
그리고 애니메이션 노티파이를 설정해주자.

적절히 클립을 재생하면서 어느 부분부터 공격을 시작하고 어디서 끝내야 할 지 판단해 노티파이를 설정해준다
이제 캐릭터에 오버랩 이벤트를 발생시킬 수 있으니 캐릭터의 공격력을 기반으로 적의 체력을 깎는 것을 들어가야겠지?
그리고 지금 플레이어 캐릭터 공격 4번째가 아무래도 이동하면서 종으로 공격하는거다 보니 적이 잘 안맞는 것 같은데 애니메이션 교체를 고려해봐야 할 것 같다
일단 여기까지..