초보 코린이의 성장 일지

UE4 Behavior, AI Hitted 본문

언리얼

UE4 Behavior, AI Hitted

코오린이 2023. 7. 31. 14:49

Behavior Tree를 통해 Enemy Hitted를 구현해 볼 것이다.

#pragma once

#include "CoreMinimal.h"
#include "BehaviorTree/BTTaskNode.h"
#include "CBTTaskNode_Hitted.generated.h"

UCLASS()
class U2212_06_API UCBTTaskNode_Hitted : public UBTTaskNode
{
	GENERATED_BODY()

public:
	UCBTTaskNode_Hitted();

protected:
	virtual EBTNodeResult::Type ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) override;
	virtual void TickTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, float DeltaSeconds) override;


};
#include "BehaviorTree/CBTTaskNode_Hitted.h"
#include "Global.h"
#include "Characters/CEnemy_AI.h"
#include "Characters/CAIController.h"
#include "Components/CStateComponent.h"

UCBTTaskNode_Hitted::UCBTTaskNode_Hitted()
{
	bNotifyTick = true;

	NodeName = "Hitted";

}

EBTNodeResult::Type UCBTTaskNode_Hitted::ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory)
{
	Super::ExecuteTask(OwnerComp, NodeMemory);

	ACAIController* controller = Cast<ACAIController>(OwnerComp.GetOwner());

	controller->StopMovement(); // Hit시 움직임 off

	return EBTNodeResult::InProgress;

}

void UCBTTaskNode_Hitted::TickTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, float DeltaSeconds)
{
	Super::TickTask(OwnerComp, NodeMemory, DeltaSeconds);

	ACAIController* controller = Cast<ACAIController>(OwnerComp.GetOwner());
	ACEnemy_AI* ai = Cast<ACEnemy_AI>(controller->GetPawn());

	UCStateComponent* state = CHelpers::GetComponent<UCStateComponent>(ai);
	if (state->IsHittedMode() == false)
	{
		// hit가 아니라면 종료.
		FinishLatentTask(OwnerComp, EBTNodeResult::Succeeded);

		return;
	}

}

1. Task Hitted 조건을 체크해주고, Hit가 아니라면 Tick에서 종료시켜준다.


#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "Components/CStateComponent.h"
#include "Characters/ICharacter.h"
#include "CEnemy.generated.h"

UCLASS()
class U2212_06_API ACEnemy
	: public ACharacter
	, public IICharacter
{
	GENERATED_BODY()

protected:
	virtual void Hitted();

public:
	virtual void End_Hitted() override;

};
#include "Characters/CEnemy.h"
#include "Global.h"
#include "CAnimInstance.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "Components/SkeletalMeshComponent.h"
#include "Components/CapsuleComponent.h"
#include "Components/CMontagesComponent.h"
#include "Components/CMovementComponent.h"
#include "Components/CStatusComponent.h"
#include "Weapons/CWeaponStructures.h"

void ACEnemy::Hitted()
{
	// Apply Damage
	{
		Status->Damage(Damage.Power);
		Damage.Power = 0;

	}
	
	//TODO: 데미지 처리
	//TODO: 사망 처리

	// Change Color
	{

		// 맞으면 색 변경
		Change_Color(this, FLinearColor::Red);

		// 데미지 저장
		FTimerDelegate timerDelegate;
		timerDelegate.BindUFunction(this, "RestoreColor");

		GetWorld()->GetTimerManager().SetTimer(RestoreColor_TimerHandle, timerDelegate, 0.2f, false);
	}

	if (!!Damage.Event && !!Damage.Event->HitData)
	{
		FHitData* data = Damage.Event->HitData;

		data->PlayMontage(this);
		data->PlayHitStop(GetWorld());
		data->PlaySoundWave(this);
		data->PlayEffect(GetWorld(), GetActorLocation(), GetActorRotation());

		if (Status->IsDead() == false)
		{
			// 때린 객체 바라보게 만들기.
			FVector start = GetActorLocation();
			FVector target = Damage.Character->GetActorLocation();
			FVector direction = target - start;
			direction.Normalize();

			LaunchCharacter(-direction * data->Launch, false, false); // 밀리는 Launch값
			SetActorRotation(UKismetMathLibrary::FindLookAtRotation(start, target)); // 바라보게 만들기

		}
	} 

	if (Status->IsDead())
	{
		State->SetDeadMode();

		return;
	}

	Damage.Character = nullptr;
	Damage.Causer = nullptr;
	Damage.Event = nullptr;
}

1. Hitted에 관련된 함수들 자식에서 재정의해서 사용할 수 있도록 virtual로 만들어준다.

2. 구조를 보면 부모인 Enemy에서 SetDeadMode로 들어오는 조건이 보일 것이다.

3. Enemy AI에서는 이 조건이 성립된 후 자신의 Hitted가 실행되기 때문에, 재정의해서 사용해야한다.


#pragma once

#include "CoreMinimal.h"
#include "Characters/CEnemy.h"
#include "CEnemy_AI.generated.h"

UCLASS()
class U2212_06_API ACEnemy_AI : public ACEnemy
{
	GENERATED_BODY()

protected:
	void Hitted() override;

public:
	void End_Hitted() override;

};
#include "Characters/CEnemy_AI.h"
#include "Global.h"
#include "Components/CWeaponComponent.h"
#include "Components/CAIBehaviorComponent.h"
#include "Components/WidgetComponent.h"
#include "Components/CStatusComponent.h"
#include "Widgets/CUserWidget_Label.h"

void ACEnemy_AI::Hitted()
{
	Super::Hitted();
	CheckTrue(State->IsDeadMode());

	Behavior->SetHittedMode();

}

void ACEnemy_AI::End_Hitted()
{
	Super::End_Hitted();

	Behavior->SetWaitMode();
}

1. 부모에서 DeadMode라면 실행하면 안되므로, 체크후 모드를 전환해준다.

2. End에서는 Wait로 돌려준다.


1. Hit될 몽타주에서 Hitted로 타입을 변경시켜준다.


수정 전
수정 후

1. Hit가되도 Hitted Task로 들어가질 않는다. 그 이유가 처음 시작할때 루트에서 Wait 상태로 들어와서 아래로 내려오는 구조이다.

2. Wait로 들어오게될때 Wait를 판단할 데코레이터가 없기 때문에, Wait를 Patrol전에 줌으로써 실행 시키게 만들어 준다.


https://www.youtube.com/watch?v=NvMKSK3eiiY 

 

'언리얼' 카테고리의 다른 글

UE4 Behavior, AI Abort, Range  (0) 2023.08.07
UE4 Behavior, AI Equip  (0) 2023.08.03
UE4 Behavior, AI Action  (2) 2023.07.27
UE4 Behavior, AI Equip  (0) 2023.07.26
UE4 Behavior Tree, Patrol  (0) 2023.07.25
Comments