이번에는 프로그래밍의 함수에 대해서 알아보고 언리얼 프로그래밍에서 함수를 만들고 사용하는 방법을 배워봅시다.
사용 엔진 버전 : 5.0.1
타임라인
0:00 인트로
0:12 함수란?
2:05 프로젝트 및 클래스 생성
2:39 변수 선언
3:36 함수 선언 및 구현
4:29 함수 만들어보기
5:15 함수 호출하기
7:52 아웃트로
스크립트
인트로
안녕하세요. 여러분들과 함께 게임 개발을 공부하는 베르입니다.
이번 영상에서는 함수의 기본적인 내용과 함께 언리얼 프로그래밍에서 함수를 만들고 호출하는 방법을 알아보도록 합시다.
함수란?
프로그래밍에서 함수는 일정한 동작을 수행하는 코드들을 묶어놓은 것을 이야기하며 이 함수는 반환형, 이름, 매개변수, 몸체를 가집니다.
차례대로 살펴보자면 먼저 반환형은 이 함수가 모든 과정을 수행하고 나서 어떤 타입의 결과값을 돌려주는지 알려주는 부분입니다.
반환형이 int로 되어있다면 이 함수가 모든 동작을 끝내고나서 돌려줄 결과 값의 타입이 정수형이라는 뜻입니다.
언리얼 프로그래밍에서 사용되는 모든 자료형은 반환형으로 사용할 수 있습니다.
또한 함수가 동작을 끝내고 나서 결과값을 돌려줄 필요가 없다면 void라는 타입으로 결과값을 돌려주지 않을 것임을 알려줄 수도 있습니다.
그리고 함수 이름은 이 함수를 호출하는데 사용되는 것으로 개발자가 원하는 대로 이름을 지어주면 됩니다.
다만 함수의 이름을 지을 때는 이 함수가 어떤 작업을 하는 함수인지 이름만 보고도 알 수 있게 하는 것이 좋습니다.
매개변수 부분은 괄호로 묶여있는데 이 부분을 통해서 함수가 동작하는데 필요한 변수를 함수 외부에서 받아와서 함수 내부에서 사용할 수 있게 됩니다.
이렇게 매개변수 괄호가 비어있을 때는 함수에 필요한 외부 변수가 없다는 뜻이 됩니다.
외부에서 변수를 받아와서 사용할 필요가 있을 때는 이렇게 매개변수의 타입과 이름을 넣어주면 됩니다.
만약 여러 개의 매개변수가 필요하다면 끝에 쉼표를 찍고 다음 매개변수의 타입과 이름을 적어주면 됩니다.
마지막으로 함수의 몸체는 이 함수가 실제로 해야할 일을 처리하는 코드를 작성하는 부분입니다.
예를 들어 캐릭터를 원하는 위치로 이동시키는 함수를 구현한다고 가정하면 반환형으로는 남은 거리를 돌려주기 위해서 float으로 하고 함수 이름은 Move로 정하게 될겁니다.
그리고 매개변수는 목적지의 좌표를 받게 되겠죠.
그 다음 함수의 몸체에서는 함수가 호출될 때마다 목적지를 향해서 캐릭터의 이동속도와 시간을 이용해서 조금씩 이동시키고 남은 거리를 반환하는 코드를 작성하게 될 겁니다.
이렇게 프로그래밍에서 함수는 어떠한 작업을 하나의 묶음으로 만들어서 필요할 때마다 재사용할 수 있게 되는데, 작업 효율을 위해서는 필요한 기능 별로 함수를 잘 만들어서 사용해야 합니다.
프로젝트 생성 및 클래스 생성
그럼 이제 언리얼 엔진에서 함수를 만들어보기 위해서 언리얼 엔진을 실행하고 새 프로젝트를 생성합니다.
카테고리는 게임으로 하고, 다른 코드 없이 완전히 비어있는 기본 템플릿을 선택하겠습니다.
그리고 프로젝트 타입을 C++로 변경한 다음 프로젝트를 생성합니다.
프로젝트가 생성되면 지난 영상인 언리얼 프로그래밍 입문 1편에서 배운 것처럼 Actor 클래스를 상속받아서 C++ 클래스를 하나 생성합니다.
클래스의 이름은 언리얼 엔진이 기본으로 추천해주는 MyActor를 그대로 쓰도록 하겠습니다.
변수 선언
클래스 생성이 끝나고 나면 헤더 파일로 가서 몇 가지 변수를 선언해주겠습니다.
int32 타입으로 TotalDamage를 선언하고 UPROPERTY 매크로를 붙여준 뒤 지정자로 EditAnywhere, BlueprintReadWrite, Category=”Damage”를 넣어줍니다.
그리고 float 타입으로 DamageTimeInSeconds와 DamagePerSecond 변수를 선언하고 UPROPERTY 매크로 붙여주되 DamageTimeInSeconds에는 TotalDamage 변수와 같은 지정자를 넣어주고 DamagePerSecond에는 지정자로 BlueprintReadOnly, VisibleAnywhere, Transient, Category="Damage”를 넣어줍니다.
그리고 생성자 함수로 가서 간단하게 변수들의 기본값을 지정해줍니다.
하지만 이중에서 DamagePerSecond 프로퍼티는 기본값을 넣지 않고 그냥 놔두겠습니다.
함수 선언 및 구현
그 다음 클래스에 함수를 만들기 위해서는 두 단계의 과정을 거쳐야 합니다.
바로 함수 선언과 구현입니다.
자세하게 설명해보자면 우선 헤더 파일에서 이 클래스에 새로 생성할 함수의 원래 형태, 즉 원형을 적어서 알려야 합니다.
이것을 함수의 선언이라고 합니다.
다시 말해서 함수 원형 선언은 이 클래스에 이런 함수가 있음을 알리는 것으로 함수의 반환형, 이름, 매개변수까지만 적고 세미콜론을 찍어주면 됩니다.
그리고 소스 파일로 가서 함수를 구현해야 합니다.
함수를 구현할 때는 반환형과 이 함수를 소유하고 있는 클래스의 이름을 적은 뒤 콜론을 두 개 찍어주고 함수 이름을 적습니다.
그리고 매개변수를 적은 뒤 중괄호를 열어서 함수의 몸체를 만들고 이 몸체 안에 실제 함수가 동작할 코드를 작성해야 합니다.
함수 만들어보기
그럼 이제 간단한 함수를 만들어 봅시다.
우선 앞에서 변수들에 기본 값을 넣어주면서 DamagePerSecond는 제외한 것을 기억할 겁니다.
TotalDamage 변수와 DamageTimeInSeconds 변수를 이용해서 이 DamagePerSecond 변수의 값을 바꿔주는 함수를 만들어 보겠습니다.
헤더 파일에서 CalculateDPS라는 이름으로 함수의 원형을 선언합니다.
함수의 원형을 완성하고 나면 소스 파일로 이동해서 함수를 실제로 구현해야 합니다.
소스 파일에서는 앞에서 한 번 이야기한 것처럼 반환형, 클래스 이름, 함수 이름, 괄호와 매개변수를 입력하고 중괄호로 함수의 몸체를 만들어주면 됩니다.
함수의 내용에는 TotalDamage를 DamageTimeInSeconds로 나눠서 DamagePerSecond 변수에 넣어주도록 코드를 작성합니다.
함수 호출하기
이렇게 함수를 작성하고 나면 이 함수가 특정한 시점에 자동으로 호출되게 해주겠습니다.
그 시점으로 알맞은 것은 TotalDamage나 DamageTimeInSeconds와 같은 변수가 초기화되거나 변경될 때 일 겁니다.
언리얼 엔진에서 오브젝트의 변수가 초기화될 때 호출되는 함수는 PostInitProperties이고 변수가 수정될 때 호출되는 함수는 PostEditChangeProperty입니다.
AMyActor의 헤더 파일로 가서 두 함수의 원형을 작성합니다.
이 두 함수는 AMyActor의 부모 클래스인 AActor에서 상속받는 함수이기 때문에 부모 클래스의 함수를 자식 클래스인 AMyActor에서 덮어쓴다는 의미로 virtual 키워드와 override 키워드를 사용해줘야 합니다.
이렇게 두 함수의 원형을 선언하고 나면 다시 소스 파일로 가서 함수를 구현해야 하는데 아까 전의 CalculateDPS 함수처럼 직접 작성할 수도 있지만, 초록색 밑줄이 그어진 함수 이름에 커서를 두고 [Ctrl + .] 단축키를 누르고 [PostInitProperties에서 'AMyActor.cpp' 정의 만들기]를 선택하면 빈 함수의 구현을 스크립트 에디터가 자동으로 해줍니다.
PostInitProperties 함수의 내용은 간단하게 Super::PostInitProperties 함수를 호출한 다음에 CalculateDPS 함수를 호출하게 만들어 줍니다.
참고로 언리얼 C++에서 Super 키워드는 클래스가 상속받은 부모 클래스에 있는 원본 프로퍼티나 함수를 가져오는데 사용되는 키워드입니다.
AMyActor에 override로 선언한 PostInitProperties 함수는 부모 클래스인 AActor 클래스의 PostInitProperties를 덮어씌워서 만든 것이기 때문에 이렇게 AMyActor에서 만든 PostInitProperties 함수에서 부모 클래스의 PostInitProperties를 다시 호출해주지 않으면 부모 클래스의 PostInitProperties에서 실제로 처리하는 작업이 실행되지 않아서 문제가 발생할 수도 있습니다.
그래서 이렇게 부모 클래스의 함수를 덮어씌워서 만드는 경우에는 Super 키워드를 이용해서 부모 클래스의 원본 함수를 한 번 실행시켜주는 것이 좋습니다.
PostInitProperties 함수를 모두 작성하고 나면 PostEditChangeProperty 함수도 역시 똑같이 작성해줍니다.
다만 PostEditChangeProperty 함수에서는 원본 PostEditChangeProperty 함수보다 CalculateDPS 함수를 먼저 호출해주도록 작성하겠습니다.
코드를 모두 작성하면 변경사항을 저장하고 에디터로 돌아가서 [Ctrl + Alt + F11] 단축키를 이용해 수정사항을 컴파일 해줍니다.
코드가 컴파일되고 나서 MyActor 클래스를 레벨에 배치해보면 디테일 패널에서 DamagePerSecond의 값이 계산되어 표시되는 것을 볼 수 있습니다.
그리고 TotalDamage 프로퍼티와 DamageTimeInSeconds 프로퍼티의 값을 변경하면 DamagePerSecond의 값도 곧바로 변경되는 것을 볼 수 있습니다.
아웃트로
이번 영상에서는 함수가 무엇인지 배우고 언리얼 프로그래밍에서 함수를 만들고 사용하는 방법을 알아보았습니다.
이번 영상에서는 프로그래밍에서 사용되는 기본적인 변수와 언리얼 프로그래밍의 특징인 UPROPERTY에 대해서 알아보도록 합시다.
변수란?
먼저 프로그래밍에서의 변수란 간단하게 예를 들어 게임에서 캐릭터의 체력, 공격력, 공격속도와 같은 값과 상태를 저장하기 위한 것입니다.
게임이나 프로그램에 필요한 여러 종류의 값들을 담아두는 것이죠.
그리고 일반적인 프로그래밍 언어에서는 담을 수 있는 값의 종류에 따라 변수 타입이 나누어져있습니다.
가장 기본적인 변수의 종류에는 0, 1, 2, 3과 같은 일반 정수, 3.14, 1.5와 같은 소수, "Hello!", "Hi" 같은 문자열, 그리고 참과 거짓을 표현되는 논리 변수가 있습니다.
물론 이외에도 언리얼 엔진에서 제공하는 여러 클래스도 있고 다른 변수들을 묶음으로 다룰 수 있는 컨테이너들 역시 변수로 사용되지만 이번 영상에서는 제일 기본이 되는 변수를 먼저 다뤄보겠습니다.
프로젝트와 클래스 생성
그럼 먼저 언리얼 엔진을 실행하고 새 프로젝트를 생성해보겠습니다.
카테고리는 게임으로 하고, 다른 코드 없이 완전히 비어있는 기본 템플릿을 선택하겠습니다.
그리고 프로젝트 타입을 C++로 변경한 다음 프로젝트를 생성합니다.
프로젝트가 생성되면 지난 영상인 언리얼 프로그래밍 입문 1편에서 배운 것처럼 Actor 클래스를 상속받아서 C++ 클래스를 하나 생성합니다.
클래스의 이름은 언리얼 엔진이 기본으로 추천해주는 MyActor를 그대로 쓰도록 하겠습니다.
변수 선언 방법
C++ 클래스를 생성하고 나면 먼저 그 클래스의 .cpp 파일이 열릴텐데 클래스의 오브젝트가 소유하고 있는 변수, 즉 멤버변수를 만들기 위해서는 클래스의 .h 파일, 즉 헤더 파일에서 선언해주어야 합니다.
새로 만든 MyActor의 헤더 파일인 MyActor.h로 이동합니다.
우선 언리얼 엔진의 C++ 클래스에서 변수를 만들고 사용하기 위해서는 변수를 만들 클래스의 헤더 파일에서 이렇게 변수의 타입과 그 변수를 인식하기 위한 이름을 적어주면 됩니다.
일반 C++ 프로그래밍에서는 이렇게 변수 타입과 변수 이름을 선언하는 것으로 변수 선언이 끝납니다.
여기에 더해서 언리얼 C++ 프로그래밍에서는 이 변수 값을 에디터에서 사용하기 위해서 UPROPERTY라는 매크로를 붙이게 됩니다.
이게 대부분의 언리얼 프로그래밍에서 사용되는 변수의 기본형입니다.
이 UPROPERTY 매크로는 프로퍼티가 언리얼 엔진 및 에디터에 이러한 프로퍼티가 있음을 알리고, 프로퍼티가 엔진과 연결되었을 때 어떻게 작동할지를 지정하기 위한 것입니다.
UPROPERTY 매크로에 넣을 수 있는 지정자는 나중에 총 정리하는 시간을 가져보겠습니다.
변수 타입
이제 앞에서 이야기한 기본적인 변수 타입을 좀 더 자세히 알아봅시다.
앞에서 언급했듯이 기본 변수 타입으로는 정수, 소수, 문자열, 논리변수가 있습니다.
각 변수 타입에 대해서 다시 한 번 천천히 풀어서 설명해보겠습니다.
정수
먼저 정수는 0, 1, 2, 3과 같은 일반 숫자를 표현하는데 쓰이는 타입입니다.
어느 정도 C++를 배워보신 분들은 정수 타입을 이야기하면 제일 먼저 떠올릴 타입은 short, int, long일 겁니다.
하지만 이런 기본 타입은 플랫폼마다 길이가 달라질 수 있기 때문에 언리얼 엔진에서는 길이가 고정되어 있는 타입으로 int8, int16, int32, int64를 제공합니다.
int 뒤에 붙어있는 숫자는 정수를 표현하는데 몇 개의 bit를 사용할 것인지를 의미합니다.
int8은 정수를 표현하는데 8개의 bit를 사용해서 127 ~ -128까지 숫자를 표현할 수 있고, int16은 16개의 bit를 사용해서 32,767 ~ -32,768까지 표현할 수 있습니다.
그리고 int32는 32개의 bit로 21억 4648만 647 ~ -21억 4648만 648까지 표현하고 int64는 64개의 bit로 무려 922경 3372조 0368억 5477만 5807 ~ -922경 3372조 0368억 5477만 5808까지 표현할 수 있습니다.
그리고 만약 해당 변수가 숫자를 음수로 표현할 필요가 없다면 각 타입 이름 앞에 u를 붙여서 uint8, uint16, uint32, uint64로 사용하면 모든 비트를 양수를 표현하는데 사용해서 앞에서 이야기한 각 수가 표현 가능한 최대수의 2배만큼 표현 범위가 넓어집니다.
uint8은 0 ~ 255, uint16은 0 ~ 65535, uint32는 0 ~ 42억 9496만 7295, uint64는 0 ~ 1844경 6744조 0737억 0955만 1615까지 표현할 수 있게 됩니다.
정수 타입을 사용할 때는 필요한 숫자의 범위를 잘 생각해서 어느 타입을 사용할지를 결정하면 됩니다.
만약 127 ~ -128 범위의 숫자가 사용될 것이라고 생각하고 int8 타입을 사용했는데 그 예상을 벗어나서 127에 1이 더해지면 -128이 되고 -128에서 1을 빼버리면 127이 되버리는 문제가 발생합니다.
이걸 프로그래밍에서는 오버플로우와 언더플로우라고 부릅니다.
가끔 몇몇 게임에서 아이템이나 돈을 열심히 모았는데 어느 순간 갑자기 0이나 마이너스가 되버리는 문제를 볼 수 있는데 그게 바로 이것 때문에 발생하는 겁니다.
물론 이런 현상을 막기 위해서 그냥 int64를 사용하면 되지 않겠나하고 생각하겠지만 int8에서 한 단계씩 올라갈 때마다 사용되는 메모리가 2배씩 늘어나기 때문에 게임의 최적화를 위해서는 적절한 범위의 타입을 사용하고 그 범위를 넘어갈 가능성을 배제할 수 없다면 예외처리를 통해서 그 숫자의 범위를 벗어나지 못하도록 예방을 하는게 좋습니다.
소수
그 다음은 0.1, 3.14, 1.5와 같은 소수를 표현하는 타입입니다.
소수를 표현하는 타입으로는 일반 C++와 똑같이 float과 double이 있습니다.
float은 32비트이며 double은 64비트 크기입니다.
보통 float은 소수점 5자리까지의 정밀도를 가지고 double은 그 두 배인 10자리까지의 정밀도를 가집니다.
게임을 만들 때는 보통 float을 사용하고 더욱 정밀한 소수 표현이 필요할 때만 double을 사용하면 됩니다.
문자열
그리고 문자열입니다.
문자열은 말그대로 문자의 집합을 의미합니다.
보통 C++에서는 std::string을 사용하고 유니티에서 자주 사용되는 C#에서는 string 클래스를 사용하지만 언리얼 C++에서는 필요에 따라서 여러가지 클래스로 문자열을 제공합니다.
가장 기본 타입은 FString 타입입니다.
저장되는 글자의 길이에 따라서 변수의 길이가 자동으로 달라지는 타입으로 기본 C++의 std::string과 유사하게 동작합니다.
보통 std::string이나 C#의 string에서는 문자열 변수에 바로 문자열을 넣을 때는 쌍따옴표("")를 사용해서 상수 문자열을 만들어서 넣지만, FString에서는 TEXT()매크로를 사용해야 합니다.
이 외에도 현지화 텍스트를 위해서 사용하는 FText나 자주 사용되는 문자열을 식별자로 지정해서 문자열을 비교할 때 소모되는 메모리와 CPU 시간을 절약하는데 쓰이는 FName, 플랫폼마다 다를 수 있는 문자열 세트와 상관없이 문자열을 저장하는 용도로 사용되는 TCHAR가 있습니다.
FString을 제외한 타입은 나중에 필요한 경우가 생기거나 언리얼 문자열 관련 영상에서 다뤄보도록 하겠습니다.
논리변수
그리고 마지막으로 true 혹은 false 값만 가지는 논리 변수는 bool이라는 타입으로 선언할 수 있습니다.
이번에는 언리얼 프로그래밍에 입문하기 위해 기본적인 내용과 C++ 클래스 생성 방법, 그리고 언리얼 액터의 가장 기본이 되는 이벤트에 대해서 알아보겠습니다.
사용 엔진 버전 : 5.0.1
타임라인
0:00 인트로
0:29 개요
1:37 언리얼 C++ 프로젝트 생성
2:35 C++ 클래스 생성
3:53 생성한 C++ 클래스 살펴보기
5:59 기본 이벤트 함수 호출 확인
7:59 아웃트로
스크립트
인트로
안녕하세요. 여러분들과 함께 게임 개발을 공부하는 베르입니다.
이번 영상에서는 언리얼 엔진 5에서 프로그래밍 하는 방법에 대한 기초를 배워보겠습니다.
이번 영상의 내용은 전반적으로 지난 언리얼 엔진 4 프로그래밍 입문 영상과 대부분 같은 내용입니다.
그럼에도 언리얼 엔진 5 버전으로 다시 만드는 이유는 언리얼 엔진 5에 입문하는데 언리얼 엔진 4 강좌를 보면서 하기에는 세부적인 부분에서는 조금씩 달라진 부분이 있기 때문입니다.
그럼 이제 언리얼 엔진 5 프로그래밍을 배워보도록 하겠습니다.
개요
먼저 언리얼 엔진에서의 프로그래밍은 게임의 기능을 만드는 것을 의미합니다.
캐릭터가 어떻게 조작되어 움직이고, 어떤 규칙으로 목적을 달성해야 게임에서 승리하게 되는지 등 이런 전반적인 것을 프로그래밍 언어나 다른 여러 가지 방법으로 만드는 것입니다.
먼저 언리얼 엔진에서 게임 기능을 만드는 방법은 C++와 블루프린트, 이렇게 두 가지가 있습니다.
C++는 프로그래머가 직접 타이핑으로 코드를 작성해서 프로그래밍을 하는 방법이고, 블루프린트는 눈에 보이는 노드와 노드를 이어 기능의 흐름을 눈으로 보면서 만들어내는 비주얼 스크립팅입니다.
C++ 클래스로만 게임을 제작하거나 블루프린트 클래스만을 이용해서 게임을 제작하는 것도 충분히 가능하지만, 이 두 방법을 완전히 따로 사용하는 것보다는 프로그래머가 C++로 베이스가 되는 게임 기능을 만들고, 디자이너가 블루프린트를 이용해서 그 기본 게임 기능을 가지고 게임의 재미를 위한 기능들을 제작하는 방식으로 함께 사용할 때 시너지 효과를 발휘합니다.
앞으로 C++ 클래스와 블루프린트 클래스를 넘나들면서 언리얼 엔진에서 게임 기능을 만드는 방법을 배우게 될 겁니다.
참고로 언리얼 C++ 프로그래밍을 배우기 위해서는 먼저 C++나 다른 프로그래밍 언어를 어느 정도 익숙한 수준으로 다룰 줄 아는 편이 좋지만 이런 부분까지 포함해서 하나씩 배워나가 봅시다.
C++ 프로젝트 생성
그럼 제일 먼저 언리얼 엔진에서 C++ 프로젝트를 만드는 방법을 배워봅시다.
우선 언리얼 엔진을 실행하겠습니다.
그리고 새 프로젝트의 카테고리를 게임으로 선택합니다.
그 다음 템플릿에서는 여러 가지 게임 장르의 기반이 되는 다양한 템플릿을 볼 수 있는데 지금은 완전히 빈 프로젝트를 생성하는 기본 템플릿을 선택하겠습니다.
프로젝트 디폴트 세팅에서는 프로젝트 타입과 퀄리티, 타깃 플랫폼, 시작용 콘텐츠 포함 여부 등을 선택할 수 있는데 여기서 중요한 부분은 프로젝트 타입입니다.
기본으로 블루프린트가 선택되어 있는데 우리는 여기서 C++를 선택해야 합니다.
만약 여기서 블루프린트를 선택하고 프로젝트를 생성하면 해당 프로젝트에서는 C++ 클래스를 생성하지 못하고 블루프린트만을 이용해서 게임을 제작해야 합니다.
우리는 언리얼 C++ 프로그래밍을 배우면서 블루프린트를 함께 다룰 계획이기 때문에 C++를 선택하겠습니다.
그리고 프로젝트 이름을 입력하고 프로젝트를 생성합니다.
C++ 클래스 생성
프로젝트의 생성이 끝나고 나면 제일 먼저 C++ 클래스를 생성하는 방법을 배워보겠습니다.
새로운 C++ 클래스를 생성하는 방법은 여러 가지가 있습니다.
첫 번째 방법은 상단 메뉴 바에서 [툴 > 새로운 C++ 클래스] 항목을 선택하는 것이고 두 번째 방법은 콘텐츠 브라우저에서 C++ 클래스 폴더를 선택하고 추가 버튼을 누르거나 콘텐츠 브라우저의 빈 영역에 우클릭해서 새 C++ 클래스 항목을 선택하는 것입니다.
그렇게하면 추가할 클래스의 부모 클래스를 선택할 수 있는 창이 뜹니다.
여기서 부모 클래스를 선택하면 우리가 만드는 클래스는 선택한 부모 클래스의 변수와 함수를 상속받아 사용할 수 있게 됩니다.
언리얼 엔진에서 미리 구현해둔 여러 클래스들이 보이는데 일단 이중에서 Actor를 선택하도록 하겠습니다.
액터 클래스는 레벨에 배치할 수 있는 오브젝트를 의미합니다.
보통 레벨에 배치되어 동작할 오브젝트를 만들 때는 이 액터 클래스를 상속받아서 클래스를 구현하면 됩니다.
다음을 클릭하고 새 액터의 이름을 정한 뒤 클래스 생성 버튼을 클릭해주면 됩니다.
지금은 그냥 MyActor라는 이름 그대로 클래스를 생성하겠습니다.
생성한 C++ 클래스 살펴보기
C++ 클래스의 생성이 끝나고 나면 콘텐츠 브라우저의 소스 패널을 열고 [C++ 클래스 > (프로젝트 이름)]의 폴더를 열어보면 방금 생성한 MyActor 클래스가 보입니다.
그리고 이 MyActor 클래스를 스크립트 에디터에서 보면 MyActor 클래스에 대한 헤더 파일(.h)과 소스 파일(.cpp)이 생겨난 것을 확인할 수 있습니다.
이미 C++에 대해서 공부한 분들이라면 알고 있겠지만 모르는 분들을 위해서 설명하자면 헤더 파일은 클래스를 선언하고, 클래스에 속하는 변수와 함수의 원형을 선언하는 용도의 파일입니다.
헤더 파일의 클래스에서는 함수의 원래 형태만 알려주는 것이기 때문에 이렇게 함수의 내용은 없고 세미콜론으로 함수가 끝나는 것을 볼 수 있습니다.
이 함수의 몸체는 이렇게 소스 파일에서 구현됩니다.
처음 만들어진 액터 클래스에는 이렇게 클래스 이름과 같은 이름을 가진 생성자, BeginPlay 함수, Tick 함수가 생성되어 있습니다.
우선 클래스 이름과 같은 이름을 가진 생성자는 클래스의 객체가 생성될 때 한 번 호출되는 함수이며 주로 생성된 액터의 프로퍼티, 즉 변수의 기본 값을 설정해주는데 사용됩니다.
BeginPlay 함수는 액터가 배치된 월드에서 게임이 시작되거나 액터가 월드에 스폰되었을 때 한 번 호출되는 함수로 게임플레이 로직을 초기화시키는 데 사용됩니다.
그리고 Tick 함수는 매 프레임마다 한 번씩 호출되는 함수입니다.
이 Tick 함수에서는 매개변수인 DeltaTime을 통해서 Tick 함수가 지난 번에 호출된 이 후로 얼마의 시간이 경과한 뒤에 다시 Tick 함수가 호출되었는지에 대한 시간을 전달받을 수 있습니다.
그리고 이 Tick 함수는 Actor가 활성화되어 있는 동안 계속해서 호출되기 때문에 주로 게임의 로직을 처리하는 기능을 구현하는데 사용됩니다.
하지만 지금 만드는 클래스에 매 프레임 호출되는 Tick 함수가 필요하지 않다면 Tick 함수를 제거해서 게임의 퍼포먼스를 상승시킬 수도 있습니다.
Actor의 틱 함수를 비활성화하기 위해서는 헤더 파일과 소스 파일에서 Tick 함수를 제거하고 소스 파일의 생성자에서 PrimaryActorTick.bCanEverTick = true 코드를 제거해주면 됩니다.
이 코드는 이 Actor가 Tick 함수를 매 프레임 호출하도록 설정하는 코드입니다.
처음 생성한 C++ 클래스는 이렇게 생성자, BeginPlay 이벤트 함수, Tick 이벤트 함수를 가진 채로 생성됩니다.
이제 여기서 어떤 변수와 함수를 만들어 나가느냐에 따라서 클래스의 기능이 결정될 겁니다.
이벤트 함수 호출 확인
그럼 마지막으로 언리얼 C++ 클래스의 생성자와 기본 이벤트 함수들이 언제 호출되는지 확인해보기 위해서 간단한 로그를 남겨봅시다.
언리얼에서 로그를 남기기 위해서는 UE_LOG라는 매크로 함수를 사용합니다.
첫 번째 매개변수에는 LogTemp, 두 번째 매개변수에는 Log, 세 번째 매개변수에는 출력할 문자열을 넣어주면 됩니다.
생성자에서는 "Constructor"라는 문자열을 출력하게 만들고 BeginPlay 이벤트 함수에서는 "BeginPlay"를, Tick 이벤트 함수에서는 "Tick"이라는 문자열을 출력하게 코드를 작성합니다.
로그 출력 코드를 모두 작성하면 코드를 저장하고 변경한 코드의 내용이 언리얼 에디터에 적용될 수 있게 컴파일을 진행해야 합니다.
언리얼 엔진 4에서는 메인 툴바에 컴파일 버튼이 있었지만 언리얼 엔진 5부터는 컴파일 버튼이 사라지게 되었습니다.
언리얼 에디터를 종료하지 않고 컴파일 하기 위해서는 콘솔 명령창에서 LiveCoding.Compile 명령어를 입력하거나 [Ctrl + Alt + F11] 단축키를 눌러 라이브 코딩 기능을 사용해야 합니다.
라이브 코딩 기능으로 컴파일을 진행하면 이렇게 라이브 코딩 창이 뜨면서 진행상황을 알려줍니다.
가끔 컴파일 후에도 바로 변경사항이 적용되지 않을 때가 있는데 그럴 때는 언리얼 에디터를 종료하고 비주얼 스튜디오의 상단 메뉴 바에서 [Build > Rebuild Solution] 항목을 선택해서 프로젝트를 다시 빌드하고 실행하면 됩니다.
변경된 코드의 내용이 에디터에 적용되고 나면 출력되는 로그를 보기 위해서 하단 툴바의 출력 로그 버튼을 눌러 출력 로그 패널을 열고 [레이아웃에 고정] 버튼을 눌러 줍니다.
그 다음에는 MyActor 하나를 씬에 배치하고 게임을 플레이 시킵니다.
그러면 게임이 시작되자마자 빠르게 Tick로그가 출력되는 것을 볼 수 있는데 게임을 일시정지시키고 로그를 거슬러 올라가면 Play In Editor 모드가 초기화될 때 MyActor의 생성자가 실행되어서 Constructor 로그가 출력되고 PIE 모드가 시작되면서 BeginPlay 로그가 출력된 것을 볼 수 있습니다.
그리고 그 아래로는 매 프레임 Tick 로그가 출력된 것을 확인할 수 있습니다.
아웃트로
이렇게 이번 영상에서는 언리얼 엔진 5 C++ 프로그래밍에 입문하기 위한 대략적인 내용과 C++ 클래스를 생성하는 방법 그리고 C++ 클래스의 생성자와 언리얼 엔진의 기본 이벤트 함수인 BeginPlay와 Tick의 동작에 대해서 알아보았습니다.
이제 이 다음 영상에서는 C++ 클래스의 변수와 함수 등 좀 더 깊은 내용에 대해서 배우게 될 겁니다.