이번에 플러그인 추가하면서 정리하는데 엄청 시간이 걸렸던 놈이다.
많은 개념이 나온다. 모르는 개념도 많기에 틀린것도 많을것이고 계속 수정하면서 정리해가야한다.
※정리해야할 개념
쓰레드 자세히
Initialzierstack에 추가되는 시점 언제인지
FObjectInitializer왜 중간에 조사식에서 관찰안되는지
CreateDefaultSubobject 자세히
내 ACharacter에 기본 CharacterMovementComponent를 변경하는 상황에서 분석하겠다.
(정확히는 포인터만 변경)
생성자 (Function)
먼저 이 생성자가 실행되는 타이밍은 CDO와 관련된 타이밍일것이다.
그렇기에 우리가 매개변수를 넣은게 아니라 언리얼에서 이 ObjectInitializer을 주어서 실행될것이다.
이 ObjectInitializer의 함수인 SetDefaultSubobjectClass로부터 시작해 나아간다.
처음의 Objectinialzer
ObjectInitialzer의 주소
SetDefaultSubobbjectClass (Function)
두가지가 오버로딩되어있는데 밑의 함수는 위의 함수를 편하게 사용하기위한 오버로딩이고  위의 함수가 핵심이다.
클래스에 정의되어 있는 하위객체 에 원하는 클래스 로 설정한다.
정의되어 있는 하위객체 : ACharacter을 예로들면 이미 정의 되어 있는 CharacterMovementComponent를 뜻한다.
원하는 클래스 : 아무 클래스나 가능하지 않고 파생클래스로만 가능하다.
AssertIfsubobjectSetupIsNotAllowed : 디버깅용 에러검사 함수이다.
compponentOverrides.Add : ~다
compponentOverrides는 FOverrides구조체의 객체이다.
기존에 정의되어 있던 CharacterMovementComponent의 이름(매개변수 SubobjectName),
바꾸려고 하는 클래스의 UClass형(매개변수 Class)을 이용하여 처음의 FObjectInitializer*을 변경하기위해
현재 FObjectInitializer*의 주소인 *this를 매개변수로 사용하여 Add라는 함수로 보냈다.
FObjectInitializer*의 값을 변경하려면 당연히 FObjectInitializer**을 매개변수로 넣어야한다.
FOverrides (struct)
Overrides 구조체는 파생클래스 에서 재정의를 관리하는데 도움을 주는 구조체이다.
파생클래스 : 우리같은 경우는 현재 ACharacter에서 파생된 ACTestCharacter클래스에서 ACharacter에 있는 기본 CharacterMovementComponent를 다른 클래스로 재정의를 하려고 한다. 여기서 ACTestCharacter를 파생클래스라 한다.
FOverride는 하나의 오버라이드할 정보이다.
뒤에s가 없음 / FOverrides 안에 FOverride가 있는것 (구조체 안에 구조체)
TArray형 배열 Overrides는 FOverride를 차곡차곡 담는다.
TInlineAllocator?
Add함수의 정의이다.
시작하면 FOverrides에 있던 Find함수부터 오지게 박고 시작하는데
Find함수 는 오버라이드 할 목록(TArray형 배열 Overrides)안에 오버라이드 할 정보가(FOverride)가 들어있는지 확인하고
없으면 INDEX_NONE(-1)이 반환된다.
만약 있다면 중복으로 오버라이드 하지 않으려고 현재 변경하려하는 하위객체(우리는 CharacterMovementComponent)의 이름과 같은지 확인하여 같다면 해당 인덱스를 리턴한다.
목록을 모두 돌아도 없다면 INDEX_NONE(-1)이 반환된다.
그래서 Find함수가 끝나면 if문 바로 첫번째 조건에 걸리게된다.
Index가 INDEX_NONE라면 우리가 매개변수로 넣었던 InComponentName, InComponentClass로
오버라이드 할 정보(FOverride)를 만들어 오버라이드 목록에 넣는다(Emplace).
INDEX_NONE이 아니라면 [나중에 작성]
이렇게 Add가 끝나면 이렇게 오버라이드 할 목록(Overrides)에 값이 추가 된다.
(좌)Add함수 시작할 때 / (우)Add함수 끝날 때
펴보면 대강 이렇다
보면 ComponentName에 우리가 바꾸려는 기존 정의된 하위객체의 이름이 들어가 있고
ComponentClass에 우리가 어떤 클래스로 바꿀지 넣은 GravityMovementComponent가 있다.
Add함수를 끝내면 SetDefaultSubobjectClass도 끝나며 다시 처음으로 돌아온다.
여태 SetDefaultSubobjectClass를 보았는데
한것은 ObjectInitialzier에 오버라이드할 정보를 추가했다고 할수있다!
쥰내 길지만 별게 없었다ㅋㅋㅋ
이제 Super를 쭉 타고 올라가보자
우리의 ACtestCharacter은 ACharacter을 상속받았다.
ACharacter은 APawn을 상속받았다.
APawn은 AActor를 상속받았다.
AActor은 UObject를 상속받았다.
그렇기에 ACtestCharacter에서 ACharacter생성자를 호출,
ACharacter생성자에서 APawn생성자를 호출,
APawn생성자에서 AActor생성자를 호출
AActor생성자에서 UObject생성자를 호출하게된다.
근데 AActor와UObject의 "생성자(const FObjectInitialzier& )" 형태의 이용 목적은 조금 다른데
AActor의 경우 현재도 사용되지만 파생 클래스에서 Super 형태로 불려졌을때를 위한 생성자이다.
UObject의 경우 이제 사용되지 않는다. 예전 버전과 호환을 위해 남겨졌다.
그래서 지금 불러지는 UObject의 생성자는 밑의 생성자가 아닌 기본 생성자로 불러진다.
UObject( ) (생성자)
UObject의 생성자부터 확인한다.
아까 말했듯이 기본 생성자로 불러진다.
EnsureNotRetrievingVTablePtr() 은 로그찍는 함수이다.
그 후 FObjectInitizler형 포인터를 하나 생성하고 FUObjectThreadContext::Get().TopInitializer()를 통해 가져온다.
FUObjectThreadContext::Get( )의 Get함수는 TThreadSingleton이라는 클래스에 있는 함수인데 어떻게 실행이 되냐면
FUObjectThreadContext클래스는 TThreadSingleton<FUObjectThreadContext>과 friend이기 때문이다.
그리고 TArray형중에 InitialzerStack이 있는데 이는
현재 사용되는 스레드의 FObjectInitialzer들의 스택이라 한다.
이 InitialzerStack는 밑에 다시 나온다.
TThreadSingleton<FUObjectThreadContext>와 friend라했으니 이제 밑의 T는 FUObjectThreadContext일것이다.
이 함수들은 쓰레드에 관한 내용이라 아직 감이 하나도 안와서 쓰레드 공부할때 다시 파봐야겠다.
어쨋든 이 함수들의 목적은 현재 쓰레드에 있는 싱글턴 객체를 반환한다 한다.
※Tls라는게 많이 나오는데 이는 ThreadLocalStorage일거 같다.
한마디로 스레드 별로 고유한 저장공간을 뜻한다.
http://egloos.zum.com/sweeper/v/1985738
현재 쓰레드에 있는 싱글턴 객체가 뭔지 생각해봤는데 현재 쓰레드에 대한 TLS을 가져오는 거라고 추측을 해본다
TLS객체가 여러개면 안되기때문에 싱글턴이라는 이름이 붙은거 같다.
중간코드에서 GetTlsValue( TlsSlot ) 부분에서 현재 Tls 번호를 통해 Tls에 접근해 함수명대로 Tls의 값을 가져와
return값으로 넣어주는거라고 생각한다.
확실하게 다시 공부해봐야한다ㅠ
아무튼 저 Return값의 ThreadSingleton값을 확인하면
밑의 데이터가 더 있지만 아직 필요없기에 생략
여기서 우리가 확인할것은 InitializerStack이다(위에서 한번 나왔었다)
이 스택값의 0번째의 주소는 0x00000020f137ad88 이다.
처음 시작할때 ACtestCharacter의 생성자에 있던 ObejctIntilaizer의 주소와 같다!
그 후 값( "FUObjectThreadContext::Get()" )에 TopInitializer( )를 호출하는데
현재 쓰레드의 현재 FObjectInitilazer을 반환한다고 한다.
한마디로 스택이니까 마지막 InitializerStack 값을 뽑아낸다고 할 수 있다.
그 후 로그나 간단한건 넘어가서
const_cast<FObjectInitializer&>(ObjectInitializer).FinalizeSubobjectClassInitialization() 가 실행되는데
기본값이 True인 FObjectInitializer::bSubobjectClassInitializationAllowed라는 bool형을 false로 바꿔주는데
이 변수는 생성자가 UObject클래스에 도달하면 False라고 한다.
그래서 UObject의 생성자에 도달하였으니 False로 바꿔주는듯 했다.
왜 쓰는지는 아직모름ㅋㅋㅋ
AActor( ) (생성자)
단순 AActor의 기본값 설정이다.
APawn( )도 마찬가지이니 넘어간다.
ACharacter( ) (생성자)
ACharacter생성자에서 기본으로 붙어있는 여러 컴포넌트들이 만들어지고 세팅된다.
CapsuleComponent, ArrowComponent, CharacterMovementComponent, SkeletalMeshComponent ...
그중에 우리가 변경할 CharacterMovementComponent를 보면
CreateDefaualtSubobject로 생성한다.
CreateDefaultSubobject( ) (Function)
이건 UObject::CreateDefaultSubobject
FUObjectThreadContext::Get().TopInitializer()을 통해 설정된 FObjectInitializer을가져온다
왜 생성자에서 매개변수로 받아온 FObjectInitilazer과 같은데 바로 사용하지 않는지 의문인데
매개변수로 받아온 FObjectInitilazer가 조사식에서 조사가 안되더라... 관련있는듯하다..
암튼 FObjectInitializer::CreateDefaultSubobject로 실행된다.
너무 길어 자름ㅋㅋ;
ComponentOverrides.Get을 이용하여 변경해야 할 클래스타입 을 얻는다.
변경해야 할 클래스타입 : 우리는 GravaityMovementComponent 만약 재정의가 아닌 기본이라면
CharacterMovementComponent가 나올것이다.
그 후 ComponentOverrides를 이용하여 FStaticConstructObjectParameters형 데이터를 만든다.
SetDefaultObject에서 FObjectInitiallizer를 설정하고
StaticConstructObject_Internal을 사용하여 Result에 객체를 받아와 반환한다.
그리고 기존의 CharacterMovementCoomponent에 대입이 되는것이다.
근데 잘 보면 캐스팅이 되지가 않았다
그래서 결국은 CharacterMovementComponent형이다!
맨 우측에 UCharacerMovementComponent*형인걸 확인할 수 있다.
그래서 현재 적용하려는 GravityMovementComponent도 이렇게 연결하고 가상함수를통해 실행되게한다.
많은 Virtual Function
혹은 다운캐스팅을 이용하여 접근하면 된다!
파생블루프린트에서 확인
사용할때는 CharacterMovementComponent로 나온다.