안녕하세요! 어제는 Unreal 프로젝트 마무리 짓느라 글을 못 적었습니다ㅠㅠ
따라서 오늘 어제 내용까지 함께 작성하도록 하겠습니다! (오늘 메뉴는 고봉밥으로 준비했습니다. ㅎㅎ)
일단은 적부터 마무리 했습니다.

Custom Event 노드를 생성해 MoveRandom으로 이름을 설정해주고 CanMove(bool), IsChasing(bool) 변수를 만들어 현재 레벨을 순찰해도 되는 상태인지, 그리고 해당 액터가 플레이어 캐릭터를 추적하고 있는 상태인지 확인 했습니다.
이후 AI MoveTo 노드를 생성해서 Self 노드를 연결해 자기 자신이 어떤 액터인지 입력해주고 Get Actor Location에서 Get Random Reachable Point in Radius를 통해 현재 위치에서 순찰할 도착 지점을 무작위(Random)로 정할 수 있도록 범위를 지정 했습니다.
그리고 도착 지점에 도달하면 바로 다음 순찰 지점을 정하는 것이 아닌 잠시 멈춰있다가 순찰할 수 있도록 해당 값도 Random Float in Range 노드로 Delay 시간을 정해주고 그 값을 Delay 노드에 연결했습니다.
마지막으로 다시 MoveRandom Event를 재귀해 반복할 수 있도록 구현하였습니다.
이제 EventBeginPlay에서 MoveRandom을 연결해주면 게임 시작 후 자동으로 MoveRandom Event가 실행되는 것을 확인할 수 있습니다.
**현재 이미지에서 MoveRandom 시작 지점에 CanMove 값을 확인하는 Branch노드 연결이 해제 되어있는 실수가...
MoveRandom(CustomEvent) 노드와 CanMove 변수를 Condition 값으로 갖는 Branch가 연결 되어있어야 합니다.**


이전 글에서 구현하였던 플레이어 추적 로직을 레벨 순찰 로직과 병행할 수 있도록 수정 해주었습니다.
Cast To BP_Character 노드에서 새로 만들 StartChase(CustomEvent) 노드로 연결을 해주고 StartChase는 레벨 순찰과 마찬가지로 CanMove를 통해 현재 움직여도 되는 상태인지 확인하는 Branch 노드를 연결, AI MoveTo를 통해 이동할 액터를 Self로 설정 해주고 추적할 액터를 Cast To BP_Character에서 받아온 Target 값으로 연결 해줍니다.
다음은 플레이어 캐릭터가 추적 대상으로 잘 연결이 됬다면 IsChasing 변수의 값을 참(True)값으로, 연결이 안됬다면 값을 거짓(False)로 설정해주고 추적 대상이 연결되지 않았을 때, 레벨 순찰을 진행할 수 있도록 MoveRandom 노드를 연결해주면 구현됩니다.
몬스터에게 알맞는 애니메이션을 적용해 보겠습니다.



컨텐츠 브라우저에서 Content/Blueprint/AI의 위치에 AnimationBlueprint Class를 생성해 SKEL_Ant_Skeleton을 선택 해주고 이름을 ABP_ANT로 설정 했습니다.
같은 위치에 Animation탭에 BlendSpace Class를 선택해, 똑같이 SKEL_Ant_Skeleton을 선택 해주고 이름을 BS_ANT로 설정 했습니다.
BS_ANT로 들어와 왼 쪽에 Axis Settings에서 Horizontal Axis의 Name을 Speed, Maximum Axis Value를 600, Grid Divisions는 편한 값으로 변경해주고 마지막으로 Snap to Grid 체크박스에 체크를 합니다.
이렇게 값의 이름을 Speed, 값의 최대치를 600, 그리드(Grid)를 마우스로 조정할 수 있게끔 설정되었습니다.
오른 쪽 하단의 Asset Browser에서 AS_Ant_Idle_Active를 가져와 Speed를 0.0, None을 50.0에 위치시키고 AS_Ant_Walk를 가져와 Speed는 300.0, None은 50.0에 위치 시킵니다.
마지막으로 AS_Ant_Run을 가져와 Speed는 600.0, None은 모두와 같이 50.0에 위치시키면 준비가 끝납니다.



이번에는 ABP_ANT로 들어와서 VARIABLES(변수)에 Speed(float) 변수를 만들어주고 오른 쪽 하단 Asset Browser에서 BS_Ant를 끌어와 AnimGraph에 놓습니다. 그리고 Speed, BS_Ant, Output Pose 순서로 연결해주면 됩니다.
왼 쪽에 GRAPHS에서 Event Blueprint Update Animation을 더블 클릭해 들어와 Event Blueprint Update Animation노드에 Set Speed 노드를 연결하고 Speed 값에 Try Get Pawn Owner, Get Velocity, Vector Length XY 노드를 순서대로 연결 해줍니다.
BP_ANT로 와서 Components 창에 Mesh (CharacterMesh0)를 클릭하고 Details 창에 Animation 탭을 보면 Animation Mode를 Use Animation Blueprint로 바꾸고 밑에 Anim Class를 ABP_ANT를 검색해서 선택합니다.








콘텐츠 브라우저에서 Content/Blueprints/Items 폴더를 만들고 안에 Blueprint Class를 선택, Pawn으로 Class를 2개 만든 다음 각각 이름을 BP_Item_Health와 BP_Item_Ammo로 만듭니다.
BP_Item_Health와 BP_Item_Ammo를 열고 Components 창에서 StaticMesh와 SphereCollision을 생성해줍니다.
각각의 StaticMesh의 Details 창에서 StaticMesh 탭의 StaticMesh를 해당 아이템에 맞는 StaticMesh 에셋으로 설정 해줍니다.
Sphere를 드래그 앤 드롭으로 BP_Item_Health(Self)와 BP_Item_Ammo(Self)에 놓으면 선택 창이 뜨고 Defualt Scene Root를 누르고 Make New Root 버튼을 누르면 SphereCollision이 최상단에 올라가면서 StaticMesh가 귀속됩니다.
Sphere가 선택되어 있는 상태에서 Details에 Collision 탭에서 Collision Presets를 Custom으로 해주고 아래에 Visibility를 Block으로 설정 해줍니다.
BP_Character로 이동해서 FUNCTIONS 탭에 함수를 추가해줍니다. 이름은 AddHealth와 AddAmmo로 지었습니다.
AddHealth 함수는 현재 체력 값(Health)에 체력 증가 값 인자(HealAmount)를 더해주고 최대 값을 넘을 경우 최대 값으로 현재 체력 값(Health)를 설정합니다.
그리고 현재 체력 값(Health)를 최대 체력 값(MaxHealth)로 나누어 백분율로 화면에 출력하는 함수입니다.
**저는 현재 총알을 충전해주는 기능에서 탄알집을 충전하고 가지고 있는 탄알집 수 만큼 재장전할 수 있는 기능으로 변경하여 코드가 다른 것을 확인할 수 있습니다.**
이제 EventGraph로 와서 HealAmount(float) 변수를 생성하고 Components 창에서 Sphere를 마우스 우 클릭 하면 Add Event 탭에서 On Component Begin Overlap (Sphere) 노드를 생성해서 Cast to BP_Character로 BP_Character와 연결하고 각각 Add Health, Add Ammo 함수를 불러와 각각의 함수에 HealAmount, AmmoAmount 값을 연결 해줍니다.
마지막으로 플레이어가 아이템을 습득 시, 아이템이 사라질 수 있도록 Destroy Actor를 연결해주면 구현이 완료 됩니다.

현재 BP_ANT 에 Attack(공격)과 Damage(피격) 로직에 마지막에 On Ant Killed Event가 연결되어 있는데요.
OnAntKilled(CustomEvent)는 게임 승리 조건으로 모든 몬스터가 사라졌을 때, 화면에 "게임 승리!" 문구가 출력되고 재시작 및 종료 버튼을 활성화 하는 Event입니다.
OnAntKilled(CustomEvent)를 구현하기에 앞써, "게임 승리!"를 출력하기 위한 UI 화면을 구성해야 됩니다.



콘텐츠 브라우저에서 Content/Blueprints/UI 위치에 Widget Preview Blueprint를 생성해주고 WBP_MainHUD로 이름을 설정 해주었습니다.
WBP_MainHUD로 들어가서 Hierarchy(하이어라키)창에 순서대로 Canvas, Text(5개), Button(2개)를 Palette(팔레트)에 검색하여 생성 해줍니다.
그리고 위에 이미지와 같이 Text 5개는 남은 시간(글씨와 값), 남은 적(글씨와 값), Result(결과 값)로 글자 입력하고 위치도 원하는 곳에 위치 시켜줍니다.
Button 2개는 원하는 곳에 위치 시켜주고 Button의 Text도 각각 재시작과 종료로 글자를 입력합니다.
전체 화면에서 오른 쪽 상단에 보면 Designer와 Graph가 있는데 "Graph"를 클릭 해줍니다.


왼 쪽 My Blueprint 창에서 VARIABLES 탭에 ExitButton과 RestartButton을 클릭해 보면 Details 창에 Events 탭을 통해 Event를 생성할 수 있도록 되어있습니다.
그 곳에서 ExitButton과 RestartButton의 On Clicked(Event) 노드를 생성 해줍니다.
다음은 위에 이미지와 같이 ExitButton에는 QuitGame 노드를 연결, RestartButton에는 Open Level (by Object Reference) 노드를 연결하고 안에 Level 부분에 현재 게임 레벨을 설정 해줍니다.
이렇게 게임 종료 버튼(ExitButton)과 게임 재시작 버튼(RestartButton)이 완성 되었습니다.
OnAntKilled(CustomEvent)는 길어서 끊어서 말씀드리겠습니다!









우선 Content/Blueprint/GameModes에 BlueprintClass로 GameModeBase 파일을 하나 만들고 이름을 BP_MainGameMode로 설정 했습니다.
BP_MainGameMode로 들어가서 변수로 MainHUD(WBP_MainHUD), AntRemaining(int), TimeRemaining(float), IsWin(bool), Ants(BP_ANT)변수를 만들고 마지막 Ants 변수는 클릭해서 오른 쪽 Details에서 Variable Type을 Array(배열)로 설정합니다.
이제 Event BeginPlay 노드에 Set Input Mode Game Only 노드를 연결하고 Player Controller에는 Get Player Controller 노드를 생성해 연결 합니다.
이어서 Create Widget 노드로 WBP_MainHUD를 생성해주고 Return Value를 Set MainHUD 노드에 연결, 계속해서 Add to Viewport 노드에 연결해주면 준비가 끝났습니다.
밑에 CustomEvent로 OnAntKilled를 만듭니다.
OnAntKilled(CustomEvent)가 호출되면 AntRemaining(int) 변수를 1감소하고 해당 내용을 MainHUD의 EnemyCount에 SetText 노드를 통해 업데이트 해줍니다.
다음 AntRemaining 변수가 0보다 같거나 작은지 확인 후, 작다면 IsWin(bool) 변수를 참(True)로 설정하고 MainHUD의 ResultText를 SetText를 통해 "게임 승리!"로 바꿔줍니다.
다시 Event BeginPlay로 올라와 Get All Actors Of Class 노드를 For Each Loop 노드에 이어서 BP_ANT 액터들을 게임 시작과 동시에 하나씩 순차하면서 Ants 배열에 추가 해줍니다.
배열의 길이(BP_ANT 액터의 수)를 LENGTH 노드를 통해 받아 AntRemaining 변수에 넣어주고 MainHUD의 EnemyCount(Text)에 SetText 노드를 통해 출력합니다.
밑에 OnAntKilled(CustomEvent)에서 SetText 노드 뒤에 For Each Loop 노드를 붙여 해당 노드를 통해 Ants 배열을 돌면서 BP_ANT 액터가 배열 안에 있다면 해당 액터의 CanMove(bool) 변수를 거짓(False)로 만들고 화면에 Result(Text), RestartButton, ExitButton 을 활성화 하도록 Set Visibility 노드를 Visible로 설정합니다.
**글을 정리하면서 또 찾아낸 실수들(?)... 해당 Event는 몬스터를 모두 처치했을 때 나오는 Event 이므로 For Each Loop를 통해 Ants 배열을 돌면서 남은 BP_ANT 액터의 CanMove를 거짓(False)로 바꿀 이유가 없습니다... 하지만! 혹시나 발생할 수 있는 오류를 대비해 지우지 않도록 하겠습니다!
하나 더 현재 CanMove를 거짓(False)로 설정 했으면 Set Movement Mode를 None으로 바꿔 몬스터가 움직일 수 없게 설정해야 합니다. 게임이 끝나도 몬스터가 움직이면서 플레이어를 추적하거나 공격하는 상황을 방지하기 위해서 입니다.**
화면에 게임 결과와 재시작, 종료 버튼도 활성화 해주었기에 Cast To BP_Character로 BP_Character를 불러와 IsFiring을 거짓(False)로 만들어 총이 더 이상 나가지 않도록 설정 해주고 Disable Input과 Set Input Mode UI Only를 통해 플레이어 입력이 UI에만 적용되게끔 설정 해줍니다. Disable Input과 Set Input Mode UI Only 사이에 Delay를 연결해둔 이유는 플레이어의 모든 입력을 끊고 UI에 입력을 할 수 있도록 설정해야 하는데 두 노드가 거의 동시에 일어나 원활히 작동이 안될 수 있기 때문에 사이에 Delay 노드를 추가하였습니다.
마지막으로 Set Show Mouse Cursor를 참(True) 값으로 설정하여 화면에 마우스 커서가 보이게 설정하면 끝납니다.


하지만 BP_ANT에서 Event를 불러오지 않았죠!
Event BeginPlay에 마지막 Cast To BP_MainGameMode 노드를 생성해 BP_MainGameMode를 불러와 MainGameMode 변수에 연결 시켜주고 Attack(공격)과 Damage(피격) Event 마지막에 On Ant Killed 노드를 Main Game Mode에서 불러와 주면 됩니다.
다음으로 시간 종료로 게임 패배가 되는 CustomEvent를 구현하겠습니다.
내용은 OnAntKilled(CustomEvent)와 크게 다르지 않습니다.






UpdateRemainingTime(CustomEvent)이 호출되면 TimeRemaining(float) 변수가 입력 받은 Delta Time 값 만큼 감소하고 그 값을 다시 TimeRemaining(float) 변수에 대입합니다.
그 값이 0보다 같거나 작은 경우, 게임 패배 기능이 활성화되고 0보다 큰 경우는 업데이트 된 값을 60으로 나눈 값을 분(Minute), 60으로 나눈 나머지 값을 초(Second)로 Format Text 노드를 통해 MainHUD의 TimeCount에 출력해줍니다.
**수정 전의 로직에서는 분(Miunte)은 업데이트 된 시간에서 구하고 초(Second)는 업데이트 전 시간에서 구하는 잘못된 구조를 가지고 있어 같은 값으로 남은 시간을 계산할 수 있도록 수정했습니다.**
만약 업데이트 된 시간이 0보다 같거나 작을 경우, MainHUD의 TimeCount(Text)를 0:00으로 만들고 MainHUD의 Result(Text) 또한 "게임 패배!"를 출력하도록 설정합니다. 이후 로직은 OnAntKilled에 구현한 로직과 같습니다.
UpdateRemainingTime(CustomEvent)의 작성이 끝났으면 위로 올라와 Event Tick을 통해 DeltaSeconds로 시간을 받아와 Update RemainingTime 노드에 연결 해줍니다.
**Delta Time 혹은 Delta Seconds는 이전 프레임과 현 프레임 사이의 시간 간격 값으로 이번 프레임이 연산되는 시간 값을 가집니다. 60프레임은 1초에 60장의 장면을 출력하며, 대략 0.016초 입니다.**
이번엔 플레이어 체력이 0이 되었을 때, 게임 패배 로직입니다.





CustomEvent 노드를 생성하여 UpdateRemainingAlive로 이름을 설정하였습니다.
해당 노드는 BP_Character Class에서 체력 값이 0일 때 호출할 예정이므로 바로 게임 패배 로직이 실행 됩니다.
구현은 위에 OnAntKilled와 UpdateRemainingTime과 같은 방식으로 구현하였습니다.


BP_Character Class의 Event AnyDamage 노드로 와서 가장 뒷 쪽에 Play Sound 2D로 피격음을 출력한 다음, Branch를 통하여 현재 플레이어의 체력(Health) 값을 확인하고 0보다 같거나 작을 경우 IsFiring(bool) 변수를 거짓(False)로 설정하여 총이 더 이상 나가지 않도록 설정 해줍니다.
이후 MainGameMode의 UpdateRemainingAlive(CustomEvent)를 호출하면 끝입니다.
Event BeginPlay 노드로 올라와 가장 뒷 쪽에 Cast To BP_MainGameMode와 Set MainGameMode 노드를 생성 및 연결해 BP_MainGameMode의 요소를 사용할 수 있도록 해줍니다.
오늘은 일단 여기까지 정리하도록 하겠습니다.
나머지 내용은 다음 글에서 마저 정리하도록 하겠습니다!
오늘도 다들 고생하셨고 내일도 힘내서 열심히 공부하도록 하겠습니다!
'[내일배움캠프] 본 과정 학습내용' 카테고리의 다른 글
| [내배캠] 8일차 C++ 학습 내용 정리 (0) | 2026.07.07 |
|---|---|
| [내배캠] 5일차 Unreal Blueprint 내용 정리(2) 및 7일차 C++ 학습 내용 정리 (0) | 2026.07.06 |
| [내배캠] 4일차 Unreal Blueprint로 적 구현과 C언어 기초 (0) | 2026.07.01 |
| [내배캠] 3일차 Unreal Blueprint로 총 기능 구현과 C언어 기초 (0) | 2026.06.30 |
| [내배캠] 2일차 Unreal Blueprint로 캐릭터 움직임 구현과 C 언어 기초 (0) | 2026.06.29 |