현재 구현한 인벤토리 컴포넌트는 아이템을 수집만하고 별다른 액션을 수행하지는 않는다.
따라서 수집한 아이템을 장착하거나, 아니면 사용시 특정 오브젝트를 스폰, 혹은 소비를 할 수 있는등 아이템을 통한 특정 액션이 가능해야된다.
어떻게 구현해야할까 생각을 많이해봤는데 아이템을 구조체로 만든만큼 순수하게 정보만 가지고 있는게 좋을 것 같아, 아이템 액션을 수행하는 컴포넌트를 따로 만들어 캐릭터에 부착하기로했다.
물론 구조체도 클래스처럼 메소드를 만들어서 사용할 수 있다는건 알지만 애초에 USTRUCT에서는 UFUNCTION을 활용할 수 없는것도 있고, 그러면 아이템을 구조체로 만든 의미가 없다고 생각해서...
일단 아이템 구조체에는 어떤 액션을 취할지를 나타내는 정보를 추가했다.
USTRUCT(BlueprintType)
struct FInventoryItem : public FTableRowBase, public IInventoryActionBase
{
GENERATED_BODY()
public:
FInventoryItem();
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FName ItemID;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FText Name;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
int32 Weight;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
int32 Value;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
UTexture2D* Thumbnail;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FText Description;
// 추가된 아이템 액션 정보
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FString ItemAction;
bool operator==(const FInventoryItem& OtherItem) const
{
if (ItemID == OtherItem.ItemID)
return true;
return false;
}
};
이 문자열 값을 아이템 액션 컴포넌트의 Delegate Map Key로 사용하여 해당 Key값에 대응되는 함수를 실행시킬 것이다.
헤더
DECLARE_DELEGATE(FItemActionDelegate)
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class SCIFICOMBAT_API UItemActionComponent : public UActorComponent
{
// . . . . 기본적으로 생성되는 부분은 생략
public:
UPROPERTY()
class ACombatCharacter* inventory_owner;
UPROPERTY(BlueprintReadWrite)
FInventoryItem selected_item;
FORCEINLINE void SetSelectedItem(FInventoryItem item_info) { selected_item = item_info; }
// 아이템 액션 델리게이트 맵
TMap<FString, FItemActionDelegate> item_action_delegate_map;
public:
void InitializeItemActionComponent(class ACombatCharacter* _inventory_owner);
FItemActionDelegate CreateItemActionDelegate(FString action_name);
void InitializeItemActionDelegates();
// 아이템 액션 RPC
UFUNCTION()
void ExecuteItemAction(const FString& action_name);
UFUNCTION(Server, Reliable)
void ServerExecuteItemAction(const FString& action_name);
UFUNCTION(NetMulticast, Reliable)
void MulticastExecuteItemAction(const FString& action_name);
UFUNCTION()
void CallItemAction(const FString& action_name);
public:
// Item Actions
UFUNCTION()
void ItemAction_SpawnObject();
UFUNCTION()
void ItemAction_AttachMesh();
UFUNCTION()
void ItemAction_Consume();
};
구현부
// 초기화 단계
void UItemActionComponent::InitializeItemActionComponent(class ACombatCharacter* _inventory_owner)
{
inventory_owner = _inventory_owner;
InitializeItemActionDelegates();
}
void UItemActionComponent::InitializeItemActionDelegates()
{
item_action_delegate_map.Add("SpawnObj", CreateItemActionDelegate("ItemAction_SpawnObject"));
item_action_delegate_map.Add("AttachMesh", CreateItemActionDelegate("ItemAction_AttachMesh"));
item_action_delegate_map.Add("Consume", CreateItemActionDelegate("ItemAction_Consume"));
}
FItemActionDelegate UItemActionComponent::CreateItemActionDelegate(FString skill_name)
{
FItemActionDelegate item_action_delegate;
item_action_delegate.BindUFunction(this, FName(skill_name));
return item_action_delegate;
}
// 아이템 액션
// 일단 로그로 함수가 실행되는지만 확인함
void UItemActionComponent::ItemAction_SpawnObject()
{
// selected_item을 활용하여 오브젝트 스폰
UE_LOG(LogTemp, Warning, TEXT("Spawn Action"));
}
void UItemActionComponent::ItemAction_AttachMesh()
{
// selected_item을 활용하여 메시 어태치
UE_LOG(LogTemp, Warning, TEXT("Attach Mesh Action"));
}
void UItemActionComponent::ItemAction_Consume()
{
// selected_item을 활용하여 특정 값을 소비
UE_LOG(LogTemp, Warning, TEXT("Consume Action"));
}
// 아이템 액션 RPC
void UItemActionComponent::ExecuteItemAction(const FString& action_name)
{
ServerExecuteItemAction(action_name);
}
void UItemActionComponent::ServerExecuteItemAction_Implementation(const FString& action_name)
{
MulticastExecuteItemAction(action_name);
}
void UItemActionComponent::MulticastExecuteItemAction_Implementation(const FString& action_name)
{
CallItemAction(action_name);
}
void UItemActionComponent::CallItemAction(const FString& action_name)
{
if (item_action_delegate_map[action_name].IsBound())
{
item_action_delegate_map[action_name].Execute();
}
}
캐릭터 클래스에 새로 추가한 함수들 BlueprintCallable UFUNCTION 옵션이 지정되어 있어 UMG 블루프린트에서 바로 호출가능하다.
// 아이템 섬네일 하단부 버튼에서 호출되는 함수
void UInventoryComponent::SelectItem(int idx)
{
is_item_select = true;
selected_item = inventory[idx];
}
void UInventoryComponent::SelectItemReset()
{
is_item_select = false;
}
// 아이템 사용 버튼을 누르면 호출되는 함수
void UInventoryComponent::UseItem()
{
if (is_item_select)
{
//selected_item.InventoryAction.GetInterface()->UseItem();
//selected_item.UseItem();
inventory_owner->item_action_component->SetSelectedItem(selected_item);
inventory_owner->item_action_component->ExecuteItemAction(selected_item.ItemAction);
// 장착형
// 소비형
// 소환형
}
}
각 아이템 섬네일의 하단부를 클릭하여 선택한 뒤 Use 버튼을 눌러 아이템 액션 함수를 실행시켰다.
이제 실제로 아이템 액션을 제대로 구현하여 악세서리를 부착하거나 아이템을 소환하고, 아이템 선택 버튼을 클릭 시 섬네일 외곽선이 강조되거나 하는.. 그런 부분들이 필요해보인다.