본문 바로가기

흐름 제어 노드 (분기, 루프) 사용법

Branch와 Sequence, For Loop·For Each Loop의 실행 방식을 익혀 조건과 반복이 있는 그래프를 구성합니다.

지금까지 만든 그래프는 이벤트에서 출발해 노드를 한 줄로 따라가며 실행되었습니다. 하지만 실제 게임 로직은 한 줄로 끝나지 않습니다. 체력이 남아 있으면 계속 싸우고 없으면 게임 오버를 띄워야 하고, 적이 열 마리라면 열 마리 모두에게 같은 처리를 해야 합니다. 조건에 따라 길을 나누고, 같은 일을 여러 번 되풀이하고, 몇 번 실행될지를 제한하는 일이 필요합니다.

이런 일을 하는 노드를 흐름 제어(flow control) 노드라고 합니다. 도로의 갈림길이나 신호등처럼 실행이 나아갈 길을 정하는 노드들입니다. 이 글에서는 조건으로 실행을 나누는 Branch, 여러 경로를 차례로 실행하는 Sequence, 통과 횟수를 제한하는 Do N과 Do Once와 Gate, 그리고 반복을 맡는 For Loop와 For Each Loop를 차례로 살펴봅니다.


조건으로 길을 나누는 Branch

Branch 노드는 가장 기본적인 흐름 제어 노드로, 조건(condition)이 참인지 거짓인지에 따라 실행을 두 갈래로 나눕니다. "만약 이 조건이 맞으면 A를 하고, 아니면 B를 하라"는 지시를 노드 하나로 나타낸 것입니다. 텍스트 코드의 if문에 해당합니다.

Branch 노드에는 실행 입력 핀 하나와 Condition 데이터 입력 핀 하나가 있고, 오른쪽에 True와 False 실행 출력 핀이 있습니다. 실행이 들어오면 Condition에 연결된 Boolean 값을 확인해, 값이 True이면 True 출력으로, False이면 False 출력으로 실행을 넘깁니다. 두 출력 가운데 한쪽만 실행되며, 연결하지 않은 출력으로 가게 되면 그 실행은 거기서 끝납니다.

플레이어의 체력이 0보다 큰지 확인해 살아 있는지 판단하는 예를 만들어 보겠습니다. 액터가 대미지를 받을 때 실행되는 Event AnyDamage 노드의 실행 핀에서 끌어 Branch를 만듭니다. 현재 체력을 담은 정수 변수 CurrentHealth의 Get 노드를 놓고, 그 출력을 정수 비교 노드 >의 첫 번째 입력에 연결한 뒤 두 번째 입력에 0을 입력합니다. 비교 노드의 Boolean 출력을 Branch의 Condition에 연결하고, True 출력에는 플레이어 생존!을, False 출력에는 게임 오버!를 출력하는 Print String을 각각 잇습니다.

이 그래프는 지금의 체력을 검사할 뿐, 받은 Damage만큼 체력을 깎아 저장하지는 않는다는 점에 주의합니다. 대미지를 반영한 뒤의 상태로 판정하려면 Branch 앞에서 체력을 줄여 Set하는 과정을 먼저 넣어야 합니다. 또 Event AnyDamage는 서버에서만 실행되는 이벤트이므로, 네트워크 플레이에서는 서버 쪽의 실행을 확인해야 합니다. 이 내용은 블루프린트에서의 리플리케이션에서 다룹니다.


실행 순서와 통과 횟수 정하기

참과 거짓 두 갈래로 나누는 것만으로 부족할 때가 있습니다. 여러 작업을 정해진 순서로 실행하거나, 같은 경로를 몇 번까지만 통과하게 하거나, 특정 상태에서만 통과하게 하고 싶을 때 쓰는 노드들입니다.

Sequence 노드는 실행 입력 하나를 받아 여러 실행 출력 Then 0, Then 1, …을 차례로 실행합니다. 첫 출력에 이어진 노드들이 끝까지 실행되고 돌아오면 다음 출력으로 넘어가는 방식입니다. 오브젝트를 활성화하고, 효과음을 재생하고, 이펙트를 띄우는 것처럼 서로 독립적인 작업을 정해진 순서로 실행할 때 그래프를 갈래별로 정리하기 좋습니다. 다만 앞 출력에 Delay나 시간이 걸리는 작업이 있으면, Sequence는 그 작업이 끝나기를 기다리지 않고 다음 출력으로 넘어갑니다. Sequence가 기다리는 것은 앞 출력의 노드들이 지금 바로 실행할 수 있는 부분까지입니다.

Do N 노드는 Enter로 들어온 실행을 N번까지만 Exit로 통과시키고, 그 뒤로는 막습니다. N은 정수로 정합니다. 적이 특정 스킬을 최대 세 번까지만 쓰게 하는 식으로 실행 횟수를 제한할 때 쓰고, Reset 입력에 실행을 넣으면 횟수가 처음으로 돌아갑니다. Do Once는 N이 1인 Do N처럼 동작하는 별도의 노드로, 문이 처음 열릴 때만 잠금 해제 소리를 내는 것처럼 한 번만 일어나야 하는 일에 씁니다. 핀 이름은 Do N과 달라서, 통과한 실행은 Exit가 아니라 Completed 출력으로 나갑니다. 이 노드도 Reset으로 다시 한 번 통과할 수 있게 됩니다.

Gate 노드는 이름 그대로 문처럼 동작합니다. Open, Close, Toggle 입력으로 문을 열고 닫으며, Enter로 들어온 실행은 문이 열려 있을 때만 Exit로 나갑니다. 플레이어가 특정 지역 안에 있을 때만 버튼이 동작하게 하려면, 지역에 들어올 때 Open을, 나갈 때 Close를 실행하고 버튼 입력을 Enter에 이으면 됩니다.


같은 일을 되풀이하는 Loop

반복은 같은 작업을 여러 번 해야 할 때 씁니다. 블루프린트에서 가장 많이 쓰는 반복 노드는 정해진 횟수만큼 도는 For Loop와, 배열의 요소를 하나씩 도는 For Each Loop입니다.

For Loop는 First Index부터 Last Index까지 정수를 하나씩 세면서 Loop Body를 반복 실행합니다. 두 값은 양 끝을 모두 포함하므로, First Index가 0이고 Last Index가 2이면 Index 0, 1, 2로 세 번 실행됩니다. 지금 몇 번째 반복인지는 Index 출력 핀으로 알 수 있고, 모든 반복이 끝나면 Completed 실행 출력이 한 번 실행됩니다. 요소가 10개인 배열을 인덱스로 처리한다면 보통 0부터 9까지로 정합니다.

For Each Loop는 연결한 배열(array)의 요소를 처음부터 차례로 처리합니다. Array 입력에 배열을 연결하면, 반복할 때마다 Loop Body가 실행되면서 그 회차의 요소가 Array Element 핀으로, 그 요소의 위치가 Array Index 핀으로 나옵니다. 배열을 다 돌면 Completed가 실행됩니다. 레벨의 적 목록이 배열로 있을 때 모든 적에게 대미지를 주는 처리에 알맞습니다. 배열은 구조체와 열거형의 기본 이해 이후의 데이터 장에서 더 자주 쓰게 됩니다.

Loop와 Branch를 함께 쓸 때는 Branch의 위치가 뜻을 정합니다. 반복 전체를 실행할지 말지를 정하는 조건은 Loop 앞에 Branch로 두고, 요소마다 처리할지 말지를 정하는 조건은 Loop Body 뒤에 Branch로 둡니다. Loop Body 뒤의 Branch에서 False로 가더라도 그 회차의 처리만 건너뛸 뿐, 반복 자체가 멈추지는 않습니다. 원하는 요소를 찾은 뒤 반복을 중간에 끝내고 싶다면 Break 입력이 있는 For Loop with Break나 For Each Loop with Break를 씁니다.

인덱스 0부터 2까지 처리하는 작은 예로 두 실행 출력의 역할을 따라가 보겠습니다.

반복마다 실행할 일과 완료 후 실행할 일을 나눕니다

For Loop 0..2의 Loop Body가 세 번 실행되고 그 뒤 Completed가 한 번 실행되는 경로에서 실행 횟수와 현재 인덱스 상태를 확인한다.

FOR LOOP · 0 THROUGH 2

Loop Body는 세 번, Completed는 종료 후 한 번

First Index=0과 Last Index=2는 양 끝을 포함합니다. 각 Index로 처리할 일과 반복이 모두 끝난 뒤 할 일을 서로 다른 실행 출력에 연결합니다.

For Loop의 서로 다른 실행 출력First Index 0 Last Index 2에서 Loop Body는 Index 0, 1, 2로 세 번 실행되고 Completed는 반복 종료 후 한 번 실행된다. 두 출력은 다른 후속 노드로 연결된다.Loop Body · 각 반복Completed · 반복 종료 후For LoopFirst Index = 0Last Index = 2Index로 처리0, 1, 2 → 세 번전체 결과 정리완료 후 한 번
For Loop의 서로 다른 실행 출력First Index 0 Last Index 2에서 Loop Body는 Index 0, 1, 2로 세 번 실행되고 Completed는 반복 종료 후 한 번 실행된다. 두 출력은 다른 후속 노드로 연결된다.Loop BodyCompleted · 반복 종료 후For LoopFirst = 0 · Last = 2Index로 처리0, 1, 2 → 세 번전체 결과 정리완료 후 한 번

동기 작업만 있는 연결 예입니다. Loop Body에서 시작한 비동기 작업의 완료를 Completed가 기다려 주는 것은 아닙니다.

이 예의 조건

  • 본문과 원 도표에 제시된 사례의 입력·조건을 사용합니다. 설명용 수치나 독립 비교 경로는 각 단계에 표시합니다.
  • 현재 상태·값과 전환 근거의 위치는 모든 단계에서 같습니다. 조건이 맞지 않거나 실행되지 않은 경로를 성공한 동작으로 해석하지 않습니다.
예의 값과 변화
관찰·상황값·보충 정보변화·이유
Loop 시작

First 0과 Last 2로 For Loop를 시작한다.

  • FirstIndex: 0
  • LastIndex: 2
  • LastIndex를 포함하는 실행 범위다.
본문 세 번

각 index의 Loop Body를 실행한다.

  • 현재 index: 0→1→2
  • Loop Body: 총 3번
  • 본문 하나를 마친 뒤 다음 index로 진행한다.
Completed

마지막 회차 뒤 완료 핀이 한 번 실행된다.

  • Completed: 1번
  • 현재: Loop 뒤
  • Completed는 각 회차마다 실행되는 핀이 아니다.

반복마다 실행할 일과 완료 후 실행할 일을 나눕니다

For Loop 0..2의 Loop Body가 세 번 실행되고 그 뒤 Completed가 한 번 실행되는 경로에서 실행 횟수와 현재 인덱스 상태를 확인한다.

FOR LOOP · 0 THROUGH 2

Loop Body는 세 번, Completed는 종료 후 한 번

First Index=0과 Last Index=2는 양 끝을 포함합니다. 각 Index로 처리할 일과 반복이 모두 끝난 뒤 할 일을 서로 다른 실행 출력에 연결합니다.

For Loop의 서로 다른 실행 출력First Index 0 Last Index 2에서 Loop Body는 Index 0, 1, 2로 세 번 실행되고 Completed는 반복 종료 후 한 번 실행된다. 두 출력은 다른 후속 노드로 연결된다.Loop Body · 각 반복Completed · 반복 종료 후For LoopFirst Index = 0Last Index = 2Index로 처리0, 1, 2 → 세 번전체 결과 정리완료 후 한 번
For Loop의 서로 다른 실행 출력First Index 0 Last Index 2에서 Loop Body는 Index 0, 1, 2로 세 번 실행되고 Completed는 반복 종료 후 한 번 실행된다. 두 출력은 다른 후속 노드로 연결된다.Loop BodyCompleted · 반복 종료 후For LoopFirst = 0 · Last = 2Index로 처리0, 1, 2 → 세 번전체 결과 정리완료 후 한 번

동기 작업만 있는 연결 예입니다. Loop Body에서 시작한 비동기 작업의 완료를 Completed가 기다려 주는 것은 아닙니다.

이 예의 조건

  • 본문과 원 도표에 제시된 사례의 입력·조건을 사용합니다. 설명용 수치나 독립 비교 경로는 각 단계에 표시합니다.
  • 현재 상태·값과 전환 근거의 위치는 모든 단계에서 같습니다. 조건이 맞지 않거나 실행되지 않은 경로를 성공한 동작으로 해석하지 않습니다.
예의 값과 변화
관찰·상황값·보충 정보변화·이유
Loop 시작

First 0과 Last 2로 For Loop를 시작한다.

  • FirstIndex: 0
  • LastIndex: 2
  • LastIndex를 포함하는 실행 범위다.
본문 세 번

각 index의 Loop Body를 실행한다.

  • 현재 index: 0→1→2
  • Loop Body: 총 3번
  • 본문 하나를 마친 뒤 다음 index로 진행한다.
Completed

마지막 회차 뒤 완료 핀이 한 번 실행된다.

  • Completed: 1번
  • 현재: Loop 뒤
  • Completed는 각 회차마다 실행되는 핀이 아니다.

위 그림은 First Index가 0, Last Index가 2인 For Loop의 두 실행 출력을 서로 다른 일에 연결한 모습입니다. 위쪽의 Loop Body는 반복마다 실행되어 Index 0, 1, 2로 각각의 처리를 하므로 모두 세 번 실행되고, 아래쪽의 Completed는 반복이 끝난 뒤 전체 결과를 정리하는 일에 연결되어 한 번만 실행됩니다. 그림 아래의 설명처럼 이 연결은 동기 작업만 있는 예입니다. Loop Body에서 Delay나 비동기 작업을 시작했다면, Completed는 그 작업이 끝나기를 기다려 주지 않습니다.

단계 그림은 같은 흐름을 세 단계로 나눕니다. 먼저 First 0과 Last 2로 For Loop가 시작됩니다. 다음으로 Index 0, 1, 2마다 Loop Body가 한 번씩, 모두 세 번 실행됩니다. 마지막 회차가 끝난 뒤에 Completed가 한 번 실행됩니다. 그래서 반복마다 할 일과 반복이 모두 끝난 뒤에 할 일을 같은 출력에 섞어 연결하면, 정리 작업이 세 번 실행되거나 각 회차의 처리가 한 번만 일어나는 버그가 생깁니다.

반복 노드는 Delay 없이 실행된 자리에서 마지막 회차까지 이어서 실행된다는 점도 기억해 둡니다. 그래서 반복 횟수가 아주 많으면 그 실행 하나가 길어져 화면이 끊기는 등 성능에 부담이 되며, 공식 문서도 큰 반복은 성능을 떨어뜨릴 수 있다고 경고합니다. 끝나지 않는 반복은 엔진이 무한 루프로 판단해 실행을 중단시키므로, 큰 작업은 여러 번에 나누어 처리하는 방법을 검토합니다.


질문에 맞는 노드 고르기

흐름 제어 노드를 고를 때는 지금 실행 흐름에 어떤 질문을 던지고 있는지를 먼저 구분하면 쉽습니다. 참이냐 거짓이냐를 묻는다면 Branch를, 여러 작업을 어떤 순서로 할지를 정한다면 Sequence를, 몇 번까지 허용할지를 정한다면 Do N이나 Do Once를, 지금 상태에서 통과를 허용할지를 정한다면 Gate를, 같은 일을 몇 번 또는 어떤 목록에 대해 할지를 정한다면 Loop를 씁니다.

선택지가 셋 이상인 실행 분기라면 Branch를 여러 개 겹치기보다 정수나 열거형, 문자열 값에 따라 실행 출력을 고르는 Switch 계열 노드를 검토합니다. 실행 경로가 아니라 값만 고르면 되는 경우에는 조건에 따라 두 값 가운데 하나를 내보내는 Select 노드가 더 간단합니다.


정리

  • Branch는 Boolean 조건에 따라 실행을 True와 False 두 갈래로 나누며, 두 출력 가운데 한쪽만 실행됩니다.
  • Sequence는 여러 실행 출력을 차례로 실행하지만, 앞 출력에서 시작한 Delay나 비동기 작업이 끝나기를 기다리지는 않습니다.
  • Do N은 N번까지, Do Once는 한 번만 실행을 통과시키고, Reset으로 횟수를 되돌립니다. Gate는 열려 있을 때만 실행을 통과시킵니다.
  • For Loop는 First Index부터 Last Index까지 양 끝을 포함해 반복하고, For Each Loop는 배열의 요소를 Array Element와 Array Index로 차례로 내보냅니다.
  • Loop Body는 반복마다, Completed는 반복이 끝난 뒤 한 번 실행됩니다. 반복을 중간에 끝내려면 With Break 변형을 씁니다.
  • 셋 이상의 실행 분기에는 Switch를, 값만 고를 때는 Select를 검토합니다.

연습 문제

1. 반복 횟수와 결과 따로 예상하기

For Loop의 First Index를 1, Last Index를 3으로 정합니다. 반복 전에 정수 Total을 0으로 저장하고, Loop Body에서 매번 Total + Index를 Total에 Set합니다. Completed에서 Total을 출력합니다. Delay나 비동기 작업, Break는 없습니다. Loop Body는 몇 번 실행되고 마지막 출력은 얼마일까요?

해설 보기

First와 Last 모두 포함하므로 Index는 1, 2, 3이고 Loop Body는 세 번 실행됩니다. 저장된 값은 0에서 1, 3, 6으로 바뀌고, 반복이 끝난 뒤 Completed가 한 번 실행되어 6을 출력합니다.

Loop Body 뒤에 Branch를 두어 특정 Index의 처리를 건너뛰더라도, 그것만으로 반복 전체가 끝나지는 않습니다. 반복이 몇 번 돌았는지와 실제로 처리한 요소가 몇 개인지는 따로 세어야 합니다.

2. 체력 판정이 늦게 반영된다

체력이 30인 캐릭터가 대미지 50을 받았는데 플레이어 생존!이 출력되었습니다. 그래프는 Event AnyDamage에서 바로 Branch로 가고, Branch 뒤의 True 경로에서 체력을 50만큼 깎아 Set합니다. 무엇이 문제이고 어떻게 고쳐야 할까요?

해설 보기

Branch가 체력을 깎기 전의 값 30으로 판정했기 때문입니다. 30은 0보다 크므로 True로 갔고, 그 뒤에야 체력이 -20으로 저장되었습니다.

Event AnyDamage 바로 뒤에서 CurrentHealth에서 Damage를 뺀 값을 먼저 Set하고, 그다음 Branch에서 새 체력이 0보다 큰지 판정하도록 순서를 바꿉니다. 그러면 -20으로 판정되어 게임 오버!가 출력됩니다.

3. Sequence 뒤의 Delay

Sequence의 Then 0에 Delay(2초)와 Print String("A")를, Then 1에 Print String("B")를 연결했습니다. 화면에 A와 B는 어떤 순서로, 언제 나타날까요?

해설 보기

B가 먼저 거의 바로 나타나고, A는 약 2초 뒤에 나타납니다. Then 0의 실행은 Delay에 도착하면 2초 뒤에 이어서 실행되도록 예약해 둔 채 돌아오므로, Sequence는 기다리지 않고 바로 Then 1을 실행합니다.

A를 출력한 뒤에 B를 출력하고 싶다면 Sequence를 쓰지 말고 Delay, Print String("A"), Print String("B")를 한 줄의 실행 선으로 차례로 이어야 합니다.

흐름 제어 노드의 종류와 핀은 언리얼 엔진 공식 문서의 흐름 제어 설명에서 확인할 수 있습니다.

다음 글 매크로 생성 및 간단한 사용 예에서는 여러 노드를 실행 핀이 여러 개인 재사용 단위로 묶는 매크로를 만들어 보고, 함수와 무엇이 다른지 비교합니다.