UMG

Programming/UE4 2015. 2. 23. 06:03

https://www.youtube.com/watch?v=3XO_stF_r1A


UMG 튜토리얼을 하다보면 막히는 부분

https://docs.unrealengine.com/latest/INT/Engine/UMG/QuickStart/4/index.html

해결방법

https://answers.unrealengine.com/questions/42595/no-option-to-cast-object.html

에디터 개인설정 - 콘텐츠 에디터 - 블루프린트 에디터 - User Experience - Use Legacy Menuing System 체크

'Programming > UE4' 카테고리의 다른 글

back button  (0) 2015.04.05
UMG UButton with Release Event  (0) 2015.03.13
svn  (0) 2014.11.20
Time of Day  (0) 2014.11.03
substance-ue4  (0) 2014.10.27

svn

Programming/UE4 2014. 11. 20. 10:46

'Programming > UE4' 카테고리의 다른 글

UMG UButton with Release Event  (0) 2015.03.13
UMG  (0) 2015.02.23
Time of Day  (0) 2014.11.03
substance-ue4  (0) 2014.10.27
알아두면 좋을 언리얼 엔진 4 라이브러리  (0) 2014.10.27

Time of Day

Programming/UE4 2014. 11. 3. 09:00

'Programming > UE4' 카테고리의 다른 글

UMG  (0) 2015.02.23
svn  (0) 2014.11.20
substance-ue4  (0) 2014.10.27
알아두면 좋을 언리얼 엔진 4 라이브러리  (0) 2014.10.27
단축키  (0) 2014.10.26

substance-ue4

Programming/UE4 2014. 10. 27. 22:40

'Programming > UE4' 카테고리의 다른 글

svn  (0) 2014.11.20
Time of Day  (0) 2014.11.03
알아두면 좋을 언리얼 엔진 4 라이브러리  (0) 2014.10.27
단축키  (0) 2014.10.26
Getting Started: Introduction to UE4 Programming  (0) 2014.10.26

알아두면 좋을 언리얼 엔진 4 라이브러리

Programming/UE4 2014. 10. 27. 13:51
알아두면 좋을 언리얼 엔진 4 라이브러리

알아두면 좋을 언리얼 엔진 4 라이브러리

* 작성: Billy Bramer 일시: | 기능 프로그래밍 학습

UE4 에는 게임 개발에 사용하기 좋은 C++ 라이브러리가 많이 있습니다만, 규모가 큰 코드 프로젝트에서는 어디서 찾을지 항상 명확한 것은 아닙니다. 이 글에서는 특히나 유용하여 확인할 만한 가치가 있는 부분을 몇 가지 짚어보고 싶습니다. 자세한 정보는 API 문서의 'Core' 부분을 읽어보시기 바랍니다. 문서(영문).

컨테이너

저장상의 요구 충족을 위해, UE4 에는 사용할 수 있는 컨테이너 클래스가 전반적으로 구비되어 있으며, 다수는 전통적으로 C++ 표준 라이브러리의 일부였을 기본 함수성을 미러링한 것입니다. 솔직히 담을 수 없는 유일한 것은 이 모든 클래스에서 오는 기쁨 뿐일 것입니다!…미안요.

일반적인 컨테이너

TArray

(Engine\Source\Runtime\Core\Public\Containers\Array.h)

TArray 는 템플릿화된 동적인 크기의 배열로, UE4 콘테이너 중 가장 흔히 사용되는 것입니다. 동적 배열에서 기대할 수 있는 모든 기능은 물론 UPROPERTY 도 완벽 지원됩니다. TArray 의 API 에는 추가적으로 TArray 를 스택이나 힙 구조체로 간주할 수 있는 함수성이 제공됩니다.

TArray 는 UPROPERTY 로 선언 가능하기에, 자주 에디터 프로퍼티 창에 표시되고 네트워크 리플리케이션 뿐만 아니라 자동 UPROPERTY 시리얼라이제이션 대상이 되기도 합니다. 이 기능의 결과, TArray 는 종종 게임플레이 코드 구현시 선택되는 컨테이너입니다.

C++ Standard Template Library (STL) vector 클래스를 사용해 본 적이 있다면, TArray 야 말로 반가운 친구입니다.

TSet

(Engine\Source\Runtime\Core\Public\Containers\Set.h)

TSet 은 수학 집합 개념의 템플릿화된 구현으로, 교집합 합집합 차집합과 같은 일반적인 집합 연산 뿐만 아니라 한 요소가 집합의 일부인지 아닌지 빠르게 확인할 수 있는 기능까지 제공합니다 (PeopleWhoLoveTSet.Contains(Me); // 항상 true).

주의: TArray 와는 달리, TSet(과 TMap)은 UPROPERTY 로 직접 지원되지 않기에, 자동 리플리케이션, 시리얼라이제이션 등이 불가능합니다. TSet (또는 TMap) 이 UObject 강 레퍼런스(예: TSet<UObject*>)와 함께 사용된다면, 해당 레퍼런스가 제대로 시리얼라이즈되어 가비지 콜렉션 가능한지 사용자가 확인해 줘야 합니다. 누군가가 쓰레기는 치워야지요…

TSet 은 STL 집합 클래스와 유사하나, UE4 구현은 해싱을 기반으로 합니다. 새로운 유형을 만들고 TSet (또는 TMap) 에서 사용하려는 경우, 해당 유형 해시를 위해 간단한 함수를 구현해 줘야 합니다: uint32 GetTypeHash(const YourType& TypeVar). 예제가 더 필요하다면, 코드 베이스에 참고해 볼 수 있는 예제가 많이 있습니다.

TMap

(Engine\Source\Runtime\Core\Public\Containers\Map.h)

TMap 은 템플릿화된 데이터 구조체로, 한 유형에서 다른 유형으로의 (키-값 짝) 매핑을 빠른 요소 추가, 제거, 확인 기능을 포함해서 가능하게 해 줍니다. 다른 프로그래밍 언어에서 오신 분들은, TMap 이 나타내는 구조체를 'dictionary' (딕셔너리)로 알고 계실 수도 있습니다.

TSet 과 마찬가지로 TMap 역시 UPROPERTY 선언이 불가능합니다.

TMap 은 C++ STL map 클래스와 비교되지만, UE4 구현은 해싱을 기반으로 합니다.

Iterator

사용법이 C++ STL 과 똑같지 않기는 해도, UE4 컨테이너에는 이터레이터가 제공됩니다. 각 컨테이터 유형마다 지원되는 이터레이터를 확인할 수 있지며, 일반적으로 const / non-const 이터레이터를 사용할 수 있습니다.

예제:

// Example direct from the engine source:

// Initialize an iterator from the provided array (InPackages)

for (TArray<UPackage*>::TConstIterator PkgIter(InPackages); PkgIter; ++PkgIter)

{

// Access the element at the current position of the iterator with the * operator

UPackage* CurPackage = *PkgIter;

C++11 팬인 (그리고 게으른) 경우, 이터레이터에 auto 키워드를 사용할 수도 있습니다:

for (auto FileIt = Files.CreateConstIterator(); FileIt; ++FileIt)

{

const FString FileExtension = FPaths::GetExtension(*FileIt);

소팅

기본 소팅 옵션에 추가로, 소팅을 지원하는 UE4 컨테이너는 predicate 오브젝트를 통해 커스텀 소팅이 가능하기도 합니다.

예제:

// Custom struct written to serve as the predicate for sorting. Given two constant references to elements

// in the data structure (anim notify events), sort them according to their trigger time.

struct FCompareFAnimNotifyEvent

{

FORCEINLINE bool operator()(const FAnimNotifyEvent& A, const FAnimNotifyEvent& B) const

{

return A.GetTriggerTime() < B.GetTriggerTime();

}

};

 

// Sort the notifies array (TArray<FAnimNotifyEvent>) with the custom predicate

Notifies.Sort(FCompareFAnimNotifyEvent());

다른 컨테이너

TArray, TSet, TMap 은 가장 자주 사용되는 UE4 컨테이너이지만, 유일무이한 것은 아닙니다! 그 삼총사와 아이들에 대한 소스 코드를 확인해 보시려면, Engine\Source\Runtime\Core\Public\Containers 디렉토리를 보시면 됩니다.

스트링 처리

(Engine\Source\Runtime\Core\Public\Containers\UnrealString.h)

(Engine\Source\Runtime\Core\Public\UObject\NameTypes.h)

(Engine\Source\Runtime\Core\Public\Internationalization\Text.h)

UE4 는 스트링의 상호 처리에 있어 알아둬야 하는 세 종류의 클래스를 제공합니다: FString, FName, FText 입니다. 각각 저마다의 용도와 최적의 용례가 있으며, 이 문서(와 그 문서에 링크가 제공된 참고서)에 자세히 설명되어 있습니다.

수학

(Engine\Source\Runtime\Core\Public\Math\UnrealMathUtility.h)

(Engine\Source\Runtime\Core\Public\GenericPlatform\GenericPlatformMath.h)

수학 없이 게임이라니요?! 다행히도 UE4 에는 매우 탄탄한 크로스 플랫폼 수학 라이브러리가 있으며, 일반적으로 FMath 안의 스태틱 함수 시리즈로 구현됩니다. FMath 는 매우 간단한 것에서부터 복잡한 것에 이르기까지 다양한 범위의 수학 연산을 아우르고 있습니다. 수학 관련 작업을 시작하기 전, 두 헤더 파일 모두 둘러보고 이미 작성된 내용에 대한 개념을 확실히 잡아둘 가치가 있습니다!

마무리

UE4 에서 알아둬야 할 중요 라이브러리에 대해 유용한 미니 투어가 되었으면 합니다만, 정말이지 수박 겉핥기 수준일 뿐입니다. 엔진에는 어떤 용도로든 사용할 수 있는 코드가 가득합니다.

이 글에 대한 피드백도 있으시다면 환영합니다! 너무 광범위한가요? 너무 헛갈리나요? 개그가 너무 식상한가요? 질문이나 코멘트는? UE4 커뮤니티 포럼 에 참가하시거나 트위터 @EpicIrascible 에서 저랑 놀아요. 한글 사용자 분들을 위해 네이버 카페도 준비되어 있습니다.

https://www.unrealengine.com/ko/blog/ue4-libraries-you-should-know-about

'Programming > UE4' 카테고리의 다른 글

Time of Day  (0) 2014.11.03
substance-ue4  (0) 2014.10.27
단축키  (0) 2014.10.26
Getting Started: Introduction to UE4 Programming  (0) 2014.10.26
Coding Standard  (1) 2014.10.26

단축키

Programming/UE4 2014. 10. 26. 20:18

언리얼엔진4의 메뉴 - 편집 - 에디터개인설정 - 키보드 단축키로 들어가면 자신이 원하는 대로 단축키를 수정할 수 있습니다.


-----------------------------------------------------------------------------------------------------------------

기본 설정값


레벨 뷰 포트

ㄴ Ctrl + 1~0 : 북마크 설정

ㄴ 1~0 : 북마크에 저장된 위치와 방향으로 뷰포트 이동

ㄴ G :  게임뷰 토글

ㄴ Ctrl + K : 선택된 액터의 레퍼런스를 레벨 블루프린트에서 검색

ㄴ F11 :  뷰포트를 몰입 / 일만모드로 전환

ㄴ Shift + / :  사용가능한 프리뷰 메시 순환

ㄴ / : (누르고 있으면) 프리뷰 메시가 커서 아래 등장

ㄴ Alt + C :  Collision 표시

ㄴ Alt + F :  Fog 표시

ㄴ Alt + L : Landscape 표시

ㄴ Alt + R : Radius 표시

ㄴ Alt + O : Volumes 표시

ㄴ P :  Navigation 표시


레벨에디터

ㄴ Ctrl + Alt + Shift + P :  게임 시스템에 대한 C++ 코드를 즉석에서 다시 컴파일 하고 로드

ㄴ Ctrl + Shift + G :  액터 그룹 선택 

ㄴ Ctrl + Shift + S :  다른 이름으로 저장

ㄴ Ctrl + Shift + E : 선택된 액터와 연관된 애셋 편집

ㄴ Ctrl + Shift + K :  액터를 일정 거리 내에 있는 다른 액터 위치로 이동 

ㄴ Ctrl + Shift + F : 콘텐츠 브라우저 열기

ㄴ Ctrl + Shift + A : 클래스가 같은 모든 액터선택

ㄴ Ctrl + Shift + ; :  라이팅 빌드

ㄴ Ctrl + Shift + . :  셰이더 리컴파일

ㄴ Ctrl + Shift + , :  다음 프레임에 대해 GPU프로파일링

ㄴ Ctrl + end :  액터를 가장 가까이에 있는 그리드 위치로 이동

ㄴ Ctrl + O : 레별 열기

ㄴ Ctrl + G : 액터를 새 그룹으로 만들고 선택 내 기존 그룹은 제거

ㄴ Ctrl + H :  모든 액터 표시

ㄴ Ctrl + N :  새 레벨 만들기

ㄴ Ctrl + L :  선택된 액터의 레이어에 있는 모든 액터 선택

ㄴ Ctrl + M :  선택된 액터를 현재 레벨로 이동

ㄴ Ctrl + E :  선택된 액터에 연관된 에셋 편집

ㄴ Alt + N :  네비게이션 데이터를 한번에 하나씩 순

ㄴ Alt + B :  선택된 액터 붙히기

ㄴ Alt + End :  액터의 피벗 포인트를 바닥으로 붙힘

ㄴ Shift + C : 인접한 선택 표면의 공면을 모두 선택

ㄴ Shift + S : 모든면 선택

ㄴ Shift + Y : 인접 경사면 선택

ㄴ Shift + U :  인접 바닥면 선택

ㄴ Shift + W : 인접 벽면 선택

ㄴ Shift + J : 모든 인접면 선택

ㄴ Shift + H :  선택된 액터 표시

ㄴ Shift + E :  선택된 스태틱 메시를 사용하는 모든 액터 선택

ㄴ Shift + F :  모든 뷰포트의 카메라 위치를 선택된 액터 앞으로 이동

ㄴ Shift + G :  액터의 그룹 해제

ㄴ Shift + B : 선택된 표면과 같은 브러시에 속하는 표면 선택

ㄴ Shift + T : 선택된 표면과 같은 머티리얼이 같은 표면 선택

ㄴ Shift + M : 현재 표면 선택 메모리에 저장 

ㄴ Shift + Q : 현재 표면 선택 반전

ㄴ Shift + R : 헌재 선택 메모리에 저장된 선택으로 대체

ㄴ Shift + A : 현재 선택에 메모리에 저장된 표면 선택 추가

ㄴ Shift + O : 현재 선택을 현재 선택된 것에도 메모리에 저장된 선택 안에도 있는 표면으로 대체

ㄴ Shift + X : 현재 선택을 현재 선택에도 없고 메모리에 저장된 선택에도 없는 표면으로 대체

ㄴ Shift + / : 모든 파이클 시스템 리셋

ㄴ Shift + 1 : 레벨 에디터 모드 배치 활성화

ㄴ Shift + 2 : 레벨 에디터 모드 칠하기 활성화

ㄴ Shift + 3 : 레벨 에디터 모드 랜드스케이프 활성화

ㄴ Shift + 4 : 레벨 에디터 모드 폴리지 활성화

ㄴ Shift + 5 : 레벨 에디터 모드 지오메트리 편집 활성화

ㄴ Shift + End :  바운드 하단 중앙을 바닥에 붙힘

ㄴ End :  액터를 그 아래 바닥으로 붙힘

ㄴ ESC :  선택 해제

ㄴ F1 : 문서 메인 페이지 열기

ㄴ T :  반투명 선택 허용

ㄴ V :  버텍스 스텝 활성화

ㄴ H :  선택된 액터 숨기기

ㄴ M : 선택된 액터의 레벨을 현재로 레벨로 만들기

ㄴ K :  시뮬레이션 모드에서 이 액터에 변경한 내용을 액터 기본 상태에 저장

ㄴ F4 :  액터 디페일 패널 열기


레이어

ㄴ F2 :  레이어의 이름 변경


머티리얼 에디터

ㄴSpace Bar :  미리보기 강제 새로고침

ㄴEnter :  변경내용을 원본 머티리얼 및 월드에 사용중인 곳에 적용


메인 프레임

ㄴCtrl + S :  레벨 저장

ㄴShift + F11 :  전체화면 켜기

ㄴCtrl + Shift + W :  위젯 리플렉터창 활성화


사운드큐 그래프 에디터

ㄴ Spacr Bar : 사운드큐 재생/중지


시스템 

Ctrl + P : 애셋 열기

Ctrl + B : 콘텐츠브라우저에서 찾기

ㄴF1 :  언리얼엔진4 문서열기


시퀸서

ㄴ K : 선택된 액터에 대해 현재 시간에 키 설정


월드 플레이

Alt + S : 게임 시뮬레이트 시작

Alt + P : 지난번 실행된 게임 미리보기 세션과 같은 방식으로 실행 시작

Alt + Shift + P : 지난번 플레이된 게임 미리보기 세션과 같은 방식으로 플레이 시작

ㄴF5 : 다음 단계의 노드 실행

ㄴF10 : 플레이어를 카메라에 빙의 탈출

ㄴPause : 플레이 화면 재생/일시정지


주요명령

ㄴCtrl + Y : 지난 동작 다시하기

ㄴCtrl + Z : 되돌리기

ㄴCtrl + A : 모두 선택

ㄴCtrl + C : 복사하기

ㄴCtrl + W : 복제하기

ㄴCtrl + V : 붙여넣기

ㄴCtrl + X : 잘라내기

ㄴDelete : 삭제

ㄴF2 : 이름변경


출력로그

ㄴCtrl + A : 모두 선택

ㄴCtrl + C : 복사하기


콘텐츠 브라우저 

ㄴ Alt + 위 : 부모 디렉토리로 이동


뷰포트

ㄴ [ , ] : 그리드 크기감소/증가 

ㄴ Shift + [ , ] : 그리드 회전 감소/증가

 Ctrl + Shift + H : FPS표시 

Alt + 2 : 씬을 브러시 와이어 프레임 랜더링

Alt + 3 : 씬을 라이트 없이 랜더링

Alt + 4 : 씬을 라이트 포함 랜더링 

Alt + 5 : 씬을 디테일 라이팅으로 랜더링

Alt + 6 : 씬을 텍스쳐 없이 라이팅으로 랜덕링

Alt + 7 : 씬을 라이팅 복잡도 시각화 모드로 랜더링

Alt + 8 : 씬을 셰이더 복잡도 시각화 모드로 랜더링

Alt + 0 : 씬을 러아트맵 밀도 시각화 모드로 랜더링

Alt + J : 뷰포트 윗면 보기로 전환

Alt + G : 뷰포트 원근 보기로 전환

Alt + H : 뷰포트 정면 보기로 전환

Alt + K : 뷰포트 측면 보기로 전환

ㄴCtrl + R :  실시간으로 뷰포트 랜더링

ㄴF :  선택된 액터 앞으로 카메라 이동

ㄴW : 이동 모드 

ㄴE : 회전 모드

ㄴR : 스케일 모드  

ㄴ' : 트랜스폼 좌표계를 월드와 로컬스페이스 사이 전환

ㄴSpace Bar : 이동, 회전, 스케일 순환

ㄴF9 : 활성 뷰포트의화면 캡쳐


뷰포트 이동

ㄴE : 위로 이동

ㄴQ : 아래로 이동

ㄴS : 뒤로 이동

ㄴW : 앞으로 이동

ㄴA : 좌로 이동

ㄴD : 우롱 이동

ㄴC : 줌인

ㄴZ : 줌아웃

 

http://gamepod.tistory.com/152

'Programming > UE4' 카테고리의 다른 글

substance-ue4  (0) 2014.10.27
알아두면 좋을 언리얼 엔진 4 라이브러리  (0) 2014.10.27
Getting Started: Introduction to UE4 Programming  (0) 2014.10.26
Coding Standard  (1) 2014.10.26
UE4 소스 다운로드와 설치  (0) 2014.10.11

Getting Started: Introduction to UE4 Programming

Programming/UE4 2014. 10. 26. 18:52

'Programming > UE4' 카테고리의 다른 글

알아두면 좋을 언리얼 엔진 4 라이브러리  (0) 2014.10.27
단축키  (0) 2014.10.26
Coding Standard  (1) 2014.10.26
UE4 소스 다운로드와 설치  (0) 2014.10.11
Intro to Programming  (2) 2014.10.11

Coding Standard

Programming/UE4 2014. 10. 26. 17:37

 https://docs.unrealengine.com/latest/INT/Programming/Development/CodingStandard/index.html#introduction

 

Coding Standard

Introduction

At Epic, we have a few simple coding standards and conventions. This document is not meant to be a discussion or work in progress, but rather, reflects the state of Epic's current coding standards.

Code conventions are important to programmers for a number of reasons:

  • 80% of the lifetime cost of a piece of software goes to maintenance.

  • Hardly any software is maintained for its whole life by the original author.

  • Code conventions improve the readability of the software, allowing engineers to understand new code more quickly and thoroughly. We will certainly be hiring new engineers and interns over the life of this project, and we will likely be using engine modifications we make new on our next few projects.

  • If we decide to expose source code to mod community developers, we want it to be easily understood.

  • Many of these conventions are actually required for cross-compiler compatibility.

Class Organization

Classes should be organized with the reader in mind rather than the writer. Since most readers will be using the public interface of the class, that should be declared first, followed by the class's private implementation.

Copyright Notice

Any source file (.h, .cpp, .xaml, etc.) provided by Epic for distribution must contain a copyright notice as the first line in the file. The format of the notice must exactly match that shown below:

// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.

If this line is missing or not formatted properly, CIS will generate an error and fail.

Naming Conventions

  • The first letter of each word in a name (e.g. type or variable) is capitalized, and there is usually no underscore between words. For example, Health and UPrimitiveComponent, but not lastMouseCoordinates or delta_coordinates.

  • Type names are prefixed with an additional upper-case letter to distinguish them from variable names. For example, FSkin is a type name, and Skin is an instance of a FSkin.

    • Template classes are prefixed by T.

    • Classes that inherit from UObject are prefixed by U.

    • Classes that inherit from AActor are prefixed by A.

    • Classes that inherit from SWidget are prefixed by S.

    • Classes that are abstract interfaces are prefixed by I.

    • Most other classes are prefixed by F, though some subsystems use other letters.

  • Type and variable names are nouns.

  • Method names are verbs that describe the method's effect, or describe the return value of a method that has no effect.

Variable, method, and class names should be clear, unambiguous, and descriptive. The greater the scope of the name, the greater the importance of a good, descriptive name. Avoid over-abbreviation.

All variables should be declared one at a time so that a comment on the meaning of the variable can be provided. Also, the JavaDocs style requires it. You can use multi-line or single line comments before a variable, and the blank line is optional for grouping variables.

All functions that return a bool should ask a true/false question, such as "IsVisible()", or "ShouldClearBuffer()". All boolean variables must be prefixed with a "b" character (e.g. "bPendingDestruction", or "bHasFadedIn").

A procedure (a function with no return value) should use a strong verb followed by an Object. An exception is if the Object of the method is the Object it is in; then the Object is understood from context. Names to avoid include those beginning with "Handle" and "Process"; the verbs are ambiguous.

Though not required, we encourage you to prefix function parameter names with "Out" if they are passed by reference and the function is expected to write to that value. This makes it obvious that the value passed in this argument will be replaced by the function.

Functions that return a value should describe the return value; the name should make clear what value the function will return. This is particularly important for boolean functions. Consider the following two example methods:

    bool CheckTea(FTea Tea) {...} // what does true mean?
    bool IsTeaFresh(FTea Tea) {...} // name makes it clear true means tea is fresh

Examples

    /** the tea weight in kilograms */
    float TeaWeight;
    /** the number of tea leaves */
    int32 TeaCount;
    /** true indicates tea is smelly */
    bool bDoesTeaStink;
    /** non-human-readable FName for tea */
    FName TeaName;
    /** human-readable name of the tea */
    FString TeaFriendlyName;
    /** Which class of tea to use */
    UClass* TeaClass;
    /** The sound of pouring tea */
    USoundCue* TeaSound;
    /** a picture of tea */
    UTexture* TeaTexture;

Portable Aliases for Basic C++ Types

  • bool for boolean values (NEVER assume the size of bool). BOOL will not compile.

  • TCHAR for a character (NEVER assume the size of TCHAR).

  • uint8 for unsigned bytes (1 byte).

  • int8 for signed bytes (1 byte).

  • uint16 for unsigned "shorts" (2 bytes).

  • int16 for signed "shorts" (2 bytes).

  • uint32 for unsigned ints (4 bytes).

  • int32 for signed ints (4 bytes).

  • uint64 for unsigned "quad words" (8 bytes).

  • int64 for signed "quad words" (8 bytes).

  • float for single precision floating point (4 bytes).

  • double for double precision floating point (8 bytes).

  • PTRINT for an integer that may hold a pointer (NEVER assume the size of PTRINT).

Do not use the C++ int type in portable code, since it is dependent on the compiler how large it is.

Comments

Comments are communication; communication is vital. Some things to keep in mind about comments (from Kernighan & Pike The Practice of Programming):

Guidelines

  • Write self-documenting code:

    // Bad:
    t = s + l + b;
    // Good:
    TotalLeaves = SmallLeaves + LargeLeaves - SmallAndLargeLeaves;
  • Write useful comments:

    // Bad:
    // increment iLeaves
    ++Leaves;
    // Good:
    // we know there is another tea leaf
    ++Leaves;
  • Do not comment bad code - rewrite it:

    // Bad:
    // total number of leaves is sum of
    // small and large leaves less the
    // number of leaves that are both
    t = s + l + b;
    // Good:
    TotalLeaves = SmallLeaves + LargeLeaves - SmallAndLargeLeaves;
  • Do not contradict the code:

    // Bad:
    // never increment iLeaves!
    ++Leaves;
    // Good:
    // we know there is another tea leaf
    ++Leaves;

Example Formatting

We use a system based on JavaDoc to automatically extract comments from the code and build documentation, so there are some specific comment formatting rules to follow.

The following example demonstrates the format of class, state, method, and variable comments. Remember that comments should augment the code. The code documents the implementation and the comments document the intent. Make sure to update comments when you change the intent of a piece of code.

Note that two different parameter comment styles are supported, as embodied by the Steep and Sweeten methods. The @param style used by Steep is the traditional style, but for simple functions it can be more clear to integrate the parameter documentation into the descriptive comment for the function, as in the Sweeten example.

Unlike the UE3 coding standard, the method comments should only be included once, where the method is publically declared. The method comments should only contain information relevant to callers of the method, including any information about overrides of the method that may be relevant to the caller. Details about the implementation of the method and its overrides that are not relevant to callers should be commented within the method implementation.

    /** The interface for drinkable objects. */
    class IDrinkable
    {
    public:
        /**
         * Called when a player drinks this object.
         * @param OutFocusMultiplier - Upon return, will contain a multiplier to apply to the drinker's focus.
         * @param OutThirstQuenchingFraction - Upon return, will contain the fraction of the drinker's thirst to quench (0-1).
         */
        virtual void Drink(float& OutFocusMultiplier,float& OutThirstQuenchingFraction) = 0;
    };
    /** A cuppa (tea) */
    class FTea : public IDrinkable
    {
    public:
        /**
         * Calculate a delta-taste value for the tea given the volume and temperature of water used to steep.
         * @param VolumeOfWater - Amount of water used to brew in mL
         * @param TemperatureOfWater - Water temperature in Kelvins
         * @param OutNewPotency - Tea's potency after steeping starts, from 0.97 to 1.04
         * @return the change in intensity of the tea in tea taste units (TTU) per minute
         */
        float Steep(
            float VolumeOfWater,
            float TemperatureOfWater,
            float& OutNewPotency
            );
        /** Adds a sweetener to the tea, quantified by the grams of sucrose that would produce the same sweetness. */
        void Sweeten(float EquivalentGramsOfSucrose);
        /** The value in yen of tea sold in Japan. */
        float GetPrice() const
        {
            return Price;
        }
        // IDrinkable interface
        virtual void Drink(float& OutFocusMultiplier,float& OutThirstQuenchingFraction);
    private:
        /** The value of the tea in Yen. */
        float Price;
        /** The sweetness of the tea, quantified by the grams of sucrose that would produce the same sweetness. */
        float Sweetness;
    };
    float FTea::Steep(float VolumeOfWater,float TemperatureOfWater,float& OutNewPotency)
    {
        ...
    }
    void FTea::Sweeten(float EquivalentGramsOfSucrose)
    {
        ...
    }
    void FTea::Drink(float& OutFocusMultiplier,float& OutThirstQuenchingFraction)
    {
        ...
    }

What does a class comment include?

  • A description of the problem this class solves. Why was this class created?

What do all those parts of the method comment mean?

  • The purpose of the function is first; this documents the problem this function solves. As has been said above, comments document intent and code documents implementation.

  • Then come the parameter comments; each parameter comment should include units of measure, the range of expected values, "impossible" values, and the meaning of status/error codes.

  • Then comes the return comment; it documents the expected return value just as an output variable is documented.

C++ 11 and Modern Language Syntax

Unreal Engine is built to be massively portable to many C++ compilers, so we are careful to use features that are compatible with the compilers we can imagine supporting. Sometimes features are so useful that we will wrap them up in macros and use them pervasively, but usually we will wait until all of the compilers we could imagine supporting are up to the latest standard.

We are utilizing certain C++ 11 language features that appear to be well-supported across modern compilers, such as the "auto" keyword, range-based-for and lambdas. In some cases, we are able to wrap up usage of these features in preprocessor conditionals (such as rvalue references in containers.) However, certain language features we may opt to avoid entirely until we are confident we will not be surprised by a new platform appearing that cannot digest the syntax.

Unless specified below as a modern C++ compiler feature we are supporting, you should refrain from using compiler-specific language features unless they are wrapped in preprocessor macros or conditionals and used sparingly.

New Keywords for Old Macros

The old macros 'checkAtCompileTime', 'OVERRIDE', 'FINAL' and 'NULL' can now be replaced with 'static_assert', 'override', 'final' and 'nullptr'. Some of these macros may remain defined as their use is still prevalent.

One exception to this is that nullptr in C++/CX builds (e.g. Xbox One) is actually the managed null reference type. It is mostly compatible with nullptr from native C++ except in its type and some template instantiation contexts, and so you should use the TYPE_OF_NULLPTR macro instead of the more usual decltype(nullptr) for compatibility.

The 'auto' Keyword

The 'auto' keyword is supported by all compilers UE4 targets and you are encouraged to use it in your code where it makes sense to do so.

Remember you can and should use const, & or * with auto just like you would with the type name. With auto, this will coerce the inferred type to be what you want.

We encourage use of the auto keyword for iterator loops (eliminates boilerplate) - or better still: ranged-based for loops - and also when you are initializing a variable to a new instance (eliminates a redundant type name.) Some of the other uses are more contentious, but feel free to use it however you want for now and we can learn and improve best practices as we go.

Tip: If you hover over a variable in Visual Studio, it will usually tell you the inferred type.

Range Based For

This is allowed in all engine and editor code and encouraged where it can help to keep the code easier to understand and more maintainable. When migrating code that uses old TMap iterators, be aware that the old Key() and Value() functions which were methods of the iterator type are now simply Key and Value fields of the underlying key-value TPair:

    TMap<FString, int32> MyMap;
    // Old style
    for (auto It = MyMap.CreateIterator(); It; ++It)
    {
        UE_LOG(LogCategory, Log, TEXT("Key: %s, Value: %d"), It.Key(), *It.Value());
    }
    // New style
    for (auto& Kvp : MyMap)
    {
        UE_LOG(LogCategory, Log, TEXT("Key: %s, Value: %d"), Kvp.Key, *Kvp.Value);
    }

We also have range replacements for some standalone iterator types:

    // Old style
    for (TFieldIterator<UProperty> PropertyIt(InStruct, EFieldIteratorFlags::IncludeSuper); PropertyIt; ++PropertyIt)
    {
        UProperty* Property = *PropertyIt;
        UE_LOG(LogCategory, Log, TEXT("Property name: %s"), *Property->GetName());
    }
    // New style
    for (UProperty* Property : TFieldRange<UProperty>(InStruct, EFieldIteratorFlags::IncludeSuper))
    {
        UE_LOG(LogCategory, Log, TEXT("Property name: %s"), *Property->GetName());
    }

Lambdas and Anonymous Functions

Lambdas are now allowed, however we are cautious about use of stateful lambdas that capture stack variables -- we are still learning about where those are appropriate. Also, stateful lambdas cannot be assigned to function pointers which we tend to use a lot.

Lambdas are best used in conjunction with predicates in generic algorithms, for example:

    // Find first Thing whose name contains the word "Hello"
    Thing* HelloThing = ArrayOfThings.FindByPredicate([](const Thing& Th){ return Th.GetName().Contains(TEXT("Hello")); });
    // Sort array in reverse order of name
    AnotherArray.Sort([](const Thing& Lhs, const Thing& Rhs){ return Lhs.GetName() > Rhs.GetName(); });

We expect to update this documentation in the future as we establish best practices.

Strongly-Typed Enums

Enum classes are supported by all compilers and encouraged as a replacement for old-style namespaced enums, both for regular enums and UENUMs. For example:

    // Old enum
    UENUM()
    namespace EThing
    {
        enum Type
        {
            Thing1,
            Thing2
        };
    }
    // New enum
    UENUM()
    enum class EThing : uint8
    {
        Thing1,
        Thing2
    };

These are also supported as UPROPERTYs, as long they are based on uint8 - this replaces the old TEnumAsByte<> workaround:

    // Old property
    UPROPERTY()
    TEnumAsByte<EThing::Type> MyProperty;
    // New property
    UPROPERTY()
    EThing MyProperty;

Enum classes used as flags can take advantage of a new ENUM_CLASS_FLAGS(EnumType) macro to automatically define all of the bitwise operators:

    enum class EFlags
    {
        Flag1 = 0x01,
        Flag2 = 0x02,
        Flag3 = 0x04
    };
    ENUM_CLASS_FLAGS(EFlags)

The one exception to this is the use of flags in a 'truth' context - this is a limitation of the language and can be worked around using the 'double bang' operator:

    // Old
    if (Flags & EFlags::Flag1)
    // New
    if (!!(Flags & EFlags::Flag1))

Move Semantics

All of the main container types - TArray, TMap, TSet, FString - have move constructors and move assignment operators. These are often used automatically when passing/returning these types by value, but can be explicit invoked by using MoveTemp, which is UE4's equivalent of std::move.

Returning containers or strings by value can be a win for expressivity without the usual cost of temporary copies. Rules around pass-by-value and use of MoveTemp are still being established, but can already be found in some optimized areas of the codebase.

Third Party Code

Whenever you modify the code to a library that we use in the engine, be sure to tag your changes with a //@UE4 comment, as well as an explanation of why you made the change. This makes merging the changes into a new version of that library easier, and lets licensees easily find any modifications we have made.

Any third party code included in the engine should be marked with comments formatted to be easily searchable. For example:

    // @third party code - BEGIN PhysX
    #include <PhysX.h>
    // @third party code - END PhysX
    // @third party code - BEGIN MSDN SetThreadName
    // [http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx]
    // Used to set the thread name in the debugger
    ...
    //@third party code - END MSDN SetThreadName

Code Formatting

Braces { }

Brace wars are foul. Epic has a long standing usage pattern of putting braces on a new line. Please continue to adhere to that.

If - Else

Each block of execution in an if-else statement should be in braces. This is to prevent editing mistakes - when braces are not used, someone could unwittingly add another line to an if block. The line would not be controlled by the if expression, which would be bad. Worse yet is when conditionally compiled items cause if/else statements to break. So always use braces.

    if(HaveUnrealLicense)
    {
        InsertYourGameHere();
    }
    else
    {
        CallMarkRein();
    }

A multi-way if statement should be indented with each else if indented the same amount as the first if; this makes the structure clear to a reader:

    if(TannicAcid < 10)
    {
        Log("Low Acid");
    }
    else if(TannicAcid < 100)
    {
        Log("Medium Acid");
    }
    else
    {
        Log("High Acid");
    }

Tabs

  • Indent code by execution block.

  • Use tabs, not spaces, for whitespace at the beginning of a line. Set your tab size to 4 characters. However, spaces are sometimes necessary and allowed for keeping code aligned regardless of the number of spaces in a tab; e.g. when aligning code following non-tab characters.

  • If you are writing code in C#, please also use tab characters, not spaces. The reason for this is that programmers often switch between C# and C++, and most prefer to use a consistent setting for tabs. Visual Studio defaults to using spaces for C# files, so you will need to remember to change this setting when working on Unreal Engine code.

Switch Statements

Except for empty cases (multiple cases having identical code), switch case statements should explicitly label that a case falls through to the next case. Either include a break or a falls-through comment in each case. Other code control-transfer commands (return, continue, etc.) are fine as well.

Always have a default case, and include a break - just in case someone adds a new case after the default.

    switch (condition)
    {
        case 1:
            ...
            // falls through
        case 2:
            ...
            break;
        case 3:
            ...
            return;
        case 4:
        case 5:
            ...
            break;
        default:
            break;
    }

Namespaces

You can use namespaces to organize your classes, functions and variables where appropriate, as long as you follow the rules below.

  • Unreal code is currently not wrapped in a global namespace. You need to watch out for collisions in the global scope, especially when pulling in third party code.

  • Do not put "using" declarations in the global scope, even in a .cpp file (it will cause problems with our "unity" build system.)

  • It is fine to put "using" declarations within another namespace, or within a function body.

  • Note that if you put "using" within a namespace, it will carry over to other occurrences of that namespace in the same translation unit. As long as you are consistent it will be fine, though.

  • You can only use "using" in header files safely if you follow the above rules.

  • Note that forward-declared types need to be declared within their respective namespace, otherwise you will get link errors.

  • If you declare a lot of classes/types within a namespace, it can make it difficult to use those types in other global-scoped classes. (e.g. function signatures will need to use explicit namespace when appearing in class declarations.)

  • You can use "using" to alias only specific variables within a namespace into your scope (e.g. using Foo::FBar), but we do not usually do that in Unreal code.

  • We require C++ enum declarations to be wrapped in a namespace so that the enum value names are not at global scope.

C++ Enums and Namespace Scoping

  • We always prefix enum types with an "E" character in Unreal Engine code.

  • We require that all enums use namespaces (or empty structs) for scoping. The reason for this is that in C++, enum values are scoped to the same scope as the enum type itself. This can cause naming collisions, which results in a programmer having to create strange names or prefixes for enum values to make their values appear unique. Instead, we always explicitly scope new enums using namespaces. The actual enum type name within the namespace should always be declared as "Type".

  • Example of a namespace-scoped enum:

    /** Defining a enumeration within a namespace to achieve C#-style enum scoping */
    namespace EColorChannel
    {
        /** Declare EColorChannel::Type as the actual type for this enum */
        enum Type
        {
            Red,
            Green,
            Blue
        };
    }
    /** Given a color channel, returns the name of that channel. */
    FString GetNameForColorChannel(const EColorChannel::Type ColorChannel)
    {
        switch(ColorChannel)
        {
            case EColorChannel::Red:   return TEXT("Red");
            case EColorChannel::Green: return TEXT("Green");
            case EColorChannel::Blue:  return TEXT("Blue");
            default:                   return TEXT("Unknown");
        }
    }
  • Note that for locally-declared enums, you will not be able to use a namespace for scoping. In these cases, we opt to declare a local struct with no member variables, only a local enum type and use that struct for scoping.

    /** Defining a locally-scoped enumeration using structs*/
    class FObjectMover
    {
    public:
        /** Direction to move */
        struct EMoveDirection
        {
            enum Type
            {
                Forward,
                Reverse,
            };
        };
        /** Construct an FObjectMover with the specified movement direction */
        FObjectMover( const EMoveDirection::Type Direction );
    }

Physical Dependencies

  • File names should not be prefixed where possible; for example Scene.cpp instead of UnScene.cpp. This facilitates using tools like Workspace Whiz or Visual Assist's Open File in Solution by reducing the number of letters needed to disambiguate the file you want.

  • All headers should protect against multiple includes with the #pragma once directive. Note that all compilers we need to use support #pragma once these days.

    #pragma once
    <file contents>
  • In general, try to minimize physical coupling. If you can use forward declarations instead of including a header, do so. Include as fine grained as possible; do not include Core.h, include the specific headers in Core that you need definitions from.

  • Try to include every header you need directly, to make fine-grained inclusion easier. Do not rely on a header that is included indirectly by another header you include Do not rely on being included through another header; include everything you need.

  • Modules have Private and Public source directories. Any definitions that are needed by other modules must be in headers in the Public directory, but everything else should be in the Private directory. Note that in older Unreal modules, these directories may still be called Src and Inc, but those directories are meant to separate private and public code in the same way, and are not meant to separate header files from source files.

  • Do not worry about setting up your headers for precompiled header generation. UnrealBuildTool can do a better job of this than you can.

General Style Issues

  • Minimize dependency distance. When code depends on a variable having a certain value, try to set that variable's value right before using it. Initializing a variable at the top of an execution block, and not using it for a hundred lines of code, gives lots of space for someone to accidentally change the value without realizing the dependency. Having it on the next line makes it clear why the variable is initialized the way it is and where it is used.

  • Split methods into sub-methods where possible. Humans are better at looking at a big picture, and drilling down to the interesting details than to start with the details and reconstruct the big picture from them. In the same way, it is easier to understand a simple method that calls a sequence of several well named sub-methods than to understand an equivalent method that simply contains all the code in those sub-methods.

  • In function declarations or function call sites, do not add a space between the function's name and the parentheses that precedes the argument list.

  • Address compiler warnings. Compiler warning messages mean something is not as it should be. Fix what the compiler is complaining about. If you absolutely cannot address it, use #pragma to suppress the warning; this is a remedy of last resort.

  • Leave a blank line at the end of the file. All .cpp and .h files should include a blank line to play nice with gcc.

  • Never allow float to implicit convert to int32. This is a slow operation, and does not compile on all compilers. Instead, always use the appTrunc() function to convert to int32. This will ensure cross-compiler compatibility as well as generate faster code.

  • Enforce encapsulation with the protection keywords. Class members should be declared private unless they are part of the public interface to the class.

  • Interface classes (prefixed with "I") should always be abstract and must not have member variables. Interfaces are allowed to contain methods that are not pure-virtual, and even methods that are non-virtual or static, as long as they are implemented inline.

  • Use const wherever possible. Particularly on reference parameters and class methods. const is documentation as much as it is a compiler directive.

  • Debug code should either be generally useful and polished, or not checked in. Debug code intermixed with other code makes the other code much harder to read.

  • Use intermediate variables to simplify complicated expressions. If you have a complicated expression, it can be easier to understand if you split it into sub-expressions that are assigned to intermediate variables with names describing the meaning of the sub-expression within the parent expression. For example:

    if ((Blah->BlahP->WindowExists->Etc && Stuff) &&
        !(bPlayerExists && bGameStarted && bPlayerStillHasPawn &&
        IsTuesday())))
    {
        DoSomething();
    }

    should be replaced with

    const bool bIsLegalWindow = Blah->BlahP->WindowExists->Etc && Stuff;
    const bool bIsPlayerDead = bPlayerExists && bGameStarted && bPlayerStillHasPawn && IsTuesday();
    if(bIsLegalWindow && !bIsPlayerDead)
    {
        DoSomething();
    }
  • Use the virtual and override keywords when declaring an overriding method. When declaring a virtual function in a derived class that overrides a virtual function in the parent class, you must use both the virtual and the override keywords. For example:

    class A
    {
    public:
        virtual void F() {}
    };
    class B : public A
    {
    public:
        virtual void F() override;
    };

Note that there is a lot of existing code that does not follow this yet due to the recent addition of the override keyword, and the override keyword should be added to that code when convenient.

  • Pointers and references should only have one space, which is to the right of the pointer / reference. This makes it easy to quickly Find in Files for all pointers or references to a certain type.

    Use this:

    FShaderType* Type

    Not these:

    FShaderType *Type
    FShaderType * Type

 

'Programming > UE4' 카테고리의 다른 글

알아두면 좋을 언리얼 엔진 4 라이브러리  (0) 2014.10.27
단축키  (0) 2014.10.26
Getting Started: Introduction to UE4 Programming  (0) 2014.10.26
UE4 소스 다운로드와 설치  (0) 2014.10.11
Intro to Programming  (2) 2014.10.11

UE4 소스 다운로드와 설치

Programming/UE4 2014. 10. 11. 18:51

'Programming > UE4' 카테고리의 다른 글

알아두면 좋을 언리얼 엔진 4 라이브러리  (0) 2014.10.27
단축키  (0) 2014.10.26
Getting Started: Introduction to UE4 Programming  (0) 2014.10.26
Coding Standard  (1) 2014.10.26
Intro to Programming  (2) 2014.10.11

Intro to Programming

Programming/UE4 2014. 10. 11. 18:11

'Programming > UE4' 카테고리의 다른 글

알아두면 좋을 언리얼 엔진 4 라이브러리  (0) 2014.10.27
단축키  (0) 2014.10.26
Getting Started: Introduction to UE4 Programming  (0) 2014.10.26
Coding Standard  (1) 2014.10.26
UE4 소스 다운로드와 설치  (0) 2014.10.11