[내일배움캠프] 본 과정 학습내용

[내배캠] 3일차 Unreal Blueprint로 총 기능 구현과 C언어 기초

홍이니 2026. 6. 30. 21:16

안녕하세요!

오늘은 오전 Unreal 강의에서 Blueprint를 이용해 FPS게임에서의 줌 기능과 총알 발사, 총알 장전 기능을 구현하였습니다.

 

이전 시간에 마우스 우클릭을 하면 화면에 "조준모드"와 "일반모드"가 번갈아 가면서 출력되도록 구현하였습니다.

오늘은 문자열 출력을 대신해 실질적으로 플레이어 화면이 줌 인 되도록 구현하였습니다.

 

Add Timeline 노드를 통해 기본 상태에서는 보간(Lerp)을 통해 카메라가 부드럽게 줌 인이 되고 조준 상태라면 부드럽게 줌 아웃이 되도록 구현했습니다.

Timeline 노드를 더블 클릭하여 들어오면 어떻게 보간할 것인지 설정할 수 있는 창이 나옵니다.

저는 총 길이(Length)를 0.25초로 설정하였고 처음 0에서 시작해서 최종적으로 1에 값이 나오는 선형보간을 설정하였습니다.

 

BP_Character의 컴포넌츠에서 Camera를 선택해 Details를 보면 Camera Settings에 Field Of View 값이 있는데 기본 값이 90도로 되어있습니다.

따라서 float 변수 DefaultFOV의 값을 90으로 설정하고 줌 인된 상태의 AimFOV를 50으로 설정하였습니다.

 

** AimFOV의 값을 50으로 설정한 이유는 FOV(Field Of View)값은 사실 시아각을 설정하는 값으로 시아각이 좁아지면 상대적으로 줌 인이 된 것 같은 효과를 얻을 수 있기 때문입니다.**

 

그리고 다시 Blueprint로 돌아와 Lerp(보간) 노드를 생성해 Alpha값을 Timeline 노드의 Alpha와 연결해주고 시작 값 A에 DefaultFOV 변수를 연결해주고 종료 값 B에 AimFOV 변수를 연결해주었습니다.

마지막으로 Set Field Of View 노드를 생성해 in Field Of View를 Lerp 노드와 연결해주고 Targat은 Camera로 가장 위에 핀은 Timeline과 연결해주는 것으로 줌 인 기능을 구현 완료하였습니다.

 

줌 아웃 기능은 줌 인 기능을 반대로 하면 되기 때문에 Branch 노드의 False와 Timeline의 Reverse를 연결해주는 것으로 간단하게 구현 하였습니다.

 

다음으로 총알 발사와 총알 장전 기능을 구현하였습니다.

 

먼저 기능 구현에 앞써 기능에 필요한 변수와 InputAction 파일들을 만들어주었습니다.

변수로는 총이 현재 발사 중인지 확인하는 isFiring 불형 변수와 현재 총알의 수를 저장할 CurrentAmmo 정수형 변수, 최대 총알의 수를 저장할 MaxAmmo 정수형 변수, 발사 간격을 저장할 FireInterval 실수형 변수를 생성 해주었습니다.

 

그리고 컨텐츠 브라우저의 Inputs 폴더 안에 총을 쏘기 위한 IA_Fire 파일을 생성하였습니다.

 

IA_Fire는 플레이어가 마우스 좌클릭을 했는지 안했는지만 판단하면 되기 때문에 Value Type을 Bool로 설정해주었고

마찬가지로 IMC_FPS에서는 마우스 좌클릭을 설정해주었습니다.

 

우선 Blueprint에서 마우스 우클릭으로 IA_Fire를 검색해 EnhancedInputAction IA_Fire 노드를 생성하고 현재 총이 발사 중인지 설정해줄 수 있도록 Set is Firing 노드를 두 개 생성하여 입력이 들어왔을 때, True(참) 값으로 설정하고 발사가 끝나면 False(거짓) 값으로 설정되도록 연결해주었습니다.

 

이후 입력이 들어오면 현재 총알의 갯수가 0보다 큰지 즉, 총을 발사할 수 있는 상태인지 확인하는 Branch(분기점) 노드를 생성해주고 True(참)일 때만 기능을 계속 수행하도록 구현하였습니다.

 

* 위의 Fire 노드는 중요하지 않습니다.

 

총구의 위치에서 총알이 발사될 때, 빛이 발광하는 효과를 넣기 위해 Fab을 통해 MuzzleFlash 에셋을 다운로드 받았습니다.

그리고 현재 MuzzleFlash 효과가 Loop되도록 설정되어있기 때문에 노란색으로 선택되어있는 노드들의 Details 안에 Loop Behavior를 모두 Once로 바꿔 한 번만 동작하도록 설정해주었습니다.

 

다시 Blueprint로 돌아와서 Spawn System Attached 노드를 생성해주고 이전 강의에서 만들어 놓았던 MuzzlePoint를 Spawn System Attached 노드의 Attach to Component에 연결해줘 효과가 MuzzlePoint에서 나올 수 있도록 설정해주고 밑에 세부 설정에서 효과의 방향을 조정, Auto Destroy를 활성화 해서 효과가 실행되고 자동으로 사라질 수 있게끔 설정해주었습니다.

 

**프로그래밍에서는 메모리 관리가 굉장히 중요한데 Destroy를 안해줄 경우, 계속 메모리를 차지해 프로그램 성능을 떨어뜨릴 수 있습니다.**

 

이후 Line Trace 기능을 이용해 총알 발사를 구현하였습니다.

 

Line Trace란?

쉽게 레이져를 발사해서 물체와 닿았는지 여부(충돌감지)와 닿는 물체의 정보 등을 받아올 수 있는 Unreal의 기능입니다.

**Unity의 RayCast와 사실상 같은 기능이라고 이해했습니다.**

 

Line Trace By Channel 노드를 생성해 Spawn System Attached 노드와 연결해주고 Line Trace가 플레이어의 카메라 중앙에서 레이져를 발사하여 물체를 감지할 수 있도록 Get Player Camera Manager를 통해 플레이어 카메라의 정보를 가져왔습니다.

Get Camera Location과 Line Trace By Channel을 연결해 레이져의 발사 지점을 설정해주고 레이져의 끝 지점을 Get Camera Location과 Get Camera Rotation의 합으로 Vector값을 구해 설정해주었습니다.

 

Get Camera Rotation값을 그대로 넣으면 레이져가 발사와 동시에 끝나기 때문에 통해 위치 값, 방향 값을 가져와 전방 방향 값을 Get Forward Vector를 연결해 불러와 주고 Get Forward Vector를 통해 카메라의 전방 Vector값을 가져와 6000을 곱해 레이져가 6000 거리까지 닿을 수 있도록 설정하고 마지막으로 Get Camera Location과 Get Camera Rotation Forward Vector의 합을 Line Trace By Channel의 End와 연결시켜줘 레이져의 끝 지점을 설정 해주었습니다.

 

** Get Camera Location과 Get Camera Rotation의 Forward Vector의 합을 넣어주는 이유에 대해서 생각을 해봤는데, 결국 Line Trace By Channel의 End에서 원하는 값은 Vector 값이기 때문에 길이가 필요합니다.

따라서 시작 지점 부터 끝 지점까지의 Vector값을 넣어주어야 하기 때문에 Get Camera Location과 Get Camera Rotation의 Forward Vector 합을 넣어주는 것이라고 결론을 지었습니다.**

 

탄 충돌 효과를 표현하기 위해 앞과 마찬가지로 Fab에서 에셋을 다운로드 받았습니다.

 

Line Trace의 실행 값을 Branch에 넣어 Line Trace가 맞았다면 참(True), 아무것도 맞지 않았다면 거짓(False)을 실행하여 Line Trace가 맞춘 물체의 닿은 지점에 효과를 실행시켜줄 수 있도록 Spawn Emitter at Location 노드를 생성해서 연결 해줍니다.

 

그리고 Line Trace가 맞춘 부분의 위치 정보를 불러오기 위해 Break Hit Result 노드를 생성하여 Impact Point(맞춘 지점) 값을 Spawn Emitter at Location 노드의 Location 핀에 연결 해줍니다.

 

이후 총의 반동을 구현하기 위해 Random Float in Range 노드를 생성해 -0.5에서 0.5사이의 값으로 각도 값이 Random(무작위)하게 변동되도록 Add Controller Yaw Input 노드와 Add Controller Pitch Input 노드를 생성해주고 Value 핀에 연결 해줍니다.

 

그리고 총 발사음 구현을 위해 Play Sound 2D 노드를 생성하고 노드 안에 Sound 탭에 총 발사음 소리 파일을 설정해줍니다.

 

마지막으로 현재 총알 갯수(Current Ammo)를 --(감소 연산자)와 연결해 감소시켜주고 화면에 업데이트 해줍니다.

화면에 남은 총알 갯수를 출력하는 기능 구현은 다음 파트에서 설명하겠습니다.

 

총알이 간격없이 발사 될 경우, 굉장히 혼란스러워 질 수 있기 때문에 발사 가능 상태가 되기 전에 Delay 노드를 생성해 Delay를 시켜주고 Delay 시간은 앞써 만들어 놓았던 Fire Interval 변수를 연결하여 설정 해줍니다.

 

추가적으로 Branch를 추가해 발사 가능 상태가 참(True)이면 바로 다음 발사가 이어지도록 연결해주면 연사도 가능하게 구현이 가능합니다.

 

바로 화면 구현에 대해 설명하겠습니다.

 

먼저 콘텐츠 브라우저에서 Blueprints 폴더에 UI 폴더를 새로 만들어 안에 User Interface의 Widget Blueprint 파일을 생성합니다.

 

그리고 Hierarchy(하이어라키)창에서 Progress Bar와 Text 두 개, Image 한 개를 생성해주고 원하는 위치로 설정 해줍니다.

위에 부터 순서대로 HP_bar로 이름 지정해주고 값을 넣어줄 수 있게 Is Variable을 설정 해주었습니다.

다음 Text 박스는 Text를 "Bullets: "로 설정 해주고 두 번째 Text 박스는 AmmoAmount로 이름을 지정해주고 나중에 남은 총알을 출력할 수 있도록 Is Variable을 설정, Text는 최대 총알 갯수인 30으로 설정 했습니다.

 

화면에 조준선은 Image를 이용해서 구현하였고 Details에서 Image에 조준선 이미지를 넣은 후, 이미지의 Alpha 값(불투명도)를 0.2로 설정하였습니다.

 

먼저 화면에 출력해주기 전에 게임이 시작되면 현재 총알 갯수(Current Ammo)값을 최대 총알 갯수(Max Ammo)값으로 설정 해주고 뒤로 와서 Create Widget 노드를 생성해 Class 탭을 아까 만들어주었던 WBP_PlayerHUD로 설정해줍니다.

 

CurrentAmmo 변수에 To Text (Integer) 노드를 연결해 화면에 출력 가능한 Text로 형변환을 해주고 Set Player HUD를 통해 WBP_PlayerHUD 안에 AmmoAmout Text 박스에 CurrentAmmo 변수를 대입해주기 위해 AmmoAmout를 SetText(Text)의 Target에 연결해주고 CurrentAmmo를 InText에 연결해줍니다.

마지막으로 Add to ViewPort의 Target에 PlayerHUD를 연결해주고 SetText(Text)노드와 Add to Viewport 노드를 연결해주면 화면에 현재 총알 갯수가 표시 됩니다.

 

다시 총알 발사 기능으로 돌아와서 총의 반동 기능과 총 발사 Delay 기능 사이에 SetText 노드를 생성해 PlayerHUD의 AmmoAmount를 Target으로 설정하고 총알 발사와 같이 감소 시켜주었던 CurrentAmmo를 To Text (Integer) 노드를 통해 Text 형태로 연결 시켜주면 총알 발사와 동시에 화면에 UI가 업데이트 되는 기능 구현이 끝납니다.

 

오늘의 마지막 구현 기능, 총 재장전 기능 입니다!!

 

이제는 눈 감고도 할 수 있을 것 같은 IA_Reload 파일과 IMC_FPS 설정을 해주었습니다.

그래도 설명을 한 번 하고 넘어가자면 IA_Reload는 플레이어의 입력이 있었는지 확인하면 되기 때문에 Bool로 Value Type을 설정하였고 IMC_FPS에는 재장전하면 가장 대중적인 단축키 "R"키를 설정하였습니다.

 

다음으로 BP_Character의 Blueprint로 넘어와 EnhancedInputAction IA_Reload를 생성해주고 입력을 받아 현재 총알 갯수(CurrentAmmo)가 최대 총알 갯수(MaxAmmo) 보다 작다면 현재 총알 갯수(CurrentAmmo)를 최대 총알 갯수(MaxAmmo)의 값으로 업데이트 되도록 구현 하였습니다.

 

추가로 플레이어 UI에도 재장전을 표출해야 되기 때문에 SetText를 통해 WBP_PlayerHUD의 AmmoAmount를 Target에 연결하고 설정된 CurrentAmmo를 To Text (Integer) 노드를 통해 Text 형태로 형변환하여 Text 핀에 연결해주었습니다.

 

오후에 진행된 기초 C언어 강의에서는 배열(Array)에 대해 학습하였습니다.

 

배열(Array)

같은 자료형(Type)의 값을 여러 개 묶어서 연속된 메모리에 저장하는 형태를 뜻합니다.

기본적인 선언 형태로는 자료형 배열명[배열의 크기] = {값1, 값2, ...}; 의 형태를 갖춥니다.

 

배열은 자리의 번호가 0부터 시작합니다! (중요)

따라서 int exampleArray[5] = {90, 57, 29, 16, 85}; 라는 배열이 있을 때, 90의 인덱스(Index) 즉, 자리 번호는 0번 입니다.

 

배열은 여러가지 초기화 방법이 있습니다.

 

int a[3] = {1, 2, 3};

가장 기본적인 초기화 방법입니다.

 

int b[3] = {1};

입력하지 않은 인덱스(Index)의 공간은 0으로 채워집니다.

 

int c[] = {1, 2, 3};

크기를 생략해도 컴파일러가 3으로 생성해줍니다.

 

int d[3] = {0};

전체를 0으로 초기화하는 관용구 {0, 0, 0} 입니다.

 

배열의 크기를 구하는 방법은 전체 배열의 데이터 크기 값을 한 칸의 배열의 데이터 크기 값으로 나누는 것으로 구할 수 있습니다.

말이 어렵죠... 그래서 실제로 해보겠습니다.

 

ex)

int arr[10];

int n = sizeof(arr) / sizeof(arr[0]);

 

printf("%d", n);

 

실행 결과 값

 

10

 

즉, int형 변수 1개는 4byte 10개는 40byte가 되고 arr 0 번의 크기는 4byte 이므로 40/4 = 10이 됩니다.

따라서 배열 arr의 크기는 10이 됩니다.

 

2차원 배열

행과 열로 형성된 배열입니다.

기본 선언 형태는 자료형 배열명[행 크기][열 크기] = { { 값1, 값2, ... }, { 값10, 값11, ... } }; 의 형태를 갖춥니다.

 

ex)

int grid[2][3] = { {1, 2, 3}, {4, 5, 6} }; 의 2차원 배열이 있다면

 

{ {1, 2, 3}     <= 첫 번째 행

, {4, 5, 6} }   <= 두 번째 행

 

각 행의 첫 번째 열 원소들 => 1, 4

각 행의 두 번째 열 원소들 => 2, 5

각 행의 세 번째 열 원소들 => 3, 6

 

의 형태를 입니다.

 

**C언어에서는 배열의 크기를 넘은 인덱스를 호출해도 막아주지 않습니다. 따라서 생성하지 않은 인덱스 값을 호출하지 않도록 조심해야 합니다.**

 

문자열

문자열 또한 문자(배)열입니다.

C++이나 C#등 다른 언어에서는 문자열을 별도의 자료형으로 가지는 경우가 있지만 C언어에서는 별도의 자료형을 가지지 않습니다.

따라서 문자의 배열로 문자열을 표현하는데 문자열은 문자열 끝에 널문자(\0)이 붙습니다.

"안녕하세요"를 C에서 문자열로 표현하면 크기가 5인 배열이 아니라 6인 배열이 됩니다.

{ 안, 녕, 하, 세, 요, \0 }가 되기 때문입니다.

 

ex)

char name[] = "Claude";

 

printf("%s\n", name); // Claude (%s는 \0 만날 때까지 출력)

 

즉, 배열 name의 크기는 6이 아닌 7이 됩니다.

 

이전에 배열에 대해 정리한 글이 있어 URL을 첨부 해두겠습니다.

https://blog.naver.com/hiroaki9902/223720037614

 

C++ 프로그래밍 (3일차)

조건문(Conditional Statement) 조건문(Conditional Statement)이란 조건이 참일 경우, 실행되는 구문...

blog.naver.com

 

오늘은 이렇게 해서 Unreal Blueprint와 기초 C언어 강의를 들었습니다!

내일도 힘내서 열심히 공부하도록 하겠습니다!