TIL 2025.02.11 기록
0. 개요
오늘은 강의를 마저 듣고, 과제를 계속 진행했다.
어려움이 좀 많았는데, 강의를 빠르게 들으면서 정리를 하다보니..
머리속에서 내용 정리가 아직 안되어 속도가 좀처럼 나지 않았었다.. ㅠ
차라리 강의를 들으면서 코드를 과제 프로젝트에 치면서 진행했더라면.. 이라는 후회도 조금 들었다. ㅠㅠ
그래도 일단.. 오늘 과제를 어떻게 진행할지 무엇무엇을 만들지, 어떤 기능들을 추가해주어야 할지 생각도 모두 마쳤고, 빨리빨리 만들 수 있도록 기본 세팅을 모두 마쳐서 내일 최대한 작업 속도를 올려봐야겠다.
1. 강의 정리
1. 게임 루프 설계를 통한 게임 흐름 제어하기
<게임 모드와 게임 스테이트의 관계>
이전 강의에서 게임 모드는 게임모드 베이스가 아닌 게임 모드를 사용하고, 게임 스테이트는 게임 스테이트 베이스를 사용했더니 경고가 떳다.
두 클래스가 서로 긴밀한 관계이기 때문에 기능을 사용할 때 문제가 발생할 수 있다는 결론이 나서 다시 게임 스테이트를 게임 스테이트로 만들어주었다.
우리는 어제 말한 것 처럼 GameState에 게임 루프를 만들어볼 것이다.
<GameMode가 아닌 GameState로 전역 데이터를 한 이유>
게임 전역 상태를 관리하는 클래스라는 점이다.
또, 우리가 만들 게임은 싱글 플레이 게임이라는 점도 있다.
GameMode에도 게임 루프를 만들 수 있는데, 보통은 서버 전용 로직을 구현할 때 이곳에서 주로 구현한다.
<구현할 로직 설계>
- 게임 시작
ㄴ> 그중 베이직 레벨을 열어주어야 한다.
GameState의 BeginPlay에서 Level을 열어 줄 것이다. - 레벨에서 40개의 아이템이 랜덤한 위치로 소환이 된다.
ㄴ> 레벨에서 코인을 다 먹었다면, 시간제한 상관없이 즉시 다음 레벨로 넘어감
ㄴ> 한 레벨당 30초의 시간이 주어짐
ㄴ> 타임오버가 되도 다음 레벨로 넘어감
ㄴ> 체력이 0이 되면, 바로 즉시 게임 오버
ㄴ> 게임 종료 이후 메뉴창에 UI를 띄움
<월드에 존재하는 액터 가져오기>
UGameplayStatics::GetAllActorsOfClass(GetWorld(), ASpawnVolume::StaticClass(), FoundVolumes);
위 코드는 월드에 존재하는 모든 SpawnVolume액터들을 가져온다.
즉, 월드에 존재하는 특정 클래스 액터들을 가져올 수 있다!
<액터가 어떤 클래스로 만들어졌는지 확인하는 방법>
SpawnedActor->IsA(ACoinItem::StaticClass())
물론 Cast<>()를 이용해도 되지만, 이런 용이한 함수가 있다는 것을 알게되었다.
<레벨 전환을 해주는 언리얼 함수>
UGameplayStatics::OpenLevel(GetWorld(), LevelMapNames[CurrentLevelIndex]);
레벨 전환은 이렇게 레벨의 이름으로 찾아서 열어주면 된다.
<Game Instance>
레벨이 전환될 때마다 게임스테이트가 계속 새로 만들어지기 때문에 우리가 계속 갱신해 사용하는 데이터들이 소실된다.. 때문에 이러한 데이터들은 Game Instance 라는 곳에 저장하면 된다.
Game Instance란
게임 전체에서 계속 유지되는 싱글톤 객체로 우리가 레벨 전환을 하고나서도 데이터를 유지해야할 때 사용한다.
2. UI 위젯 설계와 실시간 데이터 연동하기
<HUD>
HUD (Heads Up Display) 란
게임의 정보를 시각화해서 보여주는 것을 말한다.
언리얼에서는 2가지 HUD를 지원한다.
- Canvas 기반 HUD
- HUD라는 클래스가 있어서 택스트를 2D 형태로 그려서 만드는 방식이다.
- 조금 옛 방식이라고 생각하면 된다고 하신다.
- UMG (Unreal Motion Graphics)
- 요즘엔 보통 UMG로 HUD를 만든다.
- Widget Blueprint를 이용해 HUD를 설계한다.
(우리는 UMG를 제작해볼 것이다.)
<Widet Blueprint>
우선 위젯 블루프린트는 콘텐츠 브라우저에서 우클릭을해 유저 인터페이스에서 만들 수 있다.
디자이너 탭은 우리가 UI를 디자인하는 공간이다.
그래프쪽이 UI에다가 정보를 실시간으로 연동시킬 수 있는 곳이다.
패널쪽에 캔버스 패널이 있는데, 이게 우리가 주로 사용할 것이고, 음.. 대충 UI를 그릴 큰 도화지라고 생각하면 된다.
보통 사용하는 화면인 1920 X 1080으로 보려면 화면 크기 - > 모니터 -> 21.5-24를 선택하면 된다.
<사진>
이제, 팔래트 위에 택스트를 출력해야하는데, 일반탭에 택스트가 있다.
폰트는 기본적으로 ROBOTO밖에 없다.
때문에 폰트를 변경하려면 폰트를 임포트 해주면 된다.
출력할 메세지의 경우 우선 디자인에서는 임시로 입력해주는 것이고, 나중에 코드상에서 직접 텍스트를 전달해 만들게 될 것이다.
이렇게 만든 HUD는 플레이어 컨트롤러에 배치해야 한다. 왜냐하면, 플레이어 컨트롤러가 입력을 받아 플레이어를 처리하기 때문이다.
즉, 화면상이긴 하지만 플레이어로부터 입력을 받아 화면상에 보여주는 것이기 때문이다.
<위잿관련 함수>
CreateWidget<>()은 UI위잿 인스턴스를 생성하는 함수이다.
HUDWidget->AddToViewport()는 Viewport 상에 랜더링을 해달라는 것이다.
<빌드를 위해 해야할 일>
문제는 이렇게만 했을 때 빌드가 안될 것이다.
// bulid.cs 파일 내
PublicDependencyModuleNames.AddRange(new string[] { "Core",
"CoreUObject",
"Engine",
"InputCore",
"EnhancedInput" });
bulid.cs 파일을 열어서 해주어야 한다. bulid.cs 파일은 C#으로 이루어져있고, 모듈의 빌드 규칙을 설정해주는 파일이다.
ㄴ> 때문에 컴파일러가 빌드할 때, 어떤 모듈을 포함할 건지를 설정해주는 곳이다.
지금 우리가 원래 포함하던 모듈들은 이렇게 총 5개이다. 이곳에 UMG를 추가해주어야 한다.
HUD나 UI등등은 모두 UMG(Unreal Motion Graphics)라는 모듈안에 다 들어가 있기 때문에 빌드할 모듈에 추가를 해주어야 하는 것이다.
<텍스트 프로퍼티에 데이터 전달 방법>
이제 UI 항목들에 데이터들을 전달해 알맞은 텍스트들을 보여줄 것이다.
데이터 전달 방법은 크게 2가지 있는데,
- 데이터 바인딩 방법
- 코드를 짜지 않고 BluePrint 그래프 상에서 코딩하는 방법이다.
- 때문에 초보자들에게 용이하다.
- 방식이 직관적이고, 편리해서 많이 사용한다.
- 하지만 단점이 있는데, 바인딩 함수는 항상 tick을 돌리기 때문에 우리가 원하는 시점마다 갱신을 시켜주지 못한다.
- 즉, 최적화를 해줄 수 없다.
- 직접 코드로 작성해 사용하는 방법
<데이터 바인딩 방법을 활용해 전달하기>
텍스트의 옆에 버튼을 눌러서 바인딩 생성을 해준다.
이후 노드들을 이용해 데이터를 전달해주면 된다.
3. 게임 흐름에 맞춘 메뉴 UI 구현하기
HUD UI 개발에 이어서 MenuUI, ButtenUI를 구현해볼 것이다.
<보더>
<사진>
이때, 어제와 다르게 UI화면 뒤를 조금 어둡게 만들기 위해서 보더를 사용해주었다.
<사진>
UI를 만들때, 계층 구조를 잡아가면서 만들 수 있다.
<위잿 관련 함수>
RemoveFromViewport()
를 이용해 붙여두었던 UI를 제거할 수 있다.
bShowMouseCursor = true;
마우스가 뷰포트에 보이거나 보이지 않게 설정 해준다.
SetInputMode(FInputModeUIOnly());
FInputModeUIOnly라는 구조체를 전달함으로써 UI에만 마우스 포커싱이 잡히도록 할 수 있다.
즉, UI가 열려있다면, 마우스를 회전해도 게임의 플레이어 카메라는 회전하지 않도록 설정해주는 것이다.
<게임 진행도 초기화시 주의할 점>
StartGame의 경우엔 게임이 완전 새로 시작하는 것이기 때문에 GameInstance에 있던 전역 정보들까지 초기화를 해주어야 한다.
<UI에 함수 바인딩 하기>
아직 버튼같은 곳에 만든 함수를 바인딩하지 않았는데, 이는 버튼쪽에 변수 여부를 체크해주면
아래쪽에 이밴트들을 추가할 수 있도록 생긴다.
이제 PlayerController의 Beginplay()에서 메뉴들을 직접 생성해주는게 아니라 현재 Level이 메인 메뉴 Level 이라면, 메인 메뉴만 띄워주면 된다.
ㄴ> 나머지 로직들은 모두 다른 곳으로 옮겼기 때문이다.
2. 과제
1. 초기 설정하기
<Git Repository 만들기>
GitHub - Blanquette0315/Sparta_Homework_08: Sparta_Homework_08
Sparta_Homework_08. Contribute to Blanquette0315/Sparta_Homework_08 development by creating an account on GitHub.
github.com
<사용할 에셋, 플러그인 등 프로젝트에 임포트하기>
- 드론 (이전 과제에서 구현한 드론)
- Blockout Tools Plugin
- 각종 함정들 (?) (이전이전 과제에서 구현한 것들)
플러그인도 처음 넣어보았다..!!
또 드론의 경우 움직임이 생각보다 별로였어서.. 동작방식을 좀 수정해주었다..
<간단한 레벨 만들기>
1차 구현..!
많이 어둡긴한데, 저 넓은 공간에 아이템들이 스폰되도록 설정해 배치할 것이고,
통로쪽에는 유리 머티리얼을 사용해 넘어갈 수 있는 벽 갈 수 없는 벽등의 함정도 배치할 수 있을 것이다.
즉! 웨이브? 마다 여러 장애물도 충분히 넣을 수 있다..!!
원래 지금까지는 기능구현을 모두 마치고 레벨을 마지막에 진행했는데, 이번에는 레벨을 어느정도 만들어 두어야 할 것 같았다.
왜냐하면, 스폰 영역을 잡아서 스폰을 해야하고, 레벨이 3개까지 있어야하면서... 멀티 웨이브까지 구현해야하기 때문이다... ㅠ
멀티 웨이브의 경우에는 레이스 게임의 트랙처럼 한 바퀴를 돌았다면, 그것을 트리거로 진행해주면 어떨까 생각중이다.
(이전 강의에서는 단순히 타임어택으로 진행했었다.)
<아이템 만들기>
- 회복 아이템
- 코인 아이템
- 큰 코인
- 작은 코인
- 공중 지뢰
- 아이템 스포너
3. 마무리
으아.... 초반 설정하는게 생각보다 오래걸렸다.. 마감일 다가오는데 오늘도 더 남아서 과제를 더 진행해야할 것 같다..