목차

     

     


     

     

     
    Plugins
     
      Weapon
     
        Resource
     
          Icon128.png
    weapon_thumbnail_icon.png
     
        Source
     
          Weapon  
          SWeaponCheckBoxes.h .cpp
    SWeaponDetailsView.h .cpp

    SWeaponEquipmentData.h .cpp
    SWeaponLeftArea.h .cpp
    Weapon.Build.cs
    WeaponAssetEditor.h .cpp
    WeaponAssetFactory.h .cpp
    WeaponCommand.h .cpp 
    WeaponContextMenu.h .cpp
    WeaponModule.h .cpp
    WeaponStyle.h .cpp 
     
       

     

     

     

     

     

    CheckBox와 데이터 연결하기

     


     

    일반적인 프로그래밍과 게임 프로그래밍의 차이

     

    일반적인 프로그래밍

    Client
    Server
        - DB
    - MVC

     

     

    게임 프로그래밍

    Client Server
    - Memory Map
    - 렌더링 데이터
    동기화 (Replication) - Memory Map
    - 게임 운용 데이터
        - Thread 구조로 처리
    Session Socket 생성   Thread 할당( Socket 생성 메모리 영역 생성)


     

     

    SWeaponCheckBoxes

     

    SWeaponCheckBoxes.h

    더보기
    #pragma once
    
    #include "CoreMinimal.h"
    
    //스마트 포인터 내부에서 this를 참조해야 되는 경우, 아래와 같이 자기 자신을 명시해서 상속받는다. TSharedFromThis<자기 자신>
    class WEAPON_API SWeaponCheckBoxes
    	: public TSharedFromThis<SWeaponCheckBoxes> 
    {
    public:
    	//IPropertyHandle는 하나의 Property에 대한 식별자
    	void AddProperties(TSharedPtr<IPropertyHandle> InHandle);
    
    	TSharedRef<SWidget> Draw(bool bBackground = false);
    	void DrawProperties(TSharedRef<IPropertyHandle> InPropertyHandle, IDetailChildrenBuilder* InChildrenBuilder);//해당 프로퍼티를 그릴지말지 결정
    
    	void SetUtilities(TSharedPtr<class IPropertyUtilities> InUtilities);//새로고침 값을 넘기는 역할
    	
    private:
    	void OnCheckStateChanged(ECheckBoxState InState, int32 InIndex);//CheckBox의 true, false를 바꾸어주는 함수
    
    private:
    	//내부 구조체
    	struct FInternalData
    	{
    		bool bChecked;//체크 되었는가
    		FString Name;//이름
    		TSharedPtr<IPropertyHandle> Handle;//식별자
    
    		FInternalData(TSharedPtr<IPropertyHandle> InHandle)
    		{
    			bChecked = false;//기본값은 false로 설정.
    			Handle = InHandle;
    
    			Name = Handle->GetPropertyDisplayName().ToString();//핸들 내의 DisplayName을 출력이름으로 설정.
    		}
    	};
    	TArray<FInternalData> InternalDatas;//구조체 Data 전부를 포괄한 배열변수
    
    	TSharedPtr<class IPropertyUtilities> Utilities;//멤버 변수
    };

    함수 추가

    • void DrawProperties(TSharedRef<IPropertyHandle> InPropertyHandle, IDetailChildrenBuilder* InChildrenBuilder);
    • void SetUtilities(TSharedPtr<class IPropertyUtilities> InUtilities);

     

    변수 추가

    • TSharedPtr<class IPropertyUtilities> Utilities;

     

     

    SWeaponCheckBoxes.cpp

    더보기
    #include "SWeaponCheckBoxes.h"
    #include "SWeaponDetailsView.h"
    #include "Widgets/Layout/SUniformGridPanel.h"
    #include "IPropertyUtilities.h"
    #include "IDetailPropertyRow.h"
    #include "IDetailChildrenBuilder.h"
    #include "DetailWidgetRow.h"
    
    void SWeaponCheckBoxes::AddProperties(TSharedPtr<IPropertyHandle> InHandle)
    {
    	uint32 number = 0;
    	InHandle->GetNumChildren(number);//핸들 내의 자식 property가 몇 개 있는지 가져온다.
    
    	for (uint32 i = 0; i < number; i++)
    		InternalDatas.Add(FInternalData(InHandle->GetChildHandle(i)));
    
    }
    
    TSharedRef<SWidget> SWeaponCheckBoxes::Draw(bool bBackground)
    {
    	TSharedPtr<SUniformGridPanel> panel;
    	SAssignNew(panel, SUniformGridPanel);
    	panel->SetMinDesiredSlotWidth(150);//최소폭 150으로 설정.
    
    	for(int32 i=0; i< InternalDatas.Num(); i++)
    	{
    		//그려서 넣는 부분
    		panel->AddSlot(i, 0)//한줄로 넣는다.
    		[
    			SNew(SCheckBox)
    			.IsChecked(InternalDatas[i].bChecked)
    			.OnCheckStateChanged(this, &SWeaponCheckBoxes::OnCheckStateChanged, i)//i는 가변 파라미터
    			[
    				SNew(STextBlock)
    				.Text(FText::FromString(InternalDatas[i].Name))//InternalDatas의 이름들 출력
    			]
    		];
    	}
    
    	return panel.ToSharedRef();
    }
    
    void SWeaponCheckBoxes::DrawProperties(TSharedRef<IPropertyHandle> InPropertyHandle,
    	IDetailChildrenBuilder* InChildrenBuilder)
    {
    	for (int32 i = 0; i < InternalDatas.Num(); i++)//uint가 아닌 int를 사용해야 한다.
    	{
    		if (InternalDatas[i].bChecked == false)//그릴 필요가 없는 경우
    			continue;
    
    		TSharedPtr<IPropertyHandle> handle = InPropertyHandle->GetChildHandle(i);
    		IDetailPropertyRow& row = InChildrenBuilder->AddProperty(handle.ToSharedRef());//handle를 가지고 기본모양을 추가하여 만들어준다. 
    
    		row.CustomWidget()
    		.NameContent()
    		[
    			handle->CreatePropertyNameWidget()
    		]
    		//줄이거나 늘렸을 때 Min 이하로는 고정. Max 이상으로는 고정.
    		.ValueContent()
    		.MinDesiredWidth(FEditorStyle::GetFloat("StandardDialog.MinDesiredSlotWidth"))
    		.MaxDesiredWidth(FEditorStyle::GetFloat("StandardDialog.MaxDesiredSlotWidth"))
    		[
    			handle->CreatePropertyValueWidget()
    		];
    	}
    }
    
    void SWeaponCheckBoxes::SetUtilities(TSharedPtr<IPropertyUtilities> InUtilities)
    {
    	Utilities = InUtilities;
    }
    
    void SWeaponCheckBoxes::OnCheckStateChanged(ECheckBoxState InState, int32 InIndex)
    {
    	InternalDatas[InIndex].bChecked = !InternalDatas[InIndex].bChecked;//bChecked값을 뒤집어준다.
    
    	SWeaponDetailsView::OnRefreshByCheckBoxes();
    	{
    		Utilities->ForceRefresh();//새로고침이 된다.
    	}
    	SWeaponDetailsView::OffRefreshByCheckBoxes();
    }

    헤더 추가

    • #include "SWeaponDetailsView.h"

     

    함수 정의

    • void SWeaponCheckBoxes::DrawProperties(TShaderRef<IPropertyHandle> InPropertyHandle,
      IDetailChildrenBuilder* InChildrenBuilder)
    • void SWeaponCheckBoxes::OnCheckStateChanged(ECheckBoxState InState, int32 InIndex)

     

    uint가 아닌 int를 사용해야 하는 이유는?

    • void SWeaponCheckBoxes::DrawProperties(TSharedRef<IPropertyHandle> InPropertyHandle,
      IDetailChildrenBuilder* InChildrenBuilder)
      {
      for (int32 i = 0; i < InternalDatas.Num(); i++) //uint가 아닌 int를 사용해야 한다.
      { ... } }

     


     

    SWeaponDetailsView

     

    SWeaponDetailsView.h

    더보기
    #pragma once
    
    #include "CoreMinimal.h"
    #include "IDetailCustomization.h"
    
    class WEAPON_API SWeaponDetailsView
    	: public IDetailCustomization
    {
    public:
    	static TSharedRef<IDetailCustomization> MakeInstance();
    	void CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) override;
    
    public:
    	//외부에서 CheckBoxes 바꿔주는 역할
    	static void OnRefreshByCheckBoxes() { bRefreshByCheckBoxes = true; }
    	static void OffRefreshByCheckBoxes() { bRefreshByCheckBoxes = false; }
    
    private:
    	static bool bRefreshByCheckBoxes;
    };

    static 함수 추가: 외부에서 CheckBoxes를 바꿔주는 역할을 수행하는 static 함수.

    • static void OnRefreshByCheckBoxes() { bRefreshByCheckBoxes = true; }
    • static void OffRefreshByCheckBoxes() { bRefreshByCheckBoxes = false; }

     

    static 변수 추가

    • static bool bRefreshByCheckBoxes;

     

     

    SWeaponDetailsView.cpp

    더보기
    #include "SWeaponDetailsView.h"
    #include "SWeaponCheckBoxes.h"
    #include "SWeaponEquipmentData.h"
    #include "DetailLayoutBuilder.h"
    #include "DetailCategoryBuilder.h"
    #include "IDetailPropertyRow.h"
    #include "Weapons/CWeaponAsset.h"
    
    bool SWeaponDetailsView::bRefreshByCheckBoxes = false;//static 변수 초기화.
    
    TSharedRef<IDetailCustomization> SWeaponDetailsView::MakeInstance()
    {
    	//자신의 클래스 타입을 만들어서 return해준다.
    	return MakeShareable(new SWeaponDetailsView());
    }
    
    void SWeaponDetailsView::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)
    {
    	UClass* type = UCWeaponAsset::StaticClass();//UClass 타입 하나를 받아온다.
    
    	DetailBuilder.HideCategory("CWeaponAsset");//CWeaponAsset 카테고리를 숨겨준다. 현재 작업의 편의를 위해 숨겨주고 추후에 보여주게 만들 예정이다. 
    
    	//Class Settings
    	{
    		//.EditCategory 해당 타입에 해당 카테고리가 있으면 그 카테고리를 return, 없으면 새로 만들어서 return
    		//ClassSettings는 없으므로 새로 만들어서 return
    		IDetailCategoryBuilder& category = DetailBuilder.EditCategory("ClassSettings", FText::FromString("Class Settings"));
    		//CWeaponAsset에서 직렬화된 변수들을 카테고리에 추가한다.
    		category.AddProperty("AttachmentClass", type);//CWeaponAsset의 AttachmentClass를 카테고리에 추가.
    		category.AddProperty("EquipmentClass", type);//CWeaponAsset의 EquipmentClass를 카테고리에 추가.
    		category.AddProperty("DoActionClass", type);//CWeaponAsset의 DoActionClass를 카테고리에 추가.
    	}
    
    	//EquipmentData
    	{
    		//.EditCategory 해당 타입에 해당 카테고리가 있으면 그 카테고리를 return, 없으면 새로 만들어서 return
    		IDetailCategoryBuilder& category = DetailBuilder.EditCategory("EquipmentData", FText::FromString("Equipment Data"));
    		IDetailPropertyRow& row = category.AddProperty("EquipmentData", type);
    
    		if (bRefreshByCheckBoxes == false)//새로고침이 아닐 때
    		{
    			TSharedPtr<SWeaponCheckBoxes> checkBoxes = SWeaponEquipmentData::CreateCheckBoxes();//카테고리가 처음에 만들어질 때 checkBox를 만든다.
    			checkBoxes->AddProperties(row.GetPropertyHandle());//checkBoxes에 실제로 가진 Handle를 추가
    		}
    	}
    }

    static 변수 초기화

    • bool SWeaponDetailsView::bRefreshByCheckBoxes = false;
      • SWeaponDetailsView에서 선언한 static 변수를 초기화해준다.

     

    함수 정의 수정

    • void SWeaponDetailsView::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)
      • EquipmentData 부분 수정
      • if (bRefreshByCheckBoxes == false) {
        TSharedPtr<SWeaponCheckBoxes> checkBoxes = SWeaponEquipmentData::CreateCheckBoxes();
        checkBoxes->AddProperties(row.GetPropertyHandle());
        }

     

     

    SWeaponEuipmentData

     

    SWeaponEuipmentData.h

    더보기
    #pragma once
    #include "CoreMinimal.h"
    #include "IPropertyTypeCustomization.h"
    
    class WEAPON_API SWeaponEquipmentData
    	: public IPropertyTypeCustomization
    {
    public:
    	static TSharedRef<IPropertyTypeCustomization> MakeInstance();
    	static TSharedPtr<class SWeaponCheckBoxes> CreateCheckBoxes();
    
    	void CustomizeHeader(TSharedRef<IPropertyHandle> InPropertyHandle, FDetailWidgetRow& InHeaderRow, IPropertyTypeCustomizationUtils& InCustomizationUtils) override;
    
    	void CustomizeChildren(TSharedRef<IPropertyHandle> InPropertyHandle, IDetailChildrenBuilder& InChildBuilder, IPropertyTypeCustomizationUtils& InCustomizationUtils) override;
    
    private:
    	static TSharedPtr<class SWeaponCheckBoxes> CheckBoxes;
    };

    변동사항 없음

     

     

     

    SWeaponEuipmentData.cpp

    더보기
    #include "SWeaponEquipmentData.h"
    #include "IPropertyUtilities.h"
    #include "IDetailPropertyRow.h"
    #include "IDetailChildrenBuilder.h"
    #include "SWeaponCheckBoxes.h"
    #include "DetailWidgetRow.h"
    
    TSharedPtr<SWeaponCheckBoxes> SWeaponEquipmentData::CheckBoxes;//초기화
    
    TSharedRef<IPropertyTypeCustomization> SWeaponEquipmentData::MakeInstance()
    {
    	//자신의 클래스 타입을 만들어서 return해준다.
    	return MakeShareable(new SWeaponEquipmentData());
    }
    
    TSharedPtr<SWeaponCheckBoxes> SWeaponEquipmentData::CreateCheckBoxes()
    {
    	if (CheckBoxes.IsValid())
    	{
    		CheckBoxes.Reset();
    		CheckBoxes = nullptr;
    	}
    
    	return CheckBoxes = MakeShareable(new SWeaponCheckBoxes());
    }
    
    void SWeaponEquipmentData::CustomizeHeader(TSharedRef<IPropertyHandle> InPropertyHandle, FDetailWidgetRow& InHeaderRow, IPropertyTypeCustomizationUtils& InCustomizationUtils)
    {
    	CheckBoxes->SetUtilities(InCustomizationUtils.GetPropertyUtilities());
    
    	InHeaderRow
    	.NameContent()
    	[
    		InPropertyHandle->CreatePropertyNameWidget()
    	]
    	.ValueContent()
    	.MinDesiredWidth(FEditorStyle::GetFloat("StandardDialog.MinDesiredSlotWidth"))
    	.MaxDesiredWidth(FEditorStyle::GetFloat("StandardDialog.MaxDesiredSlotWidth"))
    	[
    		CheckBoxes->Draw()//CheckBoxes를 그려준다(=생성한다).
    	];
    }
    
    void SWeaponEquipmentData::CustomizeChildren(TSharedRef<IPropertyHandle> InPropertyHandle, IDetailChildrenBuilder& InChildBuilder, IPropertyTypeCustomizationUtils& InCustomizationUtils)
    {
    	//uint32 number = 0;
    	//InPropertyHandle->GetNumChildren(number);
    
    	//for(uint32 i = 0; i < number; i++)
    	//{
    	//	TSharedPtr<IPropertyHandle> handle = InPropertyHandle->GetChildHandle(i);
    	//	IDetailPropertyRow& row = InChildBuilder.AddProperty(handle.ToSharedRef());//handle를 가지고 기본모양을 추가하여 만들어준다. 
    	//	
    	//	row.CustomWidget()
    	//	.NameContent()
    	//	[
    	//		handle->CreatePropertyNameWidget()
    	//	]
    	//	//줄이거나 늘렸을 때 Min 이하로는 고정. Max 이상으로는 고정.
    	//	.ValueContent()
    	//	.MinDesiredWidth(FEditorStyle::GetFloat("StandardDialog.MinDesiredSlotWidth"))
    	//	.MaxDesiredWidth(FEditorStyle::GetFloat("StandardDialog.MaxDesiredSlotWidth"))
    	//	[
    	//		handle->CreatePropertyValueWidget()
    	//	];
    	//}
    
    	CheckBoxes->DrawProperties(InPropertyHandle, &InChildBuilder);
    }

    함수 정의 수정

    • void SWeaponEquipmentData::CustomizeHeader(TSharedRef<IPropertyHandle> InPropertyHandle, FDetailWidgetRow& InHeaderRow, IPropertyTypeCustomizationUtils& InCustomizationUtils)
      • 체크박스를 세팅하는 부분 추가
        • CheckBoxes->SetUtilities(InCustomizationUtils.GetPropertyUtilities());
    • void SWeaponEquipmentData::CustomizeChildren(TSharedRef<IPropertyHandle> InPropertyHandle, IDetailChildrenBuilder& InChildBuilder, IPropertyTypeCustomizationUtils& InCustomizationUtils)

      • 체크박스를 그리는 부분 추가
        • CheckBoxes->DrawProperties(InPropertyHandle, &InChildBuilder);

     


     

     

     

    실행화면