partial_sum을 이용해 연속 구간의 합을 간단하게 구할 수 있다.
vector<int> v = {3, 5, 8, 10};
vector<int> psum(4);
partial_sum(v.begin(), v.end(), psum.begin());
cout << psum[0] << endl; // 3
cout << psum[1] << endl; // 8 (3 + 5)
cout << psum[2] << endl; // 16 (3 + 5 + 8)
cout << psum[3] << endl; // 26 (3 + 5 + 8 + 10)
이런 식으로 사용해 간단하게 구할 수 있다.
int sum = psum[3] - psum[1]; // 구간 [8, 10]의 합
이런 식으로 활용도 할 수 있다.
unique는 연속된 중복 원소를 제거하고 끝 부분 iterator를 반환하는 함수이다.
이 함수를 쓰기 위해선 우선 정렬이 필수이다.
이 함수를 사용하면 앞쪽에 중복되지 않은 원소로 나열해주고 뒤쪽은 그냥 의미없는 값들이다.
그래서 반환된 iterator부터 erase()해주면 완벽하게 정렬한 값을 얻을 수 있다.
vector<int> v = {9, 9, 8, 7, 7, 7, 8, 6};
sort(v.begin(), v.end()); // 정렬하면 {6, 7, 7, 7, 8, 8, 9, 9} 이렇게 됨
auto newEnd = unique(v.begin(), v.end());
v.erase(newEnd, v.end()); //중복되는 구간 전부 삭제
for (int x : v)
cout << x << " "; //6 7 8 9 출력
accumulate는 총합을 구하는데 사용한다.
초기값도 설정 가능하다.
int sum = accumulate(vec.begin(), vec.end(), 0);
이런식으로 쓰면 vec안에 있는 값을 총 합쳐서 반환해준다.
4번째 매개변수로 법칙을 정해서 전해줄 수도 있다.
예를들어 총곱을 알고싶다면 multiplies<int>을 쓰면 된다.
int product = accumulate(vec.begin(), vec.end(), 1, multiplies<int>);
이렇게 하면 전체 곱을 구할 수 있다.
이진 탐색으로 위치값을 찾는다.
이진 탐색으로 찾기 때문에 반드시 오름차순으로 정렬되어 있어야만 사용할 수 있다는 단점이 있지만, 속도는 O(log n)으로 빠른 편이다.
lower_bound와 upper_bound로 할 수 있다.
이 둘은 약간의 차이점이 있는데
lower_bound는 찾는 값 이상인 값의 위치를 반환하고
upper_bound는 초과한 값의 위치를 반환한다.
vector<int> v = {1,2,3,3,3,4,4,4,5,6,7};
auto a = lower_bound(v.begin(), v.end(), 4); //5를 반환
auto b = upper_bound(v.begin(), v.end(), 4); //8을 반환
transform을 이용해 구간 내의 원소들을 내가 원하는대로 변환할 수 있다.
주의할 점은 반복문이나 for_each 함수 처럼 순서대로 접근하는 것이 보장되지 않는다
사용법은 2가지다
아래처럼 사용해 소문자/대문자 치환 같은거 할 때 쓰면 좋다.
transform(str.begin(), str.end(), str.begin(), toupper);
위는 단항 연산이며, 매개변수의 의미는 순서대로
범위 시작, 범위 끝, 저장할 위치 시작, 변환 방식(함수 등) 이다.
이항 연산은 여기서 2번째 범위 시작 위치를 추가해서 할 수 있다.
vector<int> vec1 = {1, 2, 3, 4, 5};
vector<int> vec2 = {6, 7, 8, 9, 10};
vector<int> saveVec(5);
transform(vec1.begin(), vec1.end(), vec2.begin(), saveVec.begin(), [](int a, int b)
{
return a + b;
});
매개변수의 의미는 순서대로
첫번째 범위 시작, 첫번째 범위 끝, 두번째 범위 시작, 저장할 위치 시작, 변환 방식(함수 등) 이다.
위의 예시는 각 int를 더해서 saveVec에 넣게된다.
A 오브젝트가 B 오브젝트를 참조하여 A를 로드하면 B가 로드되게 만드는 방식
일반적으로 많이 사용하는 방식이며, 메모리에 로드된 상태인 것에 접근한다.
당장 사용되지 않아도 메모리에 로드해버리기 때문에 메모리 소모가 심할 수 있다.
대표적으로 UPROPERTY로 공개 처리한 변수가 있다. 에디터에서 직접 조절하며 이미 값이 설정되어 있다.
ConstructorHelpers를 사용한 오브젝트 검사도 강 참조다. 보통 생성자 단계에서 ConstructorHelpers::FObjectFinder를 쓰게 되는데 이 때, 경로상의 오브젝트가 없다면 nullptr을 반환하고 크래시가 날 수 있다.
오브젝트 경로같은 문자열 형태의 간접 메커니즘을 통해 오브젝트 A 가 오브젝트 B 를 참조하게 만드는 방식
비동기식으로 로드하는걸 말하는 건가 싶긴도 한데. 그것 보단 느린 초기화를 말하는 것 같다. 아마 보통은 비동기식 로드랑 같이 섞여 쓰이지 않을까 싶다.
더 자세한 정보는
TSubclassOf : 하드 레퍼런스(강 참조)
TSoftClassPtr : 소프트 레퍼런스(약 참조)
TSoftObjectPtr : 소프트 레버런스(약 참조)
PlayerState와 GameState는 동기화를 할 때 굉장히 중요한 클래스이다.
GameMode를 상속 받아 쓰는데 GameStateBase쓰면 문제가 생길 수 있다.
이 둘은 강하게 연결 되있는데 GameStateBase는 간소화 되있기 때문이다.
걍 GameState를 상속받아 만들도록 하자.
이분이 이 4개의 사용처와 분리를 잘 정리해놨으니 한번 보도록 하자.
C++에서 쓰고 싶다면 .Build.cs에 "UMG"를 추가해야한다.
UI는 PlayerController에 붙여줘야함
CreateWidget<UUserWidget>(this, 클래스 타입);으로 위젯을 생성할 수 있다.
메뉴 UI 같은거 띄울 땐 메뉴 UI 전용 맵을 따로 만드는게 좋다라고 함.
Title 레벨을 만들라는 소리인 듯
UI 요소가 이벤트를 받으려면 변수 여부(Is Variable)를 체크해야한다.

WidgetComponent에서 WidgetSpace로 스크린 모드과 월드 모드를 설정할 수 있다.
스크린 모드는 가리는 오브젝트가 있어도 계속 화면에 보이고, 월드 모드는 액터마냥 3D 공간에 존재해 가릴 수 있는 UI 모드이다.
WidgetComponent에서 GetUserWidgetObject()로 UUserWidget*을 받아올 수 있다.
Unreal Interface를 상속받아 제작했을 때 U인터페이스이름, I인터페이스이름 이런식으로 생성되는데 둘 다 필요하니 건드리지 말자.
cpp 파일도 있는게 최대한 문제가 터질 가능성을 줄일 수 있으니 가만히 냅두자
실제 인터페이스로 쓰는건 I인터페이스이름에 순수 가상 함수를 선언해 사용하면 된다.
.AddDynamic(Object, FuncName) 매크로로 델리게이트에 함수를 체인할 수 있다.
물론 매개변수가 같아야하며 BeginPlay에 등록해야 문제가 안생긴다.

이렇게 연결하는 함수는 반드시 언리얼의 리플렉션 시스템에 등록이 되어야해서 UFUNCTION()을 붙어줘야 하는데 부모 최상단에서 한번만 등록 시키면 이미 등록됬으므로 자식에서 매크로를 추가하지 않아도 됨
Colllision 컴포넌트엔 GetOverlappingActors()라는 함수가 있다.
이걸로 Overlap된 엑터들 가져올 수 있다.
Spawner 같은거 만들 때 SpawnVolume라고 이름을 많이 짓는다고 함
GetAllRows()를 쓸 때, 첫번째 인자로 TCHAR*를 받는데 디버깅 용도로 어디서 쓰는지 string으로 남기는거임
UGamePlayStatics::ApplyDamage() : 데미지를 입히는 함수, static 이다.
AActor::TakeDamage() : 데미지를 받을 때 호출, override해서 사용하면 된다.
UGameplayStatics::GetAllActorsOfClass로 월드 안에 있는 원하는 클래스 타입의 액터를 가져올 수 있다.
IsA()로 해당 오브젝트의 클래스 타입을 검사할 수 있다.