목차

     

     


     

     
    Plugins
     
      Weapon
     
        Resource
     
          Icon128.png
    weapon_thumbnail_icon.png
     
        Source
     
          Weapon  
          SWeaponLeftArea.h .cpp 생성
    Weapon.Build.cs
    WeaponAssetEditor.h .cpp
    WeaponAssetFactory.h .cpp
    WeaponCommand.h .cpp 
    WeaponContextMenu.h .cpp
    WeaponModule.h .cpp
    WeaponStyle.h .cpp 
     
       

     

     

     

    Weapon Plugin 3

     

     


     

    SCompoundWidget

     

    SCompoundWidget

    • 공간 분할해주는 역할
    • 연관된 것을 묶어줄 때 사용.
    • 요소들
      • SAssetSearchBox
      • SListView
      • STextBlock

     

    https://docs.unrealengine.com/4.27/en-US/API/Runtime/SlateCore/Widgets/SCompoundWidget/

     

    SCompoundWidget

    A CompoundWidget is the base from which most non-primitive widgets should be built.

    docs.unrealengine.com


     

     

    Weapon.Build.cs

     

    Weapon.Build.cs

    더보기
    using UnrealBuildTool;
    public class Weapon : ModuleRules
    {
    public Weapon(ReadOnlyTargetRules Target) : base(Target)
    {
    PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
    PrivateIncludePaths.Add(ModuleDirectory);
    PublicDependencyModuleNames.Add("Core");
    PrivateDependencyModuleNames.Add("U2212_06");//프로젝트명
    PrivateDependencyModuleNames.Add("CoreUObject");
    PrivateDependencyModuleNames.Add("Engine");
    PrivateDependencyModuleNames.Add("Slate");
    PrivateDependencyModuleNames.Add("SlateCore");
    PrivateDependencyModuleNames.Add("UnrealEd");//UFactory
    PrivateDependencyModuleNames.Add("EditorStyle");
    PrivateDependencyModuleNames.Add("InputCore");
    }
    }

     

     

    PrivateDependencyModuleNames.Add("InputCore"); 추가

     


     

     

     

    SWeaponLeftArea 생성

     

    새 C++ 클래스 - 없음 - SWeaponLeftArea 생성

    CWeaponAsset의 왼쪽에 해당되는 부분을 작업하기 위해  SWeaponLeftArea를 생성하였다.

     

     

     

     

     

     

     

     

     

     

    SWeaponLeftArea.h 

    더보기
    #pragma once
    #include "CoreMinimal.h"
    #include "Widgets/SCompoundWidget.h"
    #include "Widgets/Views/STableRow.h"
    struct FWeaponRowData
    {
    int Number;
    FString Name;
    class UCWeaponAsset* Asset;
    FWeaponRowData()
    {
    }
    FWeaponRowData(int32 InNumber, FString InName, class UCWeaponAsset* InAsset)
    : Number(InNumber), Name(InName), Asset(InAsset)
    {
    }
    static TSharedPtr<FWeaponRowData> Make(int32 InNumber, FString InName, class UCWeaponAsset* InAsset)
    {
    return MakeShareable(new FWeaponRowData(InNumber, InName, InAsset));
    }
    };
    typedef TSharedPtr<FWeaponRowData> FWeaponRowDataPtr;//실제 출력할 자료형
    //한줄에 여러개 SMultiColumnTableRow
    class WEAPON_API SWeaponTableRow
    : public SMultiColumnTableRow<FWeaponRowDataPtr>
    {
    public:
    SLATE_BEGIN_ARGS(SWeaponTableRow) {}
    SLATE_ARGUMENT(FWeaponRowDataPtr, RowData)
    SLATE_END_ARGS()
    public:
    void Construct(const FArguments& InArgs, const TSharedRef<STableViewBase>& InOwnerTable);
    protected:
    TSharedRef<SWidget> GenerateWidgetForColumn(const FName& InColumnName) override;//STableRow.h에서 상속받아 오버라이드.
    private:
    FWeaponRowDataPtr Data;//실제로 받아서 쓸 자료형 FWeaponRowDataPtr의 변수 Data.
    };
    class WEAPON_API SWeaponLeftArea
    : public SCompoundWidget
    {
    public:
    SLATE_BEGIN_ARGS(SWeaponLeftArea) {}
    SLATE_END_ARGS()
    public:
    void Construct(const FArguments& InArgs);
    private:
    TSharedRef<ITableRow> OnGenerateRow(FWeaponRowDataPtr InRow, const TSharedRef<STableViewBase>& InTable);
    FText OnGetAssetCount() const;
    private:
    void ReadDataAssetList();
    private:
    TArray<FWeaponRowDataPtr> RowDatas;//데이터 자료형을 받는 배열변수
    TSharedPtr<SListView<FWeaponRowDataPtr>> ListView;
    };

    SMultiColumnTableRow을 상속받아 클래스 생성

    • class WEAPON_API SWeaponTableRow
      : public SMultiColumnTableRow<FWeaponRowDataPtr>

     

    SCompoundWidget을 상속받아 클래스 생성

    • class WEAPON_API SWeaponLeftArea : public SCompoundWidget

     

     

     

    SWeaponLeftArea.cpp

    더보기
    #include "SWeaponLeftArea.h"
    #include "Weapons/CWeaponAsset.h"
    #include "EngineUtils.h"
    void SWeaponTableRow::Construct(const FArguments& InArgs, const TSharedRef<STableViewBase>& InOwnerTable)
    {
    Data = InArgs._RowData;//InArgs를 통해서 들어온다.
    SMultiColumnTableRow<FWeaponRowDataPtr>::Construct
    (
    FSuperRowType::FArguments().Style(FEditorStyle::Get(), "TableView.DarkRow"), InOwnerTable
    );
    }
    TSharedRef<SWidget> SWeaponTableRow::GenerateWidgetForColumn(const FName& InColumnName)
    { //InColumnName에 아래 Construct 내부의 SHeaderRow::Column("이름, 번호 등")이 들어간다.
    FString str;
    if (InColumnName == "Number")//InColumnName이 숫자면
    str = FString::FromInt(Data->Number);//str에 숫자를 넣어준다.
    else if (InColumnName == "Name")//InColumnName이 이름이면
    str = Data->Name;//str에 이름을 넣어준다.
    return SNew(STextBlock)
    .Text(FText::FromString(str));//출력할 문자str를 생성하여 리턴해준다.
    }
    /*모양을 디자인 해주는 역할*/
    void SWeaponLeftArea::Construct(const FArguments& InArgs)
    {
    ChildSlot
    [
    SNew(SVerticalBox)
    + SVerticalBox::Slot()
    .FillHeight(1)//1이 100%를 의미한다. 한줄을 다 채우겠다는 의미.
    [
    SAssignNew(ListView, SListView<FWeaponRowDataPtr>)//자료형은 FWeaponRowDataPtr
    .HeaderRow
    (
    SNew(SHeaderRow)
    + SHeaderRow::Column("Number")
    .DefaultLabel(FText::FromString(""))
    .ManualWidth(40)//칸의 너비
    + SHeaderRow::Column("Name")
    .DefaultLabel(FText::FromString("Name"))
    )
    .ListItemsSource(&RowDatas)
    .OnGenerateRow(this, &SWeaponLeftArea::OnGenerateRow)//한줄한줄 어떻게 표현할지 모양을 정해달라는 의미.
    ]
    +SVerticalBox::Slot()
    .AutoHeight()
    .VAlign(VAlign_Center)
    .HAlign(HAlign_Right)
    .Padding(FMargin(8, 2))
    [
    SNew(STextBlock)
    .Text(this, &SWeaponLeftArea::OnGetAssetCount)//const TAttribute<FText>. 여기의 text 내용이 바뀌면 자동으로 갱신해준다. Attribute의 특성 때문이다. Attribute를 함수로 줄 수 있다. 여기서는 &SWeaponLeftArea::OnGetAssetCount.
    ]
    ];
    //테스트용 출력
    //RowDatas.Add(FWeaponRowData::Make(1, "aaa", nullptr));
    //RowDatas.Add(FWeaponRowData::Make(2, "bbb", nullptr));
    //RowDatas.Add(FWeaponRowData::Make(3, "ccc", nullptr));
    ReadDataAssetList();
    }
    TSharedRef<ITableRow> SWeaponLeftArea::OnGenerateRow(FWeaponRowDataPtr InRow, const TSharedRef<STableViewBase>& InTable)
    {
    /*실제모양을 만들어 리턴*/
    return SNew(SWeaponTableRow, InTable)
    .RowData(InRow);
    }
    void SWeaponLeftArea::ReadDataAssetList()
    {
    RowDatas.Empty();//데이터를 불러오기 전에 비워주고 시작.
    //"/Game"은 콘텐츠 폴더. 모든 에셋들을 찾아 objects에 리턴하겠다.
    ///ATL_Class는 클래스 타입, ATL_Regular는 나머지 타입(에셋을 불러와야 하므로 이것 사용).
    TArray<UObject*> objects;
    EngineUtils::FindOrLoadAssetsByPath("/Game/Weapons/", objects, EngineUtils::ATL_Regular);
    int32 index = 0;
    for (UObject* obj : objects)
    {
    UCWeaponAsset* asset = Cast<UCWeaponAsset>(obj);
    if (asset == nullptr) continue;
    FString name = asset->GetName();
    RowDatas.Add(FWeaponRowData::Make(++index, name, asset));//데이터를 하나씩 넣어준다.
    }
    RowDatas.Sort([](const FWeaponRowDataPtr& A, const FWeaponRowDataPtr& B)
    {
    return A->Number < B->Number;//오름차순 정렬
    });
    ListView->RequestListRefresh();//다른곳에서 사용하기 때문에 재갱신를 해준다.
    }
    FText SWeaponLeftArea::OnGetAssetCount() const
    {
    FString str = FString::Printf(L"%d 에셋", RowDatas.Num());
    return FText::FromString(str);
    }

     

     

     

    ※ 참고 - STableRow.h

     

     

    SLATE_ARGUMENT | Unreal Engine Documentation

     

    SLATE_ARGUMENT

    Overload list

    docs.unrealengine.com

     

    SCustomDialog::SLATE_EVENT | Unreal Engine Documentation

     

    SCustomDialog::SLATE_EVENT

    Event triggered when the dialog is closed, either because one of the buttons is pressed, or the windows is closed.

    docs.unrealengine.com

     

     


     

    WeaponAssetEditor

     

    WeaponAssetEditor.h 

    더보기
    #pragma once
    #include "CoreMinimal.h"
    #include "Toolkits/AssetEditorToolkit.h"
    class WEAPON_API FWeaponAssetEditor
    : public FAssetEditorToolkit
    {
    public:
    static void OpenWindow(FString InAssetName = "");
    static void Shutdown();
    private:
    static TSharedPtr<FWeaponAssetEditor> Instance;
    private:
    void Open(FString InAssetName);
    public:
    void RegisterTabSpawners(const TSharedRef<FTabManager>& InTabManager) override;
    private:
    TSharedRef<SDockTab> Spawn_ListViewTab(const FSpawnTabArgs& InArgs);
    public:
    FName GetToolkitFName() const override;
    FText GetBaseToolkitName() const override;
    FString GetWorldCentricTabPrefix() const override;
    FLinearColor GetWorldCentricTabColorScale() const override;
    private:
    TSharedPtr<class SWeaponLeftArea> LeftArea;
    private:
    static const FName EditorName;
    static const FName ListViewTabId;
    static const FName DetailTabId;
    private:
    FReply OnClicked();
    };

     

     

    WeaponAssetEditor.cpp

    더보기
    #include "WeaponAssetEditor.h"
    #include "SWeaponLeftArea.h"
    #include "Weapons/CWeaponAsset.h"
    //설정한 이름들
    const FName FWeaponAssetEditor::EditorName = "WeaponAssetEditor";
    const FName FWeaponAssetEditor::ListViewTabId = "ListView";
    const FName FWeaponAssetEditor::DetailTabId = "Details";
    TSharedPtr<FWeaponAssetEditor> FWeaponAssetEditor::Instance = nullptr;//헤더에서 선언한 static 외부 초기화.
    void FWeaponAssetEditor::OpenWindow(FString InAssetName)
    {
    //아래의 코드 대신에 Shutdown()을 사용하면 안 된다! Shutdown()은 다른 곳에서도 콜이 되기 때문에 아래의 코드 대신에 사용하면 문제가 된다.
    if (Instance.IsValid()) //창이 만들어졌다면
    {
    Instance->CloseWindow();//창을 닫는다.
    Instance.Reset();
    Instance = nullptr;
    }
    Instance = MakeShareable(new FWeaponAssetEditor());
    Instance->Open(InAssetName);
    }
    void FWeaponAssetEditor::Shutdown()
    {
    if (Instance.IsValid())
    {
    Instance->CloseWindow();
    Instance.Reset();
    Instance = nullptr;
    }
    }
    void FWeaponAssetEditor::Open(FString InAssetName)
    {
    LeftArea = SNew(SWeaponLeftArea);//SWeaponLeftArea에서 받은 자료형을 생성하여 넣어준다.
    //Layout 설정
    TSharedRef<FTabManager::FLayout> layout = FTabManager::NewLayout("WeaponAssetEditor_Layout")
    ->AddArea //전체화면의 메인 영역
    (
    FTabManager::NewPrimaryArea()->SetOrientation(Orient_Vertical)
    ->Split
    (
    FTabManager::NewStack()
    ->SetSizeCoefficient(0.1f)//10%만 사용하겠다.
    ->AddTab(GetToolbarTabId(), ETabState::OpenedTab)
    )
    ->Split
    (
    FTabManager::NewSplitter()->SetOrientation(Orient_Horizontal)
    ->Split
    (
    FTabManager::NewStack()
    ->SetSizeCoefficient(0.175f)//왼쪽 17.5% 사용
    ->AddTab(ListViewTabId, ETabState::OpenedTab)//ListViewTabId
    ->SetHideTabWell(true)
    )
    ->Split
    (
    FTabManager::NewStack()
    ->SetSizeCoefficient(0.725f)//오른쪽 72.5% 사용
    ->AddTab(DetailTabId, ETabState::OpenedTab)//DetailTabId
    ->SetHideTabWell(true)
    )
    )
    );
    UCWeaponAsset* asset = NewObject<UCWeaponAsset>();//현재 없는 상태이므로 일단 빈 상태로 만들어준다.
    FAssetEditorToolkit::InitAssetEditor(EToolkitMode::Standalone, TSharedPtr<IToolkitHost>(), EditorName, layout, true, true, asset);
    }
    void FWeaponAssetEditor::RegisterTabSpawners(const TSharedRef<FTabManager>& InTabManager)
    {
    FAssetEditorToolkit::RegisterTabSpawners(InTabManager);
    FOnSpawnTab tab;
    tab.BindSP(this, &FWeaponAssetEditor::Spawn_ListViewTab);
    TabManager->RegisterTabSpawner(ListViewTabId, tab);
    }
    TSharedRef<SDockTab> FWeaponAssetEditor::Spawn_ListViewTab(const FSpawnTabArgs& InArgs)
    {
    //TSharedPtr<SDockTab> tab = SNew(SDockTab)
    //[
    // SNew(SButton)
    // .OnClicked(this, &FWeaponAssetEditor::OnClicked)//OnClicked 함수 연결
    // [
    // SNew(STextBlock)
    // .Text(FText::FromString("Test"))
    // ]
    //];
    //return tab.ToSharedRef();
    //return SNew(SDockTab);
    return SNew(SDockTab)
    [
    LeftArea.ToSharedRef()
    ];
    }
    FName FWeaponAssetEditor::GetToolkitFName() const
    {
    return EditorName;//외부에서 어떤 에디터 이름을 쓸 지 정해준다.
    }
    FText FWeaponAssetEditor::GetBaseToolkitName() const
    {
    return FText::FromName(EditorName);//외부에서 어떤 에디터 이름을 쓸 지 정해준다.
    }
    FString FWeaponAssetEditor::GetWorldCentricTabPrefix() const
    {
    return EditorName.ToString();////외부에서 어떤 식별자를 쓸 지 정해준다.
    }
    FLinearColor FWeaponAssetEditor::GetWorldCentricTabColorScale() const
    {
    return FLinearColor(0, 0, 1);//파란색으로 설정.
    }
    FReply FWeaponAssetEditor::OnClicked()
    {
    GLog->Log("Test");
    return FReply::Handled();//Handled()는 처리하고 끝낸다.
    }

     

     

     

     

    좌: 적용 전
    RowDatas.Add로 테스트

     


     

    실행화면