초보 코린이의 성장 일지

UE4 Weapon Plugin (9) 본문

언리얼

UE4 Weapon Plugin (9)

코오린이 2023. 6. 14. 12:47

DoActionData에 배열로 정보를 보여주기 위해 작업을 시작해 보겠다.

우선 DoActionData 카테고리 안에 DoActionDatas 배열 구역을 나타내는 또 다른 Header를 만들어 줘야한다.


EquipmentData.cpp

더보기
#pragma once

#include "CoreMinimal.h"
#include "IPropertyTypeCustomization.h"

class WEAPON_API SWeaponDoActionData
	: public IPropertyTypeCustomization
{
public:
	void CustomizeHeader(TSharedRef<IPropertyHandle> InPropertyHandle, FDetailWidgetRow& InHeaderRow, IPropertyTypeCustomizationUtils& InCustomizationUtils) override;
	void CustomizeChildren(TSharedRef<IPropertyHandle> InPropertyHandle, IDetailChildrenBuilder& InChildBuilder, IPropertyTypeCustomizationUtils& InCustomizationUtils) override;


private:
	static TArray<TSharedPtr<class SWeaponCheckBoxes>> CheckBoxes; // 여러개 담을 배열로 선언
	
};
#include "SWeaponDoActionData.h"
#include "WeaponStyle.h"
#include "IPropertyUtilities.h"
#include "IDetailPropertyRow.h"
#include "IDetailChildrenBuilder.h"
#include "DetailWidgetRow.h"
#include "SWeaponCheckBoxes.h"
#include "Widgets/Notifications/SNotificationList.h"

void SWeaponDoActionData::CustomizeHeader(TSharedRef<IPropertyHandle> InPropertyHandle, FDetailWidgetRow& InHeaderRow, IPropertyTypeCustomizationUtils& InCustomizationUtils)
{
    // GLog->Log("CustomizeHeader");
    int32 index = InPropertyHandle->GetIndexInArray(); // 배열안에 번호를 return 해달라,
    // 자동으로 Header, Children이 콜이되므로 for문으로 돌리지 않아도된다.
    CheckBoxes[index]->SetUtilities(InCustomizationUtils.GetPropertyUtilities()); 

     // 접히는 라인
    InHeaderRow
        .NameContent()
        [
                // Handle에는 기본 모양이 존재하지만, 커스텀 한 모양대로 만들어주기
                InPropertyHandle->CreatePropertyNameWidget(FText::FromString(name))
        ]
		.ValueContent() // 슬레이트 UI 다루는 문법과 동일
		 // 사이즈 조절, 마우스로 끌어서 크기를 변경할때 적정선을 못넘어 가도록 고정 (최대, 최소 사이즈)
        .MinDesiredWidth(FWeaponStyle::Get()->DesiredWidth.X)
        .MaxDesiredWidth(FWeaponStyle::Get()->DesiredWidth.Y)
        [
            // InPropertyHandle->CreatePropertyValueWidget() // 접히는 부분
            CheckBoxes[index]->Draw() // 바로 그려준다.
        ];
}

1. 자신의 배열이 몇번 배열인지를 알아야 체크박스 배열과 싱크를 맞춰줄 수 있다.

2. EquipmentData에 카테고리를 만들어 주는 Header부분을 그대로 가져와서 Index로 변경해주면 된다.

1. 각 배열에 알맞은 정보 체크박스가 나오는걸 확인할 수 있다.

더보기
#include "SWeaponDoActionData.h"
#include "WeaponStyle.h"
#include "IPropertyUtilities.h"
#include "IDetailPropertyRow.h"
#include "IDetailChildrenBuilder.h"
#include "DetailWidgetRow.h"
#include "SWeaponCheckBoxes.h"
#include "Widgets/Notifications/SNotificationList.h"

void SWeaponDoActionData::CustomizeHeader(TSharedRef<IPropertyHandle> InPropertyHandle, FDetailWidgetRow& InHeaderRow, IPropertyTypeCustomizationUtils& InCustomizationUtils)
{
    // GLog->Log("CustomizeHeader");
    int32 index = InPropertyHandle->GetIndexInArray(); // 배열안에 번호를 return 해달라,
    // 자동으로 Header, Children이 콜이되므로 for문으로 돌리지 않아도된다.
    CheckBoxes[index]->SetUtilities(InCustomizationUtils.GetPropertyUtilities()); 

    FString name = InPropertyHandle->GetPropertyDisplayName().ToString(); // 이름으로 출력
    name = "DoAction Data - " + name;

     // 접히는 라인
    InHeaderRow
        .NameContent()
        [
            SNew(SBorder)
            .BorderImage(FWeaponStyle::Get()->Array_Image.Get())
	        [
                // Handle에는 기본 모양이 존재하지만, 커스텀 한 모양대로 만들어주기
                InPropertyHandle->CreatePropertyNameWidget(FText::FromString(name)) // 문자열로 변경
		    ]
        ]
		.ValueContent() // 슬레이트 UI 다루는 문법과 동일
		 // 사이즈 조절, 마우스로 끌어서 크기를 변경할때 적정선을 못넘어 가도록 고정 (최대, 최소 사이즈)
        .MinDesiredWidth(FWeaponStyle::Get()->DesiredWidth.X)
        .MaxDesiredWidth(FWeaponStyle::Get()->DesiredWidth.Y)
        [
            // InPropertyHandle->CreatePropertyValueWidget() // 접히는 부분
            CheckBoxes[index]->Draw() // 바로 그려준다.
        ];
}

1. 번호로만 나오게되면 구분하기 어려움으로, 이름으로 구별할 수 있게끔 문자열로 변경해준다.

1. 조금 더 보기 편하게 변경되었다.

2. 이제 각 배열에 Data안에 체크박스들 내용들이 아래에 나오도록 그려줄 것이다.

 

더보기
#include "SWeaponDoActionData.h"
#include "WeaponStyle.h"
#include "IPropertyUtilities.h"
#include "IDetailPropertyRow.h"
#include "IDetailChildrenBuilder.h"
#include "DetailWidgetRow.h"
#include "SWeaponCheckBoxes.h"
#include "Widgets/Notifications/SNotificationList.h"

void SWeaponDoActionData::CustomizeChildren(TSharedRef<IPropertyHandle> InPropertyHandle, IDetailChildrenBuilder& InChildBuilder, IPropertyTypeCustomizationUtils& InCustomizationUtils)
{
   // GLog->Log("CustomizeChildren");
   
    int32 index = InPropertyHandle->GetIndexInArray(); // 배열안에 번호를 return 해달라,
    CheckBoxes[index]->DrawProperties(InPropertyHandle, &InChildBuilder); // index 배열안에 그려주기
}

1. Children을 그려줌으로써 배열안에 체크박스 데이터가 나올 수 있도록 만들어 준다.

1. 체크해보면 잘 나오게된다.


더보기
#include "SWeaponDetailsView.h"
#include "SWeaponCheckBoxes.h"
#include "SWeaponEquipmentData.h"
#include "SWeaponDoActionData.h"
#include "DetailLayoutBuilder.h"
#include "DetailCategoryBuilder.h"
#include "IDetailPropertyRow.h"
#include "Weapons/CWeaponAsset.h"

#include "Particles/ParticleSystem.h"
#include "NiagaraSystem.h"
#include "Animation/AnimMontage.h"

void SWeaponDetailsView::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)
{
    // DoActionData
    {
        IDetailCategoryBuilder& category = DetailBuilder.EditCategory("DoActionData", FText::FromString("DoAction Data"));
        IDetailPropertyRow& row = category.AddProperty("DoActionDatas", type); // 변수명, WeaponAsset 있는 변수명과 일치 시켜주면 된다.

        if (bRefreshByCheckBoxes == false) // 새로고침 됐는지 체크
        {
            uint32 count = 0;
            row.GetPropertyHandle()->GetNumChildren(count); // 배열의 개수 return,  ChildrenHandle는 배열에 담아져있다.

            SWeaponDoActionData::EmptyCheckBoxes(); // 처음추가 하는 것이므로 새롭게 열릴때 이전에 있는거 다 비워주고 추가.

            // 몇개인지 확인,
            FDoActionData data;
            for (uint32 i = 0; i < count; i++)
            {
                // 현재 HeaderHandle에서 자식꺼 Handle 가져오기
                TSharedPtr<IPropertyHandle> handle = row.GetPropertyHandle()->GetChildHandle(i);

                TSharedPtr<SWeaponCheckBoxes> checkBoxes = SWeaponDoActionData::AddCheckBoxes(); // checkBoxes 추가
                checkBoxes->AddProperties(handle); // 몇개의 정보가 남을지,

                int32 index = 0;
                checkBoxes->CheckDefaultObject(index++, data.Montage);
                checkBoxes->CheckDefaultValue(index++, data.PlayRate);
                checkBoxes->CheckDefaultValue(index++, data.bCanMove);
                checkBoxes->CheckDefaultValue(index++, data.bFixedCamera);
                checkBoxes->CheckDefaultObject(index++, data.Effect);
                checkBoxes->CheckDefaultValue(index++, data.EffectLocation);
                checkBoxes->CheckDefaultValue(index++, data.EffectScale);

            }

        } // if (bRefreshByCheckBoxes)
    }
}

1. for문마다 체크박스를 가지므로 출력될 정보들을 for문에서 돌려줘야한다.


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

        PublicDependencyModuleNames.Add("Niagara");


    }
}

1. 컴파일을 해주면 오류가 하나 발생하는데 그 오류는 Niagara에서 일어난다.

2. 모듈에서 Private를 Public로 바꿔줘야한다. 

1. 오류를 제거 후 실행해보면 각 정보들이 나오는걸 확인할 수 있다.

2. 하지만 여전히 데이터들이 많이 추가되면 보기 어려우므로 수정을 더 해줄 것이다.


1. Brush는 Style 영역과 다르게 EnginContentDir 안에 내용물을 사용해야한다.

2. 사용할 아이콘이 엄청 작게 표현되어 있다. 이 크기를 늘려서 사용할 것이다.

 

더보기
#pragma once

#include "CoreMinimal.h"

class WEAPON_API FWeaponStyle
{
public:
	FWeaponStyle();
	~FWeaponStyle();

public:
	FSlateIcon ToolBar_Icon; // 크기만 조정

	TSharedPtr<struct FSlateImageBrush> Array_Image; // 이미지 사이즈를 조정할 수 있다, Brush는 ICon과는 다르다 
};
#include "WeaponStyle.h"
#include "Styling/SlateStyle.h"
#include "Styling/SlateStyleRegistry.h"

FWeaponStyle::FWeaponStyle()
{
    StyleSet = MakeShareable(new FSlateStyleSet(StyleSetName));


    FString path = "";

    path = FPaths::ProjectPluginsDir() / "Weapon" / "Resources";
    RegisterIcon("ToolBar_Icon", path / "weapon_thumnail_icon.png", FVector2D(40, 40), ToolBar_Icon);

    FSlateStyleRegistry::RegisterSlateStyle(*StyleSet.Get());

    // Engin ICon 사용
    path = FPaths::EngineContentDir() / "Editor" / "Slate" / "Common/Selection.png";
    Array_Image = MakeShareable(new FSlateImageBrush(path, FVector2D(8, 8), FLinearColor(1, 1, 1, 0.1f)));

}

FWeaponStyle::~FWeaponStyle()
{
	// 제거
    if (Array_Image.IsValid())
        Array_Image.Reset();

    if (StyleSet.IsValid() == false) return;

    FSlateStyleRegistry::UnRegisterSlateStyle(StyleSetName);
    StyleSet.Reset();
}

1. Engin 안에 ICon을 사용해주고, 글자를 가리지 않도록 색을 설정.

2. 다 사용했으면 제거해준다.


더보기
#include "SWeaponDoActionData.h"
#include "WeaponStyle.h"
#include "IPropertyUtilities.h"
#include "IDetailPropertyRow.h"
#include "IDetailChildrenBuilder.h"
#include "DetailWidgetRow.h"
#include "SWeaponCheckBoxes.h"
#include "Widgets/Notifications/SNotificationList.h"

void SWeaponDoActionData::CustomizeHeader(TSharedRef<IPropertyHandle> InPropertyHandle, FDetailWidgetRow& InHeaderRow, IPropertyTypeCustomizationUtils& InCustomizationUtils)
{
    if (SWeaponCheckBoxes::CanDraw(InPropertyHandle, CheckBoxes.Num()) == false)
        return;

    // GLog->Log("CustomizeHeader");
    int32 index = InPropertyHandle->GetIndexInArray(); // 배열안에 번호를 return 해달라,
    // 자동으로 Header, Children이 콜이되므로 for문으로 돌리지 않아도된다.
    CheckBoxes[index]->SetUtilities(InCustomizationUtils.GetPropertyUtilities()); 

    FString name = InPropertyHandle->GetPropertyDisplayName().ToString(); // 이름으로 출력
    name = "DoAction Data - " + name;

     // 접히는 라인
    InHeaderRow
        .NameContent()
        [
            SNew(SBorder) // SBorder 한 줄을 출력해줄때 사용
            .BorderImage(FWeaponStyle::Get()->Array_Image.Get()) // 만들어 놓은 Image 넣어주기, 스마터포인터므로 Get로 return
	        [
                // Handle에는 기본 모양이 존재하지만, 커스텀 한 모양대로 만들어주기
                InPropertyHandle->CreatePropertyNameWidget(FText::FromString(name)) // 문자열로 변경
		    ]
        ]
		.ValueContent() // 슬레이트 UI 다루는 문법과 동일
		 // 사이즈 조절, 마우스로 끌어서 크기를 변경할때 적정선을 못넘어 가도록 고정 (최대, 최소 사이즈)
        .MinDesiredWidth(FWeaponStyle::Get()->DesiredWidth.X)
        .MaxDesiredWidth(FWeaponStyle::Get()->DesiredWidth.Y)
        [
            // InPropertyHandle->CreatePropertyValueWidget() // 접히는 부분
            CheckBoxes[index]->Draw(true) // 바로 그려준다.
        ];
}

1. SBorder를 사용해주고, 만들어 놓은 Image를 넣어준다.

2. Draw(true) 일떄는 배경색이 칠해지도록 설정.

1. 각 Data 영역에 색이 칠해져 표시되므로, 더 보기 편하게 변경되었다.


더보기
#include "SWeaponCheckBoxes.h"
#include "WeaponStyle.h"
#include "SWeaponDetailsView.h"
#include "Widgets/Layout/SUniformGridPanel.h"
#include "IPropertyUtilities.h"
#include "IDetailPropertyRow.h"
#include "IDetailChildrenBuilder.h"
#include "DetailWidgetRow.h"

TSharedRef<SWidget> SWeaponCheckBoxes::Draw(bool bBackground)
{
    TSharedPtr<SUniformGridPanel> panel;
    SAssignNew(panel, SUniformGridPanel); // SNew와 다르게 변수를 선언해 놓는 SAssignNew
    panel->SetMinDesiredSlotWidth(150); // 간격 크기 고정

    for (int32 i = 0; i < InternalDatas.Num(); i++)
    {
        panel->AddSlot(i, 0) // 한줄만 사용, 여러줄사용하려면 ex) i % 5
            [
                SNew(SCheckBox) // 체크박스는 컨텐츠 영역을 가지고 있다.
                .IsChecked(InternalDatas[i].bChecked) // 체크 여부 출력, 0과 1로만 판단
            .OnCheckStateChanged(this, &SWeaponCheckBoxes::OnCheckStateChanged, i) // 체크 했는지 판단
            [
                SNew(STextBlock)
                .Text(FText::FromString(InternalDatas[i].Name)) // 이름 출력
            ]
            ];
    }

    if (bBackground == false)
		 return panel.ToSharedRef(); // 현재의 Panel를 return

    TSharedPtr<SBorder> border = SNew(SBorder)
	.BorderImage(FWeaponStyle::Get()->Array_Image.Get())
	[
        // 실제로 그려지게 될 Image
		panel.ToSharedRef()
    ];
    return border.ToSharedRef(); // 추가를 하게되면, 추가된 그자체를 return
}

1. 체크박스가 체크되었다면 색이 칠해지도록, 더 보기 편하게 만들기 위해 선행작업을 시작할 것이다.

2. 현재의 패널을 조건 체크해주고, 그게 아니라면 실제로 그려지게될 Panel이 보이도록 만들어 준다.

1. 더 보기 편하도록 변경되었다.

2. 여기서 문제가 하나 발생한다 Data 배열을 추가해주면 터지게된다. 그 이유는 당연할 수 있겠지만 배열 범위가 벗어났으므로, 추가한 데이터가 들어가게끔 만들어 주려고한다.

3. + 를 누르는 순간 Header과 Children이 콜이 되면서 생성이 되야 하는데 이를 늘려주는 코드를 작성을 하지 않았기 때문에 발생하는 것이다.


더보기
#pragma once

#include "CoreMinimal.h"

class WEAPON_API SWeaponCheckBoxes
    : public TSharedFromThis<SWeaponCheckBoxes> // 직접적으로 상속받으면 주소가 일치하게된다.
{

public:
    // 배열 개수하고 맞는지, 맞다면 정상적으로 그릴수 있는지 확인
    // 해당 데이터가 배열에 없거나 범위를 벗어났다면 그리지 않고 버릴 것이다.
    static bool CanDraw(TSharedPtr<IPropertyHandle> InHandle, int InCount);
    
};
#include "SWeaponCheckBoxes.h"
#include "WeaponStyle.h"
#include "SWeaponDetailsView.h"
#include "Widgets/Layout/SUniformGridPanel.h"
#include "IPropertyUtilities.h"
#include "IDetailPropertyRow.h"
#include "IDetailChildrenBuilder.h"
#include "DetailWidgetRow.h"

bool SWeaponCheckBoxes::CanDraw(TSharedPtr<IPropertyHandle> InHandle, int InCount)
{
    bool bCheck = true;
    bCheck &= InCount > 0; // 배열 개수는 0보다 크게

    int32 index = InHandle->GetIndexInArray(); // Handle에 배열번호 가져오기.
    bCheck &= index >= 0; // Handle 번호가 0번 보다는 크거나 같아야 한다.
    bCheck &= index < InCount; // index가 Count 보다 작아야 범위안에 있다는 의미이므로 그래야 그릴수 있다.

    return bCheck;
}

1. 배열이 늘어나는걸 판단해주고, 그릴지 안그릴지 판단해준다.


더보기
#include "SWeaponDoActionData.h"
#include "WeaponStyle.h"
#include "IPropertyUtilities.h"
#include "IDetailPropertyRow.h"
#include "IDetailChildrenBuilder.h"
#include "DetailWidgetRow.h"
#include "SWeaponCheckBoxes.h"
#include "Widgets/Notifications/SNotificationList.h"

void SWeaponDoActionData::CustomizeHeader(TSharedRef<IPropertyHandle> InPropertyHandle, FDetailWidgetRow& InHeaderRow, IPropertyTypeCustomizationUtils& InCustomizationUtils)
{
    // false면 안그리고 끝내버린다, true면 그려준다.
    if (SWeaponCheckBoxes::CanDraw(InPropertyHandle, CheckBoxes.Num()) == false)
        return;

    // GLog->Log("CustomizeHeader");
    int32 index = InPropertyHandle->GetIndexInArray(); // 배열안에 번호를 return 해달라,
    // 자동으로 Header, Children이 콜이되므로 for문으로 돌리지 않아도된다.
    CheckBoxes[index]->SetUtilities(InCustomizationUtils.GetPropertyUtilities()); 

    FString name = InPropertyHandle->GetPropertyDisplayName().ToString(); // 이름으로 출력
    name = "DoAction Data - " + name;

     // 접히는 라인
    InHeaderRow
        .NameContent()
        [
            SNew(SBorder) // SBorder 한 줄을 출력해줄때 사용
            .BorderImage(FWeaponStyle::Get()->Array_Image.Get()) // 만들어 놓은 Image 넣어주기, 스마터포인터므로 Get로 return
	        [
                // Handle에는 기본 모양이 존재하지만, 커스텀 한 모양대로 만들어주기
                InPropertyHandle->CreatePropertyNameWidget(FText::FromString(name)) // 문자열로 변경
		    ]
        ]
		.ValueContent() // 슬레이트 UI 다루는 문법과 동일
		 // 사이즈 조절, 마우스로 끌어서 크기를 변경할때 적정선을 못넘어 가도록 고정 (최대, 최소 사이즈)
        .MinDesiredWidth(FWeaponStyle::Get()->DesiredWidth.X)
        .MaxDesiredWidth(FWeaponStyle::Get()->DesiredWidth.Y)
        [
            // InPropertyHandle->CreatePropertyValueWidget() // 접히는 부분
            CheckBoxes[index]->Draw(true) // 바로 그려준다. true일때는 배경색이 칠해지도록 설정.
        ];
}

void SWeaponDoActionData::CustomizeChildren(TSharedRef<IPropertyHandle> InPropertyHandle, IDetailChildrenBuilder& InChildBuilder, IPropertyTypeCustomizationUtils& InCustomizationUtils)
{
   // GLog->Log("CustomizeChildren");

    if (SWeaponCheckBoxes::CanDraw(InPropertyHandle, CheckBoxes.Num()) == false)
        return;

    int32 index = InPropertyHandle->GetIndexInArray(); // 배열안에 번호를 return 해달라,
    CheckBoxes[index]->DrawProperties(InPropertyHandle, &InChildBuilder); // index 배열안에 그려주기
}

1. if (SWeaponCheckBoxes::CanDraw(InPropertyHandle, CheckBoxes.Num()) == false)로 그릴지 안그릴지 조건 체크.

2. 정상적으로 코드는 실행이 되지만 보이지는 않는다.

1. +를 눌러서 4로 배열이 증가했지만 아래에는 보이지 않는다.

2. 해당 줄이 범위를 벗어나면 보이지 않도록 설정했기 때문이다 나머지 처리들도 해줘야한다.


1. 지금 커스텀 해주고 있는 자료형은 WeaponAsset이다.

2. 데이터에 부모인 DataAsset로 들어간다.

1. DataAsset 또 한 최상위 Object를 부모로 두고 있다.

1. 가상함수인 ChangeChainProperty를 가져와서 사용할 것이다.

더보기
#pragma once

#include "CoreMinimal.h"
#include "Engine/DataAsset.h"
#include "Weapons/CWeaponStructures.h"
#include "CWeaponAsset.generated.h"

UCLASS()
class U2212_06_API UCWeaponAsset : public UDataAsset
{
	GENERATED_BODY()
    
#if WITH_EDITOR
	virtual void PostEditChangeChainProperty(struct FPropertyChangedChainEvent& PropertyChangedEvent) override;
#endif // WITH_EDITOR

};
#include "Weapons/CWeaponAsset.h"
#include "Global.h"
#include "CAttachment.h"
#include "CEquipment.h"
#include "CDoAction.h"
#include "GameFramework/Character.h"

#if WITH_EDITOR
void UCWeaponAsset::PostEditChangeChainProperty(FPropertyChangedChainEvent& PropertyChangedEvent)
{
	// 게임모듈 쪽이므로 Super 사용 가능. PropertyChangedEvent가 중요하다
	Super::PostEditChangeChainProperty(PropertyChangedEvent);

	// GetPropertyName 어떤 변수가 바뀌었는지 확인할 수 있다.
	CLog::Log(PropertyChangedEvent.GetPropertyName().ToString()); 
	// 어떤 명령에 의해서 바뀌었는지 확인, ChangeType는 직렬화가 안되어있으므로 int로 출력해준다.
	CLog::Log((int32)PropertyChangedEvent.ChangeType); 

}
#endif // WITH_EDITOR

1. PropertyChangedEvent이 중요하다.

2. 안에 함수들에 좋은 기능들이 있으며, 변경한 사항들을 알 수 있다.

 

1. ChangeType 안에 내용을 Log로 확인해 본다면, 그 기능에 해당하는 출력될 숫자들을 알려준다.

1. Test를 위해 DataAsset를 하나 생성해주고, Class를 눌러 추가하거나, Data 배열을 추가해주면 왼쪽 출력 로그에 알맞게 나오는걸 확인할 수 있다.


https://www.youtube.com/watch?v=6vTIqhWUCmg 

 

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

UE4 SubAction Fist  (0) 2023.06.20
UE4 Weapon Plugin (10)  (0) 2023.06.15
UE4 Weapon Plugin (8)  (2) 2023.06.13
UE4 Weapon Plugin (7)  (0) 2023.06.12
UE4 Weapon Plugin (6)  (0) 2023.06.09
Comments