-
블루아카이브 루트슈터 팬게임 프로젝트(11) - 방어구 추가루트슈터 프로젝트 2024. 1. 9. 22:44
안녕하세요. 이번에는 방어구를 추가해보았습니다. 아무래도 루트슈터 게임에 무기만 있다면 너무 심심하죠. 그래서 한가지 고정된 스탯 하나와 아이템 등급에 따라서 랜덤한 스탯을 제공하는 방어구를 3개 착용할 수 있게 하기로 했습니다.
NormalArmor.h
UCLASS() class PROJECTBLUELANDS_API ANormalArmor : public AItemBase { GENERATED_BODY() public: ANormalArmor(); protected: // Called when the game starts or when spawned virtual void BeginPlay() override; virtual void PostInitializeComponents(); public: UPROPERTY(VisibleAnyWhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true")) class UNiagaraComponent* dropEffect; //땅바닥에 떨어진 방어구의 등급별 이펙트 UPROPERTY(VisibleAnywhere) UStaticMeshComponent* armorMesh; UPROPERTY(EditAnywhere, BlueprintReadWrite) FArmorStat armorStat; //방어구 스탯에 대한 구조체 UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) class USceneComponent* rootComp; UPROPERTY(VisibleAnywhere) class UBoxComponent* triggerBox; //바닥에 떨어진 방어구에 가까이 갈 시 트리거를 감지하는 박스 UFUNCTION() void OnCharacterOverlap(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, //오버랩 될 경우 call될 함수 UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult); void MoveToInventory(class APlayerCharacter* player, int slotNum); };
방어구에 대한 헤더파일입니다. ItemBase를 상속받고 있습니다. 이렇게 보니까 드랍 이펙트와 트리거 박스는 그냥 ItemBase에 올려놓는게 좋을거 같긴 합니다만 일단은 이곳에 두기로 했습니다.
NormalArmor.cpp
void ANormalArmor::PostInitializeComponents() { Super::PostInitializeComponents(); triggerBox->OnComponentBeginOverlap.AddDynamic(this, &ANormalArmor::OnCharacterOverlap); } void ANormalArmor::OnCharacterOverlap(UPrimitiveComponent *OverlappedComp, AActor *OtherActor, UPrimitiveComponent *OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult &SweepResult) { APlayerCharacter* player = Cast<APlayerCharacter>(OtherActor); if(player) //플레이어가 있다면 { UE_LOG(LogTemp, Log, TEXT("Player Overlapped!!!")); for(int i=0; i < 40; i++) { if(player->itemInventory[i].itemType == EItemType::EIT_DEFAULT) //비어있는 인벤토리 슬롯이 있다면 { MoveToInventory(player, i); if(Destroy()){} else UE_LOG(LogTemp, Error, TEXT("destroy failed!!!")); return; } } UE_LOG(LogTemp, Error, TEXT("No inventory slot!")); } } void ANormalArmor::MoveToInventory(APlayerCharacter *player, int slotNum) //방어구 줍기 기능 { player->itemInventory[slotNum].name = name; player->itemInventory[slotNum].itemLevel = GetItemLevel(); player->itemInventory[slotNum].itemRarity = GetItemRarity(); player->itemInventory[slotNum].itemType = itemType; player->itemInventory[slotNum].itemIcon = icon; player->itemInventory[slotNum].armorStat = armorStat; player->RefreshInventory(); //인벤토리 새로고침 }
방어구는 무기와 달리 다른 기능 없이 플레이어에게 추가 스탯만 제공하기 때문에 cpp에는 별거 없습니다. 바닥에 떨어진 방어구를 인벤토리로 집어갈 수 있도록 트리거 박스의 반응 함수만 작성합니다.
CustomDataTables.h
USTRUCT(BlueprintType) struct FArmorOptionTableRow : public FTableRowBase { GENERATED_BODY() public: UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Armor tier") int32 tier; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Armor Main Option") int32 mainOption; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Armor name") FString armorName; };
그리고 방어구의 메인 옵션(메인 스탯)에 대해 데이터 테이블을 작성하여 불러오기 위해 구조체를 하나 추가로 작성해줍니다. 제가 작성하는 방어구는 레벨구간마다 드랍하는 방어구 티어가 존재하고, 메인 옵션과 방어구 이름이 존재합니다.
이런 방식으로 데이터 테이블을 작성해줬습니다. 시계의 메인 옵션은 치명타 확률 + 입니다. 그 외에도 가방, 모자 등의 데이터 테이블을 작성해두었습니다.
GameModeBase.cpp
static ConstructorHelpers::FObjectFinder<UDataTable> ArmorOptionTable(TEXT("/Game/DataTables/DT_Armor_Option")); if(ArmorOptionTable.Succeeded()) armorOptionDataTable = ArmorOptionTable.Object; // 방어구 랜덤옵션 테이블 불러옴 UE_LOG(LogTemp, Display, TEXT("all optionsdqwe: %d"), armorOptionDataTable->GetRowNames().Num()); static ConstructorHelpers::FObjectFinder<UDataTable> hatArmorOptionTable(TEXT("/Game/DataTables/DT_HatArmor_Option")); if(hatArmorOptionTable.Succeeded()) hatOptionDataTable = hatArmorOptionTable.Object; // 모자방어구 랜덤옵션 테이블 불러옴 static ConstructorHelpers::FObjectFinder<UDataTable> watchArmorOptionTable(TEXT("/Game/DataTables/DT_WatchArmor_Option")); if(watchArmorOptionTable.Succeeded()) watchOptionDataTable = watchArmorOptionTable.Object; // 손목시계방어구 랜덤옵션 테이블 불러옴 static ConstructorHelpers::FObjectFinder<UDataTable> backpackArmorOptionTable(TEXT("/Game/DataTables/DT_BackpackArmor_Option")); if(backpackArmorOptionTable.Succeeded()) backpackOptionDataTable = backpackArmorOptionTable.Object; // 가방방어구 랜덤옵션 테이블 불러옴
그리고 적 처치시 아이템 랜덤드랍을 위해 데이터 테이블을 게임모드 베이스에서 읽어옵니다.
void ABluelandsGameModeBase::ArmorOptionGenerator(AItemBase* armor, int level, int legendDropRate, int rareDropRate, int uncommonDropRate) { ANormalArmor* normalArmor = Cast<ANormalArmor>(armor); if(!normalArmor) return; FArmorOptionTableRow* armorOptionRow = nullptr; switch(normalArmor->armorStat.armorType) { case EArmorType::EAT_HAT: //모자 방어구, 무기 공격력 증가 armorOptionRow = hatOptionDataTable-> FindRow<FArmorOptionTableRow>(FName(*(FString::FormatAsNumber(level))), FString("")); normalArmor->icon = hatArmorIcons[(*armorOptionRow).tier - 1]; break; case EArmorType::EAT_WATCH: //손목시계, 치명타 확률 증가 armorOptionRow = watchOptionDataTable-> FindRow<FArmorOptionTableRow>(FName(*(FString::FormatAsNumber(level))), FString("")); normalArmor->icon = watchArmorIcons[(*armorOptionRow).tier - 1]; break; case EArmorType::EAT_BACKPACK: //가방, 체력 증가 armorOptionRow = backpackOptionDataTable-> FindRow<FArmorOptionTableRow>(FName(*(FString::FormatAsNumber(level))), FString("")); normalArmor->icon = backpackArmorIcons[(*armorOptionRow).tier - 1]; break; } FString temp = (*armorOptionRow).armorName; normalArmor->name = FName(*temp); normalArmor->armorStat.tier = (*armorOptionRow).tier; normalArmor->armorStat.mainStat = (*armorOptionRow).mainOption; normalArmor->itemType = EItemType::EIT_ARMOR; normalArmor->SetItemLevel(level); normalArmor->SetItemRarity(GetArmorRandomRarity(legendDropRate, rareDropRate, uncommonDropRate, normalArmor)); }
그리고 방어구의 랜덤으로 옵션을 생성하여 붙여줍니다. 나머지 작성한 함수에 대해서는 무기에서 만든것과 매우 흡사하기에 따로 추가하지는 않겠습니다. 사실 인벤토리의 툴팁 기능도 매우 유사하기 때문에 따로 적지 않겠습니다. 다른점이 있다면 방어구 툴팁의 UI 부분입니다.
방어구 툴팁의 예시입니다. 방어구의 아이템 레벨, 방어구 이름, 몇티어인지, 어떤 메인 옵션을 얼마나 제공하는지에 대해 적혀있고 밑의 공백에는 추가 랜덤옵션이 적혀질 예정입니다.
결과물은 이런방식입니다. 인코딩 문제가 있어서 메인 옵션 텍스트가 제대로 나오지 않습니다만, 인코딩 문제로 한글이 깨져셔 그렇습니다. 사실 밑의 랜덤옵션이 영어인 이유도 이것때문인데, 따로 수정을 해야할 것 같습니다. 이것저것 설명을 스킵한 것이 많은데, 툴팁 기능은 무기툴팁과 매우 유사하기 때문에 따로 적지 않았습니다. 혹시 원하시는 분들이 계시다면 따로 포스팅을 올리도록 하겠습니다.
이만 줄이겠습니다. 내일은 방어구 장비 슬롯을 추가하고 플레이어에게 추가적인 스탯을 제공할 수 있도록 해야겠네요.
'루트슈터 프로젝트' 카테고리의 다른 글
블루아카이브 루트슈터 팬게임 프로젝트(13) - 체력, 잔탄 UI (1) 2024.01.11 블루아카이브 루트슈터 팬게임 프로젝트(12) - 방어구 스탯 적용 (1) 2024.01.10 블루아카이브 루트슈터 팬게임 프로젝트(10) - 데미지 표시 & 코드 정비 (0) 2024.01.07 블루아카이브 루트슈터 팬게임 프로젝트(9) - 인벤토리 확장 (1) 2024.01.05 블루아카이브 루트슈터 팬게임 프로젝트(8) - 적 체력바 (1) 2024.01.04