UserWidget 생성자
객체 복사 막는 방법
UserWidget vs WidgetComponent
AddDynamic vs AddUObject
기타
UUW_HPText::UUW_HPText(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{}
그냥 기본 복사생성자나 복사대입연산자는 얕은 복사를 해줌.
그러다보니, 동적할당한 메모리도 얕은 복사로 주소만 복사해주기 때문에 댕글링 포인터가 발생할 수 있어 막음
싱글톤, mutex, 파일 핸들러 같이 하나만 있어야 하는 것들때문에 막음
복사비용이 비싸서
복사생성자랑 복사대입연산자 private로 설정 (근데 friend class는 사용 가능)
private:
A(const A& a): {}
A& operator=(const A& aa) { }
C++11부터 delete로 제거가 가능해짐
복사불가능한 객체를 상속받는 식으로 재사용
class Uncopyable {
protected:
Uncopyable() = default;
~Uncopyable() = default;
private:
Uncopyable(const Uncopyable&) = delete; // 복사 생성자 금지
Uncopyable& operator=(const Uncopyable&) = delete; // 복사 대입 연산자 금지
};
// Uncopyable 상속으로 복사 금지 적용 클래스 예제
class MyClass : private Uncopyable {
public:
MyClass() = default;
// MyClass에서는 명시적으로 복사 생성자나 복사 대입 연산자 정의 없이 복사 방지
};
UserWidget은 UI, HUD같은거 쓸 때 사용. WidgetComponent는 컴포넌트로 액터에 붙여서 해당 액터근처에 위젯뜨도록 함. 머리위에 이름 뜨게도 가능하고, 상자위에 코인 개수 띄울수있음.
근데 애초에 UI를 디자인하는 것은 UserWidget으로 디자인하는 것. 텍스트, 버튼 등을 배치해 디자인하여 UI자체를 생성. 그래서 위젯컴포넌트도 UI 자체는 UserWidget으로 만들고 이를 사용. 내부적으로 UserWidget에셋을 Widget Class로 지정해 생성하고 관리함. 정말 그냥 위젯을 컴포넌트처럼 가지게 해주는 역할임. 실제 UI 내부 디자인, 업데이트는 GetWidget으로 접근해서 UserWidget을 업데이트
위젯컴포넌트를 Screen으로 안 하고, World로 했음. 스크린하면 좀 이상해서. 그럼 World로 한 대신, 클라 화면방향으로 항상 바라보도록 위젯을 띄어야 함. 그래서 캐릭터 Tick함수에서 자신의 위젯컴포넌트의 회전값을 현재 클라에 존재하는 카메라의 LookAt벡터 방향으로 설정해줌. 모든 캐릭터의 틱마다 방향을 조정해주니까 각 클라 화면에서 모든 캐릭터의 위젯컴포넌트가 잘 보이도록 방향이 조정됨
void ADXCharacter::Tick(float DeltaTime)
{
// ... //
// 클라일 경우에만
if (IsValid(HPTextWidgetComponent) && HasAuthority() == false)
{
FVector WidgetComponentLocation = HPTextWidgetComponent->
GetComponentLocation();
FVector LocalPlayerCameraLocation =
UGameplayStatics::GetPlayerCameraManager(this, 0)->GetCameraLocation();
HPTextWidgetComponent->
SetWorldRotation(UKismetMathLibrary::FindLookAtRotation(
WidgetComponentLocation, LocalPlayerCameraLocation));
}
}
AddDynamic을 이용하여 함수를 연결하였음. 이 방법은 연결할 함수가 UFUNCTION으로 선언되어 있어야함. 즉, 블루프린트, RPC에서 사용될 함수라 UFUNCTION선언된 함수를 연결할 때 사용.UFUNCTION선언 안 한 순수 멤버함수를 연결할때는 AddUObject함수를 사용. UObject를 상속받은 클래스의 멤버함수만 가능하며 C++로직으로만 필요한 함수여서 UFUNCTION안쓰는 함수를 연결할 때 사용void ADXCharacter::SetHPTextWidget(UUW_HPText* HPTextWidget)
{
if (IsValid(HPWidget))
{
// OnCurrentHPChanged 델리게이트에 C++멤버함수 연결
Component->OnCurrentHPChanged.AddUObject(HPWidget,
&UUW_HPText::OnCurrentHPChange);
}
}
Owner을 따라감. 그래서 컴포넌트의 롤을 확인하고 싶으면, 자신의 액터의 로컬롤과 리모트롤을 확인해야함APawn* OwnerPawn = MyComponent->GetOwner();
if (IsValid(OwnerPawn))
{
EnetRole LocalRole = OwnerPawn->GetLocalRole();
EnetROle RemoteRole = OwnerPawn->GetRemoteRole();
}
Destroy처럼 액터 없애는 로직 역시 서버에서 해야한다
DOREPLIFETIME_CONDITION을 사용하면 원하는 조건대로 레플리케이션 가능
// Owning Client에만 MaxHP를 레플리케이션하고, Other Client에는 레플리케이션 안 해줌
DOREPLIFETIME_CONDITION(ThisClass, MaxHP, COND_OwnerOnly);
서버 터진 이유 찾기 : 해당프로젝트 파일의 Saved\Logs에 들어가면 많은 파일들이 있음. 이 파일들 중에서 backup날짜가 없는 파일들이 서버와 클라이언트의 가장 최근 로그파일임. 각 파일들어가서 IsServer값을 찾아 서버인것을 확인하고, 로그를 보면 왜 터졌는지 확인 가능. 그 이전 로그들은 backup날짜가 이름에 붙어있음


에픽게임즈에서 엔진 검증, visual studio installer에서 복구 버튼을 눌러서 초기화해주면 가끔 생기는 이상한 에러 잡을 수 있음
![]() | ![]() |
|---|
타이틀레벨에서 컨트롤러를 UI모드로 설정해놨다가, 메인레벨로 넘어가면서 컨트롤러가 바뀌어도 이 모드가 초기화 안 될 수 있음. 그래서 항상 컨트롤러의 BeginPlay에서 입력모드 초기화해주기
위젯 버튼 속성 중에서 IsFocusable이 참이면, tab키 내비게이션, 방향키, 게임패드 조작 등으로 버튼에 포커스가 이동 가능하며, 포커스가 있을 때 키 입력을 받을 수 있습니다. 거짓이면 마우스 클릭에만 반응함