Professional Documents
Culture Documents
Shooter TopDown
Shooter TopDown
Para controlar o comportamento da nosa nave espacial crearemos unha clase de C++
de tipo Pawn á que chamaremos SpaceShipController. Ao final do proceso abriranse
dous arquivos: .h (header) é a interfaz e .cpp a implementación dos métodos que se
chaman desde a interfaz.
Arrastramos a clase desde o Content Browser ao Viewport e asociámoslle unha
compoñente cono para poder velo. Axustamos a posición, rotación (Y=-90) e cor do
elemento.
No input das preferencias do proxecto definimos dúas Axis Mapping: MoveY (coas
frechas esquerda e dereita) e MoveX (coas teclas arriba e abaixo). Os valores de
esquerda e abaixo deben ser -1.0.
UPROPERTY (EditAnywhere)
UShapeComponent* CollisionBox;
UPROPERTY (EditAnywhere)
float Speed;
FVector CurrentVelocity;
Velocity vai ter máis información que a velocidade, sentido, orientación etc.
No arquivo de implementación, engadimos:
1
#include "Components/BoxComponent.h"
#include "Components/InputComponent.h"
#include "Engine/World.h"
CollisionBox = CreateDefaultSubobject<UBoxComponent>(TEXT("Root"));
AutoPossessPlayer = EAutoReceiveInput::Player0;
Speed = 10.0f;
if (!CurrentVelocity.IsZero()){
FVector NewLocation = GetActorLocation() + Speed *
CurrentVelocity * DeltaTime;
SetActorLocation(NewLocation);
}
Agora temos que programar eses dous métodos que acabamos de enlazar coas
funcións de entrada de teclado:
Agora vamos a definir as balas e para iso crearemos unha clase de C++ de tipo Actor á
que chamaremos BulletController. Arrastramos a clase desde o Content Browser ao
Viewport e asociámoslle unha compoñente esfera para poder vela. Axustamos a
posición, tamaño e cor do elemento.
Vamos facer que a bala se mova cara arriba un pouco en cada Tick, para iso, no arquivo
de interfaz do controlador (.h) engadimos:
2
UPROPERTY(EditAnywhere)
UShapeComponent* RootBox;
UPROPERTY(EditAnywhere)
float Speed;
E despois no construtor (.cpp) engadimos dentro do ABulletController:
Speed = 300.0f;
PrimaryActorTick.bCanEverTick = true;
RootBox = CreateDefaultSubobject<UBoxComponent>(TEXT("Root"));
Así asignamos á variable RootBox a caixa por defecto que englobará a nosa bala, e
que nos servirá despois para saber se choca con algo.
Vamos a convertir a bala nun Blueprint, porque o que queremos é que apareza ao
pulsar a tecla espaciadora. Asegurámonos de que está colocada no 0,0,0 e eliminamos
a bala do editor.
Na sección Input das Project Settings incluímos unha nova Action Mapping de nome
Shoot asociada á barra espaciadora.
Agora programaremos a acción no SpaceShipController. No arquivo de cabeceira
incluímos:
void OnShootPress();
void ASpaceShipController::OnShootPress(){
UWorld * World = GetWorld(); //referencia á escena
3
if (World) {
FVector Location = GetActorLocation();
World->SpawnActor<ABulletController>(BulletBlueprint,
Location, FRotator::ZeroRotator);
}
}
Estamos facendo unha referencia ao controlador das balas, polo tanto teremos que
incluír ao comezo do arquivo:
#include "BulletController.h"
Agora vamos a crear o inimigo, e para iso creamos unha nova clase de C++ de tipo
Actor chamada Enemy. O inimigo aparecerá na parte superior da pantalla e irá
baixando, definimos a súa velocidade e crearemos unha caixa de colisión. No arquivo
de cabeceira engadimos:
UPROPERTY(EditAnywhere)
UShapeComponent * RootBox;
UPROPERTY(EditAnywhere)
float Speed;
Speed = -150.0f;
PrimaryActorTick.bCanEverTick = true;
RootBox = CreateDefaultSubobject<UBoxComponent>(TEXT("Root"));
E dentro do Tick:
Enemy.h:
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Enemy.generated.h"
4
UCLASS()
class SHOOTERC_API AEnemy : public AActor
{
GENERATED_BODY()
protected:
virtual void BeginPlay() override;
public:
AEnemy();
virtual void Tick(float DeltaTime) override;
UPROPERTY(EditAnywhere)
UShapeComponent* RootBox;
UPROPERTY(EditAnywhere)
float Speed;
};
Enemy.cpp
#include "Enemy.h"
#include "Components/BoxComponent.h"
AEnemy::AEnemy()
{
PrimaryActorTick.bCanEverTick = true;
Speed = -150.0f;
RootBox =
CreateDefaultSubobject<UBoxComponent>(TEXT("Root"));
}
void AEnemy::BeginPlay()
{
Super::BeginPlay();
5
Agora vamos a crear un blueprint a partir do inimigo para xeralos de forma aleatoria.
Despois, creamos unha nova clase de tipo GameMode para controlar as variables do
xogo, chamarémoslle SpaceShooterGameMode e un Blueprint de esta clase que
acabamos de crear.
public:
UPROPERTY (EditAnywhere, Category="Spawing")
TSubclassOf<class AEnemy> Enemy_Blueprint;
protected:
int Score = 0; //Puntuación. Variable que non é pública
De novo estamos facendo referencia ao controlador do inimigo, polo que teremos que
incluílo na sección de includes.
No construtor, o primeiro que temos que facer é instanciar os métodos que vamos
empregar cunha referencia ás superclases ás que pertencen:
void ASpaceShooterGameMode::BeginPlay()
{
Super::BeginPlay();
}
Teremos que referenciar estes métodos no arquivo de cabeceira, antes das variables
colocamos o seguinte cógido, indicando que non son nosos, que vamos a sobreescribir
os métodos do mesmo nome da clase nai:
if (EnemyTimer<=0.0f) {
//Debo xerar un novo inimigo
//Debo decidir canto tempo debe pasar ata o próximo inimigo
}
6
float MIN_TIME_SPAWN = 0.5f;
float MAX_TIME_SPAWN = 2.5f;
float TIME_TO_MAX_DIFF = 60.0f;
if (EnemyTimer<=0.0f) {
float DiffPercentage = FMath::Min(GameTimer/TIME_TO_MAX_DIFF,
1.0f);
EnemyTimer = MAX_TIME_SPAWN - (MAX_TIME_SPAWN - MIN_TIME_SPAWN)*
DiffPercentage;
UWorld* World = GetWorld();
if (World){
FVector NewLocation = FVector(600.0f, FMath::RandRange(-
700.0f, 700.0f), 70.0f);
World->SpawnActor<AEnemy>(Enemy_Blueprint, NewLocation,
FRotator::ZeroRotator);
}
}
GameModeBase.h:
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
#include "shooterCGameModeBase.generated.h"
UCLASS()
class SHOOTERC_API AshooterCGameModeBase : public AGameModeBase
{
GENERATED_BODY()
public:
AshooterCGameModeBase();
UPROPERTY(EditAnywhere, Category = "Spawing")
TSubclassOf<class Aenemy> Enemy_Blueprint;
UPROPERTY(EditAnywhere, Category="Spawn")
float MIN_TIME_SPAWN;
UPROPERTY(EditAnywhere, Category = "Spawn")
float MAX_TIME_SPAWN;
UPROPERTY(EditAnywhere, Category = "Spawn")
float TIME_TO_MAX_DIFF;
7
protected:
virtual void BeginPlay() override;
int Score = 0; //Puntuación. Variable que non é pública
};
GameModeBase.cpp:
#include "shooterCGameModeBase.h"
#include "Enemy.h"
AshooterCGameModeBase::AshooterCGameModeBase()
{
PrimaryActorTick.bCanEverTick = true;
MIN_TIME_SPAWN = 0.5f;
MAX_TIME_SPAWN = 2.5f;
TIME_TO_MAX_DIFF = 60.0f;
void AshooterCGameModeBase::BeginPlay()
{
Super::BeginPlay();
}
8
Vamos a programar agora a colisión entre o inimigo e a nave, que asociaremos á nave,
para non repetir o código (podemos ter varios inimigos). O primeiro que facemos é
definir no arquivo de cabaceira unha variable booleana que nos diga se estamos
mortos ou vivos e unha función de colisións que desenvolveremos no arquivo cpp.
bool Died;
UFUNCTION()
void OnOverlap(UPrimitiveComponent* OverlapedComponent,
AActor* OtherActor,
UPrimitiveComponent* OtherComponent,
int32 OtherBodyIndex,
bool bFromSweep,
const FHitResult& SweepResult);
void ASpaceShipController::OnOverlap(UPrimitiveComponent*
OverlapedComponent,AActor* OtherActor,UPrimitiveComponent*
OtherComponent, int32 OtherBodyIndex, bool bFromSweep, const
FHitResult& SweepResult){
if (OtherActor->IsA(AEnemy::StaticClass())){
Died = true;
this->SetActorHiddenInGame(true);
UGameplayStatics::SetGamePaused(GetWorld(), true);
}
Estamos facendo referencia ao controlador do inimigo, polo que teremos que incluílo
na sección de includes:
#include "Enemy.h"
Para que todo isto funcione temos que facer que a caixa de colisión estea atenta a
posibles choques, para iso dentro da función do controlador incluímos:
CollisionBox->SetGenerateOverlapEvents(true);
CollisionBox->OnComponentBeginOverlap.AddDynamic(this,
&ASpaceShipController::OnOverlap);
SpaceShipController.h:
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Pawn.h"
#include "SpaceShipController.generated.h"
UCLASS()
class SHOOTERC_API ASpaceShipController : public APawn
{
GENERATED_BODY()
protected:
virtual void BeginPlay() override;
9
public:
ASpaceShipController();
virtual void Tick(float DeltaTime) override;
virtual void SetupPlayerInputComponent(class UInputComponent*
PlayerInputComponent) override;
UPROPERTY(EditAnywhere)
UShapeComponent* CollisionBox;
UPROPERTY(EditAnywhere)
float Speed;
FVector CurrentVelocity;
void OnShootPress();
bool Died;
UFUNCTION()
void OnOverlap(UPrimitiveComponent* OverlapedComponent,
AActor* OtherActor, UPrimitiveComponent* OtherComponent, int32
OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
};
SpaceShipController.cpp:
#include "SpaceShipController.h"
#include "Components/BoxComponent.h"
#include "Components/InputComponent.h"
#include "Engine/World.h"
#include "BulletController.h"
#include "Enemy.h"
#include "shooterCGameModeBase.h"
#include "Kismet/GameplayStatics.h"
ASpaceShipController::ASpaceShipController()
{
PrimaryActorTick.bCanEverTick = true;
Speed = 10.0f;
CollisionBox =
CreateDefaultSubobject<UBoxComponent>(TEXT("Root"));
AutoPossessPlayer = EAutoReceiveInput::Player0;
CollisionBox->SetGenerateOverlapEvents(true);
CollisionBox->OnComponentBeginOverlap.AddDynamic(this,
&ASpaceShipController::OnOverlap);
}
10
void ASpaceShipController::BeginPlay()
{
Super::BeginPlay();
if (!CurrentVelocity.IsZero())
{
FVector NewLocation = GetActorLocation() + Speed *
CurrentVelocity * DeltaTime;
SetActorLocation(NewLocation);
}
}
void ASpaceShipController::SetupPlayerInputComponent(UInputComponent*
PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
PlayerInputComponent->BindAxis("MoveX", this,
&ASpaceShipController::MoveXAxis);
PlayerInputComponent->BindAxis("MoveY", this,
&ASpaceShipController::MoveYAxis);
void ASpaceShipController::OnShootPress()
{
UWorld* World = GetWorld();
if (World)
{
FVector Location = GetActorLocation();
World->SpawnActor<ABulletController>(Bullet, Location,
FRotator::ZeroRotator);
}
}
11
void ASpaceShipController::OnOverlap(UPrimitiveComponent*
OverlapedComponent, AActor* OtherActor, UPrimitiveComponent*
OtherComponent, int32 OtherBodyIndex, bool bFromSweep, const
FHitResult& SweepResult)
{
if (OtherActor->IsA(AEnemy::StaticClass()))
{
Died = true;
this->SetActorHiddenInGame(true);
UGameplayStatics::SetGamePaused(GetWorld(), true);
}
RootBox->SetGenerateOverlapEvents(true);
RootBox->OnComponentBeginOverlap.AddDynamic(this,
&ABulletController::OnOverlap);
12
Para resetear a partida o primeiro que facemos é definir unha nova Action Mapping á
que chamaremos “Reset” e que estará asociada coa tecla R e despois definiremos a
función no controlador da nave.
No arquivo H do SpaceShipController engadimos:
void OnResetPress();
bExecuteWhenPaused=true indica que a función pode ser chamada aínda que o xogo
estea en pausa.
void ASpaceShipController::OnResetPress()
{
if (Died)
{
UGameplayStatics::OpenLevel(this, FName(*GetWorld()-
>GetName()), false);
//Abrimos de novo o nivel (collemos o nome do mundo) e
reseteamos todas as variables do nivel co parámetro "false"
}
}
Vamos incluír unha interface que nos mostre a puntuación. O primeiro que temos que
facer é modificar o arquivo con extensión .Build.cs engadindo o módulo UMG e
quitando o comentario da liña inferior.
Crearemos unha nova clase C++ de tipo UserWidget á que chamamos GameWidget, e
no seu arquivo de cabeceira engadimos os seguintes includes:
#include "Runtime/UMG/Public/UMG.h"
#include "Runtime/UMG/Public/UMGStyle.h"
#include "Runtime/UMG/Public/IUMGModule.h"
#include "Runtime/UMG/Public/Slate/SObjectWidget.h"
#include "Runtime/UMG/Public/Blueprint/UserWidget.h"
13
Definimos 3 funcións: unha para cargar a interface, outra para a puntuación e a última
para a finalización do xogo e unha propiedade de tipo TextBlock onde mostraremos
esta información.
void Load();
void SetScore(int score);
void OnGameOver(int score);
UPROPERTY()
UTextBlock* ScoreTextBlock;
CPP:
#include "GameWidget.h"
void UGameWidget::Load()
{
const FName TextBlockName =
FName(TEXT("GameTextBlock"));
if (ScoreTextBlock == nullptr)
{
ScoreTextBlock = (UTextBlock*)(WidgetTree-
>FindWidget (TextBlockName));
}
}
14
Para que podamos velo temos que colocalo no Viewport, e isto farémolo desde o
GameMode. Na parte pública do arquivo de cabeceiras engadimos:
UFUNCTION(BlueprintCallable)
void ChangeMenuWidget(TSubclassOf<UUserWidget>
NewWidgetClass);
void IncrementScore();
void GameOver();
E na parte protexida, de forma que non se poda modificar desde outras clases,
engadimos unha subclase do Widget do usuario, que será o widget inicial, e un
punteiro ao widget actual, o que está activo na pantalla:
UPROPERTY(EditAnywhere, BlueprintReadOnly)
TSubclassOf<UUserWidget> StartingWidgetClass;
UPROPERTY()
UUserWidget* CurrentWidget;
void
ASpaceShooterGameMode::ChangeMenuWidget(TSubclassOf<UUserWidge
t> NewWidgetClass)
{
if (CurrentWidget != nullptr)
{
CurrentWidget->RemoveFromViewport();
CurrentWidget = nullptr;
}
if (NewWidgetClass != nullptr)
{
CurrentWidget = CreateWidget<UUserWidget> (GetWorld(),
NewWidgetClass);
if(CurrentWidget != nullptr)
{
CurrentWidget->AddToViewport();
}
}
}
15
Podemos compilar e, despois de asociar o BP do noso Widget no campo “Starting
Widget Class” do BP do Game Mode, veremos que aparece o texto na pantalla.
O último paso será actualizar os datos da interface cos puntos do usuario, para iso
engadimos 2 novos métodos no arquivo de cabeceiras da clase do noso GameMode
(onde xa temos definida a variable Score para gardar a puntuación):
void IncrementScore();
void GameOver();
#include "GameWidget.h"
void ASpaceShooterGameMode::IncrementScore()
{
Score += 10;
((UGameWidget*)CurrentWidget)->SetScore(Score);
}
void ASpaceShooterGameMode::GameOver()
{
((UGameWidget*)CurrentWidget)->OnGameOver(Score);
}
#include "SpaceShooterGameMode.h"
((ASpaceShooterGameMode*)GetWorld()->GetAuthGameMode())-
>IncrementScore();
((ASpaceShooterGameMode*)GetWorld()->GetAuthGameMode())-
>GameOver();
16
// SpaceShipController.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Pawn.h"
#include "SpaceShipController.generated.h"
UCLASS()
class TOPDOWN_API ASpaceShipController : public APawn
{
GENERATED_BODY()
public:
ASpaceShipController();
UPROPERTY (EditAnywhere)
UShapeComponent* CollisionBox;
UPROPERTY (EditAnywhere)
float Speed;
FVector CurrentVelocity;
bool Died;
UFUNCTION()
void OnOverlap(UPrimitiveComponent*
OverlapedComponent,
AActor* OtherActor,
UPrimitiveComponent* OtherComponent,
int32 OtherBodyIndex,
bool bFromSweep,
const FHitResult& SweepResult);
};
17
// SpaceShipController.cpp
#include "SpaceShipController.h"
#include "Components/BoxComponent.h"
#include "Components/InputComponent.h"
#include "Engine/World.h"
#include "Kismet/GameplayStatics.h"
#include "BulletController.h"
#include "Enemy.h"
#include "SpaceShooterGameMode.h"
ASpaceShipController::ASpaceShipController()
{
PrimaryActorTick.bCanEverTick = true;
Speed = 10.0f;
Died= false;
CollisionBox =
CreateDefaultSubobject<UBoxComponent>(TEXT("Root"));
AutoPossessPlayer = EAutoReceiveInput::Player0;
CollisionBox->SetGenerateOverlapEvents(true);
CollisionBox->OnComponentBeginOverlap.AddDynamic(this,
&ASpaceShipController::OnOverlap);
if (!CurrentVelocity.IsZero()){
FVector NewLocation = GetActorLocation() + Speed *
CurrentVelocity * DeltaTime;
SetActorLocation(NewLocation);
}
void
ASpaceShipController::SetupPlayerInputComponent(UInputComp
onent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent)
;
18
PlayerInputComponent->BindAction("Shoot", IE_Pressed,
this, &ASpaceShipController::OnShootPress);
PlayerInputComponent->BindAction("Reset", IE_Pressed,
this,
&ASpaceShipController::OnResetPress).bExecuteWhenPaused=tr
ue;
void ASpaceShipController::OnShootPress(){
UWorld * World = GetWorld(); //referencia á escena
if (World) {
FVector Location = GetActorLocation();
World-
>SpawnActor<ABulletController>(BulletBlueprint, Location,
FRotator::ZeroRotator);
}
}
void ASpaceShipController::OnOverlap(UPrimitiveComponent*
OverlapedComponent,AActor* OtherActor,UPrimitiveComponent*
OtherComponent, int32 OtherBodyIndex, bool bFromSweep,
const FHitResult& SweepResult){
if (OtherActor->IsA(AEnemy::StaticClass()))
{
Died = true;
this->SetActorHiddenInGame(true);
((ASpaceShooterGameMode*)GetWorld()-
>GetAuthGameMode())->GameOver();
UGameplayStatics::SetGamePaused(GetWorld(), true);
19
void ASpaceShipController::OnResetPress()
{
if (Died)
{
UGameplayStatics::OpenLevel(this,
FName(*GetWorld()->GetName()), false);
//Abrimos de novo o nivel (collemos o nome do
mundo) e reseteamos todas as variables do nivel co
parámetro "false"
}
}
// Enemy.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Enemy.generated.h"
UCLASS()
class TOPDOWN_API AEnemy : public AActor
{
GENERATED_BODY()
protected:
virtual void BeginPlay() override;
public:
AEnemy();
virtual void Tick(float DeltaTime) override;
UPROPERTY(EditAnywhere)
UShapeComponent * RootBox;
UPROPERTY(EditAnywhere)
float Speed = -150.0f;
};
20
// Enemy.cpp
#include "Enemy.h"
#include "Components/BoxComponent.h"
AEnemy::AEnemy()
{
PrimaryActorTick.bCanEverTick = true;
RootBox =
CreateDefaultSubobject<UBoxComponent>(TEXT("Root"));
}
void AEnemy::BeginPlay()
{
Super::BeginPlay();
}
// BulletControler.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "BulletController.generated.h"
UCLASS()
class TOPDOWN_API ABulletController : public AActor
{
GENERATED_BODY()
public:
ABulletController();
virtual void Tick(float DeltaTime) override;
UPROPERTY(EditAnywhere)
UShapeComponent * RootBox;
UPROPERTY(EditAnywhere)
21
float Speed;
UFUNCTION()
void OnOverlap(UPrimitiveComponent*
OverlapedComponent,
AActor* OtherActor,
UPrimitiveComponent* OtherComponent,
int32 OtherBodyIndex,
bool bFromSweep,
const FHitResult& SweepResult);
};
// BulletControler.cpp
#include "BulletController.h"
#include "Components/BoxComponent.h"
#include "Kismet/GameplayStatics.h"
#include "Engine/World.h"
#include "Enemy.h"
#include "SpaceShooterGameMode.h"
ABulletController::ABulletController()
{
PrimaryActorTick.bCanEverTick = true;
Speed = 400.0f;
RootBox =
CreateDefaultSubobject<UBoxComponent>(TEXT("Root"));
RootBox->SetGenerateOverlapEvents(true);
RootBox->OnComponentBeginOverlap.AddDynamic(this,
&ABulletController::OnOverlap);
}
22
void ABulletController::OnOverlap(UPrimitiveComponent*
OverlapedComponent, AActor* OtherActor,
UPrimitiveComponent* OtherComponent, int32 OtherBodyIndex,
bool bFromSweep, const FHitResult& SweepResult){
if (OtherActor->IsA(AEnemy::StaticClass())){
this->Destroy();
OtherActor->Destroy();
((ASpaceShooterGameMode*)GetWorld()-
>GetAuthGameMode())->IncrementScore();
}
}
// SpaceShooterGameMode.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
#include "Enemy.h"
#include "SpaceShooterGameMode.generated.h"
UCLASS()
class TOPDOWN_API ASpaceShooterGameMode : public
AGameModeBase
{
GENERATED_BODY()
protected:
virtual void BeginPlay() override;
int Score; //Puntuación. Variable que non é pública
UPROPERTY(EditAnywhere, BlueprintReadOnly)
TSubclassOf<UUserWidget> StartingWidgetClass;
UPROPERTY()
UUserWidget* CurrentWidget;
public:
23
ASpaceShooterGameMode();
virtual void Tick(float DeltaTime) override;
UFUNCTION(BlueprintCallable)
void ChangeMenuWidget(TSubclassOf<UUserWidget>
NewWidgetClass);
void IncrementScore();
void GameOver();
};
// SpaceShooterGameMode.cpp
#include "SpaceShooterGameMode.h"
#include "GameWidget.h"
ASpaceShooterGameMode::ASpaceShooterGameMode()
{
PrimaryActorTick.bCanEverTick = true;
Score = 0;
MIN_TIME_SPAWN = 0.5f;
MAX_TIME_SPAWN = 2.5f;
TIME_TO_MAX_DIFF = 60.0f;
}
void ASpaceShooterGameMode::BeginPlay()
{
Super::BeginPlay();
ChangeMenuWidget(StartingWidgetClass);
((UGameWidget*)CurrentWidget)->Load();
}
if (EnemyTimer<=0.0f) {
float DiffPercentage =
FMath::Min(GameTimer/TIME_TO_MAX_DIFF, 1.0f);
EnemyTimer = MAX_TIME_SPAWN - (MAX_TIME_SPAWN -
MIN_TIME_SPAWN)* DiffPercentage;
UWorld* World = GetWorld();
24
if (World){
FVector NewLocation = FVector(600.0f,
FMath::RandRange(-700.0f, 700.0f), 70.0f);
World->SpawnActor<AEnemy>(Enemy_Blueprint,
NewLocation, FRotator::ZeroRotator);
}
}
}
void
ASpaceShooterGameMode::ChangeMenuWidget(TSubclassOf<UUserW
idget> NewWidgetClass)
{
if (CurrentWidget != nullptr)
{
CurrentWidget->RemoveFromViewport();
CurrentWidget = nullptr;
}
if (NewWidgetClass != nullptr)
{
CurrentWidget = CreateWidget<UUserWidget>
(GetWorld(), NewWidgetClass);
if(CurrentWidget != nullptr)
{
CurrentWidget->AddToViewport();
}
}
}
void ASpaceShooterGameMode::IncrementScore()
{
Score += 10;
((UGameWidget*)CurrentWidget)->SetScore(Score);
}
void ASpaceShooterGameMode::GameOver()
{
((UGameWidget*)CurrentWidget)->OnGameOver(Score);
}
25
// GameWidget.h
#pragma once
#include "Runtime/UMG/Public/UMG.h"
#include "Runtime/UMG/Public/UMGStyle.h"
#include "Runtime/UMG/Public/IUMGModule.h"
#include "Runtime/UMG/Public/Slate/SObjectWidget.h"
#include "Runtime/UMG/Public/Blueprint/UserWidget.h"
#include "CoreMinimal.h"
#include "Blueprint/UserWidget.h"
#include "GameWidget.generated.h"
UCLASS()
class TOPDOWN_API UGameWidget : public UUserWidget
{
GENERATED_BODY()
public:
void Load();
void SetScore(int score);
void OnGameOver(int score);
UPROPERTY()
UTextBlock* ScoreTextBlock;
};
// GameWidget.cpp
#include "GameWidget.h"
void UGameWidget::Load()
{
const FName TextBlockName =
FName(TEXT("GameTextBlock"));
if (ScoreTextBlock == nullptr)
{
ScoreTextBlock = (UTextBlock*)(WidgetTree-
>FindWidget(TextBlockName));
}
}
26
void UGameWidget::OnGameOver(int score)
{
if (ScoreTextBlock != nullptr)
{
ScoreTextBlock-
>SetText(FText::FromString(FString(TEXT("Score: "))
+
FString::FromInt(score)
+
TEXT("\n Pulsa R para reiniciar")));
}
}
27