Infinite Fighter 개발일지 (18)

유영준·2023년 7월 17일
0

UE5 UNSEEN

목록 보기
18/18

23.07.10 ~ 2023.07.16 개발일지

추가한 부분

스테이지 클래스 추가

적의 생성, 레벨 시작 등이 포함되어 있는 스테이지 클래스를 추가했다

IFStage.cpp

// Fill out your copyright notice in the Description page of Project Settings.


#include "Stage/IFStage.h"
#include "AI/IFEnemy.h"
#include "Components/SceneComponent.h"
#include "Kismet/KismetMathLibrary.h"
#include "Kismet/GameplayStatics.h"
#include "Character/IFPlayerController.h"
#include "Components/BoxComponent.h"
#include "Components/WidgetComponent.h"
#include "CommonUserWidget.h"

// Sets default values
AIFStage::AIFStage()
{
	SceneComponent = CreateDefaultSubobject<USceneComponent>(TEXT("SceneComponent"));
	SetRootComponent(SceneComponent);

	static ConstructorHelpers::FObjectFinder<UDataTable> StageDataTable
	(TEXT("/Game/InFiniteFighter/Miscellaneous/DataTable/DT_StageTable.DT_StageTable"));
	if (StageDataTable.Succeeded())
	{
		const UDataTable* DataTable = StageDataTable.Object;

		TArray<uint8*> ValueArray;
		DataTable->GetRowMap().GenerateValueArray(ValueArray);
		Algo::Transform(ValueArray, StageTable, [](uint8* Value) { return *reinterpret_cast<FIFStageTable*>(Value); });
	}

	MaxLevel = StageTable.Num()- 1;

	// setting Stage Blocker
	StageBlockerBox = CreateDefaultSubobject<UBoxComponent>(TEXT("StageBlocker"));
	StageBlockerBox->SetupAttachment(SceneComponent);
	StageBlockerBox->SetRelativeLocation(FVector(0.0f, 1000.0f, 0.0f));
	StageBlockerBox->SetBoxExtent(FVector(400.0f, 200.0f, 800.0f));
	StageBlockerBox->SetCollisionProfileName(TEXT("NoCollision"));

	// setting Stage Trigger Box
	StageTriggerBox = CreateDefaultSubobject<UBoxComponent>(TEXT("StageTrigger"));
	StageTriggerBox->SetupAttachment(SceneComponent);
	StageTriggerBox->SetRelativeLocation(FVector(-1200.0f, 250.0f, -350.0f));
	StageTriggerBox->SetBoxExtent(FVector(150.0f, 150.0f, 150.0f));
	
	// setting Interaction
	InteractionWidget = CreateDefaultSubobject<UWidgetComponent>(TEXT("InteractionWidget"));
	InteractionWidget->SetupAttachment(StageTriggerBox);
	InteractionWidget->SetRelativeLocation(FVector(0.0f, 300.0f, 30.0f));

	static ConstructorHelpers::FClassFinder<UCommonUserWidget>InteractionWidgetRef
	(TEXT("/Game/InFiniteFighter/Widget/Game/Interaction.Interaction_C"));
	if (::IsValid(InteractionWidgetRef.Class))
	{
		InteractionWidget->SetWidgetClass(InteractionWidgetRef.Class);
		InteractionWidget->SetWidgetSpace(EWidgetSpace::Screen);
		InteractionWidget->SetDrawSize(FVector2D(50.0f, 50.0f));
		InteractionWidget->SetCollisionEnabled(ECollisionEnabled::NoCollision);
	}

	bCanGameStart = false;
	CurrentLevel = 0;
}

void AIFStage::BeginPlay()
{
	Super::BeginPlay();

	InteractionWidget->SetVisibility(false);
}

void AIFStage::PostInitializeComponents()
{
	Super::PostInitializeComponents();

	StageTriggerBox->OnComponentBeginOverlap.AddDynamic(this, &AIFStage::OnTriggerBoxBeginOverlap);
	StageTriggerBox->OnComponentEndOverlap.  AddDynamic(this, &AIFStage::OnTriggerBoxEndOverlap);
}

void AIFStage::SetStage()
{ 
	SpawnNumber = StageTable[CurrentLevel].Count;
	UE_LOG(LogTemp, Warning, TEXT("%d"), SpawnNumber);

	for (int i = 0; i < SpawnNumber; ++i)
	{
		float Radius = 1000.0f;
		float VectorZ = GetActorLocation().Z;
		FVector RandomPoint = GetActorLocation() + UKismetMathLibrary::RandomUnitVector() * FMath::FRandRange(500.0f, Radius);
		RandomPoint.Z = VectorZ + 88.0f;
		FTransform SpawnTransform(RandomPoint);
		AIFEnemy* Enemy = GetWorld()->SpawnActor<AIFEnemy>(RandomPoint, FRotator::ZeroRotator);
		if(::IsValid(Enemy))
		{
			Enemy->OnDestroyed.AddDynamic(this, &AIFStage::SetReward);
			Enemy->SetEnemy(StageTable[CurrentLevel].MaxHp, StageTable[CurrentLevel].Attack);
			UE_LOG(LogTemp, Warning, TEXT("EnemyLeft : %d"), SpawnNumber);
		}
	}
}

void AIFStage::StartGame()
{
	if (bCanGameStart)
	{
		StageBlockerBox->SetCollisionProfileName(TEXT("InvisibleWall"));
		StageTriggerBox->SetCollisionProfileName(TEXT("NoCollision"));
		InteractionWidget->SetVisibility(false);
		bCanGameStart = false;
		SetStage();
	}
}

void AIFStage::OnTriggerBoxBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, 
	UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
	if (OtherActor == UGameplayStatics::GetPlayerCharacter(GetWorld(), 0))
	{
		InteractionWidget->SetVisibility(true);
		bCanGameStart = true;
	}
}

void AIFStage::OnTriggerBoxEndOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, 
	UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
	if (OtherActor == UGameplayStatics::GetPlayerCharacter(GetWorld(), 0))
	{
		InteractionWidget->SetVisibility(false);
		bCanGameStart = false;
	}
}

void AIFStage::SetReward(AActor* DestroyedActor)
{
	SpawnNumber--;
	UE_LOG(LogTemp, Warning, TEXT("EnemyLeft : %d"), SpawnNumber);

	if (SpawnNumber == 0)
	{
		UE_LOG(LogTemp, Warning, TEXT("StageEnd"));
		CurrentLevel++;
		auto PlayerController = Cast<AIFPlayerController>(UGameplayStatics::GetPlayerController(GetWorld(), 0));
		if (::IsValid(PlayerController))
		{
			PlayerController->SetRewardHUD();
		}
	}
}

적의 생성은 만들어둔 데이터 테이블을 참조하도록 구현하였고,

보상의 획득 부분은 플레이어 컨트롤러에서 전담하게 된다

스테이지는 대략 이런 느낌이다


보상 버튼 만들기

보상 버튼 같은 경우엔 UCommonButtonBase를 상속받고,

애셋 매니저를 통해 아이템의 정보를 받아와 이를 버튼에 할당해주는 방식으로 구현하였다

IFItemButton.cpp

// Fill out your copyright notice in the Description page of Project Settings.


#include "UI/IFItemButton.h"
#include "Components/TextBlock.h"
#include "Item/IFItemData.h"
#include "Engine/AssetManager.h"

void UIFItemButton::NativeConstruct()
{
    ItemName->SetText(FText::FromString(TEXT("ItemNameTemp")));
    ItemDescription->SetText(FText::FromString(TEXT("ItemDescriptionTemp")));
}

void UIFItemButton::SetItem()
{
	UAssetManager& Manager = UAssetManager::Get();

	TArray<FPrimaryAssetId> Assets;
	Manager.GetPrimaryAssetIdList(TEXT("IFItemData"), Assets);
	UE_LOG(LogTemp, Warning, TEXT("%d"), Assets.Num());
	ensure(0 < Assets.Num());

	int32 RandomIndex = FMath::RandRange(0, Assets.Num() - 1);
	FSoftObjectPtr AssetPtr(Manager.GetPrimaryAssetPath(Assets[RandomIndex]));
	if (AssetPtr.IsPending())
	{
		AssetPtr.LoadSynchronous();
	}
	Item = Cast<UIFItemData>(AssetPtr.Get());

    ItemName->SetText(FText::FromString(Item->ItemName));
    ItemDescription->SetText(FText::FromString(Item->ItemDescription));
}

(버튼의 UI를 간단하게 만들어보았다..)

이를 HUD에 등록해주고, 간단한 TArray에 두고 관리하였다

IFPlayerController.cpp

    for (const auto& Button : HUDWidget->Buttons)
    {
        Button->OnClicked().AddLambda([&]
            {
                IIFApplyItemInterface* PlayerCharacter = Cast<IIFApplyItemInterface>((GetCharacter()));
                if(PlayerCharacter)
                    PlayerCharacter->ApplyItem(Button->GetItem());

                HUDWidget->SetItemSlotVisible(false);

                AIFStage* Stage = Cast<AIFStage>(UGameplayStatics::GetActorOfClass(GetCharacter()->GetWorld(), AIFStage::StaticClass()));
                if (::IsValid(Stage))
                    Stage->SetStage();

                FInputModeGameOnly GameOnlyInputMode;
                SetInputMode(GameOnlyInputMode);
            });
    }

Onclick의 함수를 추가해주었다

이때 아이템의 적용은 인터페이스를 통해 구현해주었다

클릭을 하게 되면 다음 스테이지가 진행되기 때문에, 보상을 획득하고, 다시 전투를 하게 되는 흐름을 이어가게 된다

다음주까지 마지막으로 메인화면, 엔딩 등만 간단하게 만들어주고 마무리를 하고자 한다

profile
토비폭스가 되고픈 게임 개발자

4개의 댓글

comment-user-thumbnail
2023년 7월 17일

글 잘 봤습니다, 많은 도움이 되었습니다.

1개의 답글
comment-user-thumbnail
2023년 7월 21일

소액 결제를 구현해야 합니다.

1개의 답글