Game!

전체 글 (93)

  1. 2022.05.07 [UE] Unreal C++ 스크립트 규칙

    언리얼에서는 자료형을 int32 등으로 사용하게 된다. int 는 4바이트 int32 는 4바이트 int 뒤에 붙는 32 표기는 비트 정보이다 int32 는 8*4 = 32 비트 ▶ 언리얼은 PC용 게임으로만 제작되는 것이 아니기 때문에 멀티플랫폼을 지원한다. 그래서 자료형 크기를 변환할 수 있게 사용 플랫폼마다 int 크기가 다를 수 있다. (phthon) C++ 스트립트에서 데이터 경로로 블루프린트의 클래스타입 쓸 때는.. 경로 마지막에 _C 를 붙여줘야 한다. ( 블루프린트로 레퍼런스를 가지고 올 경우 _C ) ▶ 오브젝트가 아니라고 명시 EX. 레퍼런스로 가져온 경로 : AnimBlueprint'/Game/ABP_CPlayer.ABP_CPlayer' CHelpers::GetClass(&animIn..

  2. 2022.05.06 [C++] 함수 포인터

    함수 포인터 함수의 주소값을 저장하는 포인터 (함수의 시작 주소를 가리킨다) 함수 : 프로그램(exe 파일)이 실행될 때, 우리가 소스파일에 정의한 모든 함수가 메인 메모리(프로그램이 실행되면서 할당받은 메모리)에 올라간다. 이때. 우리가 작성한 함수이름은 함수의 시작 주소를 가리키는 포인터가 되는데, 이 포인터가 가리키는 주소가 변경되면 안되니까.. 포인터 상수로 함수의 시작 주소를 가리키게 된다. 이것을 함수 포인터 라고 부른다. 포인터 상수 : const ptr 포인터를 상수로 처리했다 는 의미이다. ---> "포인터를 상수로" 라고 생각하면 쉽다. cf. 상수 포인터 : ptr 상수를 가리키는 포인터 라는 의미이다. ---> "상수를 가리키는 포인터" 라고 생각하면 쉽다. ① 함수 포인터 선언 문..

  3. 2022.05.02 컴퓨터와 프로그램 메모리

    프로그램(exe) 메모리 프로그램을 실행하면 운영체제가 프로그램을 위한 메모리 공간을 할당한다. 우리가 하는 일은 모두 이 연속된 메모리 안에서 일어나게 된다. 프로그램의 메모리 공간은 네 개의 영역으로 나눠서 관리된다 ( 위의 이미지 ) 1. 코드 영역 : 프로그램에 작성된 코드가 저장된다. 2. 데이터 영역 : 프로그램의 글로벌 데이터들이 저장된다. 3. 힙 영역 ( 위에서부터 아래로 데이터를 쌓아간다. ) : 단, 너무 많은 동적할당을 할 경우.. 힙 데이터가 스택 영역을 침범하게 된다. 이것을 힙 오버플로우라고 한다. 4. 스택 영역 ( 아래에서 부터 위로 데이터를 쌓아간다. ) : 함수 데이터들이 차곡차곡 쌓이는 공간이다. 코드에서 각각의 함수를 호출할 때마다 지역 변수나 매개 변수, *반환 주..

  4. 2022.05.01 [UE] 언리얼 오브젝트와 UClass

    언리얼 오브젝트 언리얼 엔진의 관리를 받는 특수한 객체 언리얼 오브젝트가 아닌, 입력에 따라 결과 값만 받고싶은 C++ 클래스도 언리얼 프로젝트에서 전혀 문제없이 쓸 수 있다. 언리얼 엔진에서는 이러한 오브젝트를 구분하기 위해 클래스에 접두어를 사용한다. - 언리얼 오브젝트 (언리얼 엔진의 관리를 받는 객체) : U로 시작. ex) UObject, UMeshComponent... - 일반 C++ 오브젝트 ( C++ 네이티브 객체) : F로 시작. ex) FName, FVector... 언리얼 오브젝트를 생성하기 위한 최상단 기본 클래스는 UObject 이다. 언리얼 오브젝트는 언리얼 엔진이 자체적으로 만들어 제공하는 프레임웍이기 때문에 언리얼 헤더 툴 ( UHT ) 이라는 프로그램의 도움을 받아야 한다...

  5. 2022.04.30 [UE] 리플렉션

    리플렉션 프로그램이 실행시간(런타임)에 자기 자신을 조사하는 기능 ▶ 자기자신의 정보를 들여다본다. ① 런타임 우리가 코드를 짜고 빌드를 하게되면 컴파일을 하게 되는데 컴파일의 순서는 아래와 같다. 번역된 어셈블리는 우리가 미리 작성한 스크립트(소스파일) 기준으로 생성이 된다. 런타임 때( = 어셈블리가 동작할 때 )는 메서드가 호출되거나 필드의 값을 변경하는 것들이 모두 프로그래머가 빌드하기 전에 스크립트로 미리 작성해놓은 대로 실행된다. 즉, 런타임 때 동작하는 것들은 스크립트에 정의해놓은 일련의 작업일 뿐이다. --> ex. ( C++ 스타일 ) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 switch( selectType ) { cas..

 

 

언리얼에서는 자료형을 int32 등으로 사용하게 된다.

int 는 4바이트
int32 는 4바이트

int 뒤에 붙는 32 표기는 비트 정보이다
int32 는 8*4 = 32 비트


언리얼은 PC용 게임으로만 제작되는 것이 아니기 때문에 멀티플랫폼을 지원한다. 그래서 자료형 크기를 변환할 수 있게 사용 플랫폼마다 int 크기가 다를 수 있다. (phthon)
C++ 스트립트에서 데이터 경로로 블루프린트의 클래스타입 쓸 때는.. 경로 마지막에 _C 를 붙여줘야 한다.
( 블루프린트로 레퍼런스를 가지고 올 경우 _C ) 
오브젝트가 아니라고 명시

EX.
레퍼런스로 가져온 경로 : AnimBlueprint'/Game/ABP_CPlayer.ABP_CPlayer'
CHelpers::GetClass<UAnimInstance>(&animInstance, "AnimBlueprint'/Game/ABP_CPlayer.ABP_CPlayer_C'");
언리얼 오브젝트가 아닌, 언리얼에서 제공하는 일반 C++ 오브젝트들은 모두 ToString이라는 함수를 가지고 있다.

- 언리얼 오브젝트 : UObject, UMesh...  (언리얼의 관리를 받는 오브젝트)
- 언리얼에서 제공하는 일반 C++ 오브젝트 : FVector, FRotator...
GEngine

언리얼 자체에 GEngine 객체가 있는데 많은 것을 가지고 있기 때문에 무겁다.
필요한 곳에서만 사용하는 것이 좋다. 

EX.
CLog.cpp
UE_LOG 를 출력할 때 문자열 타입이 FString 일 경우 포인터로 넘겨주어야한다.

EX.
FString testStr;
UE_LOG( Category, Verbority, L"%s", *testStr);

 

 

 


함수 포인터

함수의 주소값을 저장하는 포인터 (함수의 시작 주소를 가리킨다)


함수 :
프로그램(exe 파일)이 실행될 때, 우리가 소스파일에 정의한 모든 함수가 메인 메모리(프로그램이 실행되면서 할당받은 메모리)에 올라간다. 
이때. 우리가 작성한 함수이름은 함수의 시작 주소를 가리키는 포인터가 되는데, 이 포인터가 가리키는 주소가 변경되면 안되니까.. 포인터 상수로 함수의 시작 주소를 가리키게 된다.
이것을 함수 포인터 라고 부른다.

포인터 상수 : const ptr 
포인터를 상수로 처리했다 는 의미이다. ---> "포인터를 상수로" 라고 생각하면 쉽다.

cf.
상수 포인터 : ptr
상수를 가리키는 포인터 라는 의미이다. ---> "상수를 가리키는 포인터" 라고 생각하면 쉽다.

 

 


① 함수 포인터 선언 문법

void (*FuncPtrName)(int, int)
---> 반환값과 매개변수 타입이 동일한 함수만 호출 가능

FuncPtrName : 함수 포인터의 이름


문법상 괄호와 포인터 표기는 필수!

- 괄호
예를 들어, int (*FuncPtrName) ( ); 에서.. 괄호는 제거하게 되면
int* FuncPtrName( ); 이렇게 된다. 이것은 int형 포인터를 반환하는 함수 의 전방선언으로 해석되기 때문에, 함수 포인터를 선언할 때는 문법상 괄호는 꼭 넣어줘야 한다.

- 포인터 :
함수 포인터도 포인터니까.. 포인터 변수 선언하듯 이름 앞에 붙여준다.


② 상수 함수 포인터 선언 문법

상수 함수 포인터 : 함수 포인터가 가리키는 주소를 변경할 수 없도록 상수화 처리한 것

void (*const FuncPtrName)(int, int);

포인터 뒤에 const 를 붙인다.
이 함수 포인터는 초기화 외에는 주소값 변경이 불가능하다.

 

 


함수 포인터 사용 ① : 기본

double Add( double left, double right )
{
     return left + right ;
}

void main( )
{
     // double (*FuncPtrCalc)(double, double) ---> 자체가 변수 선언이다.
     // 변수 이름은 함수 포인터가 된다.

     double (*FuncPtrCalc)(double, double) = NULL;
     FuncPtrCalc = Add;

     double value1 = FuncPtrCalc( 1.234, 3.141592 );
     double value2 = GetCalcTypeValue( Add, 1.234, 3.141592 );
}

double GetCalcTypeValue( double (*InFuncPtr)(double, double), double left, double right )
{
     return InFuncPtr( left, right );
}

해당 방식은 가독성이 떨어져서 잘 사용하지 않는다.

함수 포인터 사용 ② : typedef 로 일반 함수처럼 보이게 만든다

typedef double (*FuncPtrCalc)(double, double);

double Add( double left, double right )
{
     return left + right ;
}

void main( )
{
     FuncPtrCalc = Add;

     double value1 = FuncPtrCalc( 1.234, 3.141592 );
     double value2 = GetCalcTypeValue( Add, 1.234, 3.141592 );
}

double GetCalcTypeValue( FuncPtrCalc funcCalc, double left, double right )
{
     return funcCalc( left, right );
}

가독성을 위해 가장 일반적으로 사용한다.

함수 포인터 사용 ③ : 가독성을 높이기 위해 C++11 에 추가된 방법

ⓐ using 을 사용한 방법

using FuncPtrCalc = double ( * )(double, double);

double Add( double left, double right )
{
     return left + right ;
}

void main( )
{
     FuncPtrCalc = Add;

    double value1 = FuncPtrCalc( 1.234, 3.141592 );
    double value2 = GetCalcTypeValue( Add, 1.234, 3.141592 );
}

double GetCalcTypeValue( FuncPtrCalc funcCalc, double left, double right )
{
    return funcCalc( left, right );
}



ⓑ std::function 키워드를 사용한 방법
단, #include <functional> 을 추가해줘야 사용가능하다.

double Add( double left, double right )
{
     return left + right ;
}

void main( )
{
     std::function<double (double, double)> FuncPtrCalc;

     FuncPtrCalc = Add;

     double value1 = FuncPtrCalc( 1.234, 3.141592 );
     double value2 = GetCalcTypeValue( Add, 1.234, 3.141592 );
}

double GetCalcTypeValue( std::function<double (double, double)> funcCalc, double left, double right)
{
     return funcCalc( left, right );
}

 

 

 

 

 

 

 

'Language > C++' 카테고리의 다른 글

[C] 문자열 상수  (0) 2022.10.23
[C++] 코드 위치나 정보를 받아오는 매크로 들  (0) 2022.05.08
[C++] 문법 - 범위 지정 연산자  (0) 2022.04.01
C++ for_each 함수  (0) 2018.03.21
wsprintf 정리  (0) 2018.02.28

코드와 데이터는 정적 영역이며, 사용할 데이터들이 컴파일 시점에 이미 결정이 되어 있다. 힙과 스택은 유동적으로 결정된다.

 


프로그램(exe) 메모리

프로그램을 실행하면 운영체제가 프로그램을 위한 메모리 공간을 할당한다.

우리가 하는 일은 모두 이 연속된 메모리 안에서 일어나게 된다.


프로그램의 메모리 공간은 네 개의 영역으로 나눠서 관리된다 ( 위의 이미지 )

1. 코드 영역 : 프로그램에 작성된 코드가 저장된다.

2. 데이터 영역 : 프로그램의 글로벌 데이터들이 저장된다.

3. 힙 영역 ( 위에서부터 아래로 데이터를 쌓아간다. ) :
단, 너무 많은 동적할당을 할 경우.. 힙 데이터가 스택 영역을 침범하게 된다. 이것을 힙 오버플로우라고 한다.

4. 스택 영역 ( 아래에서 부터 위로 데이터를 쌓아간다. ) :
함수 데이터들이 차곡차곡 쌓이는 공간이다. 코드에서 각각의 함수를 호출할 때마다 지역 변수나 매개 변수, *반환 주소 같은 것들이 쌓이게 된다.
단, 호출 스택이 너무 길어지거나.. 지역 변수를 너무 많이 만들게 되면 운영체제가 할당해준 스택 영역 범위를 벗어나게 되고. 결국 힙 영역을 침범하게 된다. 이것을 스택 오버플로우 라고 한다.


* 반환 주소 : 프로그램이 함수를 호출하고 반환하는 과정은 반환 주소를 통해 이루어진다.
그래서 함수들이 호출되는 과정을 Call Stack 이라고 부른다.


cf.
버퍼 오버플로우 : 힙 오버플로우와 스택 오버플로우를 합쳐 부르는 단어

 

'CS' 카테고리의 다른 글

배열 할당 : 메모리 상태  (0) 2015.11.06
메모리의 구조과 프로그램 완성과정  (0) 2015.06.03

 


언리얼 오브젝트

언리얼 엔진의 관리를 받는 특수한 객체

언리얼 오브젝트가 아닌, 입력에 따라 결과 값만 받고싶은 C++ 클래스도 언리얼 프로젝트에서 전혀 문제없이 쓸 수 있다.
언리얼 엔진에서는 이러한 오브젝트를 구분하기 위해 클래스에 접두어를 사용한다.
- 언리얼 오브젝트 (언리얼 엔진의 관리를 받는 객체) : U로 시작.
ex) UObject, UMeshComponent...
- 일반 C++ 오브젝트 ( C++ 네이티브 객체) : F로 시작.
ex) FName, FVector...

언리얼 오브젝트를 생성하기 위한 최상단 기본 클래스는 UObject 이다.


언리얼 오브젝트는 언리얼 엔진이 자체적으로 만들어 제공하는 프레임웍이기 때문에
언리얼 헤더 툴 ( UHT ) 이라는 프로그램의 도움을 받아야 한다.
헤더파일에서 *클래스 선언을 언리얼 오브젝트 규격에 맞게 선언해주면 언리얼 헤더 툴 ( UHT ) 이 이를 먼저 파싱하고 분석한다.
규격에 맞지 않으면 에러를 출력하고, 규격이 맞으면 UHT 이 부가적인 메타 정보를 담은 소스코드를
프로젝트 내의 Intermediate 폴더에 메타 파일을 생성한다.
이 작업이 모두 끝나면 컴파일이 진행된다.

* 클래스 선언을 언리얼 오브젝트 규격에 맞게 선언 ▶ UCLASS( ) 매크로를 사용해서 선언해준다.
참고로 멤버 변수는 UPROPERTY( ), 멤버 함수는 UFUNCTION( ) 매크로를 지정해주면 앞으로 언리얼 엔진의 관리를 받게된다. (언리얼 오브젝트 기능을 사용할 수 있게된다)


언리얼 오브젝트가 되면 생기는 기능
1. CDO : 객체의 초기값을 자체적으로 관리한다.
2. Reflection : 객체 정보를 런타임에서 실시간 조회가 가능하다.
3. GC ( Garbage Collection ) : 참조되지 않는 객체를 메모리에서 자동으로 해제한다.
4. Serialization : 객체와 속성 정보를 통으로 안전하게 보관하고 로딩한다.
5. Delegate : 함수를 묶어서 효과적으로 관리하고 호출 가능하다.
6. Replication : 네트워크 상에서 객체 간에 동기화를 시킬 수 있다.
7. Editor Integration : 언리얼 에디터에서 값을 편집할 수 있다.

 

 


UCLASS( ) 매크로 ( 헤더파일 class 위에 선언되어 있는 것 ) ▶ 이것은 매크로 함수일 뿐...

클래스 선언 시, 언리얼 오브젝트로 사용하고 싶을 때 지정해주는 매크로

UCLASS( ) 와 *GENERATED_BODY( ) 는 짝을 이루어야 한다.

* GENERATED_BODY( ) : 
리플렉션된 클래스나 구조체에 필수적. UHT 에 의해 클래스 본문에 추가적인 함수나 typedef 를 주입해준다.


UCLASS( ) 매크로는 UObject 에게 자신이 언리얼에서 기반으로 삼은 유형에 대해 설명해주는 UClass 타입을 레퍼런스로 넘겨준다. ( UHT 에 의해 )

 

 


UClass ( 클래스 타입 )

언리얼 오브젝트에 대한 클래스 계층 구조 정보 + 멤버 변수/멤버 함수 를 모두 기록한다.
CDO 를 유지한다.

UClass 타입은 StaticClass( ) 또는 GetClass( ) 함수로 가져올 수 있다. (=접근이 가능하다)
- 컴파일 단계 : StaticClass( ) ▶ UClass 타입의 정보를 얻어올 때 사용
- 런타임 단계 : GetClass( ) ▶ 실제 객체의 클래스를 조회할 때 사용
> 이 함수들은 우리가 선언한 적이 없지만 GENERATED_BODY( ) 에 의해 UHT 이 추가적인 함수를 생성해줬기 때문에 호출이 가능하다.

cf.
StaticClass 함수는 컴파일 단계에 이미 정해진 것이다.
하지만 포인터가 가리키는 실제 객체 타입은 런타임에서 달라질 수 있으므로
실제 객체의 클래스를 리턴하는 GetClass 함수의 리턴 값은 다를 수 있다.

 

 


CDO ( Class Default Object )

*실행 초기의 런타임 과정에서는 언리얼 오브젝트마다 클래스 정보와 함께 언리얼 오브젝트의 인스턴스가 생성된다. 이 특별한 인스턴스는 언리얼 오브젝트의 기본 세팅을 지정하는데에 사용된다.
이 특별한 인스턴스를 CDO 라고 한다.

*실행 초기의 런타임 과정 = 생성자
언리얼 오브젝트 클래스의 생성자는 인스턴스를 초기화해서 CDO 를 만들기 위한 목적으로 사용된다.
이 생성자 코드는 초기화에서만 실행되고 실제 게임 플레이에서 생성자 코드는 사용할 일이 없다.
언리얼 엔진에서 게임 플레이에서 사용할 초기화 함수는 Init 또는 BeginPlay 함수를 제공한다.
( → 아.. 그래서 생성자에 코드 짜고 빌드하면.. 클래스 블루프린트 열었을 때 그 내용들이 적용되어있는거구나... )

언리얼 클래스 생성자 심화 : 생성자에 매개변수를 추가할 수 있는데 
UTESTObject::UTESTObject(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) 의 경우
FObjectInitializer 매개변수는 const 마킹이 되어있음에도 불구하고 내장된 mutable 함수를 통해서 프로퍼티와 서브오브젝트를 덮어쓰도록 설정할 수 있다. 
그로 인해서 생성되는(덮어씌워진) UObject는 이러한 매개변수로 들어온 변경사항에 영향을 받을 것이며, 이를 사용해서 등록된 프로퍼티나 컴포넌트의 값을 변경할 수 있다.


CDO 를 만드는 이유
언리얼 오브젝트를 생성할 때마다 매번 초기화하지 않고, 기본 인스턴스( CDO ) 를 복사해서 프로퍼티만 변경하는 방식의 매커니즘으로 구성되어 있다.
( 수많은 오브젝트들이 로드가 될 경우 캐릭터를 하나씩 처음부터 생성하고 초기화시키는 것보다, 미리 큰 기본 객체 덩어리를 복제한 후 속성값만 바꾸고 로드하는 것이 훨씬 빠르기 때문이다 )
▶ CreateNewObject( ) 함수나 SpawnActor( ) 같은 함수로 생성할 경우 생성된 오브젝트에 CDO 가 복사될 것이다.


언리얼 에디터도 일종의 언리얼 어플리케이션이다보니, 사용하는 모듈이 초기화되거나 갱신된 코드로 컴파일이 진행되면 CDO 가 생성된다.
뿐만 아니라, 에디터의 플레이 버튼을 누르면 에디터와 독립된 게임을 시뮬레이션 해야하기 때문에 또 CDO 가 생성된다.

 

 


언리얼 오브젝트 로딩 과정

1. 모듈 로딩 ( 작은 퍼센트 창 뜨는 거 )
2. 모듈( 프로젝트 )에 있는 모든 UObject의 클래스 정보 등록
3. 각 클래스 정보의 CDO 인스턴스 생성
4. 언리얼 오브젝트(클래스)의 생성자를 호출해 CDO 정보를 완성

▶ 이 모든 과정이 끝나면 초기화 수치가 100% 이 된다. 이 때 비로소 에디터가 뜨게 된다.

 

 

 

 

 

[UE] 리플렉션2022. 4. 30. 01:13

 


리플렉션

프로그램이 실행시간(런타임)에 자기 자신을 조사하는 기능
▶ 자기자신의 정보를 들여다본다.

 

 

 

① 런타임

우리가 코드를 짜고 빌드를 하게되면 컴파일을 하게 되는데
컴파일의 순서는 아래와 같다.

 

번역된 어셈블리는 우리가 미리 작성한 스크립트(소스파일) 기준으로 생성이 된다.

런타임 때( = 어셈블리가 동작할 때 )는 메서드가 호출되거나 필드의 값을 변경하는 것들이 모두 프로그래머가 빌드하기 전에 스크립트로 미리 작성해놓은 대로 실행된다.

즉, 런타임 때 동작하는 것들은 스크립트에 정의해놓은 일련의 작업일 뿐이다.

 

--> ex. ( C++ 스타일 ) 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 
 
switch( selectType )
{
    case 1:
        {
            cout << " A 값을 " + object.a + " 값으로 변경했습니다." <<endl;
        }
        break;
    case 2:
        {
            cout << " B 값을 " + object.b + " 값으로 변경했습니다." <<endl;
        }
        break;
    case 3:
        {
            cout << " C 값을 " + object.c + " 값으로 변경했습니다." <<endl;
        }
        break;
}
 
 
 
cs

 

> 이 코드는 단순히 이미 알고있는, 미리 정의된 동작만 수행한다.

(case 1, 2, 3 ---> 케이스가 추가되면 상황에 따라 프로그래머가 작성해야한다.)

 

 

 

하지만!! 리플렉션을 사용하게 되면 형식(Type)에 정의된 필드나 메서드를 조사해서 값을 가져오거나 설정하거나 호출할 수 있게된다.

즉, 위의 예제처럼 일일히 하나씩 미리 구현할 필요가 없어진다.

 

--> ex. ( C# 스타일 - 리플렉션 코드 )

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
 
 
// GetType() : 오브젝트의 형식(Type)을 가져온다.
// GetField() : 형식(Type) 내의 필드 정보를 조사한다.
// Where((i) => i.FieldType == typeof(int)) : int 즉, 정수 형식 필드만 사용하겠다.
var _getField = myObject.GetType()
                        .GetField( BindingFlags.Instance | BindingFlags.Public )
                        .Where( (i) => i.FieldType == typeof(int) )
                        .ToArray();
 
// Console.ReadLine() : C# 에서 콘솔 입력 받는 표준 문법
int _userInputValue = Console.ReadLine();
 
if ( selectType >= 1 && selectType <= _getField.Length )
{
    _getField[selectType - 1].SetValue(object, _userInputValue);
    
    Log.Debug(" {0} 값을 {1} 값으로 변경했습니다. ", _getField[selectType - 1], _userInputValue);
}
 
 
// 런타임 때 
// GetType()     예제로         정보를 가져와서
// GetField()     예제로         정보를 알아내서
// SetValue()     예제로         적절히 맞춰서 사용
 
 
cs

 

 

 

위의 예제를 참조하면

런타임 때 코드가 직접 정보를 알아내서(알 수 있어서) 적절히 맞춰서 사용할 수 있다.

▶ 리플렉션

 

 

 

② 리플렉션의 장점

 

위의 예제에서 Class에 필드가 추가되거나 제거되거나 수정이 되어도

코드 부분은 이를 반영하여 결과를 유동적으로 표시할 수 있다는 것.

 

 

 

 


언리얼의 리플렉션

1.
에디터의 디테일 패널, 시리얼라이제이션, 가비지 콜렉션, 네트워크 리플리케이션, 블루프린트와 C++의 커뮤니케이션 등 다수의 시스템에 탑재되어 있다.

리플렉션은 JAVA, C# 에서는 기본적으로 제공하는 기능이지만 C++ 에서는 제공하지 않아서 언리얼에서 자체적으로 제공하며 C++ 클래스, 구조체, 함수 멤버변수, 열거형... 등 관련 정보를 수집/질의/조작하는 별도의 시스템이 구축되어 있다.
---> 이것을 언리얼 프로퍼티 시스템 이라고도 부르며, 매크로 함수 형태로 되어있다.


cf.
에디터의 디테일 패널 :
언리얼 에디터도 일종의 언리얼 어플리케이션이다. 그래서 리플렉션을 통해 에디터가 켜져있는 중에도 컴파일이 끝나면 디테일 패널이 갱신된다. 


2. 
리플렉션 기능이 적용됐으면하는 유형이나 프로퍼티에 특수한 주석을 달아주면 Unreal Hear Tool ( UHT )이 그 프로젝트를 컴파일 할 때 해당 정보를 수집한다.
단, 리플렉션이 있는 유형으로 처리하려면 헤더에 특수한 include 를 해주어야 한다.
---> #include "FileName.generated.h"

* 특수한 주석 :
UCLASS( ), USTRUCT( ), UENUM( ), EFUNCTION( ), UPROPERTY( ) ....
---> 이 매크로 각각은 유형 및 멤버 선언 전에 오며, 추가적인 키워드를 담을 수 있다. (EditAnywhere 등)



----------------------------
★ 주의할 점 !!
UCLASS( )
class A:
private:
     int8 MyTeamCount;

리플렉션 클래스에 네이티브 변수를 넣을 수도 있다.
단, 리플렉션된 프로퍼티가 아닌 변수는 리플렉션된 값을 그대로 저장할 경우 ( 예를 들어, UObject* 와 같은 )
가비지 콜렉터가 레퍼런스를 확인할 수 없기에 조심해야한다.

 

 

 

출처:

https://kyoun.tistory.com/126

https://cs-solution.tistory.com/20?category=1046133

 

1 2 3 4 5 6 7 8 ··· 19