// Fill out your copyright notice in the Description page of Project Settings.


#include "BTService_Detect.h"
#include "HamsterDemoCharacter.h"
#include "MonsterAIController.h"
#include "BehaviorTree/BlackboardComponent.h"
#include "DrawDebugHelpers.h"

UBTService_Detect::UBTService_Detect()
{
	NodeName = TEXT("Detect");
	Interval = 1.0f;
}

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

	APawn* ControllingPawn = OwnerComp.GetAIOwner()->GetPawn();
	if (ControllingPawn == nullptr)
	{
		UE_LOG(LogTemp, Log, TEXT("Detect Failed - ControllingPawn nullptr"));
		return;
	}

	UWorld* World = ControllingPawn->GetWorld();
	FVector Center = ControllingPawn->GetActorLocation();
	float DetectRadius = 800.0f;

	if (World == nullptr)
		return;

	TArray<FOverlapResult> OverlapResults;
	FCollisionQueryParams CollisionQueryParam(NAME_None, false, ControllingPawn);
	bool bResult = World->OverlapMultiByChannel(
		OverlapResults,
		Center,
		FQuat::Identity,
		ECollisionChannel::ECC_GameTraceChannel3,
		FCollisionShape::MakeSphere(DetectRadius),
		CollisionQueryParam
	);

	if (bResult)
	{
		for (auto OverlapResult : OverlapResults)
		{
			AHamsterDemoCharacter* HamsterDemoCharacter = Cast<AHamsterDemoCharacter>(OverlapResult.GetActor());

			if (HamsterDemoCharacter && HamsterDemoCharacter->GetController()->IsPlayerController())
			{
				OwnerComp.GetBlackboardComponent()->SetValueAsObject(AMonsterAIController::TargetKey, HamsterDemoCharacter);
				DrawDebugSphere(World, Center, DetectRadius, 16, FColor::Green, false, 0.2f);
				DrawDebugPoint(World, HamsterDemoCharacter->GetActorLocation(), 10.0f, FColor::Blue, false, 0.2f);
				DrawDebugLine(World, ControllingPawn->GetActorLocation(), HamsterDemoCharacter->GetActorLocation(), FColor::Blue, false, 0.2f);
				return;
			}
		}
	}
	else
	{
		OwnerComp.GetBlackboardComponent()->SetValueAsObject(AMonsterAIController::TargetKey, nullptr);
	}

	DrawDebugSphere(World, Center, DetectRadius, 16, FColor::Red, false, 0.2f);
}

플레이어가 일정 범위 내에 있을 때 이를 감지하여 플레이어를 따라오는, BTService 를 기반으로 한 Detect 

 

프로젝트 폴더의 config ->DefaultEngine.ini 에서 오브젝트에 배정된 트레이스 채널을 확인할 수 있다.

 

 

'[햄] 작업일기' 카테고리의 다른 글

230817 Blackboard, Behavior Tree  (0) 2023.08.17
230816 AI  (0) 2023.08.16
230805  (0) 2023.08.06
230804  (0) 2023.08.04
230801  (0) 2023.08.01

비헤이비어 트리에서 태스크는 독립적으로 실행될 수 없고, 반드시 컴포짓 노드Composite Node 를 거쳐 실행되어야 함.

컴포짓 노드에는 대표적으로 셀렉터Selector시퀀스Sequence가 있음.

- Sequence: 연결된 Task들을 false 결과가 나올 때까지 왼쪽에서 오른쪽으로 수행함

- Selector: 지정된 조건을 기준으로 조건에 맞는 노드를 실행함

 

비헤이비어 트리는 태스크를 실행할 때 태스크 클래스의 ExecuteTask라는 멤버 함수를 실행하는데, ExecuteTask는 다음 넷 중 하나의 값을 반환해야 함.

- Aborted: 태스크 실행 중 중단됨. 결과적으로 실패함.

- Failed: 태스크를 수행했지만 실패함

- Succeeded: 태스크를 성공적으로 수행함.

- InProgress: 태스크를 계속 수행하는 중임. 실행 결과는 향후 알려줄 예정임.

ExecuteTask 함수의 실행 결과에 따라 컴포짓 내의 다음 태스크를 계속 수행할지, 중단할지 여부가 결정됨. 현재 사용 중인 시퀀스 컴포짓은 자신에 속한 태스크를 실패할 때까지 계속 실행함.

 

 

'[햄] 작업일기' 카테고리의 다른 글

230820  (0) 2023.08.20
230816 AI  (0) 2023.08.16
230805  (0) 2023.08.06
230804  (0) 2023.08.04
230801  (0) 2023.08.01

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

C++ Linking

liking: source(C++) file to~ actual executable binary

compilation = compile + link

find where each symbol is & link them together
files are actually seperated, so we need to link them together in one program -> purpose of linker
분리되어있지 않은 한 파일의 코드일지라도 main() 을 찾아서 시작해야 하므로 사실상 main 과 다른 function들을 링크해야 함

LNK error = linking error

static function() >> this function is only declared for this unit

if cannot find the exact one >> linking error!

 

'끄적끄적' 카테고리의 다른 글

Insertion Sort (C++)  (0) 2022.05.01
Insertion Sort (Lisp)  (0) 2022.05.01
Iterative Quicksort (C++)  (0) 2022.05.01

https://velog.io/@jm450_/UE4-AI-Controller%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%9C-AI%EA%B5%AC%ED%98%84

 

[UE4] AI Controller를 사용한 AI구현

언리얼 엔진에서 캐릭터는 controller의 지배를 받는다. 이를 빙의(Poss)라고 부른다.player character가 만들어지게 되면 player controller가 종속적으로 만들어지게 되고 player character는 player controller에

velog.io

 

AIController 에서

블랙보드, 비헤이비어 트리 만들고 생성자에서 연결해주기

OnPossess -> 빙의 시 실행되는 함수 / OnUnpossess -> 빙의 해제 시 실행되는 함수

RunAI() -> AI를 실행시켜 주는듯 (블랙보드랑 트리)

StopAI() -> AI 실행 정시

 

MonsterCharacter의 생성자에서

	AIControllerClass = AMonsterAIController::StaticClass();
	AutoPossessAI = EAutoPossessAI::PlacedInWorldOrSpawned;

AI컨트롤러 클래스를 AMonsterAIController로 지정하고

( Type::StaticClass() 를 사용하면 컴파일 타임에서 UClass 타입의 정보를 얻어온다. GetClass는 런타임에서 객체의 클래스를 조회할 때 사용되므로 StaticClass() 와 GetClass() 의 결과는 다를 수 있다. )

PlacedInWorldOrSpawned -> 레벨에 배치되거나 새롭게 스폰되는 MonsterCharacter 마다 MonsterAIController 가 생성되고, 플레이어의 조종을 받는 캐릭터가 아니면 AIController의 지배를 받음.

 

 

'[햄] 작업일기' 카테고리의 다른 글

230820  (0) 2023.08.20
230817 Blackboard, Behavior Tree  (0) 2023.08.17
230805  (0) 2023.08.06
230804  (0) 2023.08.04
230801  (0) 2023.08.01

Projectile 에서

총을 쏨 > 총알이 어딘가에 맞았음(안사라지고) > 맞은 물체를 가져와서 > 맞은 물체가 떄릴수있는애면 > 데미지를 줘 
OnFire > OnHit > HitResult.GetActor > if(Damagable) > HitResult의 TakeDamage 호출

 

OtherActor = 맞은 애(hit result)

 


"비정적 멤버 참조는 특정 개체에 상대적이어야 합니다" 오류

인스턴스를 생성하지 않고 호출해서 생기는 오류이다

https://softwareengineering.stackexchange.com/questions/284088/why-static-methods-cant-call-non-static-methods-directly

 

Why static methods can't call non-static methods directly?

I don't understand why most programming language don't allow developers/users to call non-static method from static method directly? What is the reason behind it? p.s. I know, you can create objec...

softwareengineering.stackexchange.com


 

 

'[햄] 작업일기' 카테고리의 다른 글

230817 Blackboard, Behavior Tree  (0) 2023.08.17
230816 AI  (0) 2023.08.16
230804  (0) 2023.08.04
230801  (0) 2023.08.01
230725 DataTable을 출력하기  (0) 2023.07.25

캐릭터의 캡슐컴포넌트의 콜리전 프리셋을 HamsterCharacter로 변경해줌

 

프리셋 설정

 


하다 보니까 firstperson 에 구현되어있는 기존의 projectile을 활용하려면 좀 다른 방법을 찾아야 할 것 같았다.

https://velog.io/@yoo06/%EC%96%B8%EB%A6%AC%EC%96%BC-%EC%97%94%EC%A7%84-HitScan%EA%B3%BC-Projectile-%EB%B9%84%EA%B5%90

 

언리얼 엔진 HitScan과 Projectile 비교

언리얼 엔진에서의 HitScan 과 projectile 방식의 비교

velog.io

 

https://docs.unrealengine.com/4.26/ko/ProgrammingAndScripting/ProgrammingWithCPP/CPPTutorials/FirstPersonShooter/3/4/

 

3.4 - 월드와 상호작용하는 프로젝타일

일인칭 슈팅 프로젝트에서 프로젝타일이 월드와 상호작용하도록 하는 법을 배워봅니다.

docs.unrealengine.com

 

피격 판정을 낼 때는 두 가지 방법이 있다

- Projectile 투사체

바람, 중력 등의 영향을 받는 현실적인 총알, 연산 多, 부하 ↑

- HitScan 

Line Trace(RayCast), 연산 少, 부하 ↓

 

Projectile에 이미 OnHit가 구현되어 있으니 OnHit에 TakeDamage를 추가하면 되지 않을까?

 

 

Character의 Controller 를 받아와서 Projectile 에 넘겨줘야 함 (takedamage에서 사용) 

'[햄] 작업일기' 카테고리의 다른 글

230816 AI  (0) 2023.08.16
230805  (0) 2023.08.06
230801  (0) 2023.08.01
230725 DataTable을 출력하기  (0) 2023.07.25
230721  (0) 2023.07.21

projectile 총 쏘는 거 트레이스 채널 확인 / attack 채널 확인 / 데미지 프레임워크 확인 //

 

콜리전과 트레이스 채널에 대한 메모

물리 엔진의 활용을 위해서는 콜리전Collision이라는 물리적 충돌 영역을 설정해야 함 
언리얼 엔진에서 콜리전의 제작 방법은 다음과 같음.
- 스태틱메시 애셋: 스태틱메시 애셋에 콜리전 영역을 심을 수 있음. 에디터에서 설정 가능. 설정 시 스태틱메시 컴포넌트에서 비주얼과 충돌 두 가지 기능을 설정하여 관리할 수 있음.
- 기본 도형Primitive 컴포넌트: 구체, 박스, 캡슐 등의 기본 도형으로 충돌 영역을 지정함. 스켈레탈 메시를 움직일 때 주로 사용함.
- 피직스 애셋: 특정 상황에서 캐릭터의 관절이 흐느적거리는 Ragdoll 효과를 구현할 때 사용함 (일반적으로 캐릭터의 이동은 캡슐 컴포넌트를 사용). 스켈레탈 메시에만 사용 가능함.

플레이어의 상호작용을 가능하게 하려면 물리 설정을 알아야 함. 
1. 콜리전 채널과 기본 반응
2. 콜리전 채널의 용도
3. 다른 콜리전 채널과의 반응

 

충돌체에는 반드시 하나의 콜리전 채널을 설정해야 함. 콜리전 채널은 액터의 종류에 따라 부여되는 컴포넌트가 달라짐.
- WorldStatic: 움직이지 않는 정적인 기본 액터에 사용함. 주로 스태틱메시 액터에 있는 스태틱메시 컴포넌트에 사용함.
- WorldDynamic: 움직이는 액터에 사용함. 블루프린트에 속한 스태틱메시 컴포넌트에 사용함. 
- Pawn: 플레이어가 조종하는 물체에 사용함. 캐릭터의 충돌을 담당하는 캡슐 컴포넌트에 설정.
- Visibility: 배경 물체가 시각적으로 보이는지 탐지하는 데 사용함. 탐지에서 폰은 제외됨. 마우스롤 물체를 선택하는 피킹Picking 기능을 구현할 때 사용함.
- Camera: 카메라 설정을 위해 카메라와 목표물 간에 장애물이 있는지 탐지하는 데 사용함. (GTA 방식의 뷰를 쓸 때 장애물이 캐릭터를 가리면 카메라를 장애물 앞으로 줌인하는 기능에 Camera 채널이 사용되었음)
- PhysicsBody: 물리 시뮬레이션으로 움직이는 컴포넌트에 설정함.
- Vehicle: 탈 것에 사용
- Destructible: 부서질 수 있는 액터에 사용

 

폰의 CapsuleComponent > Collision Enabled 에서
- Query: 두 물체의 충돌 영역이 서로 겹치는지 테스트하는 설정. 충돌 영역이 겹치면(오버랩Overlap) 관련 컴포넌트에 BeginOverlap 이벤트가 발생함. 지정 영역에 물체가 충돌하는지 탐지하는 레이캐스트Raycast와 스윕Sweep도 Query에 속함. 
- Physics: 물리적인 시뮬레이션을 사용할 때 설정.
- Query and Physics: 두 기능을 모두 사용하는 설정.


액터마다 필요한 기능을 파악해 Query와 Physics 설정을 구분하여 지정하는 것이 효과적임. 
콜리전 채널 간의 반응은 다음과 같음
- 무시Ignore: 콜리전이 있어도 충돌 X
- 겹침Overlap: 물체가 뚫고 지나갈 수 있지만 이벤트 발생 X
- 블록Block: 물체가 뚫고 지나가지 못하도록 막음

 

오브젝트 채널: 콜리전 영역에 지정하는 콜리전 채널 (WorldStatic,WorldDynamic, Pawn, PhysicsBody, Vehicle, Destructible)
트레이스 채널: 어떤 행동에 설정하는 콜리전 채널 (Visibility, Camera)
공격이란 공격 애니메이션이 일어나는 특정 타이밍에 공격 범위 안에 위치한 액터가 있는지 감지하고 감지된 액터에게 대미지를 전달하는 행위라고 할 수 있음
어떠한 행동에 대한 물리 판정이 필요할 때도 물리 엔진을 활용 가능함
행동 판정을 위한 트레이스 채널이 제공됨.


트레이스 채널의 활용 예시)
프로젝트 설정 > Collision > 트레이스 채널에 Attack을 추가, 기본 반응을 무시로 설정 
Preset 설정 > 앞서 제작한 콜리전 프리셋 ABCharacter에서 Attack 트레이스 채널과의 설정은 블록으로 지정하면
Attack 트레이스 채널을 사용하는 액터의 물리적 행동은, 캐릭터의 캡슐 컴포넌트에 설정한 ABCharacter 콜리전 프리셋에만 반응하게 됨.

공격 판정을 내리는 로직을 위해 트레이스 채널을 사용해 물리적 충돌 여부를 가리는 함수인 SweepSingleByChannel을 사용.
기본 도형을 인자로 받은 후 시작 지점에서 끝 지점까지 쓸면서sweep 해당 영역 내에 물리 판정이 일어났는지 조사함.
위 함수의 인자로 넣은 요소들
- HitResult: 물리적 충돌이 탐지된 경우 관련된 정보를 담을 구조체
- Start: 탐색을 시작할 위치
- End: 탐색을 끝낼 위치
- Rot: 탐색에 사용할 도형의 회전
- TraceChannel: 물리 충돌 감지에 사용할 트레이스 채널 정보
- CollisionShape: 탐색에 사용할 기본 도형 정보. 구체, 캡슐, 박스 사용
- Params: 탐색 방법에 대한 설정 값을 모아둔 구조체
- ResponseParams: 탐색 반응을 설정하기 위한 구조체


언리얼 엔진은 게임에서 활용할 수 있도록 총 32개의 콜리전 채널을 제공함.
32개 중 8개는 언리얼 엔진이 기본으로 사용, 6개는 엔진에서 다른 용도로 사용하도록 예약되어있음.
게임 프로젝트에서 우리는 18개만 사용 가능함. 
게임 프로젝트에서 새로 생성하는 오브젝트 채널과 트레이스 채널은 ECC_GameTraceChannel 1번부터 18번까지 중 하나를 배정받음.
이는 프로젝트의 Config 폴더의 DefaultEngine.ini에서 확인 가능함.

콜리전 채널 지정 > FCollisionShape::MakeSphere 함수로 탐지에 사용할 도형 제작 > 도형의 탐색 영역 지정 > 탐색 방법 설정 > 탐색 반응 설정 > 액터 충돌 탐지 시 해당 액터에 대한 정보를 얻기 위해 구조체를 넘겨줌

언리얼 실행 환경은 메모리에 떠 있는 언리얼 오브젝트가 사용 중인지 아닌지를 주기적으로 검사한 후 사용하지 않는 물체는 메모리에서 자동으로 제거함 (가비지 콜렉션Garbage Collection)
오브젝트의 사용 여부는 다른 오브젝트가 해당 오브젝트를 참조하는지로 판단함.
FHitResult의 멤버 변수 Actor의 선언이 일반 참조로 선언된다면 해당 함수에서의 참조로 제거되어야 할 액터가 메모리에 그대로 남아있는 문제가 발생함.
해당 문제를 방지하기 위해 FHitResult는 참조로부터 자유롭게 포인터 정보를 전달해주는 약 포인터TWeakObjectPtr 방식으로 멤버 변수를 선언함.
약 포인터로 지정된 액터는 IsValid 함수를 사용해 해당 액터가 유효한지 점검한 후 사용해야 함.

디버그 드로잉Debug Drawing을 활용하여 공격 범위 파악 등의 디버깅을 로그를 보지 않고도 할 수 있음.

언리얼 엔진이 제공하는 대미지 프레임워크를 사용하면 대미지에 관련된 여러 기능을 처리할 수 있음.
언리얼 엔진의 액터 클래스 AActor는 TakeDamage 함수가 구현되어 있음.
- DamageAmount: 전달할 대미지의 세기
- DamageEvent: 대미지 종류
- EventInstigator: 공격 명령을 내린 가해자
- DamageCauser: 대미지 전달을 위해 사용한 도구
대미지 전달 행위에는 피해를 입히는 주체인 가해자와 대미지를 받는 피해자가 있음.
대미지를 가한 진정한 가해자는 폰이 아니라 폰에게 명령을 내린 플레이어 컨트롤러이므로 EventInstigator에는 컨트롤러의 정보를 보내야 함.

액터 설정의 Can be Damaged 속성을 해제하면 대미지의 결과가 모두 0이 되는 무적 상태가 됨.

무기는 캐릭터에 트랜스폼으로 배치하는 것이 아니라 메시에 착용해야 애니메이션에 따라 움직임.
언리얼 엔진은 캐릭터에 무기 등을 부착하기 위해 소켓 시스템을 제공함.

액터에 고정으로 무기를 장착하지 않고 필요에 따라 무기를 교체할 수 있게 하려면 무기를 액터로 분리해 만드는 것이 좋음.

 


 

Projectile은 기본 반응이 블록으로 되어 있음

 

대충 추가한 것들...

 

'[햄] 작업일기' 카테고리의 다른 글

230805  (0) 2023.08.06
230804  (0) 2023.08.04
230725 DataTable을 출력하기  (0) 2023.07.25
230721  (0) 2023.07.21
0718 대화 내용을 엑셀 데이터로 불러오기  (0) 2023.07.18

https://wergia.tistory.com/154#google_vignette

 

[UE4] Programming - 데이터 테이블(Data Table)

데이터 테이블(Data Table) 사용하기 작성 기준 버전 :: 4.21.1 게임을 제작할 때 레벨업에 필요한 경험치량이나 스킬의 계수 등 추후에 밸런스 수정 작업이 필요한 값들은 함부로 코드에 상수로 넣어

wergia.tistory.com

https://algorfati.tistory.com/69

 

[Unreal] 테이블 데이터 이용 방법 (DataTable)

Data Table 대량의 데이터를 table 형태로 관리해야하는 상황도 생길 수 있다. 이런 경우 unreal engine의 data table을 사용하면 유용하다. 소스코드 #include "Engine/DataTable.h" USTRUCT() struct FPhysicsPoseData : public

algorfati.tistory.com

 

 

UHamsterGameInstance::UHamsterGameInstance()
{
	FString DialogueDataPath = TEXT("/Game/FirstPerson/Data/HamsterDialogueSample2.HamsterDialogueSample2");
	static ConstructorHelpers::FObjectFinder<UDataTable> DT_HamDialogue(*DialogueDataPath);
	if (DT_HamDialogue.Succeeded())
	{
		UE_LOG(LogTemp, Log, TEXT("Dialogue TableData succeeded"));
	}
	else
	{
		UE_LOG(LogTemp, Log, TEXT("Dialogue TableData not succeeded"));
		return;
	}

	HamsterDialogueTable = DT_HamDialogue.Object;
	HamsterDialogueTable->GetAllRows("", OUT DialogueArray);
}

FHamsterDialogueData* UHamsterGameInstance::GetDialogueData(int index)
{
	if (DialogueArray.Num() <= index)
	{
		return HamsterDialogueTable->FindRow<FHamsterDialogueData>(FName(*(FString::FormatAsNumber(index))), TEXT(""));
	}
	else
	{
		return DialogueArray[index];
	}
} //항목 하나씩 가져오는 함수


TArray<FHamsterDialogueData*> UHamsterGameInstance::GetAllDialogueData()
{
	return DialogueArray;

} //DialogueArray 전체를 가져오는 함수

 

void ATalkableObject::BeginPlay()
{
	auto HamsterGameInstance = Cast<UHamsterGameInstance>(UGameplayStatics::GetGameInstance(GetWorld()));
	DialogDatas = HamsterGameInstance->GetAllDialogueData();

    .
    .
    .
}

bool ATalkableObject::BeginInteract(UHamsterInteractorComponent* InteractorComponent)
{
	if (!TalkPopup->IsVisible()) //뷰포트에 없으면 위젯 띄우기
	{
		TalkPopup->AddToViewport();
	}

	if (CurrentDialogIndex >= DialogDatas.Num())
	{
		CurrentDialogIndex = 0;
		
		if (TalkPopup->IsVisible())
		{
			UE_LOG(LogTemp, Log, TEXT("talkable end interact - close talkpopup"));
			TalkPopup->RemoveFromParent();
		}
	}
	else
	{
		TextName->SetText(FText::FromString(DialogDatas[CurrentDialogIndex]->Name));
		TextLine->SetText(FText::FromString(DialogDatas[CurrentDialogIndex++]->Line));
	}

	return true;
}

 

 

인자를 맞게 쓴 것 같은데 함수가 안 되는 것 같으면 냅다 포인터 갈겨보기 / 포인터 빼보기 ㅠ

 

게임인스턴스는 레벨 내내 계속 살아있는데 토커블의 다이얼로그를 가지고 있으면 웃김
나중에 빼야 할듯?
게임인스턴스((((((토커블 관리용 클래스 ))))))))))))) 느낌으로

 

어디가 되고 어디가 명확하게 안 되는지 디버깅으로 확실하게 파악하기!!!!!

 

 

'[햄] 작업일기' 카테고리의 다른 글

230804  (0) 2023.08.04
230801  (0) 2023.08.01
230721  (0) 2023.07.21
0718 대화 내용을 엑셀 데이터로 불러오기  (0) 2023.07.18
230717 대화 진행, EndInteract 키 변경  (0) 2023.07.17

+ Recent posts