초보 코린이의 성장 일지

UE4 Behavior, AI Range, EQS 본문

언리얼

UE4 Behavior, AI Range, EQS

코오린이 2023. 8. 8. 16:37

Eenmy가 Player를 감지하게 되면 Bow를 장착하였다. 이제는 감지 범위에 들어왔을시 장착 후 화살을 날릴 수 있도록 구현을 해 볼 것이다.

여기서 하나 중요한점은 Player가 Bow로 화살을 날릴때 조건이 AimMode인 상태에서만 발사할 수 있도록 설계가 되어있다. Enemy도 동일하게 AimMode를 넣어줄 것이다.


1. BP에서 사용할 수 있도록 BlueprintReadOnly로 해준다.


1. SubAction을 BP에서 부를수 있도록 BlueprintCallable로 해준다.2

2. Pressed, Released도 동일하게 만들어준다.


1. C++내에 있는 함수를 불러다가 BP에서 처리를해 줄 것이다.

1. C++에있는 함수를 사용하게 될 것임으로, 부모를 콜해줘야한다.

2. 키가 눌렸는지, 키를 땠는지 콜해준다. 두 함수에서 SubAction이 Null인지 이미 체크가 되어있기 때문이다.


1. BP로 만들어준, Equipment를 선택해준다.


1. 아직까지 활을 장착만할뿐, 쏘지는 못한다 이유는 Pressed에 SpringArm과 Camera에서 Enemy는 Null이 뜨기 때문이다. 

2. 이 조건문을 Player, Enemy 둘다 사용이 가능하도록 변경해줘야 한다.

#include "Weapons/SubActions/CSubAction_Bow.h"
#include "Global.h"
#include "AIController.h"
#include "GameFramework/Character.h"
#include "GameFramework/PlayerController.h"
#include "GameFramework/SpringArmComponent.h"
#include "Camera/CameraComponent.h"
#include "Components/CStateComponent.h"
#include "Weapons/Attachments/CAttachment_Bow.h"

void UCSubAction_Bow::Pressed()
{
	CheckTrue(State->IsSubActionMode());

	// Bow를 장착하고 Arrow를 날리기위한 객체가 GetController로 누군지 체크되기 떄문에
	// Enemy가 사용할시 이 조건으로 들어온다.
	if (!!Owner->GetController<AAIController>())
	{
		Super::Pressed();
		State->OnSubActionMode();

		return;
	}

	CheckNull(SpringArm);
	CheckNull(Camera);

	Super::Pressed();

	State->OnSubActionMode();

	OriginData.TargetArmLength = SpringArm->TargetArmLength;
	OriginData.SocketOffset = SpringArm->SocketOffset;
	OriginData.bEnableCameraLag = SpringArm->bEnableCameraLag;
	OriginData.CameraLocation = Camera->GetRelativeLocation();

	SpringArm->TargetArmLength = AimData.TargetArmLength;
	SpringArm->SocketOffset = AimData.SocketOffset;
	SpringArm->bEnableCameraLag = AimData.bEnableCameraLag;
	Camera->SetRelativeLocation(AimData.CameraLocation);

	Timeline.PlayFromStart();  // 타임라인 동작 시작.
}

void UCSubAction_Bow::Released()
{
	CheckFalse(State->IsSubActionMode());

	if (!!Owner->GetController<AAIController>())
	{
		Super::Released();
		State->OffSubActionMode();

		return;
	}

	CheckNull(SpringArm);
	CheckNull(Camera);

	Super::Released();

	State->OffSubActionMode();

	SpringArm->TargetArmLength = OriginData.TargetArmLength;
	SpringArm->SocketOffset = OriginData.SocketOffset;
	SpringArm->bEnableCameraLag = OriginData.bEnableCameraLag;
	Camera->SetRelativeLocation(OriginData.CameraLocation);

	Timeline.ReverseFromEnd(); // 뒤집어 주기.

}

1. GetController로 Enemy가 선택되면 위에 변경된 조건으로 들어가면서 AimMode가 실행된다. 

2. 그게 아닌 Player라면 아래에 조건에 부합하여 실행되기 때문.

1. 장착만하는게 아닌, AimMode가 실행되는걸 확인할 수 있다.


1. Action을 연결시켜준다.

1. Enemy가 Arrow를 날려서 공격하는 모습을 확인할 수 있다.


EQS를 다뤄 볼 것이다. 

1. EQS를 생성해준다. 

2. BP에서 가져와서 사용할 것이다.

1. 중요한거 한가지 매개변수 Instance가 TWeakObjectPtr로 return 해주는 이유는 객체의 위치가 들어올수도 있고, Actor만 들어올 수도 있고 어떠한게 들어올지 모르기 때문에 최상위 객체로 받아서 return 시켜준다.

#pragma once

#include "CoreMinimal.h"
#include "EnvironmentQuery/EnvQueryContext.h"
#include "CEnvQueryContext_Target.generated.h"

UCLASS()
class U2212_06_API UCEnvQueryContext_Target : public UEnvQueryContext
{
	GENERATED_BODY()

private:
	// 내장되어있는 부모 함수 Override 사용
	void ProvideContext(FEnvQueryInstance& QueryInstance, FEnvQueryContextData& ContextData) const override;

};
#include "BehaviorTree/CEnvQueryContext_Target.h"
#include "Global.h"
#include "EnvironmentQuery/EnvQueryTypes.h"
#include "EnvironmentQuery/Items/EnvQueryItemType_Actor.h" // 아래에 Target를 Actor로 받을 것이기 때문에 ItemType.h 필요
#include "Characters/CEnemy_AI.h"
#include "Characters/CAIController.h"
#include "BehaviorTree/BlackboardComponent.h"

void UCEnvQueryContext_Target::ProvideContext(FEnvQueryInstance& QueryInstance, FEnvQueryContextData& ContextData) const
{
	Super::ProvideContext(QueryInstance, ContextData);

	ACEnemy_AI* ai = Cast<ACEnemy_AI>(QueryInstance.Owner.Get());
	// 사용하는 AIController 가져오기, 컨트롤러 안에 Blackboard가 있기 때문에 사용하기 위한 용도.
	ACAIController* controller = ai->GetController<ACAIController>();
	// 공급해줄 Target Key값이 Blackboard에 있어서 가져온다.
	UBlackboardComponent* blackboard =  controller->GetBlackboardComponent();
	AActor* target = Cast<AActor>(blackboard->GetValueAsObject("Target")); // Target을 캐스팅해서 확인.

	UEnvQueryItemType_Actor::SetContextHelper(ContextData, target); // Helper로 return

}

1. Blackboard에 Target Key값을 가져와서 return 받아준다.

1. 위에 사진에 있는건 매개변수 Single를 사용하고 있지만, SetContextHelper 매개변수에 배열로 받는것도 존재한다.

2. 배열로 받는 경우는 EQS로 검사를 해야할 객체들이 여러개가 있을 경우 활용할 수 있다.

3. 여러개로 활용하는 경우에 예로 Enemy가 10개의 객체 사이를 피해서 어딘가에 숨어야 할때 검사를 할 객체가 10개가 됨으로, 배열을 사용할 수 있다.


1. 인바이런먼트를 생성해준다.

1. Circle로 생성해주고, 설정을 해준다.

1. Dot을해서 점수를 매길것이기 때문에, Dot로 생성해준다.

2. Line A에 Line From은 위에서 만들어준 Target이 선택된다.

3. Target -> Querier / Target -> Item으로 내적값을 구한다.

4. Value Max는 0.5로 뒤쪽 검사 제거해준다.

5. Inverse Linear로 뒤집어준다.

1. EQS를 넣은 모습.


1. 위치를 저장할 Location Key를 생성해준다.


1. EQ_Range를 넣어주고, Key는 결과값 저장해줄 Loaction을 선택해준다.

1. 완성된 Behavior Tree.


1. 내적한 점수가 각 구역에서 매겨지고 있는 모습을 확인할 수 있다.


https://www.youtube.com/watch?v=67PpwXqf2VA 

 

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

UE4 Behavior, AI Avoid EQS  (0) 2023.08.09
UE4 Behavior, AI Abort, Range  (0) 2023.08.07
UE4 Behavior, AI Equip  (0) 2023.08.03
UE4 Behavior, AI Hitted  (0) 2023.07.31
UE4 Behavior, AI Action  (2) 2023.07.27
Comments