장점: 칼에 캡슐 콜리젼을 붙이므로 공격 애니메이션에 맞춰 칼이 적 객체에 충돌할 때 충돌처리 판정을 진행합니다.
단점: BeginOverlap으로 충돌처리 판정을 진행하므로 충돌이 됐다는 사실은 알 수 있지만 충돌된 위치는 알 수 없습니다.
->충돌처리 된 Location이 (0,0,0)인것을 확인해 볼 수 있다.
장점: 공격 방향에 맞춰 내 앞에 캡슐컬리젼을 소환하므로 구현이 쉽고 충돌처리 판정이 비교적 넓은 범위에 되어서 판정처리가 후합니다.
단점: 위에서 말한 장점이 단점이 되는데 판정처리가 후하다는 점이 섬세한 충돌처리를 요구하는 소울류 게임에는 어울리지 않는다.
장점: 틱마다 라인트레이스를 남기기 때문에 1번과 비슷하게 적 객채에 충돌할 때 충돌처리 판정을 진행할 수 있으며 충돌처리 된 위치를 알 수 있습니다.
단점: 게임프레임이 떨어지면 틱마다 라인트레이스를 남기는 숫자가 줄어들어 정확한 판정이 어려울 수 있습니다.
충돌처리에 꼭 필요한 요소
소울류 게임은 정확한 판정으로 공격을 진행할 때 공격범위에 대해서 신경쓰며 이를 통해 공격에 성공했을 때 게이머가 느낄 수 있는 쾌감을 높일 수 있습니다.
충돌처리 된 위치로 파티클 생성 및 위치에 맞는 충돌처리 모션 등 다양한 요소가 필요할 수 있기 때문입니다
위와 같은 이유로 충돌처리 된 위치를 알 수 있고 정확하게 판정을 진행할 수 있는 AnimNotify를 이용하여 칼 이동 위치에 라인트레이스를 선택하였습니다.
결론 : 프레임이 10까지 떨어지지 않는 이상 판정에는 문제가 없을거다라고 판단하고 AnimNotifyState를 사용하기로 하였습니다.
UCLASS()
class SOULLIKE_API UEnableCollisionNotifyState : public UAnimNotifyState
{
GENERATED_BODY()
public:
virtual void NotifyBegin(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, float TotalDuration) override;
virtual void NotifyTick(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, float FrameDeltaTime) override;
virtual void NotifyEnd(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation) override;
private:
void MakeLineTrace(class ASoulLikeCharacter* Character);
UPROPERTY()
TSet<AActor*> HitActors;
UPROPERTY(EditAnywhere,Category = "GAS")
TSubclassOf<class UGameplayEffect> AttackDamageEffect;
};
void UEnableCollisionNotifyState::NotifyTick(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, float FrameDeltaTime)
{
if (AActor* Owner = MeshComp->GetOwner())
{
if (ASoulLikeCharacter* Character = Cast<ASoulLikeCharacter>(Owner))
{
if (Character->GetSwordWeapon())
{
MakeLineTrace(Character);
}
}
}
}
void UEnableCollisionNotifyState::MakeLineTrace(ASoulLikeCharacter* Character)
{
if (Character->GetSwordWeapon())
{
USkeletalMeshComponent* SwordMesh = Character->GetSwordWeapon()->Weapon;
if (SwordMesh && SwordMesh->DoesSocketExist(FName("SowrdBoneStart")) && SwordMesh->DoesSocketExist(FName("SowrdBoneEnd")))
{
FVector StartLocation = SwordMesh->GetSocketLocation(FName("SowrdBoneStart"));
FVector EndLocation = SwordMesh->GetSocketLocation(FName("SowrdBoneEnd"));
FHitResult HitResult;
FCollisionQueryParams CollisionParams;
CollisionParams.AddIgnoredActor(Character);
if(Character->GetWorld()){
bool bHit = Character->GetWorld()->LineTraceSingleByChannel(HitResult, StartLocation, EndLocation, ECC_WorldStatic, CollisionParams);
if (bHit&&!HitActors.Contains(Character))
{
HitActors.Add(Character);
DrawDebugLine(Character->GetWorld(), StartLocation, EndLocation, FColor::Red, false, 1.0f);
DrawDebugSphere(Character->GetWorld(), HitResult.ImpactPoint, 10, 12, FColor::Yellow, false, 1.0f);
if (ASoulNoneCharacter* HitCharacter = Cast<ASoulNoneCharacter>(HitResult.GetActor()))
{
UAbilitySystemComponent* ASC= UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(HitCharacter);
if (ASC)
{
float PlayRate = 1.0f; // 몽타주 재생 속도
if (HitCharacter->HitAnimMontage)
{
ASC->PlayMontageSimulated(HitCharacter->HitAnimMontage, PlayRate);
... 게임 이펙트 적용
}
}
}
}
else
{
DrawDebugLine(Character->GetWorld(), StartLocation, EndLocation, FColor::Blue, false, 2.0f);
}
}
}
}
}
라인트레이스를 생성할 때 StartLocation과 EndLocation위치를 설정하기 위함입니다.
bool bHit = Character->GetWorld()->LineTraceSingleByChannel(HitResult, StartLocation, EndLocation, ECC_WorldStatic, CollisionParams);
-> LineTraceSingleByChannel를 통해 WorldStatic인 요소들에게 충돌처리를 진행합니다.
TSet<AActor*> HitActors;//.h
if (bHit&&!HitActors.Contains(Character))//.CPP
->Tset을 이용하여 중복된 오브젝트가 여러개 들어가지 않게 하고 !HitActors.Contains(Character)를 통해 한 번 충될된 오브젝트는 다시 충돌될 수 없게 하였습니다.(여러번 충돌되게 되면 조건발동이 많이 되기 때문입니다.)
->Tset을 사용하지 않고 라인트레이스 충돌처리(충돌시 생성되는 노란 구체가 여러개 생성되는 것을 확인해볼 수 있습니다.)
->Tset을 사용했을 때 라인트레이스 충돌처리(충돌시 생성되는 노란 구체가 하나만 생성되는 것을 확인해 볼 수 있습니다.)
충돌 처리 방법을 바꿈으로서 보다 정확하게 판정할 수 있게 되었습니다.
다음에는 공격시 게임이펙트(상대 캐릭터에 데미지 적용)적용 콤보와 방어를 구현해보도록 하겠습니다.
마지막으로 게임제작에 도움이 되고 있는 피의 거짓 영상으로 마무리하겠습니다.