vector<int> number = {-2, 3, 0, 2, -5};
↑ ↑ ↑ ↑ ↑
[0] [1] [2] [3] [4]
| 인덱스(i) | 값 (number[i]) |
|---|---|
| 0 | -2 |
| 1 | 3 |
| 2 | 0 |
| 3 | 2 |
| 4 | -5 |
number[0] == -2
number[3] == 2
숫자는 벡터 안에 차례대로 들어있고 number[i]로 i번째 숫자를 꺼내온다
i = 0 → number[0] = -2
j = 2 → number[2] = 0
k = 3 → number[3] = 2
number[i] + number[j] + number[k]
= number[0] + number[2] + number[3]
= -2 + 0 + 2
= 0
조합 한 개 찾음! a++;
조건 number[i] + number[j] + number[k] == 0 만족
이 조합은 정답 1개 → answer++ 해줘야 함
for (i = 0; i < N-2; i++) // 첫 번째 숫자
{
for (j = i+1; j < N-1; j++) // 두 번째 숫자
{
for (k = j+1; k < N; k++) // 세 번째 숫자
{
if (number[i] + number[j] + number[k] == 0)
{
answer++; // 정답 1개 찾음!
}
}
}
}
for문 3개 돌면서 (i, j, k) 조합을 만들고 그 세 개 숫자의 합이 0이면 정답이니까 answer++ 하기
언리얼 엔진이 설계한 시스템의 단위 오브젝트이고 UObject의 상속을 받는 액터 및 액터 컴포넌트 클래스가 있다.
특정한 접두사 규칙이 있는데 일반 C++ 오브젝트나 구조체에는 F, 언리얼 오브젝트에는 U, 월드에 스폰 가능한 언리얼 오브젝트에는 A를 붙여 사용한다.
언리얼 헤더 툴이 컴파일 이전 단계에서 프로젝트 헤더 파일을 분석하면 언리얼 리플렉션 시스템이 사용할 메타데이터를 생성해 Intermediate폴더에 메타데이터를 포함한 코드 파일이 생성된다.
메타데이터는 언리얼 엔진이 관리하는 UClass 인스턴스에 저장되는데, UClass와 리플렉션 시스템으로 언리얼 엔진이 에디터 통합, 직렬화, 가비지 컬렉션이 가능하다.
런타임 중 클래스의 구조(메타데이터)를 검사하고 조작할 수 있는 기능이다.
에디터의 디테일 패널, 시리얼라이제이션(데이터 저장/불러오기), 가비지 콜렉션, 네트워크 리플리케이션(서버에서 일어난 걸 클라이언트에게도 보여줌: 복제), 직렬화/역직렬화, 블루프린트와 C++ 커뮤니케이션 등 핵심 기능들의 기반이 되는 매커니즘이다.
멤버 변수에 UPROPERTY를 붙이거나 멤버 함수에 UFUNCTION을 붙여 사용할 수 있다.
필요 없는 객체를 자동으로 메모리에서 지우는 시스템이다.
UPROPERTY로 선언하면 GC가 추적해 메모리가 유지되지만 선언하지 않으면 GC가 추적하지 못하므로 삭제될 수 있다. → GC가 추적하지 못하면 오히려 삭제 안되지 않나요?
FString은 유일하게 조작(수정)이 가능한 문자열 클래스로 내부적으로 TCHAR 배열을 포함하고 있다.
대소문자 변환, 부분 문자열 파싱(추출, 수정), 역순 등의 메서드가 많이 있다. 매번 문자열이 메모리에 저장되어 메모리 소비가 크다. 또한 문자 단위로 비교를 하기 때문에 비교 속도가 느리다.
FText는 유일하게 현지화를 위한 문자열 클래스로 다른 언어를 사용해 보여줄 문자열들은 FText를 사용해야 한다.
보통 UI 텍스트나 현지화 시스템에서 관리한다.
FName은 경량화된 문자열로 데이터 테이블에 한 번만 저장되어 동일한 FName을 만들어 써도 메모리를 절약할 수 있다. 그리고 내부적으로 해시테이블로 관리되어 접근 속도가 빠르고, 문자열 보다는 변수 이름, 에셋 이름, 태그 등에 사용한다. 빌드 과정에서 해시값으로 변환되어 처리된다.
C++ 문자열은 기본적으로 ASCII 기반의 char 배열(1바이트)로 처리되어 영어만 표현 가능하고 한국어나 일본어 같은 다국어는 깨진다. 이런 1~2 바이트 혼합의 멀티바이트(MBCS) 방식을 사용하면 가변 길이 때문에 현재 바이트가 문자의 시작인지 두 번째 바이트인지 판단해야 해서 문자열 처리가 복잡하고 코드페이지(컴퓨터가 문자를 숫자로 바꿀 때 사용하는 문자 집합의 표준 번호 체계, 나라마다 다름)에 의존해 플랫폼 간 이식성이 낮다.
이 문제를 보완하기 위해서 언리얼 엔진은 유니코드(UTF-16, 대부분의 문자를 2바이트로 표)를 기반으로 문자열을 처리하고 빌드 설정에 따라 char, wchar_t로 유동적으로 바뀌는 문자타입인 TCHAR 타입과 문자열 리터럴을 자동으로 플랫폼에 맞는 형태로 변환해주는 TEXT(” “) 매크로를 제공한다. 이를 통해서 MBCS의 단점이었던 다국어 지원과 플랫폼 독립성이 보장된다.
액터는 월드에 배치 가능한 독립된 객체이다. 액터를 만들고 월드에 배치하면 메시가 없어서 투명하고 루트 컴포넌트가 없어 (0, 0, 0)에 배치된다. 그래서 액터는 최소한 하나 이상의 컴포넌트가 있어야 제대로된 액터의 기능을 갖게 된다.
컴포넌트는 기능을 가진 액터의 부품이라 생각하면 되는데 액터에 부착이 가능하다. 그리고 액터의 하위 요소이므로 부모 액터의 변환(위치, 회전, 크기)에 영향을 받는다.
컨테이너 데이터 타입에는 TArray, TMap, TSet이 있다. 여기 앞에 있는 T는 템플릿의 약자로, 어떤 자료형이 들어오든 내부 로직은 하나의 코드로 공통 처리를 할 수 있게 한다. 예를 들어, T를 사용하면 TArray든 TArray든 동일한 방식으로 배열 로직을 수행할 수 있다.
TArray는 가변 배열로, STL의 vector와 유사하다. 인덱스로 접근이 가능하고 연속된 메모리에 저장되어 캐시 효율(CPU가 메모리에서 데이터를 빠르게 읽어올 수 있는 정도)이 높다. 그리고 인덱스로 접근해 임의 접근이 빠르고 순차 순회도 빠르며 성능이 좋다. 게임에서 중요한 캐시 지역성에 유리하다. 그렇지만 중간 삽입, 삭제를 하려면 뒤의 요소들을 이동시켜야 하므로 느리고, 배열의 용량이 초과하면 재할당과 복사 비용이 발생한다. 따라서 많이 추가할 걸 미리 알고 있다면 Reserve()로 미리 공간을 확보하면 좋다.
TMap은 해시 테이블 기반으로 STL의 unordered_map처럼 동작한다. 일반 리스트나 배열은 값을 찾으려면 순차 검색을 해야 하지만 해시 테이블은 계산 한 번으로 위치를 바로 찾을 수 있다. key-value 쌍을 저장하고 key를 기준으로 빠르게 검색할 수 있다. 여기서 key는 중복이 불가능하다.
TSet은 해시테이블 기반으로 STL의 unordered_set처럼 동작한다. 값만 저장하고 key는 없다. 중복이 불가하며 순서도 없다. Contains()로 빠른 포함 여부를 확인할 수 있어, 중복 없는 ID 목록, 활성 상태 유닛 모음 등 중복 없이 값을 저장하고 싶을 때나 특정 값이 있는지 빠르게 확인하고 싶을 때 사용하면 좋다.
클래스의 인스턴스를 하나만 생성하고 그 인스턴스를 어디서든 접근할 수 있도록 만드는 디자인 패턴이다. 전역에서 접근 가능한 하나의 객체로 상태 관리나 설정값 공유에 사용된다. GameInstance, GameMode, Subsystem등이 해당된다.
이전 프레임과 현재 프레임 사이의 시간 차이(초 단위)를 의미한다. 컴퓨터마다 사양이 다르기 때문에 delta time을 곱하면 프레임 속도가 달라도 움직임이나 애니메이션, 물리 등이 일관되게 보일 수 있다.
언리얼 빌드 툴(UBT)은 다양한 빌드 환경설정을 통해 언리얼 엔진 및 프로젝트의 소스코드를 빌드하는 과정을 관리하는 커스텀 툴이다. 빌드 툴은 프로젝트의 Source 폴더를 분석해 솔루션 파일과 프로젝트 파일을 생성하고, build.cs 파일을 통해 각 모듈이 어떤 엔진 기능이나 어떤 모듈에 의존하는지 명시한다.
예를 들어 UI 기능을 사용하려면 .build.cs 파일에 “UMG” 를 추가해 빌드 시 자동으로 프로젝트에 포함되어 UI 기능을 사용할 수 있게 된다.
언리얼 헤더 툴(UHT)은 UObject 시스템을 지원하는 맞춤형 파싱 및 코드 생성 툴로 C++ 클래스의 .generated.h 파일을 자동으로 생성하고 관리한다. 이 파일에는 클래스의 메타데이터, 리플렉션 정보(UPROPERTY, UFUNCTION)가 포함되고 만약 헤더 파일에서 변경 사항이 생기면 UHT가 감지해서 .generated.h 파일을 자동으로 갱신한다.
c : 리플렉션 시스템은 매크로가 있어야 언리얼 헤더 툴이 멤버들을 인식하고 메타데이터를 생성할 수 있다.
델리게이트는 함수를 객체처럼 다룰 수 있게 해주며, 구체 클래스 대신 추상화를 통해 의존성을 낮추는 느슨한 결합 구조를 만든다. 따라서 유지보수에 용이하며 기능 확장 및 시스템 규모 조정에 유리하다. C++ 함수 포인터보다 안정적이고 간결하다. 델리게이트를 사용하면 발행-구독 패턴을 구현할 수 있다. 델리게이트가 중간자 역할을 수행하고 발행자는 콘텐츠(이벤트)를 발행하고 구독자는 해당 알림을 받아 처리한다. 이 때 발행자와 구독자는 서로 몰라도 된다. 델리게이틑 DECLARE{옵션}_DELEGATE{함수 정보} 형태로 매크로를 선언된다.
인터페이스는 객체가 반드시 구현해야 하는 기능(행동)을 명시하는 추상적인 타입으로, 다형성과 느슨한 결합 설계에 유용하다. 특정 기능을 여러 클래스에 강제로 적용하고 싶을 때 사용하며, 기능 중심으로 설계되어 의존성 분리와 유지보수성 향상에 도움을 준다.
추상 클래스와의 차이점이 있는데, 인터페이스는 반드시 구현해야 할 함수만을 선언하며 구현을 포함하지 않는다. 추상 클래스는 일부 기본 기능을 구현한 상태에서 확장을 허용하도록 설계된 클래스이다.
월드에는 세 가지 방식으로 충돌 판정 기능을 제공하며, 액터나 컴포넌트 간의 상호작용을 감지할 수 있다. 선을 투사해 경로 상에서 충돌한 오브젝트를 감지하는 LineTrace, 도형(구, 캡슐)을 투사해 충돌 여부를 감지하는 Sweep, 지정한 범위의 도형 안에 다른 오브젝트가 있는지 감지하는 Overlap이 있다.
UWorld::OverlapMultiByChannel을 사용하고 TraceChannel 인자에 ECC_XXX(채널 정보)값에 넣으면 된다.
베이직 반에서 테스트를 보면서 그동안 배운 내용을 정리할 수 있어서 좋았다.
테스트를 통해 내가 얼마나 이해했는지 점검할 수 있었고 부족한 부분을 더 집중해서 공부해야 할 것 같다. 아자잣~