簡単な2Dゲームを作ろう
2Dゲーム制作の練習シリーズです。
アンリアルエンジンで2Dゲーム制作方法をまとめていきます。
説明を省略したり、細かな数値設定などの説明をしない場合もありますので、ご参考程度で確認ください。
※作業環境:UEバージョン5.3.2 Windows11 VSCode
スコア機能を実装する
敵を倒したときに、点数が追加されるスコアを実装していきます。
コードの移し替え
スコアを実装する前に、敵の追跡設定するコードを移設します。
敵が生成されたときに、敵の追跡行動をセットしたいので「EnemySpawner」のコードを編集していきます。
EnemySpawner.h
インクルードを追加します。
#include "Enemy.h"
// 追加↓
#include "PlayerCharacter.h"
// ここまで↑
「public:」内にコードを追加します。
UPROPERTY(EditAnywhere, BlueprintReadWrite)
float DecreaseSpawnTimerByEveryInterval = 0.05f;
// 追加↓
APlayerCharacter* Player;
// ここまで↑
関数も追加します。
void OnSpawnTimerTimeout();
void StartSpawning();
void StopSpawning();
void SpawnEnemy();
// 追加↓
void SetupEnemy(AEnemy *Enemy);
// ここまで↑
EnemySpawner.cpp
インクルードを追加します。
#include "EnemySpawner.h"
// 追加↓
#include "Kismet/GameplayStatics.h"
// ここまで↑
「AEnemySpawner::BeginPlay(){}」にコードを追加します。
void AEnemySpawner::BeginPlay()
{
Super::BeginPlay();
// 追加↓
AActor *PlayerActor = UGameplayStatics::GetActorOfClass(GetWorld(), APlayerCharacter::StaticClass()); // プレイヤーを取得する
if (PlayerActor) // プレイヤーが存在するなら
{
Player = Cast<APlayerCharacter>(PlayerActor); // キャストする
}
// ここまで↑
StartSpawning();
}
追加した関数の定義を作成します。
// 関数定義
void AEnemySpawner::SetupEnemy(AEnemy *Enemy)
{
if (Enemy) // 敵が生成されたなら
{
Enemy->Player = Player; // 追跡対象をプレイヤーに設定する
Enemy->CanFollow = true; // 追跡モードをオンにする
}
}
「AEnemySpawner::SpawnEnemy(){}」内にコードを追加します。
AEnemy *Enemy = GetWorld()->SpawnActor<AEnemy>(EnemyActorToSpawn, EnemyLocation, FRotator::ZeroRotator);
// 追加↓
SetupEnemy(Enemy); // 敵の追跡機能を設定する
// ここまで↑
敵の追跡行動をセットするコードが作成できたので、「Enemy.cpp」にある類似コードを削除します。
削除するコード
Enemy.cpp
void AEnemy::BeginPlay()
{
Super::BeginPlay();
// 削除↓
// if (!Player) // プレイヤーをまだ取得していない場合
// {
// AActor *PlayerActor = UGameplayStatics::GetActorOfClass(GetWorld(), APlayerCharacter::StaticClass()); // プレイヤーを取得する
// if (PlayerActor) // プレイヤーが存在するなら
// {
// Player = Cast<APlayerCharacter>(PlayerActor); // キャストする
// CanFollow = true; // 追跡モードをオン
// }
// }
// ここまで↑
}
これでコードの入れ替えは完了です。
デリゲートを実装する
スコアを実装していきます。
まずは、デリゲートを実装していきます。
デリゲート(DELEGATE)とは、通知を送るための仕組みです。
デリゲートを使って、関数を他の場所から呼び出したりできます。
Enemy.h
デリゲートを追加します。
#include "Enemy.generated.h"
// 追加↓
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FEnemyDiedDelegate);
// ここまで↑
UCLASS()
class PAPERGAME1_API AEnemy : public AActor
{
「public:」内にコードを追加します。
UPROPERTY(EditAnywhere, BlueprintReadWrite)
float StopDistance = 20.0f;
// 追加↓
FEnemyDiedDelegate EnemyDiedDelegate;
// ここまで↑
AEnemy();
デリゲートを有効にするために、リフレッシュ&コンパイルします。
Enemy.cpp
「AEnemy::Die(){}」にコードを追加します。
EnemyFlipbook->SetFlipbook(DeadFlipbookAsset);
EnemyFlipbook->SetTranslucentSortPriority(1);
// 追加↓
EnemyDiedDelegate.Broadcast(); // デリゲートに関連付けた関数をすべて呼び出す
// ここまで↑
EnemySpawner.h
インクルードを追加します。
#include "PlayerCharacter.h"
// 追加↓
#include "GM_MyGameMode.h"
// ここまで↑
#include "EnemySpawner.generated.h"
「public:」内にコードを追加します。
FTimerHandle SpawnTimer;
// 追加↓
AGM_MyGameMode* MyGameMode;
// ここまで↑
AEnemySpawner();
関数も追加します。
void SetupEnemy(AEnemy *Enemy);
// 追加↓
UFUNCTION()
void OnEnemyDied();
// ここまで↑
EnemySpawner.cpp
追加した関数の定義を作成します。
// 関数定義
void AEnemySpawner::OnEnemyDied()
{
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::White, TEXT("Enemy died")); // デバック表示する
}
ここに、後でスコア換算コードを配置します。
今は、デバック表示のみにして、きちんと機能するか確認しています。
「AEnemySpawner::BeginPlay(){}」内のコードを追加します。
void AEnemySpawner::BeginPlay()
{
Super::BeginPlay();
// 追加↓
AGameModeBase *GameMode = UGameplayStatics::GetGameMode(GetWorld()); // ゲームモードを取得する
if (GameMode) // ゲームモードが存在すれば
{
MyGameMode = Cast<AGM_MyGameMode>(GameMode); // キャストする
check(MyGameMode); // 成功しているか確認
}
// ここまで↑
「AEnemySpawner::SetupEnemy(){}」内にコードを追加します。
void AEnemySpawner::SetupEnemy(AEnemy *Enemy)
{
if (Enemy)
{
Enemy->Player = Player;
Enemy->CanFollow = true;
// 追加↓
Enemy->EnemyDiedDelegate.AddDynamic(this, &AEnemySpawner::OnEnemyDied); // デリゲートに関数を関連付ける
// ここまで↑
}
}
保存して、リフレッシュ&コンパイルします。
敵を倒したときにデバックが表示されればOKです。
スコアを設定する
敵を倒したときにスコアを加算させていきます。
スコア数はゲームモードで管理させます。
GM_MyGameMode.h
「public:」内にプロパティを追加します。
UPROPERTY(EditAnywhere, BlueprintReadWrite)
float WinResetTime = 3.0f;
// 追加↓
UPROPERTY(VisibleAnywhere, BlueprintReadWrite)
int Score = 0;
// ここまで↑
関数も追加します。
FTimerHandle ResetGameTimer;
// 追加↓
AGM_MyGameMode();
virtual void BeginPlay() override;
void SetScore(int NewScore);
void AddScore(int AmountToAdd);
// ここまで↑
GM_MyGameMode.cpp
追加した関数の定義を作成します。
// 関数定義
AGM_MyGameMode::AGM_MyGameMode()
{
PrimaryActorTick.bCanEverTick = true; // ティック機能をオンにする
SetScore(0); // スコアを0にする
}
// 関数定義
void AGM_MyGameMode::BeginPlay()
{
Super::BeginPlay();
SetScore(0); // ゲーム開始時にも0にする
}
// 関数定義
void AGM_MyGameMode::SetScore(int NewScore)
{
if (NewScore >= 0) // スコア数が0以上なら
{
Score = NewScore; // スコア更新
}
}
// 関数定義
void AGM_MyGameMode::AddScore(int AmountToAdd)
{
int NewScore = Score + AmountToAdd; // 新しいスコアを計算する
SetScore(NewScore); // スコアを更新する
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::White, FString::Printf(TEXT("Score: %d"), Score)); // デバックでスコアを表示する
}
EnemySpawner.cpp
敵を倒したときにスコアを更新させます。
「AEnemySpawner::OnEnemyDied(){}」内のコードを編集します。
void AEnemySpawner::OnEnemyDied()
{
// 編集&追加↓
// GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::White, TEXT("Enemy died")); // 消す
int ScoreToAdd = 10; // 加算するスコア10点
MyGameMode->AddScore(ScoreToAdd); // スコアを更新
// ここまで↑
}
保存して、リフレッシュ&コンパイルし、テストプレイします。
敵を倒すごとにスコアが増えていればOKです。
スコア機能の実装完了
今回はここまでです。
次回は「スコア表示するUIを作成する」です。
前後記事
前回の記事
・徐々に敵を増やす
次回の記事
・スコア表示するUIを作成する
他の記事を探す
他の記事も気になる方は、以下の記事の目次を確認ください。