본문 바로가기

변수 유형과 기본 활용법

불리언·정수·실수·문자열·벡터·오브젝트 참조의 차이를 이해하고 변수를 생성해 그래프에서 활용합니다.

게임은 끊임없이 무언가를 기억해야 합니다. 플레이어의 현재 체력, 지금까지 얻은 점수, 캐릭터의 이름, 문이 열렸는지 닫혔는지 같은 정보가 매 순간 바뀌고, 로직은 그 값을 읽어서 다음 행동을 정합니다. 블루프린트에서 이런 바뀌는 정보를 담아 두는 곳이 변수(variable)입니다. 1장에서 BeginPlay에 고정된 문구를 출력했다면, 이제 값을 저장하고 바꾸고 다시 읽는 그래프로 넘어갈 차례입니다.

이 글에서는 블루프린트 변수의 주요 타입과 각 타입이 담는 값을 살펴본 뒤, 변수를 만들고 Get과 Set 노드로 읽고 쓰는 방법을 익힙니다. 마지막으로 점수 변수를 출력하고, 100을 더해 저장하고, 다시 출력하는 작은 그래프로 읽기와 쓰기의 순서를 따라가 봅니다.


이름이 붙은 값의 상자

변수는 데이터를 담는, 이름표가 붙은 상자라고 생각할 수 있습니다. 상자에는 숫자, 글자, 참과 거짓 같은 값이 들어가고, 실행 중에 다른 값으로 바꿔 넣을 수 있습니다. 다만 상자마다 담을 수 있는 값의 종류가 정해져 있는데, 이것을 변수의 타입(type)이라고 합니다. 정수 상자에 글자를 넣을 수 없듯이, 정수 변수의 핀은 정수 핀에만 연결됩니다.

블루프린트에서 만든 변수는 그 블루프린트의 인스턴스마다 따로 존재합니다. BP_WelcomeMessage 인스턴스를 두 개 배치했다면 변수도 두 벌이 생기며, 한 인스턴스의 값을 바꿔도 다른 인스턴스의 값은 그대로입니다. 1장에서 디버그 오브젝트로 인스턴스를 골라야 했던 이유가 여기에 있습니다.


변수의 주요 타입

블루프린트 변수의 타입은 많지만, 대부분의 게임 로직은 다음 몇 가지 기본 타입으로 시작합니다. 핀의 색은 타입마다 정해져 있어서 그래프에서 값의 종류를 빠르게 구분하는 데 도움이 됩니다. 표의 색 이름은 공식 문서의 표기를 우리말로 옮긴 것입니다.

타입담는 값사용 예핀 색
Boolean참(True) 또는 거짓(False)문이 열렸는가, 적을 발견했는가적갈색
Integer소수점 없는 32비트 정수(양수, 음수, 0)점수, 남은 탄약 수, 몬스터 수청록색
Float소수 부분이 있는 실수이동 속도, 남은 시간(초), 가격연두색
String글자의 나열채팅 메시지, 디버그 문구자홍색
Text화면에 보여 줄 문구UI에 표시하는 안내문, 대화분홍색
Name무언가를 가리키는 식별자소켓 이름, 데이터 테이블의 행 이름연보라색
VectorX, Y, Z 세 숫자위치, 방향, 이동량금색
Rotator3차원 회전카메라 시점, 문이 열린 각도푸른색
Transform위치, 회전, 크기를 함께새 액터를 생성할 위치와 방향, 크기주황색
Object 참조다른 오브젝트를 가리키는 값문 액터, 조명, 사운드 에셋파란색

Float는 실수를 유한한 정밀도로 근사해 저장하므로, 모든 소수를 정확히 표현하지는 못합니다. 그래서 0.1을 열 번 더한 값이 정확히 1.0과 같은지 비교하면 기대와 다른 결과가 나올 수 있고, 실수끼리 같은지 볼 때는 Nearly Equal (Float)처럼 오차를 허용하는 비교 노드를 씁니다. 0.1 같은 수가 왜 정확히 담기지 않는지는 소수·부동소수점·문자에서 비트 단위로 설명합니다. UE5부터 블루프린트의 Float는 배정밀도(double) 실수로 다뤄지며, 핀 툴팁에는 real로 표시되기도 합니다. Vector와 Rotator, Transform의 성분도 UE5에서는 큰 월드 좌표를 위해 배정밀도를 쓰므로, 이 타입들이 모두 32비트 실수라고 가정하지 않습니다.

글자를 담는 타입이 세 가지인 이유는 쓰임이 다르기 때문입니다. String은 이어 붙이거나 잘라 내는 등 문자열을 가공하기 쉬운 일반 문자열입니다. Text는 플레이어에게 보여 줄 문구를 위한 타입으로, 여러 언어로 번역하는 현지화(localization)를 지원하므로 UI 문구에 씁니다. Name은 대소문자를 구분하지 않는 식별자로, 소켓이나 행처럼 무언가를 이름으로 찾을 때 씁니다. Name 값 자체를 가공하는 기능은 적지만, Name 변수에 다른 Name 값을 대입하는 것은 자유롭습니다.

Rotator의 세 성분은 X축 회전인 Roll, Y축 회전인 Pitch, Z축 회전인 Yaw이며 단위는 도(degree)입니다. Vector는 위치뿐 아니라 방향이나 크기, RGB 색처럼 숫자 세 개로 표현되는 값이면 무엇이든 담을 수 있습니다.

색은 빠르게 알아보기 위한 보조 수단일 뿐입니다. 비슷한 색의 타입도 있으므로, 연결이 되지 않거나 헷갈릴 때는 핀에 마우스를 올려 툴팁의 정확한 타입 이름을 확인합니다.

값을 복사하지 않는 오브젝트 참조

오브젝트 참조(object reference)는 다른 타입과 성격이 다릅니다. 정수 변수가 숫자 자체를 담는다면, 오브젝트 참조 변수는 액터나 에셋을 복사하지 않고 그 대상을 가리키기만 합니다. 그래서 참조를 통해 대상의 값을 바꾸면 원래 대상이 바뀝니다.

참조는 아무것도 가리키지 않는 None일 수 있고, 가리키던 액터가 게임 도중에 파괴될 수도 있습니다. None이나 파괴된 대상에 접근하면 실행 중 오류가 나므로, 참조를 쓰기 전에는 Is Valid 노드로 유효한지 확인하는 습관을 들입니다. 참조로 다른 블루프린트와 통신하는 방법은 직접 참조를 통한 블루프린트 통신에서 다루고, 여러 값을 하나로 묶는 구조체(Struct)와 정해진 선택지를 나타내는 열거형(Enum)은 구조체와 열거형의 기본 이해에서 다룹니다.

더 깊이: String, Text, Name은 엔진 안에서 어떻게 다를까

세 타입은 엔진의 C++ 타입 FString, FText, FName에 대응하며, 값을 저장하는 방식부터 다릅니다. 문자열 처리 문서에 따르면 FString은 자기 문자 배열을 직접 가지고 있어서 검색하고 고치고 다른 문자열과 비교할 수 있지만, 그만큼 비용이 듭니다. 반면 FName은 엔진이 관리하는 전역 문자열 표(name table)에 한 번만 저장된 문자열을 가리키는 번호와 인스턴스 번호만 담습니다. 그래서 Name끼리 같은지 비교할 때는 글자를 하나씩 대조하지 않고 이 번호만 비교하므로 빠릅니다.

이 구조 때문에 Name은 바꿀 수 없고 대소문자를 구분하지 않습니다. 소켓이나 뼈, 데이터 테이블의 행처럼 이름으로 자주 찾고 비교하는 대상에 Name을 쓰는 이유가 여기에 있습니다. 대신 String을 Name으로 바꾸면 대소문자 정보가 사라질 수 있으므로, 공식 문서는 이 변환을 정보가 손실될 수 있는 변환으로 분류합니다.

Text는 플레이어에게 보여 줄 문구를 위한 타입이라, 현지화된 문구를 만들고 숫자나 날짜를 그 언어의 형식에 맞춰 문구로 바꾸는 기능을 갖추고 있습니다. 그래서 Text를 String으로 바꾸면 일부 언어에서 정보가 손실될 수 있고, String이나 Name에서 만든 Text는 자동 현지화의 대상이 되지 않습니다. 화면에 보일 문구는 처음부터 Text로 만들고, 내부에서 가공할 문자열만 String으로 두는 편이 안전합니다.


변수 만들기

변수는 마이 블루프린트(My Blueprint) 패널에서 만듭니다. Variables 묶음 옆의 + 버튼을 누르면 새 변수가 생기고 이름을 입력할 수 있게 되는데, 예제에서는 PlayerScore라고 입력합니다. 다음으로 변수를 선택한 상태에서 디테일 패널의 Variable Type 드롭다운으로 타입을 고릅니다. 예제에서는 Integer를 선택합니다. 마이 블루프린트에서 변수 이름 옆의 타입 표시를 눌러도 같은 목록이 열립니다.

기본값은 블루프린트를 한 번 컴파일한 뒤 디테일 패널의 Default Value에서 정합니다. 방금 만든 변수는 컴파일하기 전까지 기본값 칸이 나타나지 않기 때문입니다. 여기서 정한 값이 이 블루프린트로 만든 모든 인스턴스가 처음 갖는 값이 됩니다.

디테일 패널에는 변수를 어디서 고칠 수 있는지 정하는 옵션도 있습니다. Instance Editable을 켜면, 마이 블루프린트에서 변수 옆 눈 모양 아이콘이 열린 눈으로 바뀌고, 레벨에 배치한 인스턴스마다 레벨 에디터의 디테일 패널에서 값을 따로 정할 수 있습니다. 이 옵션은 에디터에서 값을 고치는 범위를 정할 뿐, 실행 중에 다른 블루프린트가 이 변수에 접근하는 것을 막는 기능은 아닙니다. 외부 블루프린트가 값을 바꾸지 못하게 하려면 Private 옵션을 따로 켭니다. 이 밖에 Expose on Spawn을 켜면 Spawn Actor from Class 노드로 액터를 생성할 때 이 변수의 값을 핀으로 넘길 수 있습니다.


Get과 Set으로 읽고 쓰기

변수를 그래프에서 쓰는 방법은 두 가지입니다. Get 노드(Getter)는 변수의 현재 값을 출력 핀으로 내보내며, 실행 핀이 없는 순수 노드입니다. Set 노드는 입력 핀으로 받은 값을 변수에 저장하는 실행 노드라서, 실행 선이 들어와야 저장이 일어납니다. 1장에서 본 것처럼 Set 노드의 값 입력만 연결하고 실행 선을 잇지 않으면 변수는 바뀌지 않습니다.

노드를 만들려면 마이 블루프린트의 변수를 그래프로 끌어다 놓고 나타나는 메뉴에서 Get이나 Set을 고릅니다. Ctrl 키를 누른 채 끌어다 놓으면 바로 Get 노드가, Alt 키를 누른 채 끌어다 놓으면 바로 Set 노드가 만들어집니다. 그래프의 빈 곳을 오른쪽 클릭해 Get PlayerScore나 Set PlayerScore를 검색해도 됩니다.


점수를 읽고, 바꾸고, 다시 읽기

이제 PlayerScore로 읽기와 쓰기의 순서를 확인하는 그래프를 만들어 봅니다. 먼저 Integer 타입의 PlayerScore 변수를 만들고, 컴파일한 뒤 기본값을 0으로 둡니다. 다음으로 Event BeginPlay의 실행 핀에 Print String을 잇고, Get PlayerScore를 In String에 연결합니다. 여기까지 컴파일하고 플레이하면 현재 점수 0이 출력됩니다.

이어서 점수에 100을 더하는 부분을 붙입니다. 첫 Print String의 출력 실행 핀에 Set PlayerScore를 잇되, BeginPlay에서 첫 Print String으로 가는 연결은 그대로 둡니다. 새 Get PlayerScore 노드를 놓고 그 출력을 + 노드(정수 덧셈)의 첫 번째 입력에 연결한 다음, 두 번째 입력에 100을 입력합니다. 덧셈 노드의 출력을 Set PlayerScore의 값 입력에 연결합니다. 마지막으로 Set PlayerScore의 출력 실행 핀에 두 번째 Print String을 잇고, 다시 Get PlayerScore를 연결해 바뀐 점수를 출력합니다.

점수 변경 전후의 출력 순서를 연결합니다

PlayerScore 0의 첫 출력→+100 계산→Set 저장 100→둘째 출력 100의 실제 읽기/쓰기 순서이다.

READ · WRITE · READ

초깃값 0에서, 출력 → 저장 → 다시 출력

PlayerScore가 0으로 시작하는 한 번의 BeginPlay 실행입니다. 첫 출력은 0이고, 현재값에 100을 더해 저장한 뒤 두 번째 출력은 100입니다.

PlayerScore가 0에서 시작하는 실행 순서BeginPlay에서 첫 Print String으로 현재값 0을 출력하고 Set PlayerScore가 현재값에 100을 더해 저장한다. 그다음 Print String이 변경된 100을 읽는다.BeginPlayPrint String변경 전: 0Set PlayerScore현재값 + 100Print String변경 후: 100
PlayerScore가 0에서 시작하는 실행 순서BeginPlay에서 첫 Print String으로 현재값 0을 출력하고 Set PlayerScore가 현재값에 100을 더해 저장한다. 그다음 Print String이 변경된 100을 읽는다.BeginPlayPrint String변경 전: 0Set PlayerScore현재값 + 100Print String변경 후: 100

호출 순서를 확인합니다

두 Print String 사이에 Delay가 없는 연결입니다. 화면에 쌓이는 메시지 위치와 실행 순서를 혼동하지 말고 Output Log나 중단점으로 확인합니다.

이 예의 조건

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

PlayerScore를 변경 전에 읽어 출력한다.

  • PlayerScore: 0
  • 첫 출력: 0
  • 읽기는 현재 저장값을 관측한다.
계산·쓰기

0에 100을 더한 값을 Set으로 저장한다.

  • 계산: 0+100=100
  • 저장: PlayerScore 100
  • 더하기 계산만으로 변수가 바뀌지 않으며 Set이 쓰기를 한다.
둘째 읽기

저장 뒤 Getter가 100을 읽는다.

  • PlayerScore: 100
  • 둘째 출력: 100
  • 출력 위치가 Set의 앞뒤인지에 따라 관측값이 다르다.

점수 변경 전후의 출력 순서를 연결합니다

PlayerScore 0의 첫 출력→+100 계산→Set 저장 100→둘째 출력 100의 실제 읽기/쓰기 순서이다.

READ · WRITE · READ

초깃값 0에서, 출력 → 저장 → 다시 출력

PlayerScore가 0으로 시작하는 한 번의 BeginPlay 실행입니다. 첫 출력은 0이고, 현재값에 100을 더해 저장한 뒤 두 번째 출력은 100입니다.

PlayerScore가 0에서 시작하는 실행 순서BeginPlay에서 첫 Print String으로 현재값 0을 출력하고 Set PlayerScore가 현재값에 100을 더해 저장한다. 그다음 Print String이 변경된 100을 읽는다.BeginPlayPrint String변경 전: 0Set PlayerScore현재값 + 100Print String변경 후: 100
PlayerScore가 0에서 시작하는 실행 순서BeginPlay에서 첫 Print String으로 현재값 0을 출력하고 Set PlayerScore가 현재값에 100을 더해 저장한다. 그다음 Print String이 변경된 100을 읽는다.BeginPlayPrint String변경 전: 0Set PlayerScore현재값 + 100Print String변경 후: 100

호출 순서를 확인합니다

두 Print String 사이에 Delay가 없는 연결입니다. 화면에 쌓이는 메시지 위치와 실행 순서를 혼동하지 말고 Output Log나 중단점으로 확인합니다.

이 예의 조건

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

PlayerScore를 변경 전에 읽어 출력한다.

  • PlayerScore: 0
  • 첫 출력: 0
  • 읽기는 현재 저장값을 관측한다.
계산·쓰기

0에 100을 더한 값을 Set으로 저장한다.

  • 계산: 0+100=100
  • 저장: PlayerScore 100
  • 더하기 계산만으로 변수가 바뀌지 않으며 Set이 쓰기를 한다.
둘째 읽기

저장 뒤 Getter가 100을 읽는다.

  • PlayerScore: 100
  • 둘째 출력: 100
  • 출력 위치가 Set의 앞뒤인지에 따라 관측값이 다르다.

위 그림은 PlayerScore가 0으로 시작할 때 한 번의 BeginPlay 실행에서 일어나는 순서를 보여 줍니다. BeginPlay에서 출발한 실행은 첫 Print String에 도착해 변경 전의 값 0을 출력하고, Set PlayerScore로 넘어가 현재 값에 100을 더한 결과를 저장한 뒤, 두 번째 Print String에서 바뀐 값 100을 출력합니다. 그림에서는 덧셈 노드를 Set PlayerScore 안의 "현재값 + 100"으로 줄여 표시했지만, 실제 그래프에서는 앞에서 만든 대로 Get, +, Set 세 노드가 따로 있습니다.

단계 그림은 이 과정을 세 단계로 나눕니다. 첫 읽기 단계에서 Getter가 변경 전의 PlayerScore를 읽어 0을 출력합니다. 계산과 쓰기 단계에서 0에 100을 더한 값을 Set이 저장합니다. 둘째 읽기 단계에서 Getter가 저장된 100을 읽습니다. 같은 Get PlayerScore라도 첫 Print String과 두 번째 Print String이 실행되는 시점의 값을 각각 읽기 때문에, 두 출력이 0과 100으로 달라집니다.

두 Print String 사이에는 Delay 같은 시간 지연이 없으므로 두 메시지는 같은 프레임에 연달아 출력됩니다. 화면에서는 새 메시지가 위쪽에 쌓이므로 메시지가 놓인 위치만 보고 실행 순서를 판단하면 헷갈릴 수 있습니다. 순서를 확실히 보려면 Output Log에 남은 순서를 보거나, 브레이크포인트를 걸고 한 노드씩 따라갑니다.


정리

  • 변수는 타입이 정해진 이름 붙은 값의 상자이며, 블루프린트의 인스턴스마다 따로 존재합니다.
  • 기본 타입으로 Boolean, Integer, Float, String, Text, Name, Vector, Rotator, Transform, 오브젝트 참조가 있고, 핀의 색으로 타입을 구분하되 정확한 이름은 툴팁으로 확인합니다.
  • Float는 근사값이므로 같은지 비교할 때 오차를 허용하며, UE5의 블루프린트 Float와 공간 타입은 배정밀도를 씁니다.
  • 오브젝트 참조는 대상을 복사하지 않고 가리키며, None이거나 파괴되었을 수 있으므로 Is Valid로 확인한 뒤 씁니다.
  • 변수는 마이 블루프린트에서 만들고, 컴파일한 뒤 기본값을 정합니다. Instance Editable은 에디터에서 인스턴스별 값을 고치게 하는 옵션이고 실행 중 접근 제어와는 다릅니다.
  • Get은 실행 핀 없이 현재 값을 내보내고, Set은 실행 선이 들어와야 값을 저장합니다.

연습 문제

1. 시작 값이 다를 때

실행 순서는 그대로 두고, 이번 플레이를 시작할 때 PlayerScore가 30이라고 가정합니다. 첫 Print String, 100을 더해 Set, 두 번째 Print String을 한 번씩 실행하면 두 출력은 얼마일까요? 덧셈 결과만 연결하고 Set의 실행을 빼면 저장된 점수가 바뀔까요?

해설 보기

첫 출력은 30입니다. 덧셈 노드가 30 + 100 = 130을 계산하고 Set이 실행되면 PlayerScore에 130이 저장되므로, 두 번째 출력은 130입니다.

Set의 실행을 빼면 덧셈은 값을 계산할 뿐 변수에 저장하지 않으므로, 저장된 점수는 30 그대로입니다. 실제로 확인할 때는 배치한 인스턴스의 시작 값과 실행 연결을 먼저 보고, 원래 예제의 기본값 0과 섞지 않습니다.

2. 알맞은 타입 고르기

다음 네 가지 값을 저장할 변수의 타입을 고르고 이유를 적어 보세요. (가) 상점 화면에 보여 줄 "구매하시겠습니까?" 문구, (나) 남은 시간 12.5초, (다) 상자가 이미 열렸는지 여부, (라) 데이터 테이블에서 찾을 행 이름 Potion.

해설 보기

(가)는 플레이어에게 보여 주는 문구이고 번역될 수 있으므로 Text가 알맞습니다. (나)는 소수 부분이 있는 값이므로 Float입니다. (다)는 열렸거나 열리지 않은 두 상태뿐이므로 Boolean입니다.

(라)는 무언가를 이름으로 찾는 식별자이므로 Name이 알맞습니다. Name은 대소문자를 구분하지 않으므로 potion으로 찾아도 같은 행을 가리킵니다.

3. 인스턴스마다 다른 시작 점수

같은 블루프린트로 만든 액터 두 개를 배치하고, 하나는 시작 점수 0, 다른 하나는 50으로 시작하게 하고 싶습니다. 블루프린트를 두 개로 나누지 않고 이렇게 하려면 어떤 옵션을 켜고 어디서 값을 정해야 할까요? 그 옵션을 켜면 다른 블루프린트가 실행 중에 이 변수를 바꾸지 못하게 될까요?

해설 보기

PlayerScore의 Instance Editable을 켜고 컴파일한 뒤, 레벨 에디터에서 두 번째 인스턴스를 선택해 디테일 패널에서 값을 50으로 바꿉니다. 첫 번째 인스턴스는 블루프린트의 기본값 0을 그대로 씁니다.

Instance Editable은 에디터에서 인스턴스별 값을 고칠 수 있게 하는 옵션일 뿐이라서, 실행 중에 다른 블루프린트의 접근을 막지는 않습니다. 외부에서 바꾸지 못하게 하려면 Private 옵션을 따로 정해야 합니다.

변수 타입과 색, 변수 옵션은 언리얼 엔진 공식 문서의 블루프린트 변수 설명에서 확인할 수 있습니다.

다음 글 함수 정의 및 기본 호출 방법에서는 여러 노드를 함수로 묶고 입력과 출력을 정해, 같은 로직을 여러 곳에서 호출하는 방법을 익힙니다.