
이번에는 언리얼 프로그래밍 튜토리얼 중에서 변수, 타이머, 이벤트 페이지를 따라서 진행 할 것이다
변수, 타이머, 이벤트 | 언리얼 엔진 문서 (unrealengine.com)
변수, 타이머, 이벤트 페이지에서는 변수와 함수를 에디터에 노출시키는 방법과 타이머를 사용하여 코드 실행을 지연 또는 반복시키는 방법, 그리고 이벤트를 사용해서 액터끼리 상호작용 하는 방법을 배울 수 있다
이 튜토리얼을 끝내고 나면 변수, 타이머, 이벤트 페이지에 있는 영상처럼 카운트다운 직후에 이펙트가 발생하는 결과물을 얻게 될 것이다
그럼 먼저 언리얼 엔진을 실행한 후 프로젝트 타입을 C++로 변경하고 시작용 콘텐츠를 포함시킨 다음 프로젝트를 생성한다
작업한 내용이 더 잘 보이도록 하기 위해 [StarterContent > Maps] 폴더에서 Minimal_Default 폴더를 더블클릭하여 맵을 열어준다
맵이 열리면 상단의 툴 창에서 [Tools > New C++ Classes] 항목을 선택하여 새로운 클래스를 생성한다
부모 클래스로 Actor를 선택하고 클래스의 이름을 Countdown으로 한 후 클래스를 생성한다
이제 언리얼엔진 문서에서 시키는 대로 헤더파일에 int32 타입의 CountdownTime 프로퍼티와UTextRenderComponent* 타입의 CountdownText 프로퍼티, 그리고 UpdateTimerDisplay( ) 함수를 선언해준다
그런데 UTextRenderComponent 부분에 마우스를 가져다 대면 클래스가 정의되어 있지 않다며 신텍스 에러가 뜬다
이것은 UTextRenderComponent가 정의된 헤더를 포함시켜주지 않았기 때문이다
이것을 정상적으로 동작하게 만들기 위해서는 "Components/TextRenderComponent.h"를 include 해야한다
[헤더를 include할 때는 항상 generated.h 위에 선언해야 한다]
그 다음에는 Countdown.cpp 파일로 이동해서 생성자 함수에 코드를 작성해야 한다
그 전에 이 클래스에서는 Tick 기능은 사용하지 않을 것이기 때문에 PrimaryActorTick.bCanEverTick은 false로 변경한다
(이 과정을 통해 퍼포먼스를 향상 시킬 수 있다)
Tick 함수 역시 선언과 구현을 모두 제거해준다
그 다음에는 다시 생성자로 돌아와서 CountdownText를 초기화해준다
언리얼5-17 C++ <프로그래밍 퀵스타트(1)> 에서 했듯이 Countdown에 사용한 CreateDefaultSubobject 함수는 템플릿인 < >안에 넣어준 타입의 컴포넌트를 생성해서 반환하는 기능을 한다
그 다음에는 SetHorizontalAlignment 함수로 렌더링 되는 텍스트를 가운데 정렬해주고 SetWorldSize 함수로 크기를 정해준다
그리고 이렇게 만든 CountdownText 컴포넌트를 이 액터의 Root Component로 만들어준다
CountdownTime은 3으로 초기화한다
초기화 작업이 끝나면 헤더파일에서 선언했던 UpdateTimerDisplay 함수를 구현할 차례이다
이 함수에서는 CountdownText의 SetText( ) 함수를 이용해서 CountdownTime을 출력하도록 해준다
[언리얼엔진 문서에서는 FText::FromString 부분이 없는데 이것을 제외하고 작성하면 에러가 발생한다]
SetText( )함수에 대해 알아보자면 이 함수는 괄호 안의 내용을 텍스트로 보여주는 역할을 한다
->제일 안의 괄호인 CountdownTime변수가 0이 될 때까지 초당 한번씩 실행되는 Max() 함수를 TEXT로 설정(Set)하는 역할을 한다
여기까지가 변수를 이용해서 카운트다운 숫자를 화면에 표현하는 작업이다
화면에 대한 준비가 끝나면 이번에는 시간을 체크할 타이머를 추가할 차례이다
타이머란 사용자가 정의한 시간마다 사용자가 지정한 동작이 실행되도록하는 것이다
이러한 동작은 Tick 함수에서도 DeltaTime 값을 받아서 같은 동작을 수행하도록 할 수 있지만, 사용자가 지정한 동작이 지속적으로 실행될 필요없이 특정한 순간에만 몇 번 실행되면 되거나 실행될 텀이 1초를 넘는 경우라면 Tick 함수에서 시간을 재서 실행하는 것보다는 타이머를 이용하는 편이 좋다
이제 타이머에 필요한 멤버 변수와 함수들을 헤더파일의 하단에 추가해보자
AdvanceTimer 함수와 CountdownHadFinished 함수, FTimerHandle 타입의 CountdownTimerHandle 변수를 선언한다
AdvanceTimer 함수는 Timer가 돌아가면서 호출될 함수고,
CountdownHasFinished 함수는 AdvanceTimer 함수가 타이머가 지정한 만큼 작동한 뒤에 마무리 처리를 하기 위한 함수,
FTimerHandle 타입의 변수는 타이머를 컨트롤하기 위한 구조체로써 CountdownTimerHandle 변수는 카운트다운이 끝났을 때 타이머가 계속해서 돌아가지 않도록 종료하기 위해서 필요하다
이제 두 함수를 구현해보자
소스파일의 AdvanceTimer 함수에서는 CountdownTime을 1씩 감소시키고 UpdateTimerDisplay 함수를 호출해서 문구를 업데이트해준다
그리고 CountdownTime이 0이 되면 World의 TimerManager를 호출해서 CountdownTimerHandle이 관리하고 있는 Timer를 멈추게 하고 CountdownHasFinished 함수를 호출해준다
그 다음 CountdownHasFinished 함수에는 CountdownText의 내용을 "Go!"로 바꾸는 코드를 작성해준다
[문서에서는 FTEXT::FromString 부분이 없는데, 마찬가지로 제외할 시 에러가 발생한다]
BeginPlay 이벤트 함수에서는 UpdateTimerDisplay 함수를 호출해서 생성자에서 초기화된 CountdownTime을 출력하게 만들어주고 WorldTimerManager의 SetTimer 함수로 타이머를 실행시켜준다
코드를 모두 작성하면 저장하고 언리얼 에디터로 이동해서 컴파일을 한다
컴파일이 완료된 다음에 우리가 만든 Countdown 클래스를 레벨 에디터의 뷰포트에 드래그 앤 드롭해서 배치한다
이제 플레이 버튼을 눌러서 실행해보면 화면의 Text 글자가 3, 2, 1, Go!로 바뀌는 것을 확인할 수 있다
이 다음 작업은 앞에서 만든 변수와 함수를 에디터에 노출시키는 것이다
앞에서는 CountdownTimer를 3초로 초기화하도록 하드코딩해서 이 값을 변경하기 위해서는 스크립트 에디터를 열고 수정한 다음 컴파일을 해야한다
이것을 에디터에 공개해주면 이런 번거로운 작업없이 디자이너가 편하게 작업하게 해줄 수 있다
변수를 에디터에 공개하기 위해서는 이렇게 헤더파일로 가서 변수에 UPROPERTY 매크로를 붙이고 지정자로 EditAnywhere을 넣어주면 된다 [추가로 변수 앞에 주석을 달아주면 언리얼 에디터에서도 볼 수 있어서 변수의 설명으로 사용할 수 있다]
코드를 저장하고 에디터로 돌아가서 컴파일하면 배치된 액터에서 공개된 변수와 툴팁으로 표시되는 주석을 볼 수 있다
그리고 공개된 변수의 값을 변경해서 테스트해보면 타이머의 시간이 변경되어 작동하는 것을 확인할 수 있다
이제 함수를 블루프린트에 공개해서 디자이너가 함수의 기능을 블루프린트에서 수정할 수 있도록 만들어보자
CountdownHasFinished 함수의 앞에 UFUNCTION 매크로를 붙이고 BlueprintNativeEvent 지정자를 넣어준다
그리고 CountdownHasFinished_Implementation이라는 이름으로 가상 함수를 선언해준다
이제 소스파일로 가서 CountdownHasFinished 함수의 이름 뒤에 _Implementation을 추가로 적어준다
이렇게 하면 C++로 기본적인 기능을 제공하면서도 프로그래머가 아닌 디자이너들이 접근해서 변경할 수 있는 함수가 완성된다
코드를 모두 저장하고 언리얼 에디터로 돌아가서 컴파일한다
이렇게 공개한 함수를 언리얼 에디터에서 블루프린트로 확장해서 다루는 방법을 알아보자
우리가 만든 Countdown C++ 클래스의 작동 방식을 언리얼 에디터에서 변경하기 위해서는 이 Countdown 클래스의 블루프린트 버전을 만들어야 한다
여기에는 두 가지 방법이 있는데,
첫번째 방법은 Countdown 클래스의 파생 블루프린트 클래스를 만들기 위해서 Content폴더를 선택하고 빈 곳에 우클릭하여 [Blueprint Class] 항목을 선택한 다음
밑에 ALL CLASSES를 펼쳐 부모 클래스로 우리가 만든 Countdown 클래스를 찾아서 선택해주면 된다
두 번째 방법으로는 레벨에 배치된 Countdown C++ 클래스 인스턴스를 선택한 후
디테일 패널에서 오른쪽 노드모양 버튼을 클릭하고
Countdown C++ 클래스를 상속받는 BP_Countdown 블루프린트 클래스를 생성한다
둘 중 하나의 방법으로 블루프린트 클래스를 생성하면 C++ 클래스인 Countdown 인스턴스는 곧바로 블루프린트 클래스로 교체된다
블루프린트 에디터가 열리고 나면 에디터의 뷰포트 탭 옆에 있는 이벤트그래프 탭을 찾아서 열어준다
그리고 Implementation으로 선언된 CountdownHasFinished 함수를 재정의 하기 위해서 이벤트 그래프의 빈 영역에 우클릭하여 CountdownHasFinished 이벤트와 Spawn Emitter at Location( )함수를 검색하여 생성한다
(Spawn Emitter at Location( )함수는 파티클 시스템 액터를 스폰하는데 사용된다)
이벤트가 추가되고 나면 CountdownHasFinished 이벤트와 Spawn Emitter at Location 함수를 연결한다
이제 이미터 템플릿에서 파티클을 선택한다 (P_Explosion을 선택해보자)
그리고 이 파티클이 나타날 위치를 위해 Get Actor Location( )함수 노드를 생성해서 연결해준다
작업이 끝나면 왼쪽 위에 있는 버튼으로 블루프린트 클래스를 컴파일하고 게임을 플레이시켜본다
그러면 카운트다운이 0이 되었을 때 폭발 이펙트가 발생하는 모습을 볼 수 있다
그런데 C++ 버전에서는 0이 아니라 Go!라고 출력되도록 만들었던 것을 기억할 것이다
이 부분을 다시 블루프린트로 만들어도 되지만 C++에서 구현해둔 기능을 블루프린트로 다시 가져와서 조합하는 것 역시 가능하다
CountdownHasFinished 이벤트 노드에 우클릭하고 Add Call to parent Function(부모 함수로의 호출 추가)를 선택하면
[Parent : CountdownHasFinished] 노드가 생겨난다
이 노드는 우리가 C++에서 구현한 CountdownHasFinished_Implementation 함수를 호출해주는 역할을 한다
이 노드를 CountdownHasFinished 이벤트 노드와 Spawn Emitter at Location 노드 사이에 연결한다
[노드 연결을 끊으려면 Alt를 누른채로 선을 클릭하면 된다]
다시 블루프린트 클래스를 컴파일하고 저장 후 게임을 플레이시켜보면 카운트다운이 끝나고 0 대신에 Go!가 출력되면서 폭발 이펙트가 발생하는 모습을 볼 수 있다
이 다음 글에서는 직접해보기 라는 항목으로 몇가지 과제를 진행할 것이다
VR게임 개발을 위한 언리얼엔진/C++ 공부한 내용 끄적이기...
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!