
Delegate는 하나 이상의 함수를 호출할 수 있는 일종의 함수 포인터이며,
다른 오브젝트가 이벤트에 반응할 수 있도록 설계된 메커니즘
Single_Cast Delegate
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MyActor.generated.h"
// Single-cast Delegate 선언
DECLARE_DELEGATE(FSimpleDelegate);
UCLASS()
class MYPROJECT_API AMyActor : public AActor
{
GENERATED_BODY()
public:
AMyActor();
// Single-cast 델리게이트 인스턴스
FSimpleDelegate MyDelegate;
void TriggerAction();
void HandleAction();
};
#include "MyActor.h"
AMyActor::AMyActor()
{
// 생성자에서 함수 바인딩
MyDelegate.BindUObject(this, &AMyActor::HandleAction);
}
void AMyActor::TriggerAction()
{
// 델리게이트가 유효하면 호출
if (MyDelegate.IsBound())
{
MyDelegate.Execute();
}
}
void AMyActor::HandleAction()
{
UE_LOG(LogTemp, Warning, TEXT("Action has been handled!"));
}
DECLARE_DELEGATE : Delegate를 선언하는 매크로입니다. 파라미터가 없는 Simple Delegate를 선언했습니다.BindUObject : 클래스를 바인딩하는 메서드로, 여기서는 this 객체와 HandleAction함수를 바인딩했습니다.Execute : Delegate에 바인딩된 함수를 호출합니다.Multi_cast Delegate
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MyActor.generated.h"
// Multicast Delegate 선언
DECLARE_MULTICAST_DELEGATE(FMulticastDelegate);
UCLASS()
class MYPROJECT_API AMyActor : public AActor
{
GENERATED_BODY()
public:
AMyActor();
// Multicast 델리게이트 인스턴스
FMulticastDelegate MyDelegate;
void TriggerEvent();
void FirstHandler();
void SecondHandler();
};
#include "MyActor.h"
AMyActor::AMyActor()
{
// 생성자에서 여러 함수 바인딩
MyDelegate.AddUObject(this, &AMyActor::FirstHandler);
MyDelegate.AddUObject(this, &AMyActor::SecondHandler);
}
void AMyActor::TriggerEvent()
{
// 모든 바인딩된 함수들을 호출
MyDelegate.Broadcast();
}
void AMyActor::FirstHandler()
{
UE_LOG(LogTemp, Warning, TEXT("First handler called!"));
}
void AMyActor::SecondHandler()
{
UE_LOG(LogTemp, Warning, TEXT("Second handler called!"));
}
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam 예시
InventoryComponent.h
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "InventoryComponent.generated.h"
// Dynamic Multicast Delegate 선언 (파라미터로 bool 전달)
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnInventoryToggled, bool, bIsOpen);
UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent))
class MYPROJECT_API UInventoryComponent : public UActorComponent
{
GENERATED_BODY()
public:
UInventoryComponent();
// Dynamic Multicast Delegate 인스턴스
UPROPERTY(BlueprintAssignable, Category = "Inventory")
FOnInventoryToggled OnInventoryToggled;
void ToggleInventory();
private:
bool bIsInventoryOpen;
void OpenInventory();
void CloseInventory();
};
InventoryComponent.cpp
#include "InventoryComponent.h"
UInventoryComponent::UInventoryComponent()
{
bIsInventoryOpen = false;
}
void UInventoryComponent::ToggleInventory()
{
if (bIsInventoryOpen)
{
CloseInventory();
}
else
{
OpenInventory();
}
// 델리게이트 호출
OnInventoryToggled.Broadcast(bIsInventoryOpen);
}
void UInventoryComponent::OpenInventory()
{
bIsInventoryOpen = true;
UE_LOG(LogTemp, Warning, TEXT("Inventory opened."));
}
void UInventoryComponent::CloseInventory()
{
bIsInventoryOpen = false;
UE_LOG(LogTemp, Warning, TEXT("Inventory closed."));
}
MyActor.h#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "InventoryComponent.h"
#include "MyActor.generated.h"
UCLASS()
class MYPROJECT_API AMyActor : public AActor
{
GENERATED_BODY()
public:
AMyActor();
protected:
virtual void BeginPlay() override;
UFUNCTION()
void HandleInventoryToggle(bool bIsOpen);
private:
UPROPERTY(VisibleAnywhere)
UInventoryComponent* InventoryComponent;
};
MyActor.cpp
#include "MyActor.h"
AMyActor::AMyActor()
{
// InventoryComponent 초기화
InventoryComponent = CreateDefaultSubobject<UInventoryComponent>(TEXT("InventoryComponent"));
}
void AMyActor::BeginPlay()
{
Super::BeginPlay();
// InventoryComponent의 델리게이트에 함수 바인딩
if (InventoryComponent)
{
InventoryComponent->OnInventoryToggled.AddDynamic(this, &AMyActor::HandleInventoryToggle);
}
}
void AMyActor::HandleInventoryToggle(bool bIsOpen)
{
if (bIsOpen)
{
UE_LOG(LogTemp, Warning, TEXT("The inventory is now open."));
}
else
{
UE_LOG(LogTemp, Warning, TEXT("The inventory is now closed."));
}
}
void AMyActor::BeginPlay()
{
Super::BeginPlay();
// InventoryComponent의 델리게이트에 함수 바인딩
if (InventoryComponent)
{
InventoryComponent->OnInventoryToggled.AddDynamic(this, &AMyActor::HandleInventoryToggle);
}
}
void UInventoryComponent::ToggleInventory()
{
// 인벤토리 열기 또는 닫기
if (bIsInventoryOpen)
{
CloseInventory();
}
else
{
OpenInventory();
}
// 델리게이트 호출 (바인딩된 모든 함수 호출됨)
OnInventoryToggled.Broadcast(bIsInventoryOpen);
}
























UImanager.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "UIManager.generated.h"
class UInventoryWidget;
UCLASS()
class MAGICIAN_PROJECT_API AUIManager : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AUIManager();
// 인벤토리 위젯을 생성하는 함수
void CreatInventoryWidget();
// 인벤토리 위젯의 Delegate를 구동하는 함수
void BindInventoryToggleDelegate();
// 제작된 위젯 정확한 지칭 한 주소
UPROPERTY()
UInventoryWidget* InventoryWidget;
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
// static 클래스 골라줘서 -> 해당 위젯형태로 제작
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "UI")
TSubclassOf<UInventoryWidget> InventoryWidgetClass;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
};
UImanager.cpp
#include "UIManager.h"
#include "InventoryWidget.h"
#include "CTPSPlayer.h"
#include "Blueprint\UserWidget.h"
// Sets default values
AUIManager::AUIManager()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
}
void AUIManager::CreatInventoryWidget()
{
if (InventoryWidgetClass)
{
// 인벤토리 위젯을 생성
InventoryWidget = CreateWidget<UInventoryWidget>(GetWorld(), InventoryWidgetClass);
if (InventoryWidget)
{
// Delegate 연결
BindInventoryToggleDelegate();
}
}
}
void AUIManager::BindInventoryToggleDelegate()
{
// Delegate를 구독할 대상 플레이어를 찾음
if (ACTPSPlayer* player = Cast<ACTPSPlayer>(GetWorld()->GetFirstPlayerController()->GetPawn()))
{
// 플레이어가 Delegate를 구독할 수 있도록 설정
InventoryWidget->OnInventoryToggled.AddDynamic(player, &ACTPSPlayer::HandleInventoryToggled);
}
}
// Called when the game starts or when spawned
void AUIManager::BeginPlay()
{
Super::BeginPlay();
CreatInventoryWidget();
}
// Called every frame
void AUIManager::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
InventoryWidget.h
#pragma once
#include "CoreMinimal.h"
#include "ItemData.h"
#include "Blueprint/UserWidget.h"
#include "InventoryWidget.generated.h"
class UGridPanel;
class UInventorySlot;
class UCanvasPanel;
class UInventoryActorComponent;
// 델리게이트 ( 대리자 ) -> 인벤토리 On / Off, 의존성 줄일려고
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnInventoryToggled, bool, bIsOpen);
UCLASS()
class MAGICIAN_PROJECT_API UInventoryWidget : public UUserWidget
{
GENERATED_BODY()
public:
// Delegate 인스턴스 생성 ( 인벤토리 열림/ 닫힘을 알림 )
UPROPERTY(BlueprintAssignable, Category = "Inventory")
FOnInventoryToggled OnInventoryToggled;
// 인벤토리 여/닫 함수
UFUNCTION(BlueprintCallable, Category = "Inventroy")
void ToggleInventory();
UFUNCTION(BlueprintCallable, Category = "Inventroy")
void SetInventoryItem(UInventoryActorComponent* InventoryComp);
public:
// 인벤토리 상태를 저장 ( 열림 또는 닫힘 )
bool bIsInventoryOpen = false;
// 해당 아이템 Array 데이터들이 --> Inventory Actor Component에서 가져오기
/*UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Inventory")
TArray<FItemData> InventoryItem;*/
// 그리드 패널 ( 아이템 배치를 위한 GridPanel )
UPROPERTY(meta = (BindWidget))
UGridPanel* GridPanel;
UPROPERTY(meta = (BindWidget))
UCanvasPanel* CanvasPanel;
// 그리드 업데이트 함수
void UpdateInventoryGrid(UInventoryActorComponent* InventoryComp);
//
UPROPERTY(EditAnywhere, Category = "Inventory")
TSubclassOf<UInventorySlot> SlotWidgetClass;
// 위젯을 열고 닫는 함수
virtual void NativeConstruct() override;
virtual void NativeDestruct() override;
};
InventoryWidget.cpp
#include "InventoryWidget.h"
#include "InventorySlot.h"
#include "Components\CanvasPanel.h"
#include "Components\GridPanel.h"
#include "Blueprint\WidgetTree.h"
#include "InventoryActorComponent.h"
void UInventoryWidget::ToggleInventory()
{
// 열려 있으면 ~
if (bIsInventoryOpen)
{
RemoveFromParent();
}
else
{
AddToViewport();
}
// 상태를 토글
bIsInventoryOpen = !bIsInventoryOpen;
// 델리게이트 호출
OnInventoryToggled.Broadcast(bIsInventoryOpen);
}
void UInventoryWidget::SetInventoryItem(UInventoryActorComponent* InventoryComp)
{
// 인벤토리 컴포넌트로부터 아이템 데이터를 받아와서 그리드를 업데이트
if (InventoryComp)
{
UpdateInventoryGrid(InventoryComp);
}
// 인벤토리 그리드 업데이트
}
/*
위젯이 생성될 때 필요한 초기화 작업을 설정할 떄 사용합니다
예를 들어, 위젯의 상태를 초기화하거나, 위젯 내부의 다른 컴포넌트를 초기화하는 작업 등을 할
*/
void UInventoryWidget::UpdateInventoryGrid(UInventoryActorComponent* InventoryComp)
{
// 인벤토리 컴포넌트 또는 슬롯 위젯 클래스, 그리드 패널이 유효하지 않으면 리턴
if (!InventoryComp || !SlotWidgetClass || !GridPanel)
return;
GridPanel->ClearChildren();
const int32 NumColums = 5; // 그리드 열 수 설정
int32 row = 0;
int32 colum = 0;
//인벤토리 맵을 순회하며 각 아이템을 그리드에 추가
// Map - > Key & Value -> Pair
for (const TPair<FName, FInventoryItem>& InventoryItem : InventoryComp->Inventory)
{
// 슬롯 위젯 생성
if (UInventorySlot* SlotWidget = CreateWidget<UInventorySlot>(this, SlotWidgetClass))
{
// InventoryItem 구조체에서 FItemData와 수량을 가져와서 슬롯에 설정
const FItemData& ItemData = InventoryItem.Value.ItemData;
int32 Quantity = InventoryItem.Value.Quantity;
// 슬롯 위젯에 아이템 데이터와 수량 설정
SlotWidget->SetItemData(ItemData, Quantity);
// GridPanel에 아이템 추가
GridPanel->AddChildToGrid(SlotWidget, row, colum);
colum++;
if (colum >= NumColums)
{
colum = 0;
row++;
}
}
}
}
void UInventoryWidget::NativeConstruct()
{
Super::NativeConstruct();
// UCanvasPanel 생성 및 위젯 트리에 추가
CanvasPanel = WidgetTree->ConstructWidget<UCanvasPanel>(UCanvasPanel::StaticClass(), TEXT("CanvasPanel"));
if (CanvasPanel)
{
// 위젯 트리의 루트로 설정
WidgetTree->RootWidget = CanvasPanel;
}
GridPanel = WidgetTree->ConstructWidget<UGridPanel>(UGridPanel::StaticClass(), TEXT("GridPanel"));
if (GridPanel && CanvasPanel)
{
CanvasPanel->AddChild(GridPanel);
}
}
void UInventoryWidget::NativeDestruct()
{
Super::NativeDestruct();
UE_LOG(LogTemp, Warning, TEXT("Inventory Widget Destruct"));
}
InventorySlot.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Blueprint/UserWidget.h"
#include "InventorySlot.generated.h"
class UImage;
class UTextBlock;
UCLASS()
class MAGICIAN_PROJECT_API UInventorySlot : public UUserWidget
{
GENERATED_BODY()
protected:
// 아이템 썸네일 이미지
UPROPERTY(meta = (BindWidget))
UImage* ItemThumnail;
// 아이템 이름
UPROPERTY(meta = (BindWidget))
UTextBlock* ItemName;
// 아이템 수량
UPROPERTY(meta = (BindWidget))
UTextBlock* stackCount;
public:
UFUNCTION(BlueprintCallable, Category = "Inventory Slot")
void SetItemData(const FItemData& ItemData, int32 Quantity);
};
InventorySlot.cpp
#include "InventorySlot.h"
#include "ItemData.h"
#include "Components\Image.h"
#include "Components\TextBlock.h"
void UInventorySlot::SetItemData(const FItemData& ItemData, int32 Quantity)
{
// 아이템 썸네일 설정
if (ItemThumnail && ItemData.ItemThumbnail)
{
ItemThumnail->SetBrushFromTexture(ItemData.ItemThumbnail);
}
// 아이템 이름 설정
if (ItemName)
{
ItemName->SetText(ItemData.ItemName);
}
// 스택 가능한 아이템의 경우 수량 설정
if (stackCount)
{
stackCount->SetText(FText::AsNumber(Quantity));
}
}
TPSPlayer.cpp
void ACTPSPlayer::BeginPlay()
{
... 생략
// UIManager를 찾아 참조
UIManagerRef = Cast<AUIManager>(UGameplayStatics::GetActorOfClass(GetWorld(), AUIManager::StaticClass()));
}
void ACTPSPlayer::ToggleInventory(const FInputActionValue& value)
{
if (UIManagerRef)
{
if (UIManagerRef->InventoryWidget)
{
UIManagerRef->InventoryWidget->ToggleInventory();
}
}
}
void ACTPSPlayer::HandleInventoryToggled(bool bIsOpen)
{
bIsInventoryOpen = bIsOpen;
if (bIsInventoryOpen)
{
UE_LOG(LogTemp, Warning, TEXT("Inventory Opened"));
}
else
{
UE_LOG(LogTemp, Warning, TEXT("Inventory close"));
}
}




