초보 코린이의 성장 일지

UE4 Weapon Plugin (3) 본문

언리얼

UE4 Weapon Plugin (3)

코오린이 2023. 5. 31. 17:40

 Weapon Plugin 영역안에 Row로 내가 지정한 이름과 항목들을 받아서 등록시키게 만들어 볼 것이다.

1. 클래스를 생성해준다.

더보기
#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;

//////////////////////////////////////////////////////////////////////////////////////

class WEAPON_API SWeaponTableRow
	: public SMultiColumnTableRow<FWeaponRowDataPtr> // 실제로 출력할 자료형 넣어주기, 한줄에 여러칸 표현 가능
{
public:
	// S가 붙었으므로, 사용할때 매크로 선언 안해놓으면, 링크 오류 발생
	SLATE_BEGIN_ARGS(SWeaponTableRow) {}
	// 일반 변수를 줄땐 SLATE_ARGUMENT 사용, 앞에 자료형, 뒤에 변수명
	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;

private:
	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);

private:
	TArray<FWeaponRowDataPtr> RowDatas;
	TSharedPtr<SListView<FWeaponRowDataPtr>> ListView;
};
#include "SWeaponLeftArea.h"

#include "Widgets/Notifications/SNotificationList.h"

void SWeaponTableRow::Construct(const FArguments& InArgs, const TSharedRef<STableViewBase>& InOwnerTable)
{
	// SLATE_ARGUMENT로 정의한걸 .으로 불러 올 수 있다.
	// 다른곳에서 부를때 ._으로 선언해놓으면 검색하였을때 그냥 버전과, _버전이 두개 검색된다
	// 그 중에 함수로 되어있는걸 사용하면 된다.
	Data = InArgs._RowData; 

	// 어떤 형식으로 보여줄건지 스타일을 지정해줘야한다.
	SMultiColumnTableRow<FWeaponRowDataPtr>::Construct
	(
		FSuperRowType::FArguments().Style(FEditorStyle::Get(), "TableView.DarkRow"), InOwnerTable
	);
}

TSharedRef<SWidget> SWeaponTableRow::GenerateWidgetForColumn(const FName& InColumnName)
{
	// 어느 Column이 들어왔는지 구분
	FString str;
	if (InColumnName == "Number") // 번호 출력
		str = FString::FromInt(Data->Number);
	else if (InColumnName == "Name") // 이름 출력
		str = Data->Name;

	return SNew(STextBlock) // 출력 return
		.Text(FText::FromString(str));
}

///////////////////////////////////////////////////////////////////////////////////////

void SWeaponLeftArea::Construct(const FArguments& InArgs)
{
	// 모양, 디자인을 하는 부분
	ChildSlot
	[
		SNew(SVerticalBox)
		+ SVerticalBox::Slot() // 한칸마다 Slot를 가지고있다.
		.FillHeight(1) // 전체 꽉 채우기, 1이 100% 를 의미
		[
			// 이 구역안에 ListView 할당해주기.
			SAssignNew(ListView, SListView<FWeaponRowDataPtr>)
			.HeaderRow // 머리말
			(
				SNew(SHeaderRow)
				+ SHeaderRow::Column("Number") // 검색할때 찾을 이름
				.DefaultLabel(FText::FromString("Number")) // 보여질 이름
				+ SHeaderRow::Column("Name") // 검색할때 찾을 이름
				.DefaultLabel(FText::FromString("Name")) // 보여질 이름
			)
			.ListItemsSource(&RowDatas) // 어떠한 데이터를 출력할 건지, (포인터이므로)&로 줘야한다
			.OnGenerateRow(this, &SWeaponLeftArea::OnGenerateRow) // 한줄 한줄 모양을 정해주기, Row배열이 5개라면 5번 콜이된다.
		]
	];

}

TSharedRef<ITableRow> SWeaponLeftArea::OnGenerateRow(FWeaponRowDataPtr InRow, const TSharedRef<STableViewBase>& InTable)
{
	// 위에 RowDatas가 만일 5개라면, 5번 return 하면서 콜이 5번 일어난다.
	return SNew(SWeaponTableRow, InTable)
		.RowData(InRow);
		
}

1. 레이아웃안에 있는 구역에 모양 및 디자인을 넣어준다.

2. 하나의 Row 칸에서 Mult로 여러가지를 추가할 수 있다. 그중 Number, Name를 출력하게 만들어준다.

3. 이제 어떠한 데이터를 받아오게 될텐데 이 구역안에 차례대로 아래에 추가되도록 만들어 준 상태. 


1. SWeaponLeftArea에서 가져와서 할당해서 생성해준다.

2. 디자인이 되기전에 생성해줘야 하는게 포인트.

3. retrun 할때  SharedRef로 넘겨주기.

4. 컴파일하면 링크 오류가 발생할 것이다. 


더보기
using UnrealBuildTool;

public class Weapon : ModuleRules
{
	public Weapon(ReadOnlyTargetRules Target) : base(Target)
	{
		PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;

		PrivateIncludePaths.Add(ModuleDirectory);

        PublicDependencyModuleNames.Add("Core");

        PublicDependencyModuleNames.Add("U2212_06");


        PrivateDependencyModuleNames.Add("CoreUObject");
        PrivateDependencyModuleNames.Add("Engine");
        PrivateDependencyModuleNames.Add("Slate");
        PrivateDependencyModuleNames.Add("SlateCore");

        PrivateDependencyModuleNames.Add("UnrealEd");
        PrivateDependencyModuleNames.Add("EditorStyle");
        PrivateDependencyModuleNames.Add("InputCore");



    }
}

1. 링크 오류 나므로, 모듈  "InputCore" 추가해주기. 

 

1. 구역이 나눠져 있는걸 확인할 수 있다.


1. 아직 데이터 출력이 안된 이유는 추가를 해준 상태가 아니므로, 임의로 추가해서 확인해 볼 것이다.

1. 테스트 데이터가 들어온 걸 확인할 수 있다.


더보기
#include "SWeaponLeftArea.h"
#include "Weapons/CWeaponAsset.h"
#include "EngineUtils.h"

void SWeaponLeftArea::ReadDataAssetList()
{
	// 콜 될때마다 비워주기
	RowDatas.Empty();

	TArray<UObject*> objects;
	// 데이터를 실제로 찾아올 경로
	// 이 안에서 모든 Asset를 다 찾아서 objects에 return 해줄 것이다.
	EngineUtils::FindOrLoadAssetsByPath("/Game/Weapons/", objects, EngineUtils::ATL_Regular);

	// 안에 있는 데이터를 for문으로 확인
	int32 index = 0;
	for (UObject* obj : objects)
	{
		UCWeaponAsset* asset = Cast<UCWeaponAsset>(obj);
		// nullptr이면 Pass 시킨다.
		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 다시 그려주기, 다른곳에서도 사용할 것이라서 재갱신
	ListView->RequestListRefresh();

}

1. 이제 데이터들 불러다가 추가 시켜줄 것이다.

2. 모든 Asset 검색해서 찾아보고, 있다면 넣어주고 없다면 패스

3. 사용할 기준점을 잡아주고, 람다식 사용

4. 찾는게 끝이나서 데이터가 들어왔다면, ListView 다시 그려준다.

1. 클래스를 찾거나 다룰거라면, ATL_Class로 사용

2. 지금은 Asset를 찾을 것이므로, ATL_Regular를 사용할 것이다.

 

더보기
#include "SWeaponLeftArea.h"
#include "Weapons/CWeaponAsset.h"
#include "EngineUtils.h"

void SWeaponLeftArea::Construct(const FArguments& InArgs)
{
	// 모양, 디자인을 하는 부분
	ChildSlot
	[
		SNew(SVerticalBox)
		+ SVerticalBox::Slot() // 한칸마다 Slot를 가지고있다.
		.FillHeight(1) // 전체 꽉 채우기, 1이 100% 를 의미
		[
			// 이 구역안에 ListView 할당해주기.
			SAssignNew(ListView, SListView<FWeaponRowDataPtr>)
			.HeaderRow // 머리말
			(
				SNew(SHeaderRow)
				+ SHeaderRow::Column("Number") // 검색할때 찾을 이름
				.DefaultLabel(FText::FromString("Number")) // 보여질 이름
				+ SHeaderRow::Column("Name") // 검색할때 찾을 이름
				.DefaultLabel(FText::FromString("Name")) // 보여질 이름
			)
			.ListItemsSource(&RowDatas) // 어떠한 데이터를 출력할 건지, (포인터이므로)&로 줘야한다
			.OnGenerateRow(this, &SWeaponLeftArea::OnGenerateRow) // 한줄 한줄 모양을 정해주기, Row배열이 5개라면 5번 콜이된다.
		]
	];

	/*RowDatas.Add(FWeaponRowData::Make(1, "kkk", nullptr));
	RowDatas.Add(FWeaponRowData::Make(2, "aaa", nullptr));
	RowDatas.Add(FWeaponRowData::Make(3, "qqq", nullptr));*/

	ReadDataAssetList();

}

1. 테스트했던 데이터값을 지워주고, 데이터를 넣어주기 위해 함수 콜 해준다.

1. 지금까지 만들어왔던 DataAsset 3가지 무기가 들어와 있는걸 확인할 수 있다.

 

#include "SWeaponLeftArea.h"
#include "Weapons/CWeaponAsset.h"
#include "EngineUtils.h"

void SWeaponLeftArea::Construct(const FArguments& InArgs)
{
	// 모양, 디자인을 하는 부분
	ChildSlot
	[
		SNew(SVerticalBox)
		+ SVerticalBox::Slot() // 한칸마다 Slot를 가지고있다.
		.FillHeight(1) // 전체 꽉 채우기, 1이 100% 를 의미
		[
			// 이 구역안에 ListView 할당해주기.
			SAssignNew(ListView, SListView<FWeaponRowDataPtr>)
			.HeaderRow // 머리말
			(
				SNew(SHeaderRow)
				+ SHeaderRow::Column("Number") // 검색할때 찾을 이름
				.DefaultLabel(FText::FromString("")) // 보여질 이름
				.ManualWidth(40) // 칸에 넓이 40 사용
				+ SHeaderRow::Column("Name") // 검색할때 찾을 이름
				.DefaultLabel(FText::FromString("Name")) // 보여질 이름
			)
			.ListItemsSource(&RowDatas) // 어떠한 데이터를 출력할 건지, (포인터이므로)&로 줘야한다
			.OnGenerateRow(this, &SWeaponLeftArea::OnGenerateRow) // 한줄 한줄 모양을 정해주기, Row배열이 5개라면 5번 콜이된다.
		]
	];

	/*RowDatas.Add(FWeaponRowData::Make(1, "kkk", nullptr));
	RowDatas.Add(FWeaponRowData::Make(2, "aaa", nullptr));
	RowDatas.Add(FWeaponRowData::Make(3, "qqq", nullptr));*/

	ReadDataAssetList();

}

1. 보여질 이름에 Number은 지워주고, ManumlWidth로 사용 넓이를 정해줄 것이다.

1. Number는 이제 출력되지 않으며, DataAsset를 새로 추가해서 확인해보면, Data안에 들어오는걸 확인할 수 있다.

더보기
#include "SWeaponLeftArea.h"
#include "Weapons/CWeaponAsset.h"
#include "EngineUtils.h"

void SWeaponLeftArea::Construct(const FArguments& InArgs)
{
	// 모양, 디자인을 하는 부분
	ChildSlot
	[
		SNew(SVerticalBox)
		+ SVerticalBox::Slot() // 한칸마다 Slot를 가지고있다.
		.FillHeight(1) // 전체 꽉 채우기, 1이 100% 를 의미
		[
			// 이 구역안에 ListView 할당해주기.
			SAssignNew(ListView, SListView<FWeaponRowDataPtr>)
			.HeaderRow // 머리말
			(
				SNew(SHeaderRow)
				+ SHeaderRow::Column("Number") // 검색할때 찾을 이름
				.DefaultLabel(FText::FromString("")) // 보여질 이름
				.ManualWidth(40) // 칸에 넓이 40 사용
				+ SHeaderRow::Column("Name") // 검색할때 찾을 이름
				.DefaultLabel(FText::FromString("Name")) // 보여질 이름
			)
			.ListItemsSource(&RowDatas) // 어떠한 데이터를 출력할 건지, (포인터이므로)&로 줘야한다
			.OnGenerateRow(this, &SWeaponLeftArea::OnGenerateRow) // 한줄 한줄 모양을 정해주기, Row배열이 5개라면 5번 콜이된다.
		]
		+SVerticalBox::Slot()
			// 크기만큼 높이가 만들어진다. 나머지 영역은 FillHeight가 잡아준다.
			// 사용할때 Auto로 특정 영역을 잡아주는 식으로 사용한다.
		.AutoHeight()
		.VAlign(VAlign_Center) // 수직 정렬
		.HAlign(HAlign_Right) // 수평 정렬
		.Padding(FMargin(8, 2)) // 마진 영역도 준다, 위아래가 8, 좌우 여백이 2가된다.
		[
			SNew(STextBlock)
			// Text안에 내용이 바뀌면 자동으로 갱신해주는 신기한 일이 일어난다.
			// 아래 OnGetAssetCount 함수를 콜한게 아닌, 이벤트로 연결했을 뿐인데
			// 원래 가지고 있던 값에서 호출한 내용이 다르다면 자동으로 바꿔서 출력해주는 특징을 가지고있다.
			.Text(this, &SWeaponLeftArea::OnGetAssetCount)
		]
	];

	/*RowDatas.Add(FWeaponRowData::Make(1, "kkk", nullptr));
	RowDatas.Add(FWeaponRowData::Make(2, "aaa", nullptr));
	RowDatas.Add(FWeaponRowData::Make(3, "qqq", nullptr));*/

	ReadDataAssetList();

}

FText SWeaponLeftArea::OnGetAssetCount() const
{
	// 실제 Data 갯수 출력
	FString str = FString::Printf(L"%d 에셋", RowDatas.Num());

	return FText::FromString(str);
}

1. Number를 제거하고서, 수직, 수평 정렬을 해주고, Padding 영역도 설정해준다.

2. 중요한 OnGetAssetCount 함수를 콜해준게 아닌 다른 함수안에서 이벤트로 연결해 준다.

3. 핵심은 원래있던 내용에서 달라지면 자동으로 바꿔서 출력해주는 특징을 가지고 있다.

1. 크게 달라지는건 없지만 이제 위에 검색창도 만들어 볼 것이다.


https://www.youtube.com/watch?v=M6nxEJ5SOak 

 

'언리얼' 카테고리의 다른 글

UE4 Weapon Plugin (5)  (2) 2023.06.07
UE4 Weapon Plugin (4)  (0) 2023.06.01
UE4 Weapon Plugin (2)  (0) 2023.05.30
UE4 Weapon Plugin  (0) 2023.05.26
UE4 Fist, Camera Shake  (0) 2023.05.11
Comments