13. 헤드업 디스플레이의 구현
초기화 프로세스 정리

- 컴포넌트 초기화(InitializeComponent)
- 액터 초기화(PostInitializeComponent)
- 비긴플레이(BeginPlay)
- 액터: 위젯 생성(CreateWidget) → UI위젯 초기화 콜백(NativeOnInitialized)
- 액터: 위젯 뷰포트 추가(AddToViewport) → UI위젯 생성(NativeConstruct)
컴포넌트 InitializeComponent를 써야하는 이유: UI에서 데이터를 사용해야하는 경우 데이터가 다뤄지는 컴포넌트의 초기화가 UI 초기화 전에 완성돼있어야 하기 떄문.
UABCharacterStatComponent::UABCharacterStatComponent(){ PrimaryComponentTick.bCanEverTick = false; bWantsInitializeComponent = true;}void UABCharacterStatComponent::InitializeComponent(){ Super::InitializeComponent(); SetLevelStat(CurrentLevel); SetHp(BaseStat.MaxHp);}- BeginPlay보다 앞선 단계 InitializeComponent로 로직 옮김
번외) Tick 함수 등을 호출 비활성화하면 성능이 개선되는 이유?
- 주변 메모리를 한꺼번에 들고 레지스터에 둠(캐시 지역성)
- 그런데 함수는 기본적으로 메모리에 연속적으로 위치해있지 않음. 고로 메모리 점프가 자주 일어남(+ 다형성 등으로 더욱 예측 불가)
- 함수 호출을 줄이면 그만큼 캐시 효율성이 높아짐
- 물론 프레임 별 처리량이 절대적으로 줄기 때문에 가벼워지는 것도 있음
GetOwningPlayerPawn()
// UserWidget.cppAPawn* UUserWidget::GetOwningPlayerPawn() const{ if (APlayerController* PC = GetOwningPlayer()) { return PC->GetPawn(); } return nullptr;}매개변수 참조와 포인터 차이
void UpdateStat(const FABCharacterStat& BaseStat, const FABCharacterStat& ModifierStat);- 호출 시: UpdateStat(MyStat, MyModifier) → 일반 변수 그대로 전달
- nullptr 전달 불가능 → 항상 유효한 값 보장
- 내부에서 . 연산자로 접근: BaseStat.AttackPower
void UpdateStat(const FABCharacterStat* BaseStat, const FABCharacterStat* ModifierStat);void UABCharacterStatWidget::UpdateStat(const FABCharacterStat* BaseStat, const FABCharacterStat* ModifierStat){ if (!BaseStat || !ModifierStat) return; // nullptr 체크 필수 // -> 연산자로 접근 // BaseStat->AttackPower;}- 호출 시: UpdateStat(&MyStat, &MyModifier) → 주소값(&) 붙여서 전달
- nullptr 전달 가능 → 반드시 nullptr 체크 필요
- 내부에서 -> 연산자로 접근: BaseStat->AttackPower