블루프린트는 "동작을 가진 설계도"다
지금까지 우리는 레벨에 바닥, 벽, 조명, 머티리얼을 배치했다. 아직은 보기 좋은 공간에 가깝다. 이제부터는 게임답게 "무슨 일이 일어나는지"를 만들 차례다.
언리얼 엔진의 블루프린트(Blueprint)는 코드를 직접 길게 쓰지 않고, 노드를 선으로 연결해 게임 규칙을 만드는 방식이다. 버튼을 누르면 문이 열리고, 아이템에 닿으면 점수가 오르고, 체력이 0이 되면 게임이 끝나는 규칙을 만들 수 있다. 블루프린트는 이벤트, 변수, 함수, 노드 그래프를 사용해 로직을 구성하는 시각적 스크립트 시스템으로 설명할 수 있다.1
이 장에서는 아주 작은 기능부터 만든다. 목표는 "수집 아이템처럼 보이는 오브젝트가 게임 시작 시 메시지를 출력하고, 내부 점수를 바꾸는 흐름"을 이해하는 것이다. 아직 진짜 충돌 수집까지 만들지는 않는다. 먼저 블루프린트가 어떻게 실행되는지 눈으로 확인하자.
AI 생성 이미지
Blueprint Class 이해하기
Blueprint Class는 기능을 가진 오브젝트의 설계도다. 예를 들어 BP_PickupCoin이라는 Blueprint Class를 만들면, 그 안에 "어떤 모양인지", "게임이 시작되면 무엇을 할지", "수집되면 점수를 얼마나 줄지" 같은 규칙을 넣을 수 있다.
여기서 중요한 구분이 있다.
Blueprint Class: 오브젝트를 만들기 위한 설계도
인스턴스(Instance): 그 설계도를 레벨에 실제로 배치한 것
비유하면 쿠키 틀이 Blueprint Class이고, 그 틀로 찍어낸 쿠키 하나하나가 인스턴스다. BP_PickupCoin을 한 번 만들어 두면 레벨에 여러 개 배치할 수 있다. 나중에 설계도를 수정하면 같은 Blueprint Class를 사용하는 아이템들의 동작도 함께 정리하기 쉬워진다.
이 방식은 아이템, 문, 버튼, 함정, 적 캐릭터처럼 "반복해서 쓰는 게임 요소"를 만들 때 특히 좋다.
에디터에서 BP_PickupCoin 만들기
먼저 간단한 블루프린트 클래스를 만들자.
Content Browser에서
Blueprints폴더를 연다. 없다면 새로 만든다.빈 공간에서 마우스 오른쪽 버튼을 누른다.
Blueprint Class를 선택한다.
부모 클래스로 Actor를 선택한다.
이름을
BP_PickupCoin으로 바꾼다.
여기서 Actor는 레벨에 배치될 수 있는 기본 오브젝트다. 아이템도 Actor로 만들 수 있고, 문이나 버튼도 Actor로 만들 수 있다.
BP_PickupCoin을 더블 클릭하면 블루프린트 에디터가 열린다. 왼쪽에는 Components 패널, 가운데에는 Viewport나 Event Graph, 오른쪽에는 Details 패널이 보인다.
이제 아이템이 화면에 보이도록 간단한 메시를 추가하자.
Components 패널에서 Add 또는 Add Component를 누른다.
Static Mesh를 추가한다.
이름을
CoinMesh로 바꾼다.Details 패널의 Static Mesh에
Sphere나 이전 장에서 사용한 수집 아이템용 메시를 넣는다.Sphere가 보이지 않으면Shape_Sphere처럼 기본 구체 메시를 검색해도 된다.머티리얼에는 이전 장에서 만든 밝은 색 머티리얼을 적용한다.
작업한 뒤에는 상단의 Compile과 Save를 누른다. 블루프린트에서는 자주 컴파일하고 저장하는 습관이 중요하다. 한 번에 많이 만들고 실행하면 어디서 문제가 생겼는지 찾기 어렵다.
Event BeginPlay: 게임이 시작될 때 한 번 실행하기
이제 Event Graph 탭으로 이동하자. 기본으로 몇 개의 이벤트 노드가 보일 수 있다. 그중 Event BeginPlay를 찾는다.
Event BeginPlay는 해당 Actor가 게임 세계에서 시작될 때 한 번 실행되는 이벤트다. 레벨에 배치된 아이템이라면 Play 버튼을 눌렀을 때 실행된다. 그래서 초기 설정, 시작 메시지, 변수 초기화 같은 작업에 자주 사용한다.
가장 먼저 확인용 메시지를 출력해 보자.
Event BeginPlay의 흰색 실행 핀에서 드래그한다.검색창에
Print String을 입력한다.Print String 노드를 선택한다.
In String 값에
Coin Ready라고 입력한다.Compile과 Save를 누른다.
BP_PickupCoin을 레벨에 드래그해 배치한다.Play를 누른다.
화면 왼쪽 위에 Coin Ready가 보이면 성공이다. 만약 BP_PickupCoin을 여러 개 배치했다면 배치한 개수만큼 메시지가 출력될 수 있다. 각 인스턴스가 자기 BeginPlay를 한 번씩 실행하기 때문이다.
Print String은 블루프린트가 제대로 실행되는지 확인할 때 쓰는 가장 쉬운 디버깅 도구다. 디버깅은 문제의 원인을 찾는 과정이다. 초보자는 "왜 안 되지?"라는 순간을 자주 만난다. 그럴 때 Print String을 중간중간 넣으면 어느 지점까지 실행됐는지 바로 확인할 수 있다.
노드와 와이어 읽는 법
블루프린트 그래프는 처음 보면 복잡해 보인다. 하지만 기본 규칙은 단순하다.
흰색 선은 실행 흐름이다. 어떤 노드가 먼저 실행되고, 다음에 어떤 노드가 실행되는지 알려 준다. 보통 왼쪽에서 오른쪽으로 읽는다. 이벤트 노드에서 시작해 흰색 선을 따라가면 실제로 실행되는 순서를 알 수 있다.
색이 있는 선은 데이터 흐름이다. 숫자, 문자열, 참/거짓, 위치 값 같은 정보가 이동한다. 실행 순서를 정하는 선이 아니라, 노드가 계산에 사용할 값을 전달하는 선이다. 블루프린트에서는 실행 핀과 데이터 핀을 구분해서 읽는 것이 중요하다.2
간단히 정리하면 이렇다.
흰색 와이어: "무엇을 먼저 실행할까?"
색 있는 와이어: "어떤 값을 사용할까?"
이벤트 노드: 실행 흐름의 시작점
Set 노드: 변수 값을 바꿈
Get 노드: 변수 값을 읽음
만약 노드를 만들었는데 아무 일도 일어나지 않는다면 먼저 흰색 실행 선이 연결되어 있는지 확인하자. 노드가 그래프에 놓여 있어도 실행 흐름과 연결되지 않으면 실행되지 않는다.
변수로 점수 저장하기
게임은 값을 기억해야 한다. 점수, 체력, 이동 속도, 남은 시간, 열쇠 개수는 모두 값이다. 블루프린트에서는 이런 값을 변수(Variable)에 저장한다.
이번에는 Score 변수를 만들어 보자.
왼쪽 My Blueprint 패널에서 Variables 옆의
+버튼을 누른다.변수 이름을
Score로 바꾼다.변수 타입을 Integer로 설정한다. Integer는 정수, 즉 0, 1, 2 같은 소수점 없는 숫자다.
Compile을 누른다.
Default Value를
0으로 둔다.
이제 점수를 1 올리는 흐름을 만들자.
Event Graph 빈 공간에서 마우스 오른쪽 버튼을 누른다.
Add Custom Event를 검색해 추가한다.이벤트 이름을
Test_AddScore로 바꾼다.Score변수를 그래프로 드래그한다.메뉴가 나오면 Get Score를 선택한다.
+노드를 만들어Score + 1이 되게 연결한다.다시
Score변수를 드래그하고 Set Score를 선택한다.Test_AddScore의 실행 핀을Set Score의 실행 핀에 연결한다.Score + 1의 결과를Set Score의 값 입력에 연결한다.Set Score뒤에Print String을 연결하고Score Added라고 출력한다.
여기서 Custom Event는 우리가 직접 이름을 붙여 만든 이벤트다. 지금은 테스트용이지만, 나중에는 아이템에 닿았을 때 CollectItem 같은 Custom Event를 호출할 수 있다. 버튼, 문, 함정, 퀘스트 시작처럼 "필요한 순간에 부를 동작"을 만들 때 사용한다.
이제 Event BeginPlay 뒤에서 Test_AddScore를 호출해 보자.
Event BeginPlay의 Print String 뒤쪽 실행 핀에서 드래그한다.Test_AddScore를 검색한다.Call Test_AddScore를 선택한다.
Compile, Save 후 Play를 누른다.
화면에 Coin Ready가 나오고 이어서 Score Added가 나오면 흐름이 실행된 것이다. 지금 점수는 화면에 숫자로 보이지 않지만, 블루프린트 안에서는 Score 값이 0에서 1로 바뀌었다.
조금 더 확인하고 싶다면 Set Score 뒤의 Print String에서 실제 점수 값을 출력할 수도 있다. 예를 들어 Score를 다시 Get으로 가져와 Print String의 In String에 연결하면, 언리얼이 숫자를 문자열로 바꾸는 변환 노드를 제안하거나 자동으로 넣어 준다. 처음에는 보기 좋은 출력 문장보다 "실행됐는지 확인하는 것"을 우선하자.
Event Tick은 조심해서 사용하기
Event Tick도 기본 이벤트 중 하나다. 이 이벤트는 매 프레임 실행된다. 프레임은 화면이 한 번 갱신되는 단위다. 게임이 초당 60프레임으로 실행된다면 Tick은 1초에 약 60번 실행된다.
그래서 Tick은 계속 변해야 하는 작업에 어울린다. 예를 들어 아이템을 천천히 회전시키거나, 시간이 흐르는 값을 계산할 때 쓸 수 있다. 하지만 무거운 계산이나 Print String을 Tick에 연결하면 화면에 메시지가 너무 많이 나오고 성능에도 좋지 않다. Event Tick은 매 프레임 실행되므로 과하게 사용하지 않는 것이 좋다.2
간단히 아이템을 회전시키고 싶다면 다음 흐름을 만들 수 있다.
변수
RotateSpeed를 만든다.타입은 Float로 설정한다. Float는 소수점이 있는 숫자다.
기본값을
90.0으로 둔다.Event Tick에서Delta Seconds와RotateSpeed를 곱한다.그 값을 Z 회전값으로 사용해
Add Actor Local Rotation에 연결한다.
여기서 Delta Seconds는 지난 프레임에서 현재 프레임까지 걸린 시간이다. 컴퓨터 성능에 따라 프레임 속도가 달라져도 비슷한 속도로 움직이게 만들 때 사용한다. 그래서 RotateSpeed만 바로 쓰는 것보다 RotateSpeed × Delta Seconds를 쓰면 프레임 수가 달라도 회전 속도를 더 일정하게 맞출 수 있다.
처음에는 Tick을 반드시 외울 필요가 없다. 규칙은 하나만 기억하자.
"계속 반복되어야 하는 일만 Tick에 넣는다."
점수 증가, 문 열기, 아이템 수집처럼 특정 순간에 한 번 일어나는 일은 BeginPlay, Custom Event, 충돌 이벤트 같은 다른 시작점을 사용하는 편이 더 좋다.
자주 막히는 부분 확인하기
블루프린트가 안 될 때는 아래 순서대로 확인하자.
첫째, Actor가 레벨에 배치되어 있는지 본다. BP_PickupCoin 클래스를 만들기만 하고 레벨에 놓지 않으면 레벨에 배치된 인스턴스의 BeginPlay가 실행될 수 없다.
둘째, 흰색 실행 와이어가 연결되어 있는지 본다. Print String 노드가 있어도 실행 핀이 연결되지 않으면 아무 메시지도 나오지 않는다.
셋째, Compile을 눌렀는지 확인한다. 변수 타입을 바꾸거나 새 노드를 연결한 뒤에는 Compile로 오류를 확인하는 습관을 들이자.
넷째, 변수의 Get과 Set을 구분한다. Get은 값을 읽기만 한다. Set은 값을 바꾼다. 점수를 올리고 싶은데 Get만 사용했다면 실제 점수는 변하지 않는다.
다섯째, Tick에 Print String을 바로 연결하지 않는다. 테스트로 잠깐 사용할 수는 있지만, 메시지가 너무 많이 출력되어 오히려 확인이 어려워진다.
다음 장으로 이어지는 생각
이번 장에서 만든 것은 아주 작다. 하지만 핵심 구조는 대부분의 게임 기능에 그대로 쓰인다.
아이템: 수집 이벤트가 실행되면 점수 변수를 올린다.
문: 버튼 이벤트가 실행되면 문 Actor를 이동하거나 회전한다.
체력: 피해 이벤트가 실행되면 Health 변수를 줄인다.
속도: 달리기 이벤트가 실행되면 Speed 변수를 바꾼다.
UI: 점수 변수가 바뀌면 화면의 숫자를 갱신한다.
블루프린트는 한 번에 많은 노드를 외우는 공부가 아니다. "언제 실행되는가", "어떤 값이 바뀌는가", "실제로 실행됐는가"를 확인하는 연습이다. 오늘은 BeginPlay, Tick, Custom Event, 변수, Print String만 기억해도 충분하다.
다음 장에서는 플레이어 캐릭터와 카메라를 다루며, 우리가 만든 게임 규칙이 플레이어의 움직임과 어떻게 연결되는지 살펴본다.
참고
1Unreal Engine Blueprint Basics - Complete Visual Scripting Guide
이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.