오늘은 물리충돌
SweepSingleByChannel
에 사용될 트레이싱 채널을 만드는 것으로 시작한다
TraceChannel
은 유니티에 Layer
와 비슷하게 검출 시 걸러주는 역할을 한다
이름은 Attack, Response 는 Ignore 로 만들어준다
이 트레이스 채널에 대한 반응을 저번에 만들어둔 ABCharacter
프리셋에서 설정해준다 설정은 Block
SweepSingleToChannel
은 파라미터 설정이 복잡한데 다음과 같다
SweeprSingleToChannel
은 하나만 검출하게 되고, 여러개를 검출하고 싶다면 SweepMultiByChannel
이 있다
HitResult
물리적 충돌이 탐지도니 경우 고나련된 정보를 담을 구조체Start
탐색을 시작하는 위치End
탐색을 끝내는 위치Rot
탐색에 사용할 도형의 회전TraceChannel
물리 충돌 감지에 사용할 트레이스 채널 정보CollisionShape
탐색에 사용할 도형 정보Params
탐색 방법에 대한 설정 값을 모아둔 구조체ResponseParams
탐색 반응을 설정하기 위한 구조체
코드는 다음과 같다
ABCharacter.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "ArenaBattle.h"
#include "GameFramework/Character.h"
#include "ABCharacter.generated.h"
UCLASS()
class ARENABATTLE_API AABCharacter : public ACharacter
{
...
void AttackCheck();
UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = Attack, Meta = (AllowPrivateAccess = true))
float AttackRange;
UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = Attack, Meta = (AllowPrivateAccess = true))
float AttackRadius;
};
공격을 체크해주는 AttackCheck
함수와 공격 범위를 지정할 변수 AttackRange
와 AttackRadius
를 설정해준다
ABCharacter.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "ABCharacter.h"
#include "ABAnimInstance.h"
// Sets default values
AABCharacter::AABCharacter()
{
...
AttackRange = 200.0f;
AttackRadius = 50.0f;
}
void AABCharacter::PostInitializeComponents()
{
...
ABAnim->OnAttackHitCheck.AddUObject(this, &AABCharacter::AttackCheck);
}
void AABCharacter::AttackCheck()
{
FHitResult HitResult;
FCollisionQueryParams Params(NAME_None, false, this);
bool bResult = GetWorld()->SweepSingleByChannel
(
HitResult,
GetActorLocation(),
GetActorLocation() + GetActorForwardVector() * AttackRange,
FQuat::Identity,
ECollisionChannel::ECC_GameTraceChannel2,
FCollisionShape::MakeSphere(AttackRadius),
Params
);
if (bResult)
{
//UE4 에서는 HitResult.Actor.IsValid()
if (HitResult.GetActor()->IsValidLowLevel())
{
//FDamageEvent DamageEvent;
//HitResult.GetActor()->TakeDamage(50.0f, DamageEvent, GetController(), this)
UGameplayStatics::ApplyDamage(HitResult.GetActor(), 50.0f, GetController(), this, UDamageType::StaticClass());
}
}
}
여기서 Params 은 인자로 Name_None
, false
, this
를 줬는데, 각각
Name_None
TraceTag 의 이름, Name_None
은 TraceTag 가 없다고 명시하는 것
false
복잡한 검사가 아닌 간단한 검사를 실행 (true 시 복잡한 검사(complex collision test) 를 하게 되며
검출되는 첫번째 오브젝트, 충돌 지점 등 상세한 정보를 얻게 된다)
this
충돌검사를 하는 객체이며 검사시에 제외된다
데미지를 추가하는 ApplyDamage 함수는 주석 처리된 FDamageEvent 를 활용한 방식과 동일하게 작동한다
다만 이렇게 끝내면, 검출이 되었는지 안되었는지 검출하는것이 어렵기 때문에 씬에 디버깅이 나오게 하려 한다
대상을 검출 시 초록색, 아니라면 빨간색으로 검출되게 할 것인다
drawDebugCapsule을 하기 위해서 DrawDebugHelpers.h 를 추가해줘야한다
// Fill out your copyright notice in the Description page of Project Settings.
#include "ABCharacter.h"
#include "ABAnimInstance.h"
#include "DrawDebugHelpers.h"
...
void AABCharacter::AttackCheck()
{
FHitResult HitResult;
FCollisionQueryParams Params(NAME_None, false, this);
bool bResult = GetWorld()->SweepSingleByChannel
(
HitResult,
GetActorLocation(),
GetActorLocation() + GetActorForwardVector() * AttackRange,
FQuat::Identity,
ECollisionChannel::ECC_GameTraceChannel2,
FCollisionShape::MakeSphere(AttackRadius),
Params
);
#if ENABLE_DRAW_DEBUG
FVector TraceVec = GetActorForwardVector() * AttackRange;
FVector Center = GetActorLocation() + TraceVec * 0.5f;
float HalfHeight = AttackRange * 0.5f + AttackRadius;
FQuat CapsuleRot = FRotationMatrix::MakeFromZ(TraceVec).ToQuat();
FColor DrawColor = bResult ? FColor::Green : FColor::Red;
float DebugLifeTime = 3.0f;
DrawDebugCapsule
(
GetWorld(),
Center,
HalfHeight,
AttackRadius,
CapsuleRot,
DrawColor,
false,
DebugLifeTime
);
#endif
if (bResult)
{
if (HitResult.GetActor()->IsValidLowLevel())
{
UGameplayStatics::ApplyDamage(HitResult.GetActor(), 50.0f, GetController(), this, UDamageType::StaticClass());
}
}
}
피격 시 초록색, 아니라면 붉은 색으로 캡슐을 그리게 된다
마지막으로 공격 시행 시 피해를 입은 대상이 데미지를 받도록 설정하겠다
먼저 ABAnimInstance.h
에 데미지를 받으면 사망할 수 있게 IsDead
변수를 추가해준다
ABAnimInstance.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "ArenaBattle.h"
#include "Animation/AnimInstance.h"
#include "ABAnimInstance.generated.h"
...
class ARENABATTLE_API UABAnimInstance : public UAnimInstance
{
GENERATED_BODY()
public:
...
void SetDeadAnim() { IsDead = true; }
private:
...
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Pawn, Meta = (AllowPrivateAccess = true))
bool IsDead;
}
이제 실질적으로 데미지를 입도록 하겠다
이는 TakeDamage
함수를 통해 가능하다
ABCharacter.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "ArenaBattle.h"
#include "GameFramework/Character.h"
#include "ABCharacter.generated.h"
UCLASS()
class ARENABATTLE_API AABCharacter : public ACharacter
{
GENERATED_BODY()
...
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
virtual void PostInitializeComponents() override;
virtual float TakeDamage(float DamageAmount, struct FDamageEvent const& DamageEvent,
class AController* EventInstigator, AActor* DamageCauser) override;
...
};
ABCharacter.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "ABCharacter.h"
#include "ABAnimInstance.h"
#include "DrawDebugHelpers.h"
...
float AABCharacter::TakeDamage(float DamageAmount, FDamageEvent const& DamageEvent,
AController* EventInstigator, AActor* DamageCauser)
{
float FinalDamage = Super::TakeDamage(DamageAmount, DamageEvent, EventInstigator, DamageCauser);
if (FinalDamage > 0.0f)
{
ABAnim->SetDeadAnim();
SetActorEnableCollision(false);
}
return FinalDamage;
}
IsDead
변수를 블루프린트에서 블랜딩 해줌으로서 마무리하자
(IsDead 변수의 True, False 여부를 통해 블랜딩)