게임의 승패조건 관리
게임 모드
멀티 플레이를 포함해 게임에서 유일하게 존재하는 게임의 심판 오브젝트
최상단에서 게임의 진행을 관리하며, 게임 판정에 관련된 중요한 행동을 주관하는데 적합.
다양한 게임 규칙을 적용할 수 있도록 핵심 기능과 분리해 설계하는 것이 바람직함.
게임의 상태와 플레이어의 상태를 별도로 저장할 수 있는 프레임워크를 제공함
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
#include "Interface/ABGameInterface.h"
#include "ABGameMode.generated.h"
/**
*
*/
UCLASS()
class ARENABATTLE_API AABGameMode : public AGameModeBase, public IABGameInterface
{
GENERATED_BODY()
public:
AABGameMode();
virtual void OnPlayerScoreChanged(int32 NewPlayerScore) override;
virtual void OnPlayerDead() override;
virtual bool IsGameCleared() override;
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = Game)
int32 ClearScore;
UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = Game)
int32 CurrentScore;
UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = Game)
uint8 bIsCleared : 1;
};
ABGameMode.h
// Fill out your copyright notice in the Description page of Project Settings.
#include "Game/ABGameMode.h"
#include "ABGameMode.h"
#include "Player/ABPlayerController.h"
AABGameMode::AABGameMode()
{
static ConstructorHelpers::FClassFinder<APawn> DefaultPawnClassRef(TEXT("/Script/Engine.Blueprint'/Game/ArenaBattle/Blueprint/BP_ABCharacterPlayer.BP_ABCharacterPlayer_C'"));
if (DefaultPawnClassRef.Class)
{
DefaultPawnClass = DefaultPawnClassRef.Class;
}
static ConstructorHelpers::FClassFinder<APlayerController> PlayerControllerClassRef(TEXT("/Script/ArenaBattle.ABPlayerController"));
if (PlayerControllerClassRef.Class)
{
PlayerControllerClass = PlayerControllerClassRef.Class;
}
ClearScore = 3;
CurrentScore = 0;
bIsCleared = false;
}
void AABGameMode::OnPlayerScoreChanged(int32 NewPlayerScore)
{
CurrentScore = NewPlayerScore;
AABPlayerController* ABPlayerController = Cast<AABPlayerController>(GetWorld()->GetFirstPlayerController());
if (ABPlayerController)
{
ABPlayerController->GameScoreChanged(CurrentScore);
}
if (CurrentScore >= ClearScore)
{
bIsCleared = true;
if (ABPlayerController)
{
ABPlayerController->GameClear();
}
}
}
void AABGameMode::OnPlayerDead()
{
AABPlayerController* ABPlayerController = Cast<AABPlayerController>(GetWorld()->GetFirstPlayerController());
if (ABPlayerController)
{
ABPlayerController->GameOver();
}
}
bool AABGameMode::IsGameCleared()
{
return bIsCleared;
}
ABGameMode.cpp
ABGameInterface 인터페이스 생성
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "UObject/Interface.h"
#include "ABGameInterface.generated.h"
// This class does not need to be modified.
UINTERFACE(MinimalAPI)
class UABGameInterface : public UInterface
{
GENERATED_BODY()
};
/**
*
*/
class ARENABATTLE_API IABGameInterface
{
GENERATED_BODY()
// Add interface functions to this class. This is the class that will be inherited to implement this interface.
public:
virtual void OnPlayerScoreChanged(int32 NewPlayerScore) = 0;
virtual void OnPlayerDead() = 0;
virtual bool IsGameCleared() = 0;
};
ABGameInterface.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "ABStageGimmick.generated.h"
DECLARE_DELEGATE(FOnStageChangedDelegate);
USTRUCT(BlueprintType)
struct FStageChangedDelegateWrapper
{
GENERATED_BODY()
FStageChangedDelegateWrapper() { }
FStageChangedDelegateWrapper(const FOnStageChangedDelegate& InDelegate) : StageDelegate(InDelegate) {}
FOnStageChangedDelegate StageDelegate;
};
UENUM(BlueprintType)
enum class EStageState : uint8
{
READY = 0,
FIGHT,
REWARD,
NEXT
};
UCLASS()
class ARENABATTLE_API AABStageGimmick : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AABStageGimmick();
public:
FORCEINLINE int32 GetStageNum() const { return CurrentStageNum; }
FORCEINLINE void SetStageNum(int32 NewStageNum) { CurrentStageNum = NewStageNum; }
protected:
virtual void OnConstruction(const FTransform& Transform) override;
// Stage Section
protected:
UPROPERTY(VisibleAnywhere, Category = Stage, Meta = (AllowPrivateAccess = "true"))
TObjectPtr<class UStaticMeshComponent> Stage;
UPROPERTY(VisibleAnywhere, Category = Stage, Meta = (AllowPrivateAccess = "true"))
TObjectPtr<class UBoxComponent> StageTrigger;
UFUNCTION()
void OnStageTriggerBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
// Gate Section
protected:
UPROPERTY(VisibleAnywhere, Category = Gate, Meta = (AllowPrivateAccess = "true"))
TMap<FName, TObjectPtr<class UStaticMeshComponent>> Gates;
UPROPERTY(VisibleAnywhere, Category = Gate, Meta = (AllowPrivateAccess = "true"))
TArray<TObjectPtr<class UBoxComponent>> GateTriggers;
UFUNCTION()
void OnGateTriggerBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
void OpenAllGates();
void CloseAllGates();
// State Section
protected:
UPROPERTY(EditAnywhere, Category = Stage, Meta = (AllowPrivateAccess = "true"))
EStageState CurrentState;
void SetState(EStageState InNewState);
UPROPERTY()
TMap<EStageState, FStageChangedDelegateWrapper> StateChangeActions;
void SetReady();
void SetFight();
void SetChooseReward();
void SetChooseNext();
// Fight Section
protected:
UPROPERTY(EditAnywhere, Category = Fight, Meta = (AllowPrivateAccess = "true"))
TSubclassOf<class AABCharacterNonPlayer> OpponentClass;
UPROPERTY(EditAnywhere, Category = Fight, Meta = (AllowPrivateAccess = "true"))
float OpponentSpawnTime;
UFUNCTION()
void OnOpponentDestroyed(AActor* DestroyedActor);
FTimerHandle OpponentTimerHandle;
void OnOpponentSpawn();
// Reward Section
protected:
UPROPERTY(VisibleAnywhere, Category = Reward, Meta = (AllowPrivateAccess = "true"))
TSubclassOf<class AABItemBox> RewardBoxClass;
UPROPERTY(VisibleAnywhere, Category = Reward, Meta = (AllowPrivateAccess = "true"))
TArray<TWeakObjectPtr<class AABItemBox>> RewardBoxes;
TMap<FName, FVector> RewardBoxLocations;
UFUNCTION()
void OnRewardTriggerBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
void SpawnRewardBoxes();
// Stage Stat
protected:
UPROPERTY(VisibleInstanceOnly, Category = Stat, Meta = (AllowPrivateAccess = "true"))
int32 CurrentStageNum;
};
ABStageGimmick.h
// Fill out your copyright notice in the Description page of Project Settings.
#include "Gimmick/ABStageGimmick.h"
#include "Components/StaticMeshComponent.h"
#include "Components/BoxComponent.h"
#include "Physics/ABCollision.h"
#include "Character/ABCharacterNonPlayer.h"
#include "Item/ABItemBox.h"
#include "Interface/ABGameInterface.h"
#include "Engine/OverlapResult.h"
#include "GameFramework/GameModeBase.h"
// Sets default values
AABStageGimmick::AABStageGimmick()
{
// Stage Section
Stage = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Stage"));
RootComponent = Stage;
static ConstructorHelpers::FObjectFinder<UStaticMesh> StageMeshRef(TEXT("/Script/Engine.StaticMesh'/Game/ArenaBattle/Environment/Stages/SM_SQUARE.SM_SQUARE'"));
if (StageMeshRef.Object)
{
Stage->SetStaticMesh(StageMeshRef.Object);
}
StageTrigger = CreateDefaultSubobject<UBoxComponent>(TEXT("StageTrigger"));
StageTrigger->SetBoxExtent(FVector(775.0, 775.0f, 300.0f));
StageTrigger->SetupAttachment(Stage);
StageTrigger->SetRelativeLocation(FVector(0.0f, 0.0f, 250.0f));
StageTrigger->SetCollisionProfileName(CPROFILE_ABTRIGGER);
StageTrigger->OnComponentBeginOverlap.AddDynamic(this, &AABStageGimmick::OnStageTriggerBeginOverlap);
// Gate Section
static FName GateSockets[] = { TEXT("+XGate"), TEXT("-XGate"), TEXT("+YGate"), TEXT("-YGate") };
static ConstructorHelpers::FObjectFinder<UStaticMesh> GateMeshRef(TEXT("/Script/Engine.StaticMesh'/Game/ArenaBattle/Environment/Props/SM_GATE.SM_GATE'"));
for (FName GateSocket : GateSockets)
{
UStaticMeshComponent* Gate = CreateDefaultSubobject<UStaticMeshComponent>(GateSocket);
Gate->SetStaticMesh(GateMeshRef.Object);
Gate->SetupAttachment(Stage, GateSocket);
Gate->SetRelativeLocation(FVector(0.0f, -80.5f, 0.0f));
Gate->SetRelativeRotation(FRotator(0.0f, -90.0f, 0.0f));
Gates.Add(GateSocket, Gate);
FName TriggerName = *GateSocket.ToString().Append(TEXT("Trigger"));
UBoxComponent* GateTrigger = CreateDefaultSubobject<UBoxComponent>(TriggerName);
GateTrigger->SetBoxExtent(FVector(100.0f, 100.0f, 300.0f));
GateTrigger->SetupAttachment(Stage, GateSocket);
GateTrigger->SetRelativeLocation(FVector(70.0f, 0.0f, 250.0f));
GateTrigger->SetCollisionProfileName(CPROFILE_ABTRIGGER);
GateTrigger->OnComponentBeginOverlap.AddDynamic(this, &AABStageGimmick::OnGateTriggerBeginOverlap);
GateTrigger->ComponentTags.Add(GateSocket);
GateTriggers.Add(GateTrigger);
}
// State Section
CurrentState = EStageState::READY;
StateChangeActions.Add(EStageState::READY, FStageChangedDelegateWrapper(FOnStageChangedDelegate::CreateUObject(this, &AABStageGimmick::SetReady)));
StateChangeActions.Add(EStageState::FIGHT, FStageChangedDelegateWrapper(FOnStageChangedDelegate::CreateUObject(this, &AABStageGimmick::SetFight)));
StateChangeActions.Add(EStageState::REWARD, FStageChangedDelegateWrapper(FOnStageChangedDelegate::CreateUObject(this, &AABStageGimmick::SetChooseReward)));
StateChangeActions.Add(EStageState::NEXT, FStageChangedDelegateWrapper(FOnStageChangedDelegate::CreateUObject(this, &AABStageGimmick::SetChooseNext)));
// Fight Section
OpponentSpawnTime = 2.0f;
OpponentClass = AABCharacterNonPlayer::StaticClass();
// Reward Section
RewardBoxClass = AABItemBox::StaticClass();
for (FName GateSocket : GateSockets)
{
FVector BoxLocation = Stage->GetSocketLocation(GateSocket) / 2;
RewardBoxLocations.Add(GateSocket, BoxLocation);
}
// Stage Stat
CurrentStageNum = 0;
}
void AABStageGimmick::OnConstruction(const FTransform& Transform)
{
Super::OnConstruction(Transform);
SetState(CurrentState);
}
void AABStageGimmick::OnStageTriggerBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
SetState(EStageState::FIGHT);
}
void AABStageGimmick::OnGateTriggerBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
check(OverlappedComponent->ComponentTags.Num() == 1);
FName ComponentTag = OverlappedComponent->ComponentTags[0];
FName SocketName = FName(*ComponentTag.ToString().Left(2));
check(Stage->DoesSocketExist(SocketName));
FVector NewLocation = Stage->GetSocketLocation(SocketName);
TArray<FOverlapResult> OverlapResults;
FCollisionQueryParams CollisionQueryParam(SCENE_QUERY_STAT(GateTrigger), false, this);
bool bResult = GetWorld()->OverlapMultiByObjectType(
OverlapResults,
NewLocation,
FQuat::Identity,
FCollisionObjectQueryParams::InitType::AllObjects,
FCollisionShape::MakeSphere(775.0f),
CollisionQueryParam
);
if (!bResult)
{
FTransform NewTransform(NewLocation);
AABStageGimmick* NewGimmick = GetWorld()->SpawnActorDeferred<AABStageGimmick>(AABStageGimmick::StaticClass(), NewTransform);
if (NewGimmick)
{
NewGimmick->SetStageNum(CurrentStageNum + 1);
NewGimmick->FinishSpawning(NewTransform);
}
}
}
void AABStageGimmick::OpenAllGates()
{
for (auto Gate : Gates)
{
(Gate.Value)->SetRelativeRotation(FRotator(0.0f, -90.0f, 0.0f));
}
}
void AABStageGimmick::CloseAllGates()
{
for (auto Gate : Gates)
{
(Gate.Value)->SetRelativeRotation(FRotator::ZeroRotator);
}
}
void AABStageGimmick::SetState(EStageState InNewState)
{
CurrentState = InNewState;
if (StateChangeActions.Contains(InNewState))
{
StateChangeActions[CurrentState].StageDelegate.ExecuteIfBound();
}
}
void AABStageGimmick::SetReady()
{
StageTrigger->SetCollisionProfileName(CPROFILE_ABTRIGGER);
for (auto GateTrigger : GateTriggers)
{
GateTrigger->SetCollisionProfileName(TEXT("NoCollision"));
}
OpenAllGates();
}
void AABStageGimmick::SetFight()
{
StageTrigger->SetCollisionProfileName(TEXT("NoCollision"));
for (auto GateTrigger : GateTriggers)
{
GateTrigger->SetCollisionProfileName(TEXT("NoCollision"));
}
CloseAllGates();
GetWorld()->GetTimerManager().SetTimer(OpponentTimerHandle, this, &AABStageGimmick::OnOpponentSpawn, OpponentSpawnTime, false);
}
void AABStageGimmick::SetChooseReward()
{
StageTrigger->SetCollisionProfileName(TEXT("NoCollision"));
for (auto GateTrigger : GateTriggers)
{
GateTrigger->SetCollisionProfileName(TEXT("NoCollision"));
}
CloseAllGates();
SpawnRewardBoxes();
}
void AABStageGimmick::SetChooseNext()
{
StageTrigger->SetCollisionProfileName(TEXT("NoCollision"));
for (auto GateTrigger : GateTriggers)
{
GateTrigger->SetCollisionProfileName(CPROFILE_ABTRIGGER);
}
OpenAllGates();
}
void AABStageGimmick::OnOpponentDestroyed(AActor* DestroyedActor)
{
IABGameInterface* ABGameMode = Cast<IABGameInterface>(GetWorld()->GetAuthGameMode());
if (ABGameMode)
{
ABGameMode->OnPlayerScoreChanged(CurrentStageNum);
if (ABGameMode->IsGameCleared())
{
return;
}
}
SetState(EStageState::REWARD);
}
void AABStageGimmick::OnOpponentSpawn()
{
const FTransform SpawnTransform(GetActorLocation() + FVector::UpVector * 88.0f);
AABCharacterNonPlayer* ABOpponentCharacter = GetWorld()->SpawnActorDeferred<AABCharacterNonPlayer>(OpponentClass, SpawnTransform);
if (ABOpponentCharacter)
{
ABOpponentCharacter->OnDestroyed.AddDynamic(this, &AABStageGimmick::OnOpponentDestroyed);
ABOpponentCharacter->SetLevel(CurrentStageNum);
ABOpponentCharacter->FinishSpawning(SpawnTransform);
}
}
void AABStageGimmick::OnRewardTriggerBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
for (const auto& RewardBox : RewardBoxes)
{
if (RewardBox.IsValid())
{
AABItemBox* ValidItemBox = RewardBox.Get();
AActor* OverlappedBox = OverlappedComponent->GetOwner();
if (OverlappedBox != ValidItemBox)
{
ValidItemBox->Destroy();
}
}
}
SetState(EStageState::NEXT);
}
void AABStageGimmick::SpawnRewardBoxes()
{
for (const auto& RewardBoxLocation : RewardBoxLocations)
{
FTransform SpawnTransform(GetActorLocation() + RewardBoxLocation.Value + FVector(0.0f, 0.0f, 30.0f));
AABItemBox* RewardBoxActor = GetWorld()->SpawnActorDeferred<AABItemBox>(RewardBoxClass, SpawnTransform);
if (RewardBoxActor)
{
RewardBoxActor->Tags.Add(RewardBoxLocation.Key);
RewardBoxActor->GetTrigger()->OnComponentBeginOverlap.AddDynamic(this, &AABStageGimmick::OnRewardTriggerBeginOverlap);
RewardBoxes.Add(RewardBoxActor);
}
}
for (const auto& RewardBox : RewardBoxes)
{
if (RewardBox.IsValid())
{
RewardBox.Get()->FinishSpawning(RewardBox.Get()->GetActorTransform());
}
}
}
ABStageGimmick.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Character/ABCharacterBase.h"
#include "InputActionValue.h"
#include "Interface/ABCharacterHUDInterface.h"
#include "ABCharacterPlayer.generated.h"
/**
*
*/
UCLASS()
class ARENABATTLE_API AABCharacterPlayer : public AABCharacterBase, public IABCharacterHUDInterface
{
GENERATED_BODY()
public:
AABCharacterPlayer();
protected:
virtual void BeginPlay() override;
virtual void SetDead() override;
public:
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
// Character Control Section
protected:
void ChangeCharacterControl();
void SetCharacterControl(ECharacterControlType NewCharacterControlType);
virtual void SetCharacterControlData(const class UABCharacterControlData* CharacterControlData) override;
// Camera Section
protected:
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, Meta = (AllowPrivateAccess = "true"))
TObjectPtr<class USpringArmComponent> CameraBoom;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, Meta = (AllowPrivateAccess = "true"))
TObjectPtr<class UCameraComponent> FollowCamera;
// Input Section
protected:
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, Meta = (AllowPrivateAccess = "true"))
TObjectPtr<class UInputAction> JumpAction;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, Meta = (AllowPrivateAccess = "true"))
TObjectPtr<class UInputAction> ChangeControlAction;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, Meta = (AllowPrivateAccess = "true"))
TObjectPtr<class UInputAction> ShoulderMoveAction;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, Meta = (AllowPrivateAccess = "true"))
TObjectPtr<class UInputAction> ShoulderLookAction;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, Meta = (AllowPrivateAccess = "true"))
TObjectPtr<class UInputAction> QuaterMoveAction;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, Meta = (AllowPrivateAccess = "true"))
TObjectPtr<class UInputAction> AttackAction;
void ShoulderMove(const FInputActionValue& Value);
void ShoulderLook(const FInputActionValue& Value);
void QuaterMove(const FInputActionValue& Value);
ECharacterControlType CurrentCharacterControlType;
void Attack();
// UI Section
protected:
virtual void SetupHUDWidget(class UABHUDWidget* InHUDWidget) override;
};
ABCharacterPlayer.h
// Fill out your copyright notice in the Description page of Project Settings.
#include "Character/ABCharacterPlayer.h"
#include "Camera/CameraComponent.h"
#include "GameFramework/SpringArmComponent.h"
#include "InputMappingContext.h"
#include "EnhancedInputComponent.h"
#include "EnhancedInputSubsystems.h"
#include "ABCharacterControlData.h"
#include "UI/ABHUDWidget.h"
#include "CharacterStat/ABCharacterStatComponent.h"
#include "Interface/ABGameInterface.h"
#include "GameFramework/GameModeBase.h"
AABCharacterPlayer::AABCharacterPlayer()
{
// Camera
CameraBoom = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraBoom"));
CameraBoom->SetupAttachment(RootComponent);
CameraBoom->TargetArmLength = 400.0f;
CameraBoom->bUsePawnControlRotation = true;
FollowCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("FollowCamera"));
FollowCamera->SetupAttachment(CameraBoom, USpringArmComponent::SocketName);
FollowCamera->bUsePawnControlRotation = false;
// Input
static ConstructorHelpers::FObjectFinder<UInputAction> InputActionJumpRef(TEXT("/Script/EnhancedInput.InputAction'/Game/ArenaBattle/Input/Actions/IA_Jump.IA_Jump'"));
if (nullptr != InputActionJumpRef.Object)
{
JumpAction = InputActionJumpRef.Object;
}
static ConstructorHelpers::FObjectFinder<UInputAction> InputChangeActionControlRef(TEXT("/Script/EnhancedInput.InputAction'/Game/ArenaBattle/Input/Actions/IA_ChangeControl.IA_ChangeControl'"));
if (nullptr != InputChangeActionControlRef.Object)
{
ChangeControlAction = InputChangeActionControlRef.Object;
}
static ConstructorHelpers::FObjectFinder<UInputAction> InputActionShoulderMoveRef(TEXT("/Script/EnhancedInput.InputAction'/Game/ArenaBattle/Input/Actions/IA_ShoulderMove.IA_ShoulderMove'"));
if (nullptr != InputActionShoulderMoveRef.Object)
{
ShoulderMoveAction = InputActionShoulderMoveRef.Object;
}
static ConstructorHelpers::FObjectFinder<UInputAction> InputActionShoulderLookRef(TEXT("/Script/EnhancedInput.InputAction'/Game/ArenaBattle/Input/Actions/IA_ShoulderLook.IA_ShoulderLook'"));
if (nullptr != InputActionShoulderLookRef.Object)
{
ShoulderLookAction = InputActionShoulderLookRef.Object;
}
static ConstructorHelpers::FObjectFinder<UInputAction> InputActionQuaterMoveRef(TEXT("/Script/EnhancedInput.InputAction'/Game/ArenaBattle/Input/Actions/IA_QuaterMove.IA_QuaterMove'"));
if (nullptr != InputActionQuaterMoveRef.Object)
{
QuaterMoveAction = InputActionQuaterMoveRef.Object;
}
static ConstructorHelpers::FObjectFinder<UInputAction> InputActionAttackRef(TEXT("/Script/EnhancedInput.InputAction'/Game/ArenaBattle/Input/Actions/IA_Attack.IA_Attack'"));
if (nullptr != InputActionAttackRef.Object)
{
AttackAction = InputActionAttackRef.Object;
}
CurrentCharacterControlType = ECharacterControlType::Quater;
}
void AABCharacterPlayer::BeginPlay()
{
Super::BeginPlay();
APlayerController* PlayerController = Cast<APlayerController>(GetController());
if (PlayerController)
{
EnableInput(PlayerController);
}
SetCharacterControl(CurrentCharacterControlType);
}
void AABCharacterPlayer::SetDead()
{
Super::SetDead();
APlayerController* PlayerController = Cast<APlayerController>(GetController());
if (PlayerController)
{
DisableInput(PlayerController);
IABGameInterface* ABGameMode = Cast<IABGameInterface>(GetWorld()->GetAuthGameMode());
if (ABGameMode)
{
ABGameMode->OnPlayerDead();
}
}
}
void AABCharacterPlayer::SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
UEnhancedInputComponent* EnhancedInputComponent = CastChecked<UEnhancedInputComponent>(PlayerInputComponent);
EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Triggered, this, &ACharacter::Jump);
EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Completed, this, &ACharacter::StopJumping);
EnhancedInputComponent->BindAction(ChangeControlAction, ETriggerEvent::Triggered, this, &AABCharacterPlayer::ChangeCharacterControl);
EnhancedInputComponent->BindAction(ShoulderMoveAction, ETriggerEvent::Triggered, this, &AABCharacterPlayer::ShoulderMove);
EnhancedInputComponent->BindAction(ShoulderLookAction, ETriggerEvent::Triggered, this, &AABCharacterPlayer::ShoulderLook);
EnhancedInputComponent->BindAction(QuaterMoveAction, ETriggerEvent::Triggered, this, &AABCharacterPlayer::QuaterMove);
EnhancedInputComponent->BindAction(AttackAction, ETriggerEvent::Triggered, this, &AABCharacterPlayer::Attack);
}
void AABCharacterPlayer::ChangeCharacterControl()
{
if (CurrentCharacterControlType == ECharacterControlType::Quater)
{
SetCharacterControl(ECharacterControlType::Shoulder);
}
else if (CurrentCharacterControlType == ECharacterControlType::Shoulder)
{
SetCharacterControl(ECharacterControlType::Quater);
}
}
void AABCharacterPlayer::SetCharacterControl(ECharacterControlType NewCharacterControlType)
{
UABCharacterControlData* NewCharacterControl = CharacterControlManager[NewCharacterControlType];
check(NewCharacterControl);
SetCharacterControlData(NewCharacterControl);
APlayerController* PlayerController = CastChecked<APlayerController>(GetController());
if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayerController->GetLocalPlayer()))
{
Subsystem->ClearAllMappings();
UInputMappingContext* NewMappingContext = NewCharacterControl->InputMappingContext;
if (NewMappingContext)
{
Subsystem->AddMappingContext(NewMappingContext, 0);
}
}
CurrentCharacterControlType = NewCharacterControlType;
}
void AABCharacterPlayer::SetCharacterControlData(const UABCharacterControlData* CharacterControlData)
{
Super::SetCharacterControlData(CharacterControlData);
CameraBoom->TargetArmLength = CharacterControlData->TargetArmLength;
CameraBoom->SetRelativeRotation(CharacterControlData->RelativeRotation);
CameraBoom->bUsePawnControlRotation = CharacterControlData->bUsePawnControlRotation;
CameraBoom->bInheritPitch = CharacterControlData->bInheritPitch;
CameraBoom->bInheritYaw = CharacterControlData->bInheritYaw;
CameraBoom->bInheritRoll = CharacterControlData->bInheritRoll;
CameraBoom->bDoCollisionTest = CharacterControlData->bDoCollisionTest;
}
void AABCharacterPlayer::ShoulderMove(const FInputActionValue& Value)
{
FVector2D MovementVector = Value.Get<FVector2D>();
const FRotator Rotation = Controller->GetControlRotation();
const FRotator YawRotation(0, Rotation.Yaw, 0);
const FVector ForwardDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
const FVector RightDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
AddMovementInput(ForwardDirection, MovementVector.X);
AddMovementInput(RightDirection, MovementVector.Y);
}
void AABCharacterPlayer::ShoulderLook(const FInputActionValue& Value)
{
FVector2D LookAxisVector = Value.Get<FVector2D>();
AddControllerYawInput(LookAxisVector.X);
AddControllerPitchInput(LookAxisVector.Y);
}
void AABCharacterPlayer::QuaterMove(const FInputActionValue& Value)
{
FVector2D MovementVector = Value.Get<FVector2D>();
float InputSizeSquared = MovementVector.SquaredLength();
float MovementVectorSize = 1.0f;
float MovementVectorSizeSquared = MovementVector.SquaredLength();
if (MovementVectorSizeSquared > 1.0f)
{
MovementVector.Normalize();
MovementVectorSizeSquared = 1.0f;
}
else
{
MovementVectorSize = FMath::Sqrt(MovementVectorSizeSquared);
}
FVector MoveDirection = FVector(MovementVector.X, MovementVector.Y, 0.0f);
GetController()->SetControlRotation(FRotationMatrix::MakeFromX(MoveDirection).Rotator());
AddMovementInput(MoveDirection, MovementVectorSize);
}
void AABCharacterPlayer::Attack()
{
ProcessComboCommand();
}
void AABCharacterPlayer::SetupHUDWidget(UABHUDWidget* InHUDWidget)
{
if (InHUDWidget)
{
InHUDWidget->UpdateStat(Stat->GetBaseStat(), Stat->GetModifierStat());
InHUDWidget->UpdateHpBar(Stat->GetCurrentHp());
Stat->OnStatChanged.AddUObject(InHUDWidget, &UABHUDWidget::UpdateStat);
Stat->OnHpChanged.AddUObject(InHUDWidget, &UABHUDWidget::UpdateHpBar);
}
}
ABCharacterPlayer.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/PlayerController.h"
#include "ABPlayerController.generated.h"
DECLARE_LOG_CATEGORY_EXTERN(LogABPlayerController, Log, All);
/**
*
*/
UCLASS()
class ARENABATTLE_API AABPlayerController : public APlayerController
{
GENERATED_BODY()
public:
AABPlayerController();
UFUNCTION(BlueprintImplementableEvent, Category = Game, Meta = (DisplayName = "OnScoreChangedCpp"))
void K2_OnScoreChanged(int32 NewScore);
UFUNCTION(BlueprintImplementableEvent, Category = Game, Meta = (DisplayName = "OnGameClearCpp"))
void K2_OnGameClear();
UFUNCTION(BlueprintImplementableEvent, Category = Game, Meta = (DisplayName = "OnGameOverCpp"))
void K2_OnGameOver();
UFUNCTION(BlueprintImplementableEvent, Category = Game, Meta = (DisplayName = "OnGameRetryCountCpp"))
void K2_OnGameRetryCount(int32 NewRetryCount);
void GameScoreChanged(int32 NewScore);
void GameClear();
void GameOver();
protected:
virtual void BeginPlay() override;
// HUD Section
protected:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = HUD)
TSubclassOf<class UABHUDWidget> ABHUDWidgetClass;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = HUD)
TObjectPtr<class UABHUDWidget> ABHUDWidget;
// Save Game Section
protected:
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = SaveGame)
TObjectPtr<class UABSaveGame> SaveGameInstance;
};
ABPlayerController.h
// Fill out your copyright notice in the Description page of Project Settings.
#include "Player/ABPlayerController.h"
#include "UI/ABHUDWidget.h"
#include "Kismet/GameplayStatics.h"
#include "ABSaveGame.h"
DEFINE_LOG_CATEGORY(LogABPlayerController);
AABPlayerController::AABPlayerController()
{
static ConstructorHelpers::FClassFinder<UABHUDWidget> ABHUDWidgetRef(TEXT("/Game/ArenaBattle/UI/WBP_ABHUD.WBP_ABHUD_C"));
if (ABHUDWidgetRef.Class)
{
ABHUDWidgetClass = ABHUDWidgetRef.Class;
}
}
void AABPlayerController::GameScoreChanged(int32 NewScore)
{
K2_OnScoreChanged(NewScore);
}
void AABPlayerController::GameClear()
{
K2_OnGameClear();
}
void AABPlayerController::GameOver()
{
K2_OnGameOver();
if (!UGameplayStatics::SaveGameToSlot(SaveGameInstance, TEXT("Player0"), 0))
{
UE_LOG(LogABPlayerController, Error, TEXT("Save Game Error!"));
}
K2_OnGameRetryCount(SaveGameInstance->RetryCount);
}
void AABPlayerController::BeginPlay()
{
Super::BeginPlay();
FInputModeGameOnly GameOnlyInputMode;
SetInputMode(GameOnlyInputMode);
SaveGameInstance = Cast<UABSaveGame>(UGameplayStatics::LoadGameFromSlot(TEXT("Player0"), 0));
if (!SaveGameInstance)
{
SaveGameInstance = NewObject<UABSaveGame>();
SaveGameInstance->RetryCount = 0;
}
SaveGameInstance->RetryCount++;
K2_OnGameRetryCount(SaveGameInstance->RetryCount);
}
ABPlayerController.cpp
게임을 저장할 때 편리하게 사용할 수 있는 SaveGame이라는 언리얼 오브젝트를 상속받아 ABSaveGame 클래스 생성
SaveGames라는 폴더에 지정한 이름으로 된 세이브 파일이 저장됨.
이 파일을 불러들여 사용함. 파일이 지워지면 저장된 데이터가 사라지고 0부터 다시 시작.
실행 파일로 빌드할 때 맵에 관련된 에셋만 뽑아서 저장이 되기 때문에 Item 관련 정보는 빠짐.
따로 추가.
설정이 완료되면 Shipping으로 설정을 변경하고 패키지 생성.
패키지를 저장한 폴더에서 exe 파일을 실행하면 게임이 실행됨.
이득우의 언리얼 프로그래밍 Part2 - 언리얼 게임 프레임웍의 이해 강의 | 이득우 - 인프런
이득우 | 대기업 현업자들이 수강하는 언리얼 C++ 프로그래밍 전문 과정입니다. 언리얼 C++ 프로그래밍을 사용해 핵&슬래시 로그라이크 게임 예제를 처음부터 끝까지 체계적으로 제작하는 방법을
www.inflearn.com
'게임 개발 > 언리얼 5' 카테고리의 다른 글
언리얼5 - 리플렉션을 활용하는 언리얼 에디터 UI와 UI의 확장 (2) | 2024.12.19 |
---|---|
언리얼 5 - 게임 플로우 다듬기 (0) | 2024.12.19 |
언리얼 5 - 헤드업디스플레이의 구현 (0) | 2024.12.18 |
언리얼 5 - 행동트리 모델의 구현 (0) | 2024.12.16 |
언리얼5 - 행동트리 모델의 이해 (1) | 2024.12.15 |