, , ,

横スクロールゲーム

Unreal Engine 5の学習で、マリオの1-1を再現


概要

  • 個人制作
  • Unreal Engine 5の学習のために制作
  • マリオの1-1を再現した作品

開発環境

Unreal Engine 5.0.3/C++

開発期間

2023年10月~2023年12月


詳細

 それまで、使用したことがなかったUnreal Engine、C++を学ぶためにBluePrintを使用せずに制作を行いました。初めて、使用するゲームエンジンであったことやC++での制作に関する情報がまったく見つからず、公式のドキュメントを見ながら制作したため、かなり時間がかかってしまいました。

 そして、Unityよりも優れている点は最初からグラフィックが美麗な点、BluePrintで感覚的に制作を進められる点の2点であると思いました。逆に、Unityの方が優れている点は、スクリプティングが簡単な点です。Unreal Engineでは、スクリプトを変更するたびに、プロジェクトをビルドし、コンパイルする必要があり、かなり苦労しました。

 この制作で理解できたことは、Unreal EngineはBluePrintを用いて制作を行うべきであること、ゲームエンジンを学ぶこととプログラミング言語を同時に学ぶことはできないことの2点に気付きました。今後は、C++の学習を中心に行っていこうと思います。


アピールポイント

クリボーを倒す

クリボーを倒す際に、当たり判定を利用する必要がありました。その際、当たり判定に使用する関数が分からず、2週間ほど調査して見つけることができました。tagで敵か判定する必要もあったため余計に苦労しました。

基本挙動

WASDの移動やジャンプの実装は簡単でしたが、ダッシュの速度やジャンプの高さなど細かいパラメータの設定に苦労しました。

Script

TPSCharacter.h

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

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "GameFramework/SpringArmComponent.h"
#include "Camera/CameraComponent.h"
#include "Components/CapsuleComponent.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "TPSCharacter.generated.h"




UCLASS()
class TPSPROJECT_API ATPSCharacter : public ACharacter
{
	GENERATED_BODY()

public:
	// Sets default values for this character's properties
	ATPSCharacter();

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

	void StarTimerExpired();

public:	
	// Called every frame
	virtual void Tick(float DeltaTime) override;

	// Called to bind functionality to input
	virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;

	void StartStarTimer();

	// 右および左への移動のための入力を処理します。
	UFUNCTION()
    void MoveRight(float Value);

	UFUNCTION()
		void SprintStart();

	UFUNCTION()
		void SprintStop();


	//キーが押された際にジャンプフラグを設定します。
	UFUNCTION()
		void StartJump();

	//キーが話された際にジャンプフラグをクリアします。
	UFUNCTION()
		void StopJump();

	//TPSカメラ
	UPROPERTY(VisibleAnyWhere)
		UCameraComponent* TPSCameraComponent;

	//メッシュ
	UPROPERTY(VisibleAnywhere)
		USkeletalMeshComponent* CharacterMesh;

	//敵に当たった際のメソッド
	UFUNCTION()
		void OnEnemyHit(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);




	



private:
	bool bIsSprinting;
	float DefaultWalkSpeed;
	int32 GrowCount;
	bool star;

	// 追加: タイマーで使用する変数
	FTimerHandle StarTimerHandle;

	// 追加: Star の効果が続く時間(秒)
	float StarDuration;

};

TPSCharacter.cpp

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


#include "TPSCharacter.h"
#include "EnemyCharacter.h"
#include "GameFramework/SpringArmComponent.h"
#include "Camera/CameraComponent.h"
#include "UObject/ConstructorHelpers.h"
#include "Kismet/GameplayStatics.h"
#include "Blueprint/UserWidget.h"
#include "Goal.h"


// Sets default values
ATPSCharacter::ATPSCharacter()
{
 	// Set this character to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;

	//三人称視点カメラコンポーネントを作成します。
	TPSCameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("ThirdPersonCamera"));
	check(TPSCameraComponent != nullptr);

	//カメラコンポーネントをカプセルコンポーネントにアタッチします。
	TPSCameraComponent->SetupAttachment(CastChecked<USceneComponent, UCapsuleComponent>(GetCapsuleComponent()));

	//カメラ位置の設定
	TPSCameraComponent->SetRelativeLocation(FVector(-100.0f, 0.0f, 200.0f));

	//ジャンプの高さ
	GetCharacterMovement()->JumpZVelocity = 1200.0f;

	DefaultWalkSpeed = GetCharacterMovement()->MaxWalkSpeed;

	bIsSprinting = false; // 初期化

	GrowCount = 0;

	StarDuration = 5.0f;

}

// Called when the game starts or when spawned
void ATPSCharacter::BeginPlay()
{
	Super::BeginPlay();
	GetCapsuleComponent()->OnComponentBeginOverlap.AddDynamic(this, &ATPSCharacter::OnEnemyHit);
	star = false;
}


// Called every frame
void ATPSCharacter::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

	
}

// Called to bind functionality to input
void ATPSCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);

	//「移動」バインディングをセットアップします。
	PlayerInputComponent->BindAxis("MoveRight", this, &ATPSCharacter::MoveRight);

	//「アクション」バインディングをセットアップします。
	PlayerInputComponent->BindAction("Jump", IE_Pressed, this, &ATPSCharacter::StartJump);
	PlayerInputComponent->BindAction("Jump", IE_Released, this, &ATPSCharacter::StopJump);

	// Shiftキーのバインディングをセットアップ
	PlayerInputComponent->BindAction("Dash", IE_Pressed, this, &ATPSCharacter::SprintStart);
	PlayerInputComponent->BindAction("Dash", IE_Released, this, &ATPSCharacter::SprintStop);

	
}

//左右移動
void ATPSCharacter::MoveRight(float Value)
{
	FVector Direction = FRotationMatrix(Controller->GetControlRotation()).GetScaledAxis(EAxis::Y);

	
	AddMovementInput(Direction, Value);
}

//ジャンプ開始
void ATPSCharacter::StartJump()
{
	bPressedJump = true;
}

//ジャンプ終了
void ATPSCharacter::StopJump()
{
	bPressedJump = false;
}

//ダッシュ開始
void ATPSCharacter::SprintStart()
{
	bIsSprinting = true;
	GetCharacterMovement()->MaxWalkSpeed = DefaultWalkSpeed * 5.0f;
}
//ダッシュ終了
void ATPSCharacter::SprintStop()
{
	bIsSprinting = false;
	GetCharacterMovement()->MaxWalkSpeed = DefaultWalkSpeed;
}

void ATPSCharacter::OnEnemyHit(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
	// オーバーラップしたアクターが AEnemyCharacter であるか確認
	AEnemyCharacter* Enemy = Cast<AEnemyCharacter>(OtherActor);

	//死んだ場合は、レベルをリロード
	if (OtherActor->ActorHasTag("GameOver"))
	{
		UWorld* World = GetWorld();
		if (World)
		{
			// 現在のレベルを取得
			FString LevelName = "TPSMap";

			// 現在のレベルをアンロード
			UGameplayStatics::OpenLevel(World, FName(*LevelName), true);
		}
	}

	//キノコを取ったときの処理
	if (OtherActor->ActorHasTag("mushroom") && GrowCount < 1)
	{
		//Scaleが小さい場合は、2倍に
		FVector NewScale = GetActorScale3D() * 2.0f;
		SetActorScale3D(NewScale);

		OtherActor->Destroy();

		GrowCount++;
	}

	if (OtherActor->ActorHasTag("star"))
	{
		//Scaleを通常サイズに変更 
		FVector NewScale = FVector(1.0f, 1.0f, 1.0f);
		SetActorScale3D(NewScale);

		OtherActor->Destroy();
		StartStarTimer();
		GrowCount = 1;
	}

	if (Enemy)
	{
	     // Playerの座標と敵の座標を比較
	     FVector PlayerLocation = GetActorLocation();
		 FVector EnemyLocation = OtherActor->GetActorLocation();

		 // Playerが敵の上にいる場合
		 if (PlayerLocation.Z > EnemyLocation.Z && !star)
		 {
				// Destroyを呼ぶ
				OtherActor->Destroy();
		 }
		 else if (PlayerLocation.Z <= EnemyLocation.Z && GrowCount > 0 && !star)
		 {
			 //Scaleを半分に
			 FVector NewScale = GetActorScale3D() / 2.0f;
			 SetActorScale3D(NewScale);

			 GrowCount -= 1;
		 }
		 else if(PlayerLocation.Z <= EnemyLocation.Z && GrowCount < 1 && !star)
	     {
			 UWorld* World = GetWorld();
			 if (World)
			 {
				 // 現在のレベルを取得
				 FString LevelName = "TPSMap";

				 // 現在のレベルをアンロード
				 UGameplayStatics::OpenLevel(World, FName(*LevelName), true);
			 }
		 }
		 else if (star)
		 {
			 // Destroyを呼ぶ
			 OtherActor->Destroy();
		 }
    }
		
	if (OtherActor->ActorHasTag("Goal"))
	{
		UGameplayStatics::GetPlayerController(this, 0)->ConsoleCommand("quit");
	}
}

void ATPSCharacter::StartStarTimer()
{
	// 一時的に star を true に設定
	star = true;

	// タイマーをセットし、指定時間後に StarTimerExpired メソッドを呼ぶ
	GetWorldTimerManager().SetTimer(StarTimerHandle, this, &ATPSCharacter::StarTimerExpired, StarDuration, false);
}

void ATPSCharacter::StarTimerExpired()
{
	// タイマー経過後に star を false に設定
	star = false;
}




Tags:

コメントを残す