이번 튜토리얼에서는 플레이어를 탐지하고 추격하여 대미지를 입히는 적 NPC를 생성해 봅니다. 적은 대미지를 받을 수 있으며 장애물을 피하기 위해 내비게이션 메시로 주변 환경을 탐색할 수도 있습니다.
이전 튜토리얼과 마찬가지로, 먼저 블루프린트 비주얼 스크립팅을 사용하여 적 로직을 생성합니다. 그런 다음 내비게이션 메시를 생성하여 AI 컨트롤러 빙의 캐릭터가 게임플레이 동안 탐색할 수 있는 구역을 정의합니다.
시작하기 전에
퍼즐 어드벤처 디자인의 이전 섹션에서 다룬 퍼즐 어드벤처 디자인하기의 다음 주제를 이해하고 있어야 합니다.
블루프린트 및 블루프린트 함수
키 만들기의 다음 에셋이 필요합니다.
BPL_FPGame블루프린트 함수 라이브러리
적 이동을 위한 내비메시 생성
레벨에 적을 추가하고 플레이어를 추격하게 하려면 먼저 적 AI가 레벨을 탐색하는 데 사용할 내비메시를 빌드해야 합니다.
내비메시(NavMesh)는 내비게이션 메시(Navigation Mesh)의 약어로, 레벨에서 AI가 탐색할 수 있는 영역을 정의합니다. 이 튜토리얼에서는 내비메시를 생성하여 적이 플레이어를 추격할 수 있는 영역을 정의합니다.
레벨에 내비게이션 메시를 추가하려면 다음 단계를 따릅니다.
언리얼 에디터의 레벨 에디터에서 메인 툴바로 이동합니다. 생성(Create) 버튼을 클릭하고 볼륨(Volumes)에서 내비메시 바운드 볼륨(Nav Mesh Bounds Volume)을 선택합니다. 그러면 프로젝트에서 AI가 탐색할 수 있는 영역이 생성됩니다.
트랜스폼(Transform) 툴을 사용하여 내비메시를 레벨의 바닥과 교차하도록 옮깁니다.
키보드에서 P를 눌러 디버그 뷰를 토글합니다. 이를 사용하면 레벨 뷰포트에서 레벨의 탐색 가능 구역 등 내비메시 디테일을 볼 수 있으며, 뷰포트 왼쪽에 텍스트 기반 디버그 정보도 표시됩니다.
디버그 정보에 '내비메시 리빌드가 필요합니다.' 메시지가 표시되면 메뉴 바에서 빌드(Build) > 경로 빌드(Build Paths)를 선택합니다. 그러면 내비메시가 리빌드됩니다.
계속해서 내비메시를 이동하고 스케일을 조정(키보드의 W 및 R 키)하여 내비메시 바운드를 수정할 수 있습니다. 이 볼륨을 원하는 만큼 스케일 조절합니다.
내비메시 하단이 오목한 바닥 부분을 포함하기에 충분할 만큼 낮은지, 상단이 적의 키를 수용할 만큼 높은지 확인합니다.
튜토리얼 샘플 레벨을 사용하는 경우에는 2개의 내비메시 바운드 볼륨을 생성하고 'Hallway 3'과 'Room 3'이 채워지도록 스케일을 조절합니다.
내비메시의 스케일을 조정하고 움직이면, 큐브 같은 방해물이 없는 한 바운딩 박스의 크기에 따라 녹색 구역이 확장되는 것을 볼 수 있습니다. 빨간색으로 표시된 잘린 부분은 게임에서 AI가 이동할 수 없는 부분을 나타냅니다.
언리얼 에디터에서 내비메시에 있어서는 안 되는 오브젝트가 있는 경우, 해당 메시 레벨 오브젝트를 선택하고 디테일(Details) 패널에서 내비게이션에 영향 주기 가능(Can Ever Affect Navigation)을 비활성화합니다.
적 생성하기
여기에서는 캐릭터 클래스에서 적을 생성하게 되는데, 이는 캐릭터 클래스가 이미 휴머노이드 적에게 적합한 무브먼트와 애니메이션 기능을 갖추고 있기 때문입니다. 캐릭터 클래스는 비히클, 카메라, 비휴머노이드 크리처에 사용되는 보다 일반적인 '컨트롤 가능 액터'인 폰을 확장합니다. 사람과 유사한 형태의 적인 경우에는 캐릭터가 더 좋은 출발점입니다.
이 캐릭터를 처음부터 만들고 있으므로 적을 시각적으로 나타내기 위해 메시를 추가하고 애니메이션 블루프린트를 사용하여 애니메이션을 재생합니다. 애니메이션 블루프린트는 프로젝트 파일로 제공되므로 처음부터 만들지 않아도 되지만, 튜토리얼에서 적 캐릭터의 블루프린트와 통합하는 방법을 배워야 합니다.
먼저 적 블루프린트를 생성합니다. 다음 단계를 따르면 됩니다.
콘텐츠 브라우저에서 콘텐츠 > AdventureGame > Designer > Blueprints > Characters로 이동합니다.
콘텐츠 브라우저의 빈 곳을 우클릭한 다음 블루프린트 클래스(Blueprint Class)를 클릭합니다.
새 창에서 캐릭터(Character)를 베이스 클래스로 선택합니다.
이 클래스를
BP_Enemy로 명명하고 블루프린트 에디터에서 더블클릭하여 엽니다.
블루프린트 클래스를 생성할 때 빌드할 부모 클래스를 선택합니다. 이 부모에는 기본으로 제공되는 기능이 이미 포함되어 있습니다. 예를 들어 액터 클래스에서 블루프린트를 만들면 트랜스폼은 물론이고 컴포넌트를 보유하고 로직을 실행하는 기능이 제공됩니다. 여기서는 캐릭터 클래스로 시작하게 되므로 무브먼트, 콜리전 및 애니메이션 지원도 활용할 수 있습니다. 베이스 클래스(Base Class)는 템플릿과 유사한 방식으로 사용할 수 있습니다. 즉, 기능을 추가한다는 것은 베이스 클래스를 확장한다는 것을 의미합니다.
BP_Enemy 블루프린트 창이 열린 상태에서 뷰포트 탭으로 이동하여 컴포넌트(Components) 패널을 살펴봅니다. 그러면 목록 하단에 캐릭터 이동(CharMoveComp) 컴포넌트를 볼 수 있습니다. 모든 캐릭터 클래스에는 캐릭터 무브먼트 컴포넌트가 포함되어 있습니다. 컴포넌트를 클릭하고 디테일 패널에서 내부 세팅을 모두 확인합니다. 프로젝트에서 적을 만들면서 이러한 세팅 중 일부를 오버라이드하게 됩니다.
적에 3D 모델 추가
캐릭터에는 메시(Mesh) 컴포넌트도 포함되어 있지만, 이 컴포넌트는 스태틱 메시가 아니라 스켈레탈 메시입니다. 지금까지 작업한 스태틱 메시는 움직이지 않고 고정된 3D 모델인 반면에 스켈레탈 메시는 내부 스켈레톤을 갖추고 있기 때문에 애니메이팅 및 디폼할 수 있습니다.
먼저 캐릭터 메시와 애니메이션을 구성해 보겠습니다. 다음 단계를 따르면 됩니다.
메시 컴포넌트를 선택하고 디테일 패널의 아래쪽을 살펴봅니다. 메시 카테고리에서 스켈레탈 메시 에셋(Skeletal Mesh Asset) 옆의 드롭다운을 클릭합니다. 목록에서
SKM_Manny_Simple모델을 검색하여 선택합니다. 그러면 Manny 모델이 블루프린트에 할당됩니다.디테일 패널에서 애니메이션(Animation) 카테고리로 이동합니다.애님 클래스(Anim Class) 옆의 드롭다운을 클릭하고
ABP_Unarmed애니메이션 블루프린트를 할당합니다. 이제 적 캐릭터가 뷰포트 탭에서 유휴 상태 애니메이션을 재생하게 됩니다.적 캐릭터에서 제대로 작동하도록 애니메이션 블루프린트를 수정합니다.
적 메시의 애님 클래스 프로퍼티에서
ABP_Unarmed옆에 있는 콘텐츠 브라우저에서 에셋 탐색(Browse to Asset in Content Browser)을 클릭합니다.ABP_Unarmed애니메이션 블루프린트를 더블클릭하여 엽니다.Sequence 노드의 Then 1 핀 뒤에 오는 로직 그룹으로 이동합니다.
적 캐릭터의 이동을 막는 가속 확인이 적용되지 않도록 And 노드와 Set Should Move 노드 사이의 와이어를 제거합니다.
Greater (>) 노드의 출력 핀을 이동해야 함(Should Move) 핀에 연결하여 가속 확인을 우회합니다.
블루프린트를 컴파일 및 저장하고 닫습니다.
뷰포트에서 적 캐릭터 근처에 실린더 모양의 캡슐 셰이프가 있는 것을 볼 수 있습니다. 이는 지면 등 다른 오브젝트와의 콜리전을 탐지하는 캡슐 컴포넌트(Capsule Component)를 시각적으로 표현한 것입니다. 캐릭터 메시의 셰이프는 보다 복잡하기 때문에 단순한 콜리전 실린더를 통해 셰이프를 스무딩하여 콜리전 탐지 퍼포먼스를 높일 수 있습니다.
메시는 오프셋되므로 캡슐에 맞춰 정렬되지 않습니다. 캐릭터의 메시가 캡슐 컴포넌트와 제대로 맞도록 하려면, 발이 캡슐 컴포넌트의 하단 부분에 닿도록 캐릭터를 아래로 옮깁니다.
캐릭터 메시를 콜리전 볼륨에 맞춰 정렬하려면 다음 단계를 따릅니다.
메시 컴포넌트를 다시 선택하여 3D 모델을 하이라이트합니다.
디테일 패널에서 트랜스폼 카테고리의 위치(Location) 필드를 사용하여 Z축(파란색으로 표시된 마지막 필드)을 변경합니다. 값을
-96으로 설정합니다.
이제 두 컴포넌트 모두 원점 중앙에 위치하므로 적이 게임에서 공중에 뜨지 않습니다.
마지막으로 캐릭터에 화살표 컴포넌트(Arrow Component)가 있는 것을 볼 수 있습니다. 이 화살표는 블루프린트에서 어느 방향이 정면인지 나타냅니다. 이 화살표가 메시가 향하는 방향과 일치하는지 확인하여 캐릭터가 올바른 방향으로 움직이게 합니다.
캐릭터 메시가 정면을 향하게 하려면 다음 단계를 따릅니다.
메시 컴포넌트를 다시 선택합니다. 디테일 패널의 트랜스폼 카테고리 또는 회전 기즈모를 사용하여 캐릭터를 Z축에서 90도 회전시킵니다. 마지막 회전(Rotation) 값은 -90이어야 합니다.
3D 모델의 머티리얼 변경
기능을 생성하기 전에, 적 모델의 컬러를 빨간색으로 변경해 보겠습니다. 현재의 흰색 대신 이렇게 변경하면 플레이어와 명확하게 구분되고 더 적처럼 느껴지게 됩니다.
적 캐릭터를 다른 컬러로 바꾸려면 다음 단계를 따릅니다.
디테일 패널의 머티리얼(Materials) 섹션에는 다음과 같은 두 엘리먼트가 있습니다.
MI_Manny_01_New는 상박, 다리, 머리와 같이 적 모델의 다양한 위치에 영향을 미치는 머티리얼입니다.MI_Manny_02_New는 전완과 몸통에 영향을 미치는 머티리얼입니다.
엘리먼트 0(Element 0) 옆에 있는 폴더와 돋보기 모양의 콘텐츠 브라우저에서 탐색(Browse in Content Browser) 버튼을 클릭합니다. 그러면 콘텐츠 브라우저에서 이 머티리얼이 있는 폴더가 열립니다. 두 번째 머티리얼인
MI_Manny_02_New도 여기에 있습니다.MI_Manny_01_New에셋을 우클릭하고 머티리얼 인스턴스 생성(Create Material Instance)을 클릭합니다.머티리얼 인스턴스를
MI_Enemy_01로 명명하고 더블클릭하여 엽니다.디테일 패널에서 01 - BaseColor를 펼치고 페인트 색조(Paint Tint) 프로퍼티를 활성화합니다.
페인트 색조 프로퍼티 옆의 컬러 견본을 클릭하고 컬러를 선택합니다. 이 튜토리얼에서는 Hex sRGB =
F60E6EFF를 사용합니다.저장(Save)을 클릭하고 창을 닫습니다.
콘텐츠 브라우저로 돌아가서
MI_Manny_02_New를 우클릭하고 머티리얼 인스턴스 생성을 클릭합니다.이 머티리얼 인스턴스를
MI_Enemy_02로 명명하고 앞서 진행한 단계를 반복하여 컬러를 변경한 다음 에셋을 저장합니다.
단계를 반복하고 나면 두 개의 새 머티리얼 인스턴스, 즉 MI_Enemy_01 및 MI_Enemy_02가 생성됩니다.
다음 단계에 따라 이러한 머티리얼을 적 캐릭터에게 할당합니다.
BP_Enemy블루프린트의 디테일 패널에서 머티리얼 섹션으로 이동합니다.엘리먼트 0 드롭다운을 클릭하고 새로 생성한 머티리얼인
MI_Enemy_01을 선택합니다.엘리먼트 1에서는
MI_Enemy_02머티리얼로 변경합니다.
레벨에 적 추가
레벨에 적을 추가하여 지금까지의 모습과 비헤이비어를 확인합니다.
콘텐츠 브라우저에서 적 블루프린트를 레벨 뷰포트에 드래그한 다음 플레이어 앞에 놓습니다.
게임을 플레이할 때 적이 유휴 애니메이션으로 애니메이팅되어야 합니다.
게임플레이 시작 시 적 구성하기
적의 시각적인 부분을 준비했으므로 이제 게임플레이 기능을 만들 수 있습니다. 먼저 적 생성 시 필요한 프로퍼티를 구성해야 합니다.
게임 시작 시 적의 무브먼트 속도를 설정하려면 다음 단계를 따릅니다.
BP_Enemy블루프린트 에디터 창에서 이벤트그래프(EventGraph) 탭으로 이동합니다. 기본적으로 여기에는 Event BeginPlay, Event ActorBeginOverlap, Event Tick이라는 세 가지 이벤트가 있습니다. 이 튜토리얼에서는 필요하지 않으므로 ActorBeginOverlap을 선택하여 삭제합니다.컴포넌트 패널을 사용하여 캐릭터 무브먼트(Character Movement) 컴포넌트를 이벤트 그래프로 드래그합니다. 그러면 Character Movement 노드가 생성됩니다. 이 컴포넌트 노드를 사용하면 그래프에서 컴포넌트의 프로퍼티를 확인하거나 변경할 수 있습니다.
Character Movement 노드에서 핀을 드래그하여 Set Max Walk Speed 노드를 생성합니다.
Set 노드의 녹색 최대 걷기 속도(Max Walk Speed) 핀을 우클릭하고 변수로 승격(Promote to Variable) 옵션을 선택합니다.
그러면 '내 블루프린트(My Blueprint)' 패널의 '변수(Variables)' 목록 아래에 새 변수가 생성됩니다.
이 변수의 이름을
Max Speed로 변경합니다. 변수 옆의 눈 모양 아이콘을 클릭하여 편집 가능한 퍼블릭 변수로 바꿉니다.카테고리를 Setup으로 변경합니다.
블루프린트를 컴파일하고 디테일 패널에서 Max Speed의 디폴트 값(Default Value)을
200으로 설정합니다.편집 가능한 변수에는 항상 디폴트 값을 할당하세요. 레벨 에디터에서 값을 변경하는 것을 잊어버리면 디폴트 값이 0으로 설정되어 적이 움직일 수 없게 됩니다.
Event BeginPlay를 Set Max Walk Speed 노드에 연결합니다.
이제 두 변수가 적의 체력을 트래킹하게 됩니다. 인스턴스 편집 가능 변수인 TotalHP는 적의 시작 HP를 설정합니다. CurrentHP는 게임플레이 동안 적이 플레이어나 환경에 의해 대미지를 입었을 때의 체력을 트래킹합니다.
적의 HP를 설정하려면 다음 단계를 따릅니다.
변수 목록에서
CurrentHP라는 플로트 타입의 새 변수를 추가합니다.CurrentHP 변수를 변수 목록에서 이벤트 그래프로 드래그하고 Set Current HP 옵션을 선택합니다. Set Max Speed 노드의 실행 핀을 드래그하여 Set Current HP 노드의 실행 핀에 연결합니다.
Set Current HP 노드에서 현재 HP(Current HP) 플로트 핀을 우클릭하고 변수로 승격을 클릭합니다.
이 변수의 이름을 TotalHP로 변경합니다. 타입을 플로트로 설정하고 눈 모양 아이콘을 클릭하여 편집 가능한 퍼블릭 변수로 만듭니다. 디테일 패널에서 카테고리를 구성(Setup)으로 설정합니다. 블루프린트를 컴파일하고 디폴트 값을
100으로 설정합니다.Set 노드들의 실행 핀을 연결합니다.
이 이벤트 그래프에서는 플레이어 캐릭터로 많은 액션을 수행하므로, 이제 플레이어에 대한 레퍼런스를 변수로 저장해 보겠습니다.
플레이어 캐릭터에 대한 변수를 구성하려면 다음 단계를 따릅니다.
변수 목록에서
PlayerRef라는 새 변수를 생성하고 타입을 캐릭터(오브젝트 레퍼런스)(Character (Object Reference))로 변경합니다.플레이어를 참조해야 할 때마다 Get Player Character 함수를 호출하는 것보다, 캐릭터 오브젝트를 위한 레퍼런스 변수를 생성하는 것이 프로젝트 퍼포먼스에 더 좋습니다.
Set Current HP 노드 다음에 Set PlayerRef 노드를 연결합니다.
Player Ref 입력 핀에 Get Player Character 노드를 연결합니다. 플레이어 인덱스(Player Index) 값을 0으로 설정합니다. 이 값이 레벨에 처음 생성되는 플레이어 캐릭터의 기본 인덱스가 됩니다.
적이 플레이어를 추격하게 만들기
다음으로 적을 플레이어를 향해 이동하도록 하는 로직을 만들어야 합니다. 커스텀 이벤트를 생성하여 이 작업을 수행할 수 있습니다. 블루프린트 어디서든 커스텀 이벤트를 호출하여 일부 로직을 트리거하고 실행할 수 있습니다.
이동 액션을 트리거하는 커스텀 이벤트를 생성하려면 다음 단계를 따릅니다.
이벤트 그래프 아무 곳이나 우클릭하고 검색창에
Custom Event를 입력합니다. 목록에서 커스텀 이벤트 추가(Add Custom Event) 옵션을 클릭하고 이 이벤트를 MoveToPlayer로 명명합니다.MoveToPlayer 이벤트 노드의 핀을 드래그하고 AI MoveTo 노드를 검색하여 생성합니다. 이 노드는 AI 컨트롤러가 있는 폰이 특정 위치로 이동하는 데 사용하는 액션입니다. 여기에서는 이를 사용하여 적을 플레이어를 향해 이동하게 합니다.
AI MoveTo 노드에서 폰(Pawn) 핀을 드래그하고 검색창에
Self를 입력합니다. 셀프 레퍼런스 가져오기(Get reference to self)를 선택하여 게임에서 이 블루프린트의 액터를 참조하는 노드를 생성합니다.AI MoveTo 노드의 타깃 액터(Target Actor) 핀을 드래그하여 Get PlayerRef 노드를 생성합니다.
허용 반경(Acceptance Radius)을
10으로 설정합니다. 이는 적이 목적지에 도달했다고 간주할 cm 단위 거리입니다. 오버랩 시 중지(Stop on Overlap)보다 더 많은 컨트롤을 제공합니다.
이 경우 Self 노드를 사용하여 이 적을 움직여야 하는 폰으로 정의합니다. 그런 다음 플레이어를 폰이 타깃으로 삼아 향할 타깃 액터로 설정합니다.
AI MoveTo 노드에는 성공 시(On Success) 및 실패 시(On Fail) 이벤트가 포함되어 있습니다. 이는 적이 플레이어에게 도달하는 경우 또는 도달에 실패하는 경우 무슨 일이 일어나는지 정의합니다.
적 이동 후 무슨 일이 일어나는지 정의하려면 다음 단계를 따릅니다.
AI MoveTo 노드에서 성공 시 핀을 드래그하여 Delay 노드를 생성합니다.
Delay 노드에서 기간(Duration) 핀을 우클릭하고 변수로 승격을 선택합니다.
이 변수를 WaitAtDestination으로 명명하고 타입은 플로트로 설정합니다.
블루프린트를 컴파일하고 WaitAtDestination의 디폴트 값을
5로 변경합니다. 눈 모양 아이콘을 클릭하여 편집 가능한 상태로 만들고 카테고리를 Setup으로 변경합니다.이벤트 그래프에서 Delay 노드의 실행 핀을 드래그하여 Move To Player 노드를 생성합니다. 그러면 플레이어가 멀어져도 적이 계속 추적하지만, 플레이어가 어느 정도 거리를 벌릴 수 있는 딜레이가 생깁니다.
AI MoveTo 노드에서 실패 시 핀을 드래그하여 새 Delay 노드를 생성합니다.
Delay의 완료됨(Completed) 핀을 드래그하여 Move To Player 노드를 생성합니다. 기간은 0.2로 둡니다. 그러면 적이 플레이어에게 아직 도달하지 않은 경우 계속 이동합니다.
로직의 Event BeginPlay 그룹을 찾습니다. 이 시퀀스의 마지막 부분에서 Set Player Ref 노드 다음에 Move To Player 노드를 연결하여 이 이벤트를 트리거해야 합니다. 그래야 게임 시작 시 적이 움직이고 적의 이동을 테스트할 수 있습니다.
블루프린트를 저장하고 컴파일(Compile)합니다.
그러면 아래와 같은 이벤트 그래프가 만들어집니다.
적 무브먼트 테스트
레벨에서 적 액터를 내비메시 경계, 즉 녹색 구역 안으로 옮깁니다.
레벨 뷰포트 또는 아웃라이너 패널에서 BP_Enemy 액터를 선택합니다. 디테일 패널의 Setup 섹션에서 블루프린트에 추가한 변수를 볼 수 있습니다. 이러한 값을 프로젝트에서 필요한 대로 변경하거나 디버그 탐지(Debug Detection)를 켜서 라인 트레이스의 디버그 드로를 활성화할 수 있습니다.
레벨을 다시 플레이하여 이제 적이 어떻게 행동하는지 확인합니다. 적이 플레이어를 향해 걸어오고, 플레이어에 도달하면 멈추고, 5초 기다렸다가 다시 플레이어를 향해 걸으려 할 것입니다.
의도한 대로 작동하지 않는 부분이 있으면, 블루프린트에서 생성한 변수가 씬의 BP_Enemy에 올바른 값으로 설정되어 있는지 확인합니다. Max Speed 또는 Max Detection Distance 값을 설정하지 않으면 적이 움직이지 않습니다. 값이 너무 낮은 경우에도 역시 움직이지 않을 수 있습니다.
플레이어에게 대미지 입히기
이제 적이 플레이어에게 도달할 수 있으므로, 플레이어와 충돌할 때 대미지를 입히는 기능을 추가해 보겠습니다. 이 튜토리얼에서는 플레이어와 접촉 시 스스로 소멸하는 적을 생성하려고 하므로, 적이 플레이어에게 한 번 대미지를 입힌 후 제거되는 로직도 추가해야 합니다.
적이 플레이어와 충돌했는지 확인하려면 다음 단계를 따릅니다.
컴포넌트 패널에서 캡슐 컴포넌트(Capsule Component)(CollisionCylinder)를 우클릭하고 이벤트 추가(Add Event)로 이동하여 Add OnComponentHit를 선택합니다.
이렇게 하면 이벤트 그래프에 캡슐 컴포넌트가 무언가를 히트할 때 실행되는 새 이벤트 노드가 생성됩니다.
On Component Hit 노드의 핀을 드래그하여 Branch 노드를 생성합니다.
조건(Condition) 핀을 On Component Hit 노드의 기타 액터(Other Actor) 핀에 연결하면 새 노드를 생성하라는 메시지가 표시됩니다.
Equal 노드를 생성합니다. Equal 노드의 Select Asset 핀을 드래그해 PlayerRef 변수에 대한 레퍼런스를 추가합니다.
이 로직은 적의 캡슐 컴포넌트가 게임에서 무언가를 히트할 때 이벤트를 트리거합니다. 이 이벤트는 캡슐 컴포넌트가 플레이어 캐릭터를 히트했는지 여부를 확인합니다.
플레이어의 1회 히트 대미지를 처리하고 적을 제거하려면 다음 단계를 따릅니다.
변수 목록에서 Eliminated라는 새 변수를 생성하고 타입을 부울로 변경합니다. 이 변수는 적을 처치하여 게임에서 제거해야 할 때 사용합니다.
플레이어에게 한 번만 대미지를 입힐 수 있도록 Branch 노드의 True 핀을 드래그하여 Do Once 노드를 생성합니다.
Do Once 노드의 완료됨 핀을 드래그하여 Apply Damage 노드를 생성합니다. 대미지를 받은 액터(Damaged Actor) 핀을 드래그하여 PlayerRef 변수의 레퍼런스에 연결합니다. 이 액터가 대미지를 받습니다.
베이스 대미지(Base Damage) 핀을 우클릭하고 변수로 승격을 선택합니다.
변수 목록에서 베이스 대미지의 눈 모양 아이콘을 클릭하여 편집 가능한 상태로 만들고, 카테고리를 Setup으로 변경합니다. 블루프린트를 컴파일하고 디폴트 값을
25로 설정합니다.변수 목록에서 Eliminated 변수를 이벤트 그래프로 드래그하고 Set를 선택합니다. 노드에서 체크박스를 클릭하고 Eliminated를 true로 설정합니다.
Apply Damage 노드의 실행 핀을 Set Removed 노드에 연결합니다.
Set Eliminated 노드의 실행 핀을 드래그하여 Delay 노드를 생성합니다. 기간을
2.0으로 설정합니다.Delay 노드의 실행 핀을 드래그하여 Destroy Actor 노드를 생성합니다. 타깃(Target) 프로퍼티는 기본 셀프(self)로 설정되어 있어야 합니다.
적이 플레이어와 충돌하면 플레이어에게 대미지를 입히고, 2초 딜레이 후 적이 처치됩니다.
이제 적이 플레이어를 향해 달려갈 수 있고, 플레이어와 충돌하면 대미지를 입히면서 자폭할 수 있습니다. 하지만 플레이어와 적 사이의 거리나 방해물은 고려하지 않습니다. 따라서 적은 거리나 시야와 관계없이 항상 플레이어를 추격합니다.
BP_Enemy의 완성된 On Component Hit 로직은 다음과 같습니다.
아래의 블루프린트 스니펫을 BP_Enemy 이벤트 그래프에 붙여 넣을 때는 변수 이름이 샘플 프로젝트의 변수 이름과 일치하는지 확인하세요.
거리 및 장애물 추가 로직
다음으로 플레이어가 적에 충분히 가까이 있을 때만 적이 플레이어를 탐지할 수 있도록 최대 탐지 거리를 추가합니다. 그런 다음 적이 벽 너머의 플레이어를 볼 수 없도록 적의 시야 제한을 구현해 보겠습니다.
이렇게 하려면 먼저 적과 플레이어 사이에서 라인 트레이스를 수행하는 새 함수를 블루프린트 라이브러리에 생성해야 합니다.
라인 트레이스로 시선 및 거리 계산하기
레이캐스트라고도 하는 라인 트레이스는 게임 개발에서 흔히 사용되는 방식입니다. 라인 트레이스는 소스 액터 같은 월드의 한 포인트부터 보이지 않는 선을 그어서 해당 선이 닿는 부분을 확인합니다. 종종 런타임에서 오브젝트나 표면을 탐지하고 트레이스 결과에 따라 로직을 트리거하는 데 사용됩니다.
이 튜토리얼에서는 적 캐릭터에서 라인 트레이스를 캐스트하여 플레이어 캐릭터가 적의 시야 내에 있을 때 탐지하게 만들어 보겠습니다.
라인 트레이스에 대한 자세한 내용은 레이캐스트와 트레이스 문서를 참고하세요.
라인 트레이스를 사용하여 플레이어를 찾는 함수를 구성하려면 다음 단계를 따릅니다.
콘텐츠 브라우저에서 Core 폴더로 이동하여
BPL_FPGame블루프린트 라이브러리를 엽니다.fnBPLFindPlayer라는 새 함수를 추가합니다.새 함수의 디테일 패널에서 입력(Inputs) 섹션으로 이동하고 추가(Add)(+)를 클릭하여 다음과 같은 입력을 생성합니다.
Player Reference(폰 오브젝트 레퍼런스)
Start Location(벡터)
Max Detection Distance(플로트)
Debug(부울)
이러한 입력을 추가하고 나면 fnBPLFindPlayer 함수 엔트리 노드에 각 변수에 해당하는 핀이 생깁니다.
함수의 디테일 패널에 있는 출력(Outputs) 섹션에서 추가(+)를 클릭하여 Found라는 이름의 부울 타입 출력 값을 생성합니다.
Found 출력을 추가하면 새 Return Node가 Found 핀과 함께 그래프에 나타납니다.
내 블루프린트 패널의 로컬 변수(Local Variables) 섹션에서 추가(+)를 클릭하여 PlayerFound라는 이름의 부울 타입 로컬 변수를 생성합니다. 디폴트 값이 false인지 확인합니다.
함수가 제대로 실행되려면 플레이어 레퍼런스가 중요하므로, 함수가 시작할 때 유효한 플레이어 레퍼런스를 체크하여 오류를 방지해야 합니다.
유효한 입력을 체크하려면 다음 단계를 따릅니다.
Alt를 누른 채 엔트리 노드와 Return Node 간의 와이어를 클릭하거나 실행 핀 하나를 클릭하여 와이어를 삭제합니다.
함수 엔트리 노드 뒤에 Is Valid 노드를 생성하여 연결합니다.
노드 액션 목록 하단 근처에 물음표(?) 아이콘으로 표시된 Is Valid 노드를 생성해야 합니다.
엔트리 노드의 Player Reference 핀을 Is Valid 노드의 입력 오브젝트(Input Object) 핀에 연결합니다.
유효하지 않음(Is Not Valid) 실행 핀을 Return Node에 연결합니다.
Found 반환 값에는 PlayerFound 변수에 대한 레퍼런스를 연결합니다.이렇게 하면 함수가 종료되고 적의 이벤트 그래프에 PlayerFound=False를 반환합니다.
플레이어가 유효한 경우, 함수는 라인 트레이스 오브젝트를 실행한 다음 PlayerFound를 반환합니다.
라인 트레이스를 구성하려면 다음 단계를 따릅니다.
유효함(Is Valid) 실행 핀에서 Sequence 노드를 추가하여 로직을 정리합니다.
Sequence 노드의 Then 0에서 드래그하여 Line Trace By Channel 노드를 추가합니다.
Line Trace By Channel은 특정 콜리전 채널의 오브젝트를 탐지합니다. 블루프린트의 콜리전 채널 세팅을 보려면 블루프린트의 콜리전 컴포넌트를 선택하고 디테일 패널의 콜리전(Collision) > 콜리전 프리셋(Collision Presets) 프로퍼티로 이동합니다. 트레이스 반응(Trace Responses) 섹션에는 비저빌리티(Visibility) 및 카메라(Camera) 채널이 있습니다.
함수 엔트리 노드에서 Start Location 핀을 드래그하여 Line Trace By Channel 노드의 시작(Start) 핀에 연결합니다. 그러면 적 캐릭터의 위치를 라인 트레이스의 시작점으로 설정합니다.
그래프를 깔끔하게 유지하기 위해 Get Player Reference 노드를 생성합니다. 노드의 오른쪽 상단 f 아이콘은 이것이 함수 입력임을 나타냅니다. 다른 변수와 마찬가지로 이러한 입력에 대한 레퍼런스를 추가할 수 있습니다.
Player Reference 핀에서 그래프의 빈 공간으로 드래그합니다. 그러면 노드 액션 목록이 열립니다. 목록에서 Get Actor Location 액터를 검색하여 생성합니다.
반환 값(Return Value)을 Line Trace 노드의 끝(End) 핀에 연결합니다. 이렇게 하면 플레이어의 위치에서 라인 트레이스가 끝납니다.
Line Trace By Channel 노드에서 드롭다운 메뉴를 사용하여 트레이스 채널(Trace Channel) 프로퍼티를 카메라로 설정합니다.
이제 라인 트레이스가 시작되고 종료되는 지점을 설정했으므로, 트레이스가 뭔가에 히트하면 어떻게 되는지 정의합니다.
라인 트레이스가 플레이어에 히트했는지 확인하려면 다음 단계를 따릅니다.
라인 트레이스의 아웃 히트(Out Hit) 핀을 드래그하여 Break Hit Result 노드를 추가합니다. 라인 트레이스가 오브젝트에 히트하면 많은 정보가 수집됩니다. 이 로직에서는 몇 가지 특정 값만 사용하면 됩니다. 따라서 반환된 데이터를 개별 컴포넌트로 나누기 위해 Break Hit Result 노드가 필요합니다.
Break Hit Result 노드에서 하단의 화살표를 클릭하여 추가 옵션을 확인합니다.
플레이어가 적과의 일정 거리 안에서 시야에 있는지 확인해야 하므로, 액터 히트(Hit Actor) 핀을 사용하여 플레이어를 체크하고 트레이스 시작(Trace Start) 및 트레이스 끝(Trace End)을 사용하여 히트한 오브젝트와의 거리를 측정합니다.
액터 히트 핀을 드래그하여 Equal 연산자 노드를 추가합니다. 하단 입력에는 레퍼런스를 Player Reference 함수 입력에 연결합니다.
함수 입력 값을 레퍼런스하려면 다른 변수와 마찬가지로 Get 노드를 추가합니다.
Distance (Vector) 노드를 생성합니다. V1을 트레이스 시작에 연결하고 V2를 트레이스 끝에 연결합니다.
벡터는 3D 공간에서 오브젝트 또는 포인트의 위치를 나타내는 X, Y, Z 값 세트입니다. Distance 노드는 이러한 포인트 사이의 거리를 계산하여 플로트 값으로 표시합니다.
Distance 노드의 반환 값을 드래그하여 Less (<) 노드를 추가합니다. 하단 입력에는 레퍼런스를 Max Detection Distance 함수 입력에 연결합니다.
AND (Boolean) 노드를 생성합니다.
Equals (==) 및 Less (<) 노드의 부울 출력 핀을 AND 노드의 입력 핀에 연결합니다.
이 로직에서는 라인 트레이스가 Player Reference와 동일한 액터를 히트하고, 트레이스 시작(적)과 끝(히트한 오브젝트) 사이의 거리가 Max Detection Distance보다 작은 경우에 AND 노드가 true를 반환합니다.
트레이스 결과를 사용하여 PlayerFound의 값을 설정하고 반환하려면 다음 단계를 따릅니다.
AND 노드의 출력에서 드래그하여 Branch 노드를 추가합니다.
Line Trace By Channel 이후 실행되도록 실행 핀에 연결합니다.
Branch 노드의 True 실행 핀에서 Set 변수 노드를 생성하여 로컬 PlayerFound 변수를 true로 설정합니다.
Branch 노드의 False 핀에서 드래그하여 Set PlayerFound 변수를 생성합니다. 이 값을 false로 유지합니다.
라인 트레이스 로직을 마무리했으므로 Sequence 노드로 돌아가서 Then 1 핀을 Return Node에 연결하여 함수를 종료하고, PlayerFound 결과를 적 블루프린트에 다시 전송합니다.
라인 트레이스 디버그 옵션 추가
레벨 에디터에서 라인 트레이스의 디버그 비주얼을 쉽게 켜고 끌 수 있어야 하는데, 라인 트레이스 노드는 디버그 드로 타입(Draw Debug Type) 목록에서 하나의 옵션만 선택할 수 있습니다. Select 노드를 사용하여 레벨 에디터에서 디버그 타입을 편집 가능하게 만들 수 있습니다.
라인 트레이스에 커스터마이징 가능한 디버그 옵션을 구성하려면 다음 단계를 따릅니다.
Line Trace By Channel 노드의 디버그 드로 타입 핀에서 드래그하여 Select 노드를 생성합니다.
Select 노드의 인덱스(Index) 핀을 함수에 대한 레퍼런스의 Debug 입력에 연결합니다.
Select 노드의 인덱스는 와일드카드이므로 부울 타입의 Debug 값을 연결하면 옵션이 False 및 True로 변경됩니다.
False 옵션을 없음(None)으로 설정합니다. 기간 동안(For Duration) 옵션을 True로 설정합니다.
Line Trace By Channel 노드 하단에서 화살표 버튼을 클릭하여 추가 옵션을 표시합니다. 드로 시간(Draw Time)을
0.1초로 변경합니다.블루프린트 함수 라이브러리를 저장 및 컴파일합니다.
이 로직은 Debug 변수를 사용하여 라인 트레이스 디버그 드로의 활성화 여부를 확인합니다.
Debug 변수가 false인 경우 디버그 드로 타입이 없음으로 설정되어 디버그 드로가 렌더링되지 않습니다.
Debug를 true로 설정하면 Select 노드가 디버그 드로 타입을 기간 동안으로 변경하여 런타임에서 라인 트레이스를 설정된 기간 동안 렌더링합니다.
완성된 fnBPLFindPlayer 함수는 다음과 같은 모습이어야 합니다.
이 블루프린트 스니펫을 프로젝트에 복사하는 경우에는 함수 엔트리 노드의 실행 및 Player Reference 핀을 Is Valid 노드의 실행 및 입력 오브젝트 핀에 연결합니다. 스니펫이 제대로 복사되려면 함수 입력 이름이 샘플 프로젝트 이름과 일치해야 합니다.
플레이어로 이동 로직 업데이트
이제 거리와 시야를 고려하여 플레이어를 찾는 함수를 호출할 수 있게 되었으므로, 적이 플레이어가 시야 내에 있거나 라인 트레이스가 플레이어를 성공적으로 발견했을 때 플레이어를 찾아 추격하도록 하는 로직을 추가해야 합니다.
적이 플레이어를 발견한 뒤 추적하게 하려면 다음 단계를 따릅니다.
BP_Enemy블루프린트로 돌아갑니다. 이벤트 그래프에서 기본으로 제공된 Event Tick 노드를 찾습니다.Event Tick 노드의 실행 핀에서 드래그하여 Branch 노드를 생성합니다.
틱 시(On Tick) 이벤트는 게임이 플레이되는 동안 프레임마다 실행됩니다. 이를 사용하여 적이 플레이어의 위치를 지속적으로 확인하고 추격 시작 여부를 판단하도록 만들 수 있습니다.
Branch 노드의 조건 핀을 드래그하여 부울 값의 반대 값을 반환하는 NOT Boolean 노드를 생성합니다.
여기에서는 Eliminated 변수의 반대 값을 반환받아야 하므로, NOT 노드의 핀을 드래그하여 Get Eliminated를 검색합니다.
이는 Branch 노드의 True 핀으로 진행하기 전에 적이 처치되지 않았는지 확인합니다.
Branch 노드의 True 핀을 드래그하여 FnBPLFindPlayer 노드를 생성합니다. 이 노드는 라인 트레이스를 수행하여 월드에서 플레이어를 찾고, 적과 플레이어 사이의 현재 거리를 확인합니다.
Fn BPFind Player 노드에서 핀을 사용하여 다음과 같이 연결합니다.
Player Reference: PlayerRef 변수에 대한 레퍼런스를 연결합니다.
Start Location: 타깃 프로퍼티가 셀프로 설정된 Get Actor Location 노드를 생성합니다. 이렇게 하면 라인 트레이스의 시작 위치가 계속 월드에서 적 자신의 위치로 할당됩니다.
Max Detection Distance: 핀을 우클릭하고 변수로 승격을 선택합니다. 눈 모양 아이콘을 클릭하여 편집 가능한 상태로 만들고 카테고리를 Setup으로 변경합니다. 컴파일하고 디폴트 값을
20000(200미터)로 설정합니다. 이 값이 작을수록 플레이어가 가까이 있어야 적이 플레이어를 탐지할 수 있습니다.Debug: 변수로 승격을 선택하고
DebugDetection으로 명명합니다. 이 변수 역시 편집 가능한 상태로 만들면 Setup 카테고리에 나타납니다.
라인 트레이스로 플레이어를 발견한 경우, 플레이어를 향해 적이 이동할 최대 이동 속도를 원하는 정도로 설정합니다. 이동하기를 원하지 않는 경우 속도를 0으로 설정하면 적이 플레이어를 향해 이동하지 않습니다.
적이 플레이어를 발견하면 움직이고 플레이어가 보이지 않으면 멈추게 하려면 다음 단계를 따릅니다.
FnBPLFindPlayer 노드에서 Found 핀을 우클릭하고 변수로 승격을 선택합니다. 그러면 새 변수를 위한 Set 노드가 자동으로 연결됩니다. 이제 이 Found 결과를 그래프의 다른 곳에서 사용할 수 있습니다.
Set 노드 뒤에 Branch 노드를 연결합니다. Branch 노드의 조건 핀을 Set Found 노드의 출력 핀으로 드래그합니다.
컴포넌트 패널에서 캐릭터 이동(CharMoveComp)을 이벤트 그래프로 드래그합니다. 핀을 드래그하여 Set Max Walk Speed 노드를 생성합니다.
Branch 노드의 True 핀을 드래그하여 Set Max Walk Speed 노드에 연결합니다.
Set 노드의 최대 걷기 속도 핀에서 Max Speed 변수에 대한 레퍼런스를 연결합니다.
Set 노드 뒤에 MoveToPlayer 노드를 연결하여 해당 이벤트를 호출하고 적이 플레이어를 찾은 경우에만 이동하게 만듭니다.
Set Max Walk Speed 노드와 Character Movement 노드를 복제합니다. Branch의 False 핀을 새 Set 노드에 연결합니다.
두 번째 Set Max Walk Speed 노드에서 걷기 속도 값이 0인지 확인합니다. 이렇게 하면 AI MoveTo 작업을 취소하고 작업을 다시 실행하지 않아도 적이 이동을 멈춥니다.
이제 적이 프레임마다 플레이어를 찾도록 만들었으므로, Event BeginPlay 로직 끝에 추가한 Move to Player 노드를 삭제합니다. 이 노드는 부분적으로 완료된 적을 테스트하는 데 유용하게 사용했지만 이제는 필요하지 않습니다.
MoveToPlayer 이벤트 로직으로 이동합니다. 이제 플레이어가 발견된 경우에만 적이 이 명령을 실행하도록 해야 합니다.
MoveToPlayer 노드에서 드래그하여 Branch 노드를 생성합니다.
Branch의 조건에서 Found 변수에 대한 레퍼런스를 추가합니다.
플레이어가 적으로부터 지정된 거리 안에 있고 둘 사이에 장애물이 없는 경우, 적의 이동 속도는 MaxSpeed 변수로 설정되고 적이 MoveToPlayer 로직을 실행합니다. 이러한 조건 중 하나라도 충족되지 않으면 적의 이동 속도가 0으로 설정되어 적이 플레이어를 향해 이동하지 않습니다.
BP_Enemy의 최종 변수 목록은 다음과 같습니다.
아래의 블루프린트 스니펫을 BP_Enemy 이벤트 그래프에 붙여 넣을 때는 변수 이름이 샘플 프로젝트의 변수 이름과 일치하는지 확인하세요.
BP_Enemy의 최종 Event BeginPlay 로직은 다음과 같습니다.
BP_Enemy의 최종 MoveToPlayer 로직은 다음과 같습니다.
BP_Enemy의 최종 Event Tick 로직은 다음과 같습니다.
적이 대미지를 받도록 하기
이 블루프린트를 마무리하기 전에, 적 역시 대미지를 받을 수 있도록 만들어야 합니다. 이 튜토리얼 시리즈 이후에도 게임 개발을 계속하는 경우, 적이 함정으로 대미지를 받게 하거나 적에게 대미지를 입히고 적을 처치할 수 있는 장착 가능한 아이템을 플레이어에게 줄 수 있습니다.
적이 대미지를 받게 하려면 다음 단계를 따릅니다.
이벤트 그래프 아무 곳이나 우클릭하고 Event AnyDamage 노드를 생성합니다.
Event AnyDamage의 실행 핀에서 드래그하여 Set CurrentHP 노드를 생성합니다.
받은 대미지를 CurrentHP에서 빼려면 현재 HP 프로퍼티의 핀에서 드래그하여 Subtract 노드를 추가합니다.
Subtract 노드의 상단 핀을 드래그하여 Get Current HP 노드를 생성합니다.
Subtract 노드의 하단 핀을 Event AnyDamage 노드의 대미지 프로퍼티에 연결합니다.
이 액터가 대미지를 받으면 이 로직이 CurrentHP 변수의 값을 취하여 대미지 수치를 뺀 다음 CurrentHP 값을 새 값으로 설정합니다.
다음으로 CurrentHP가 0 이하인지 확인하여 적을 처치해야 합니다.
적의 HP가 0일 때 적을 제거하려면 다음 단계를 따릅니다.
Set 노드의 핀을 드래그하여 새 Branch 노드를 생성합니다.
Branch 노드의 조건 핀을 드래그하여 Less Equal (<=) 노드를 생성합니다.
Set Current HP 노드의 출력 핀을 Less Equal 노드의 상단 입력 핀에 연결합니다. 하단 핀이 0으로 설정되어 있는지 확인합니다.
Branch 노드의 True 핀을 드래그하여 Do Once 노드를 생성합니다.
완료됨 핀을 드래그하여 Set Eliminated 노드를 생성합니다. Set 노드에서 Eliminated 프로퍼티를 true로 토글합니다.
Set 노드 뒤에 Delay 노드를 연결하고 기간을
2로 설정합니다.Delay 노드의 완료됨 핀을 드래그하여 Destroy Actor 노드를 생성하고 타깃 프로퍼티를 self로 설정합니다.
블루프린트를 저장하고 컴파일(Compile)합니다.
BP_Enemy의 최종 Event AnyDamage 로직은 다음과 같습니다.
완성된 적 캐릭터 테스트
이제 게임을 플레이해 보면 적과 플레이어 사이의 거리에 따라 적이 움직이지 않을 수 있는데, 적에게 가까이 다가가면 적이 플레이어를 추격하기 시작합니다.
장애물이 있는 환경에서 테스트해 보세요. 플레이 모드를 종료하고 메인 툴바의 생성 버튼을 누른 다음 셰이프(Shape) > 큐브(Cube)를 선택하여 내비메시 구역 가운데에 윤곽 작업 큐브나 벽을 생성합니다. 방해물이 내비메시를 자릅니다.
플레이어가 이 방해물 뒤에서 시작하면 라인 트레이스가 큐브에 의해 차단되어 적이 플레이어를 찾지 못하게 되고 적이 플레이어를 추격할 수 없게 됩니다. 플레이어가 큐브 주변을 걷고 있을 때 적의 시야에 들어가면 적이 플레이어를 추격하기 시작합니다. 적이 AI MoveTo 액션을 시작하면 방해물 뒤에 숨어도 적이 추격을 멈추지 않습니다.
프로젝트에서 적 위치 배치
샘플 레벨에서는 Hallway 3과 Room 3에 몇 명의 적을 배치했습니다. 첫 번째 적은 Hallway 3에 혼자 있습니다. 이는 플레이어가 적을 처음 마주쳐 적 캐릭터의 비헤이비어와 어빌리티를 파악하도록 돕기 위한 것입니다. 복도에서 적 한 명과 마주치고 나면, 플레이어에게 더 어려운 도전이 되도록 Room 3에는 두 명의 적 캐릭터를 추가로 배치했습니다.
적의 수와 유형을 다양하게 설정하여 프로젝트 전반에서 각기 다른 수준의 난이도를 제시할 수 있습니다. 플레이어에게 새로운 적과 도전 요소를 서서히 드러내면 균형 잡힌 난이도 커브를 설정할 수 있어, 플레이어가 시간이 지남에 따라 게임을 익힐 수 있게 됩니다. 플레이어가 길을 찾고 적을 물리치는 방법을 배울 수 있게 한 다음에 어려운 적과 마주치게 해야 플레이어가 성취감을 느낄 수 있는 도전이 됩니다.
다음 순서
다음 단원에서는 플레이어의 이동 세트에 전력질주 메커니즘을 추가하는 방법을 알아봅니다. 이 메커니즘은 플레이어가 레벨에서 익숙한 공간 사이를 빠르게 이동하도록 돕고, 방금 프로젝트에 추가한 적을 피할 수 있게 해줍니다.