[UE5] Lena: Dev Diary #1 - 블루프린트 OnEvent는 어떻게 만들까?

ChangJin·2024년 6월 6일
0

Unreal Engine5

목록 보기
92/114
post-thumbnail

2024.06.06

깃허브!풀리퀘!
https://github.com/ChangJin-Lee/SimpleShooter https://github.com/ChangJin-Lee/SimpleShooter/pull/2

C++에서 정의한 Blueprint Event를 호출해서 Blueprint에서 사용할 수 있도록 만들었다. 그리고 AI를 가져와서 현재 게임에 적용시키려고 하는데 수 많은 에러가 등장했다. 에러와의 싸움임.

진행상황

  • 적 캐릭터의 이동, 공격 구현
  • 적 캐릭터의 플레이어 추적 구현

미구현

  • 캐릭터 사망 애니메이션
  • 더 다양한 캐릭터가 적으로 등장하기
  • HUD에서 플레이어의 체력이 등장하지 않음



게임 제목

그리고 게임이름은 Lena로 정했다! 주인공 캐릭터 이름을 Lena로 했고 발읍하기 쉽고 캐릭터에 감정 이입을 하기 편하게 하기 위함이다. Lena는 강인하면서도 복잡한 내면을 가진 캐릭터로, 게임 내에서 다양한 도전과 역경을 마주하게 된다.

제목 : Lena: Urban Sniper



요약

두 개의 오브젝트가 있고 이 둘은 의존관계가 없을 때 A 오브젝트에서 값이 변할때 B 오브젝트에서 이 값을 감시하고 있다가 변한 값을 보고 어떤 Action을 취하려고 할때 델리게이트를 많이 쓰는데 이번에는 블루프린트에서 onEvent를 만들어서 특정 값이 변할 때 블루프린트의 Event가 실행되도록 만들었다. Base_character라는 클래스에서 플레이어와 적을 모두 만드려고 하다보니 이런 문제가 생겼다.

캐릭터 cpp와 블루프린트 구조


https://velog.io/@whoamicj/UE5-Bone-%EB%A6%AC%ED%83%80%EA%B8%B0%ED%8C%85-HUD-SimpleShooter-GameMode-%EB%B3%80%EA%B2%BD


우선 저번까지의 진행사항을 보면 플레이어에서 총 꺼내기, 총 발사하기를 구현했다. 이번에는 여기서 적 캐릭터를 구현하고 비헤이비어 트리와 블랙보드를 활용하여 AI 움직임을 만들고자 한다.

AI 이동 구현

SimpleShooter 강의에서 배운 비헤이비어 트리를 가져와서 수치를 수정해서 사용했다.



alsV4의 블루프린트에서 Base_Character를 부모 클래스로 전환하여 사용하고 있기 때문에 블루프린트에서 구현되어 있는 기능을 C++에서 그대로 사용하기가 어려웠다.
alsV4에는 Q 버튼을 눌러서 여러가지 행동을 할 수 있도록 만들어 두었는데 이때 여러 행동을 관리하는 변수가 Overlay State Enum이었다.


이 Overlay state가 어디서 Set 되는지, 어디서 Get 해서 행동을 만들어내고 있는지를 알아내면 뜯어고칠 수 있겠다고 생각했다.


Overlay State Enum

블루프린트 내용을 잘 살펴보니 이 Overlay State를 변경함으로써 행동을 만들어내고 있었다.


캐릭터 블루프린트 구조Select Overlay State 내부

그래서 다음처럼 Overlay가 변경되었을 때 Print String으로 값을 찍어보았다.


Set Overlay 부분에 Print String하기게임을 플레이하면 ViewPort에서 확인 가능

됐다. 이제 이 값을 C++에서 가져와서 변경하면 되겠다. 라고 생각했다. 그래서 다음처럼 C++ 부분에 함수를 선언하고 구현했다.

블루프린트에서 함수 call

UFUNCTION(BlueprintCallable, Category = "OverLay Slot")
void SetOverLayString(FString st);

void ABase_Character::SetOverLayString(FString st)
{
	OverLayString = st;
	UE_LOG(LogTemp, Warning, TEXT("You Select Overlay : %s"), OverLayString);
}

라이브 코딩의 결과는 폭망이었다. 오류가 Crash Reporter가 뜨면서 언리얼이 종료됐다. EXCEPTION_ACCESS_VIOLATION이라고 한다. 대체 왜..


원인은 코드에서 OverLayString의 *를 붙이지 않아서 였다. 다음처럼 적어야 한다.


void ABase_Character::SetOverLayString(FString st)
{
	OverLayString = st;
	UE_LOG(LogTemp, Warning, TEXT("You Select Overlay : %s"), *OverLayString);
}

오케이 다시 라이브 코딩을 해보자.



응 폭망이야. 그래도 조금이나마 희망적인거는 오류 메시지가 달라졌고 메시지 길이가 짧아졌다는 것이다. 오류 조금 나오는 걸로 매우 기뻤다.


이것의 원인은 내가 Blueprint에서 함수를 call해서 로직을 만들어놨는데 그 함수 안에서 부르는 변수의 접근 제한자를 바꿔버려서였다. 정말 찾기 어려웠다.


언리얼 끄고 save - Intermediate 지우고 sln 파일 켜서 다시 컴파일하고를 반복하다가 비정상 종료에서 언리얼이 복구되었을 때는 문제가 없다가 수정한 코드로 라이브 코딩을 하면 문제가 되었다. 내가 어떤 코드를 수정했는지 까먹고 코딩하고 있어서 찾아내기 매우 어려웠다.

수정한 부분에 대해 꼭 기록해두고 커밋을 작은 단위로 쪼개야함을 다시한번 느꼈다.
그리고 블루프린트보다는 C++로 구현해야함도..



절치부심해서 다시 로직을 만들고 멤버 함수 선언하고 구현하고 블루프린트에서 연결했다.




UFUNCTION(BlueprintCallable, Category = "OverLay Slot")
void SetOverLayString(FString st);

void ABase_Character::AdjustOverLay()
{
	// UE_LOG(LogTemp, Warning, TEXT("AdjustOverLay : %d"), WeaponActiveIndex);
	if(OverLayString == "Rifle")
	{
		ToggleIsArmed();
		ChangeWeapon();
	}
	else
	{
		ToggleIsArmed();
		HideAllWeapons();
	}
}

다시 라이브 코딩을 돌렸다. 결과는 또 다시 에러를 내뱉으면서 언리얼이 뻗었다.
이유가 뭘까. 이번에는 어디서 오류났다는 것도 안알려주고 0xffffffffff 주소를 읽어오는데 실패했다고 나온다.

비슷한 에러가 나왔던 사람들의 글이다.

https://www.reddit.com/r/Palworld/comments/1ajqz89/exception_access_violation_fix_the_finals/

https://forums.unrealengine.com/t/ue4-compile-error-0xffffffff/385692

원인은 정확하게 알수없다. 특정 주소를 읽어오는데 NVIDIA 그래픽카드의 문제라는 글도 있고 최신 인텔 CPU에서 일어날 수 있는 문제라는 글도 있다. 현재 쓰고 있는 cpu가 14700k인데 이거때문인가.

다시 캐시제거 - sln 재컴파일 후 재실행을 했다. 그랬더니 오류가 해결됐다. 인텔문제인거같기도하고.



돌고돌아 기능을 완성했고 적 캐릭터가 플레이어를 추적해서 따라올 수 있게 만들었다. 근데 문제가 있었다. 적 AI가 총을 발사하긴 하는데 총을 뽑는 애니메이션을 실행할 수 없었다. 정확히는 총을 아예 들지도 않고 가만히 서서 총 이펙트만 나가고 있었다.


이를 해결하기 위해서 플레이어에서 Event를 만들고 이 이벤트가 실행되면 OVerlay State를 바꾸고 바꾼 값을 적용시키는 로직을 만들었다. 왜 Event를 만들었냐면... AI에서 어떤 값의 변화가 일어났을때 이를 플레이어에서 감시하고 있다가 AI라고 판단하려고하기 때문이다.


void ABase_Character::ChangeWeapon()
{
	for(int32 WeaponIndex = 0 ; WeaponIndex < GunArray.Num(); WeaponIndex++)
	{
		if(WeaponIndex == WeaponActiveIndex)
		{
			GunArray[WeaponIndex]->SetActorHiddenInGame(false);
		}
		else
		{
			GunArray[WeaponIndex]->SetActorHiddenInGame(true);
		}
	}
}

void ABase_Character::HideAllWeapons()
{
	for(int32 WeaponIndex = 0 ; WeaponIndex < GunArray.Num(); WeaponIndex++)
	{
		GunArray[WeaponIndex]->SetActorHiddenInGame(true);
	}
}

void ABase_Character::SetOverLayString(FString st)
{
	OverLayString = st;
	UE_LOG(LogTemp, Warning, TEXT("You Select Overlay : %s"), *OverLayString);
}

void ABase_Character::AdjustOverLay()
{
	// UE_LOG(LogTemp, Warning, TEXT("AdjustOverLay : %d"), WeaponActiveIndex);
	if(OverLayString == "Rifle")
	{
		ToggleIsArmed();
		ChangeWeapon();
	}
	else
	{
		ToggleIsArmed();
		HideAllWeapons();
	}
}

bool ABase_Character::GetIsAICharacter()
{
	return IsAICharacter;
}

void ABase_Character::ToggleIsArmed()
{
	IsArmed = !IsArmed;
}

profile
게임 프로그래머

0개의 댓글