ShootGameMode.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
#include "CShootGameMode.generated.h"
// enum를 사용하는데 블루프린트로도 사용할때,
UENUM(BlueprintType)
enum class EShootGameState : uint8
{
Title,
Ready,
Start,
Playing,
Pause,
Gameover
};
UCLASS()
class SHOOTING_API ACShootGameMode : public AGameModeBase
{
GENERATED_BODY()
public:
ACShootGameMode();
virtual void BeginPlay() override;
virtual void Tick(float DeltaTime) override;
public:
// FSM상태를 만들어서 게임이 흘러갈 수 있게
UPROPERTY(VisibleAnywhere, Category = "Game")
EShootGameState myState;
void Title();
void Ready();
void Start();
void Playing();
void Pause();
void Gameover();
// 필요한 Main 위젯
UPROPERTY(EditDefaultsOnly, Category = "Game")
TSubclassOf<class UUserWidget> MainUIFactory;
UPROPERTY()
class UUserWidget* mainUI;
};
ShootGameMode.cpp
#include "CShootGameMode.h"
#include "Blueprint\UserWidget.h"
ACShootGameMode::ACShootGameMode()
{
PrimaryActorTick.bCanEverTick = true;
}
void ACShootGameMode::BeginPlay()
{
Super::BeginPlay();
mainUI = CreateWidget<UUserWidget>(GetWorld(), MainUIFactory);
if (mainUI)
{
mainUI->AddToViewport();
}
}
void ACShootGameMode::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
switch (myState)
{
case EShootGameState::Title:
Title();
break;
case EShootGameState::Ready:
Ready();
break;
case EShootGameState::Start:
Start();
break;
case EShootGameState::Playing:
Playing();
break;
case EShootGameState::Pause:
Pause();
break;
case EShootGameState::Gameover:
Gameover();
break;
default:
break;
}
}
void ACShootGameMode::Title()
{
}
void ACShootGameMode::Ready()
{
}
void ACShootGameMode::Start()
{
}
void ACShootGameMode::Playing()
{
}
void ACShootGameMode::Pause()
{
}
void ACShootGameMode::Gameover()
{
}
mainUI = CreateWidget<UUserWidget>(GetWorld(), MainUIFactory);
if (mainUI)
{
mainUI->AddToViewport();
}
CShootGameMode 클래스를 상속받은 'BP_GameMode'이름의 Blueprint를 하나 만든다
'BP_GameMode' 블루프린트로 해당 만든 MainUI윗젯를 BeginPlay()로 시작하면 UI를 띄워보자
UI를 Setting
UMG
를 추가 한다MainUI도 C++ 클래스로 제어하기 위해 C++ 클래스 UserWidget 클래스로 CMainUI 라는 이름의 클래스를 하나만든다
기존의 있던 WBP_MainUI를 만든 클래스에 접목시킨뒤 바인딩 한다
CMainUI.h
#pragma once
#include "CoreMinimal.h"
#include "Blueprint/UserWidget.h"
#include "CMainUI.generated.h"
/**
*
*/
UCLASS()
class SHOOTING_API UCMainUI : public UUserWidget
{
GENERATED_BODY()
public:
// 라이프 사이클 버튼 이벤트 함수를 호출할
virtual void NativeConstruct() override;
// GameStartbutton Binding
UPROPERTY(meta = (BindWidget))
class UButton* btn_GameStart;
// TitleCanvas Binding
UPROPERTY(meta = (BindWidget))
class UCanvasPanel* TitleCanvas;
// Ready TEXT Binding
UPROPERTY(meta = (BindWidget))
class UTextBlock* ReadyText;
UFUNCTION()
void OnGameStartBtnClicked();
};
CMainUI.cpp
#include "CMainUI.h"
#include "Components\Button.h"
#include "Components\CanvasPanel.h"
#include "Components\TextBlock.h"
#include "Kismet\GameplayStatics.h"
#include "CShootGameMode.h"
void UCMainUI::NativeConstruct()
{
Super::NativeConstruct();
btn_GameStart->OnClicked.AddDynamic(this, &UCMainUI::OnGameStartBtnClicked);
}
void UCMainUI::OnGameStartBtnClicked()
{
// 게임 상태를 Ready로 전환하고 싶다
// 1. 게임 모드가 있어야 한다
// 월드에있는 게임모드를 ACShootGameMode로 캐스팅 하겠다
auto gm = Cast<ACShootGameMode>(UGameplayStatics::GetGameMode(GetWorld()));
// 2. 게임모드의 상태를 바꿔준다
if (gm)
{
gm->myState = EShootGameState::Ready;
ReadyText->SetVisibility(ESlateVisibility::Visible);
}
// button 클릭시 숨김
TitleCanvas->SetVisibility(ESlateVisibility::Hidden);
// 입력모드를 Game으로 설정해주기
GetWorld()->GetFirstPlayerController()->SetInputMode(FInputModeGameOnly());
// 마우스 커서 안보이게 하기
GetWorld()->GetFirstPlayerController()->SetShowMouseCursor(false);
}
입력모드
// 입력모드를 Game으로 설정해주기
GetWorld()->GetFirstPlayerController()->SetInputMode(FInputModeGameOnly());
// 마우스 커서 안보이게 하기
GetWorld()->GetFirstPlayerController()->SetShowMouseCursor(false);
CShootGameMode.h
public:
UPROPERTY(EditDefaultsOnly, Category = "Game")
float ReadyDelayTime = 2;
UPROPERTY(EditDefaultsOnly, Category = "Game")
float StartDelayTime = 2;
float currentTime = 0;
CShootGameMode.cpp
#include "CMainUI.h"
void ACShootGameMode::Ready()
{
currentTime += GetWorld()->DeltaTimeSeconds;
if (currentTime > ReadyDelayTime)
{
currentTime = 0;
myState = EShootGameState::Start;
}
}
// 일정시간 지나면 상태를 Playing로 바꾼다.
void ACShootGameMode::Start()
{
currentTime += GetWorld()->DeltaTimeSeconds;
if (currentTime > StartDelayTime)
{
currentTime = 0;
myState = EShootGameState::Playing;
}
}
CPlayer.cpp
// 추가
#include "CShootGameMode.h"
void ACPlayer::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
// 게임의 상태가 Ready, Start, Playing 일때만 이동 가능
auto gm = Cast<ACShootGameMode>(GetWorld()->GetAuthGameMode());
if (gm && !(gm->myState == EShootGameState::Ready ||
gm->myState == EShootGameState::Start ||
gm->myState == EShootGameState::Playing))
{
return;
}
// 등속운동, 등가속운동
// p = p0 + vt
FVector dir(0, h, v);
FVector P0 = GetActorLocation();
FVector vt = dir * speed * DeltaTime;
FVector P = P0 + vt;
SetActorLocation(P);
}
void ACPlayer::Fire()
{
// 게임의 상태가 Ready, Start, Playing 일때만 이동 가능
auto gm = Cast<ACShootGameMode>(GetWorld()->GetAuthGameMode());
if (gm && !(gm->myState == EShootGameState::Ready ||
gm->myState == EShootGameState::Start ||
gm->myState == EShootGameState::Playing))
{
return;
}
PlayFireSound();
GetWorld()->SpawnActor<ACBullet>(
bulletFactory, GetActorLocation(), FRotator::ZeroRotator);
}
CEnemyGOD.cpp
// 추가
#include "CShootGameMode.h"
void ACEnemyGOD::CreateEnemy()
{
// 게임의 상태가 Ready, Start, Playing 일때만 이동 가능
auto gm = Cast<ACShootGameMode>(GetWorld()->GetAuthGameMode());
if (gm && !(gm->myState == EShootGameState::Playing))
{
return;
}
if (spawnPoints.Num() < -1)
{
return;
}
// spawnpoints중에 랜덤한 위치를 뽑아서 스폰하겠다
// 1. 랜덤한 스폰 인덱스 추출 하기
int32 spawnIndex = FMath::RandRange(0, spawnPoints.Num() - 1);
// 2. 스폰할 위치
FVector loc = spawnPoints[spawnIndex]->GetActorLocation();
// 3. 생성하자
GetWorld()->SpawnActor<ACEnemy>(enemyFatory, loc, FRotator::ZeroRotator);
}