본문 바로가기

[언리얼5] 블루프린트를 이용하여 간단한 슈팅방 만들기 - 3

Kwonriver 2023. 1. 3.
728x90

[개요]

언리얼 5.1과 블루프린트를 이용하여 간단하게 물리 슈팅 게임을 만들어본다.

3장에서는 투사체 발사에 제한을 설정하고 모든 투사체를 발사하였으면 일정 시간 이후 레벨을 재실행한다.

 

[언리얼5] 블루프린트를 이용하여 간단한 슈팅방 만들기 - 1

[언리얼5] 블루프린트를 이용하여 간단한 슈팅방 만들기 - 2

 


[투사체 수량 설정하기]

레벨 블루프린트를 열고 멤버 변수를 추가한다. 레벨 블루프린트 편집창 왼쪽에 변수 목록이 있고 [+] 버튼을 클릭하면 새로운 변수를 생성할 수 있다. 이름을 변경하기 위해서는 해당 변수를 클릭한 뒤 F2 를 누르거나 오른쪽에 있는 디테일 패널에서 [변수] - [변수 이름]을 변경한다.

 

이 변수는 탄창 수량을 의미하기 때문에 Ammo라고 이름을 지었으며 0과 양의 정수를 가질 것이다. 따라서 변수타입을 인티저로 변경한다. 인티저는 정수를 저장할 수 있는 변수타입이다.

더보기

Ammo의 수량이 32비트 최대치인 2,147,483,647를 넘지 않을 것이니 강제로 인티저64를 할 필요는 없다.

다만 시간 계산(ms), 아이템의 보유 가능 갯수가 21억 명을 넘는 경우 등 최대치를 넘을 수 있다면 반드시 인티저64로 하는 것이 좋다.

 

블루프린트에서 멤버 변수의 기본값은 컴파일하지 않으면 지정할 수 없다. 따라서 컴파일을 한 뒤 20으로 저장한다. 탄창의 수가 20인 것에서 알 수 있듯이 우리는 20발의 투사체만 발사할 것이다. 

 

이제 투사체가 발사되면 Ammo의 수량을 1씩 감소시키도록 한다. Alt 키를 누른 채로 변수를 드래그하면 변수값을 지정하는 Set 함수가 호출되고 이를 Fired의 출력 노드와 연결한다.

Ctrl을 누른 채로 변수를 드래그 앤 드랍하면 변수의 값을 가져올 수 있는 Get 함수가 호출된다. 이 때 - 1 연산을 하여 수량을 감소한 후 노드를 연결한다. 정상적으로 감소하는지 확인하기 위해 Print String 함수를 사용하여 체크해본다. Print String은 입력받은 String을 게임화면 좌상단에 노출시켜주는 함수이다. 

 

 

컴파일 후 실행하면 게임 화면 왼쪽 상단에 현재 남은 수량의 갯수가 나오는 것을 볼 수 있다. 먼저 출력된 것이 아래로 내려가기 때문에 최신 String이 위에 있다.

 


[투사체 제한하기]

Ammo는 탄창의 역할을 하기 때문에 0개가 되면 더 이상 발사할 수 없다. 현재 Ammo가 0개를 초과하는지 알아내기 위해 브랜치를 사용한다. C, C++의 if와 동일한 역할을 수행한다. 

입력받은 조건이 True이면 True 출력 노드가, False이면 False 출력 노드가 실행된다. 따라서 Ammo가 0보다 크다면 True를 반환하게 한 뒤 Actor를 스폰하면 된다. 

 

 

Ammo가 0보다 크다면 True를 반환할 것이고 0보다 작거나 같으면 False를 반환할 것이다. 실제로 False로 가는지 확인하기 위해 False 노드에 Print String을 이용하여 Empty Ammo 문구를 출력해준다.

 

컴파일 후 게임을 실행하고 20발을 모두 발사하고 나면 Empty Ammo 문구만 계속 출력된다.

 


[탄창 재장전하기]

20발의 투사체를 모두 발사하기 전에 R 키를 누르면 재장전을 하도록 하자. MaxAmmo라는 이름으로 새로운 변수를 하나 생성한다. 이 변수는 절대 수정되지 않을 것이다. 위와 같이 변수를 만들고 디테일 패널에 [블루프린트 읽기 전용]을 체크한다. 

이후에는 이 변수는 읽기 전용이 되고 Set 할 수 없다. 이벤트 그래프에 Alt 키를 누른채로 드래그앤 드랍하여도 Get 함수만 호출된다. Ammo를 기본값 20으로 고정하였으나 Begin Play 시에 MaxAmmo의 값이 Ammo의 값이 되도록 하자. 

 

이렇게 하면 Ammo의 기본값과 상관없이 MaxAmmo의 값이 세팅된다. 정상적으로 세팅되었는지 확인을 위해 Print String으로 찍어본다. 

 

 

이제 [키보드 R] 키를 눌렀을 때 이벤트를 발생시키도록 한다. 이벤트 그래프의 빈 공간을 우클릭한 뒤 키보드 R을 입력하면 R키에 해당하는 이벤트가 나타난다. 앞선 [스페이스 바] 키와 동일하게 R을 눌렀을 때 이벤트가 발생한다.

 

 

R을 눌렀다면 Ammo의 갯수를 다시 MaxAmmo로 변경한다. BeginPlay에서 사용했던 코드와 똑같은 동작을 수행하게 될텐데 저 노드들을 복사하여 배치할수도 있겠지만 노드가 길어질수록 복사가 힘들다. 재사용성을 위해 [함수화]한다. 함수로 접을 부분을 드래그하여 선택한 다음 우클릭하면 [함수로 접기] 기능이 있는 것을 볼 수 있다.

 

 

이를 사용하면 쉽게 블루프린트 노드를 함수로 변경할 수 있다. 함수의 이름은 변수 이름처럼 좌측 내 블루프린트 패널에서 선택한 뒤 F2를 눌러 변경할 수 있지만 디테일 패널에서는 변경할 수 없다.

 

생성한 새로운 함수를 더블클릭하면 함수의 내부로 들어갈 수 있는데 이 때는 디테일 패널에서 함수의 이름을 변경할 수 있다.

 

 

다시 이벤트 그래프로 돌아와 R키를 눌렀을 때 ReloadAmmo 함수가 실행되도록 추가한다. 게임을 실행하고 투사체를 발사한 뒤 R 키를 누르면 다시 20/20이 노출되고 Ammo가 20으로 세팅된다. 애니메이션이 있다면 ReloadAmmo를 실행하기 전에 애니메이션을 동작시키고 애니메이션이 완료되면 ReloadAmmo 함수를 실행시켜준다. 그러면 FPS 게임에서 흔히 볼 수 있는 재장전 모션이 완성된다.

 

 


[모든 투사체를 발사한 뒤 레벨 재시작하기]

레벨 내의 장애물을 너무 맞추다보면 처음부터 다시 시작하고 싶을 때가 있다. 특정 키를 눌러서 재시작할 수도 있겠지만 현재 탄창의 수량이 0이 되면 일정 시간 뒤에 자동으로 레벨을 재시작 하도록 하는 기능을 추가한다.

 

위에서 Fired 이후 현재 탄창 수량을 Print 하는 부분을 추가했었다. 그 다음 브랜치를 이용하여 현재 탄창 수량이 0개 이하일 때를 판단하여 레벨을 재시작할지 결정한다. 그런데 현재 탄창 수량이 0보다 큰지 작은지 판단하는 기능은 앞서 만들었었다. ReloadAmmo 함수와 마찬가지로 해당 기능을 함수화한다.

 

 

그런데 [함수로 접기] 기능을 사용하여 함수를 만드니 입력 노드가 존재한다. 우리는 이 함수를 통해 Ammo의 갯수가 0보다 큰지 작거나 같은지만 알아내면 되는데 입력노드까지 있을 필요가 없다. 이 때 이 함수를 [퓨어 함수]로 변경하면 입력노드 없는 함수로 만들 수 있다. (Get 함수와 같은 모습)

 

함수의 내부로 들어간 다음 디테일 패널에서 [퓨어] 를 체크한다. 그 다음 이벤트 그래프로 돌아오면 실행 노드가 없는 퓨어 함수로 변경되어 있는 것을 볼 수 있다.

 

더보기

퓨어함수(순수함수)란?

퓨어함수는 함수형 프로그래밍에서 사용하는 기법으로 이 함수를 언제 어떤 위치에서 실행하여도 항상 동일한 결과값을 기대할 수 있는 함수를 의미한다. 이를 부수효과가 없는 함수라고 부르는데 이 함수의 내부에서 외부의 어떠한 값도 변경하지 않는 경우를 말한다. 

함수 내부에서 외부 변수의 값을 변경한다면 이는 퓨어 함수가 아니다. 다만 퓨어 함수의 내부에서 값을 변경하여도 컴파일 에러가 나거나 하지는 않는다.

 

*부수 효과 : 외부 인자의 상태나 값을 변경하거나 함수로 넘어온 변수 등의 상태 또는 값을 변경하는 것

 

그런데 함수 이름이 Check Ammo Empty이다. 즉, Ammo가 비었는지 체크하는 함수이며 비었다면 True를 반환할 것이다. 따라서 함수의 내부가 조금 변경되어야 한다.

 

 

0보다 크면 True를 반환하던 코드에서 0보다 작거나 같으면 True를 반환하는 함수로 변경하였다. 이 때 작거나 같다는 <= 를 입력하면 찾을 수 있다. 또한 비었을 때가 True이므로 이벤트 그래프에서도 반대로 변경한다. 

 

다시 원래 위치로 돌아와서 탄창이 비었다면 레벨을 재시작한다. 앞서 설명했던 타이머를 이용하여 일정 시간이 지난 후 함수를 호출할수도 있지만 Delay 기능을 이용하여 보다 간단하게 만들어본다. 

 

 

딜레이는 일정 시간 동안 카운트를 진행하며 카운트가 진행되는 동안 동일한 요청이 들어와도 무시하고 카운트를 진행한다. 그렇기에 탄환이 모두 소진된 이후 레벨을 재시작하는데 안성맞춤이다. (레벨 재시작 함수가 여러번 호출되는 것을 방지)

 

특정 레벨을 오픈하는 함수는 Open Level(By Name) 이다. 열고자하는 레벨의 이름을 입력하면 현재 레벨을 종료하고 해당 레벨을 오픈하는 방식이다. 이 레벨을 최초 만들었던 Warehouse 라는 이름을 그대로 작성하여도 되지만 GetCurrentLevelName 함수를 이용하여 현재 레벨의 이름을 가져올 수 있다.

 

 

위 2가지 방법은 동일한 결과를 나타낸다. 

 


 

이렇게 간단한 투사체 슈팅방을 만들어보았다. 랜덤한 표적지가 올라오고 해당 표적지를 맞추도록한다면 간단하게 반응속도 증가 또는 에임 연습 프로그램을 제작할 수 있다.

728x90