目標
このセクションでは、ファースト パーソン シューター ゲームに発射物を実装する方法を説明します。
目的
このチュートリアルのこのセクションを完了すると、次のことができるようになります。
発射物をゲームに追加する
シューティングを実装する
発射物のコリジョンと存続期間を設定する
発射物をワールドとインタラクトさせる
照準線をビューポートに追加する
ステップ
3.1 - 発射物をゲームに追加する
3.2 - シューティングを実装する
3.3 - 発射物のコリジョンと存続期間を設定する
3.4 - 発射物をワールドとインタラクトさせる
3.5 - 照準線をビューポートに追加する
3.1 - 発射物をゲームに追加する
キャラクターの設定が完了したので、発射される武器を実装します。手りゅう弾のような単純な発射物を画面の中央から発射し、ワールドに衝突するまで飛び続けるようにプログラムします。このステップでは、入力を追加し、発射物の新しいクラスのコードを作成します。
Fire アクション マッピングを追加する
メイン メニュー パネルで [Edit (編集)] をクリックし、[Project Settings (プロジェクト設定)] を選択します。
[Project Settings] タブの左側にある [Engine (エンジン)] セクションで、[Input (インプット)] をクリックします。
右側にある [Bindings (バインディング)] で、[Action Mappings (アクション マッピング)] の隣にある [+ (プラス)] 記号をクリックします。
[Action Mappings] の左側にある 矢印 をクリックします。
表示されるテキスト フィールドに「Fire」と入力します。
ドロップダウン メニューで、[Left Mouse Button (マウスの左ボタン)] を [Mouse (マウス)] ドロップダウン リストから選択します。
ここでの入力設定は次のようになります。
[Project Settings] メニューを閉じます。
発射物クラスを追加する
[Tools (ツール)] を メイン メニュー パネルでクリックし、[New C++ Class... (新規 C++ クラス...)] を選択します。
[Choose Parent Class (親クラスを選択)] ウィンドウが表示されます。[Actor (アクタ)] を親クラスとして選択し、[Next (次へ)] をクリックします。
新規クラスに「FPSProjectile」という名前を付けてから、[Create Class (クラスを作成)] をクリックします。
USphere コンポーネントを追加する
C++ クラスが作成されると、Visual Studio が自動的に、
FPSProjectile.h
ヘッダ ファイルとFPSProjectile.cpp
実装ファイルが開いた状態で表示されます。FPSProjectile.h
クラス ヘッダ ファイルに移動します。SphereComponent ヘッダ ファイルを追加します。
#include "Components/SphereComponent.h"
USphereComponent
への参照をFPSProjectile
インターフェースに追加するには、次のコードをpublic
アクセス指定子のFPSProjectile.h
に追加します。// 球体コリジョン コンポーネント。 UPROPERTY(VisibleDefaultsOnly, Category = Projectile) USphereComponent* CollisionComponent;
FPSProjectile.h
は次のようになります。// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "GameFramework/Actor.h" #include "Components/SphereComponent.h" #include "FPSProjectile.generated.h" UCLASS() class FPSPROJECT_API AFPSProjectile : public AActor { GENERATED_BODY() public: // このアクタのプロパティのデフォルト値を設定します。 AFPSProjectile(); protected: // ゲームの開始時またはスポーン時に呼び出します。 virtual void BeginPlay() override; public: // フレームごとに呼び出します。 virtual void Tick( float DeltaTime ) override; // 球体コリジョン コンポーネント UPROPERTY(VisibleDefaultsOnly, Category = Projectile) USphereComponent* CollisionComponent; };
FPSProjectile.cpp
クラス用の実装ファイルに移動します。次のコードを
AFPSProjectile
コンストラクタのPrimaryActorTick.bcanEverTick
の後 (FPSProjectile.cpp
内) に追加します。if(!RootComponent) { RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("ProjectileSceneComponent")); } if(!CollisionComponent) { // 単純なコリジョン表現として、球体を使用します。 CollisionComponent = CreateDefaultSubobject<USphereComponent>(TEXT("SphereComponent")); // 球体のコリジョン半径を設定します。 CollisionComponent->InitSphereRadius(15.0f); // コリジョン コンポーネントとなるルート コンポーネントを設定します。 RootComponent = CollisionComponent; }
シミュレーションで動かすため、
CollisionComponent
をRootComponent
にしています。FPSProjectile.cpp
は次のようになります。// Copyright Epic Games, Inc. All Rights Reserved. #include "FPSProjectile.h" // デフォルト値を設定します。 AFPSProjectile::AFPSProjectile() { // 各フレームでこのアクタが Tick() を呼び出すよう設定します。必要ない場合は、パフォーマンス向上のためにこれをオフにすることができます。 PrimaryActorTick.bCanEverTick = true; if(!RootComponent) { RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("ProjectileSceneComponent")); } if(!CollisionComponent) { // 単純なコリジョン表現として、球体を使用します。 CollisionComponent = CreateDefaultSubobject<USphereComponent>(TEXT("SphereComponent")); // 球体のコリジョン半径を設定します。 CollisionComponent->InitSphereRadius(15.0f); // コリジョン コンポーネントとなるルート コンポーネントを設定します。 RootComponent = CollisionComponent; } } // ゲームの開始時またはスポーン時に呼び出します。 void AFPSProjectile::BeginPlay() { Super::BeginPlay(); } // フレームごとに呼び出します。 void AFPSProjectile::Tick( float DeltaTime ) { Super::Tick( DeltaTime ); }
発射物移動コンポーネントを追加する
FPSProjectile.h
クラス ヘッダ ファイルに移動します。ProjectileMovementComponent ヘッダ ファイルを追加します。
#include "GameFramework/ProjectileMovementComponent.h"
次のコードを
FPSProjectile.h
のpublic
アクセス指定子の下に追加します。// 発射物移動コンポーネント。 UPROPERTY(VisibleAnywhere, Category = Movement) UProjectileMovementComponent* ProjectileMovementComponent;
FPSProjectile.h
は次のようになります。// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "GameFramework/Actor.h" #include "Components/SphereComponent.h" #include "GameFramework/ProjectileMovementComponent.h" #include "FPSProjectile.generated.h" UCLASS() class FPSPROJECT_API AFPSProjectile : public AActor { GENERATED_BODY() public: // このアクタのプロパティのデフォルト値を設定します。 AFPSProjectile(); protected: // ゲームの開始時またはスポーン時に呼び出します。 virtual void BeginPlay() override; public: // フレームごとに呼び出します。 virtual void Tick( float DeltaTime ) override; // 球体コリジョン コンポーネント。 UPROPERTY(VisibleDefaultsOnly, Category = Projectile) USphereComponent* CollisionComponent; // 発射物移動コンポーネント。 UPROPERTY(VisibleAnywhere, Category = Movement) UProjectileMovementComponent* ProjectileMovementComponent; };
FPSProjectile.cpp
クラス用の実装ファイルに移動します。次のコード行を
AFPSProjectile
コンストラクタ (FPSProjectile.cpp
内) に追加します。if(!ProjectileMovementComponent) { // このコンポーネントを使用し、この発射物の動きを駆動します。 ProjectileMovementComponent = CreateDefaultSubobject<UProjectileMovementComponent>(TEXT("ProjectileMovementComponent")); ProjectileMovementComponent->SetUpdatedComponent(CollisionComponent); ProjectileMovementComponent->InitialSpeed = 3000.0f; ProjectileMovementComponent->MaxSpeed = 3000.0f; ProjectileMovementComponent->bRotationFollowsVelocity = true; ProjectileMovementComponent->bShouldBounce = true; ProjectileMovementComponent->Bounciness = 0.3f; ProjectileMovementComponent->ProjectileGravityScale = 0.0f; }
FPSProjectile.cpp
は次のようになります。// Copyright Epic Games, Inc. All Rights Reserved. #include "FPSProjectile.h" // デフォルト値を設定します。 AFPSProjectile::AFPSProjectile() { // 各フレームでこのアクタが Tick() を呼び出すよう設定します。必要ない場合は、パフォーマンス向上のためにこれをオフにすることができます。 PrimaryActorTick.bCanEverTick = true; if(!RootComponent) { RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("ProjectileSceneComponent")); } if(!CollisionComponent) { // 単純なコリジョン表現として、球体を使用します。 CollisionComponent = CreateDefaultSubobject<USphereComponent>(TEXT("SphereComponent")); // 球体のコリジョン半径を設定します。 CollisionComponent->InitSphereRadius(15.0f); // コリジョン コンポーネントとなるルート コンポーネントを設定します。 RootComponent = CollisionComponent; } if(!ProjectileMovementComponent) { // このコンポーネントを使用し、この発射物の動きを駆動します。 ProjectileMovementComponent = CreateDefaultSubobject<UProjectileMovementComponent>(TEXT("ProjectileMovementComponent")); ProjectileMovementComponent->SetUpdatedComponent(CollisionComponent); ProjectileMovementComponent->InitialSpeed = 3000.0f; ProjectileMovementComponent->MaxSpeed = 3000.0f; ProjectileMovementComponent->bRotationFollowsVelocity = true; ProjectileMovementComponent->bShouldBounce = true; ProjectileMovementComponent->Bounciness = 0.3f; ProjectileMovementComponent->ProjectileGravityScale = 0.0f; } } // ゲームの開始時またはスポーン時に呼び出します。 void AFPSProjectile::BeginPlay() { Super::BeginPlay(); } // フレームごとに呼び出します。 void AFPSProjectile::Tick( float DeltaTime ) { Super::Tick( DeltaTime ); }
発射物の初期速度を設定する
FPSProjectile.h
クラス ヘッダ ファイルに移動します。次の関数宣言を
FPSProjectile.h
のpublic
アクセス指定子の下に追加します。// 発射物の発射方向の速度を初期化する関数。 void FireInDirection(const FVector& ShootDirection);
この関数では物体の発射を処理します。
FPSProjectile.h
は次のようになります。// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "GameFramework/Actor.h" #include "Components/SphereComponent.h" #include "GameFramework/ProjectileMovementComponent.h" #include "FPSProjectile.generated.h" UCLASS() class FPSPROJECT_API AFPSProjectile : public AActor { GENERATED_BODY() public: // このアクタのプロパティのデフォルト値を設定します。 AFPSProjectile(); protected: // ゲームの開始時またはスポーン時に呼び出します。 virtual void BeginPlay() override; public: // フレームごとに呼び出します。 virtual void Tick( float DeltaTime ) override; // 球体コリジョン コンポーネント。 UPROPERTY(VisibleDefaultsOnly, Category = Projectile) USphereComponent* CollisionComponent; // 発射物移動コンポーネント。 UPROPERTY(VisibleAnywhere, Category = Movement) UProjectileMovementComponent* ProjectileMovementComponent; // 発射物の発射方向の速度を初期化する関数。 void FireInDirection(const FVector& ShootDirection); };
FPSProjectile.cpp
クラス用の実装ファイルに移動します。次の関数定義を
FPSProjectile.cpp
に追加します。// 発射物の発射方向の速度を初期化する関数。 void AFPSProjectile::FireInDirection(const FVector& ShootDirection) { ProjectileMovementComponent->Velocity = ShootDirection * ProjectileMovementComponent->InitialSpeed; }
ここでは発射方向を指定するだけです。発射物の速度は
ProjectileMovementComponent
で定義されているからです。FPSProjectile.cpp
は次のようになります。// Copyright Epic Games, Inc. All Rights Reserved. #include "FPSProjectile.h" // デフォルト値を設定します。 AFPSProjectile::AFPSProjectile() { // 各フレームでこのアクタが Tick() を呼び出すよう設定します。必要ない場合は、パフォーマンス向上のためにこれをオフにすることができます。 PrimaryActorTick.bCanEverTick = true; if(!RootComponent) { RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("ProjectileSceneComponent")); } if (!CollisionComponent) { // 単純なコリジョン表現として、球体を使用します。 CollisionComponent = CreateDefaultSubobject<USphereComponent>(TEXT("SphereComponent")); // 球体のコリジョン半径を設定します。 CollisionComponent->InitSphereRadius(15.0f); // コリジョン コンポーネントとなるルート コンポーネントを設定します。 RootComponent = CollisionComponent; } if(!ProjectileMovementComponent) { // このコンポーネントを使用し、この発射物の動きを駆動します。 ProjectileMovementComponent = CreateDefaultSubobject<UProjectileMovementComponent>(TEXT("ProjectileMovementComponent")); ProjectileMovementComponent->SetUpdatedComponent(CollisionComponent); ProjectileMovementComponent->InitialSpeed = 3000.0f; ProjectileMovementComponent->MaxSpeed = 3000.0f; ProjectileMovementComponent->bRotationFollowsVelocity = true; ProjectileMovementComponent->bShouldBounce = true; ProjectileMovementComponent->Bounciness = 0.3f; ProjectileMovementComponent->ProjectileGravityScale = 0.0f; } } // ゲームの開始時またはスポーン時に呼び出します。 void AFPSProjectile::BeginPlay() { Super::BeginPlay(); } // フレームごとに呼び出します。 void AFPSProjectile::Tick( float DeltaTime ) { Super::Tick( DeltaTime ); } // 発射物の発射方向の速度を初期化する関数。 void AFPSProjectile::FireInDirection(const FVector& ShootDirection) { ProjectileMovementComponent->Velocity = ShootDirection * ProjectileMovementComponent->InitialSpeed; }
発射の入力アクションをバインドする
Visual Studio の [Solution Explorer (ソリューション エクスプローラー)] に移動し、
FPSCharacter.h
クラス ヘッダ ファイルを開きます。次の関数宣言を
FPSCharacter.h
のpublic
アクセス指定子の下に追加します。// 発射物の発射を処理します。 UFUNCTION() void Fire();
FPSCharacter.h
は次のようになります。// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "GameFramework/Character.h" #include "Camera/CameraComponent.h" #include "Components/CapsuleComponent.h" #include "FPSCharacter.generated.h" UCLASS() class FPSPROJECT_API AFPSCharacter : public ACharacter { GENERATED_BODY() public: // このキャラクターのプロパティのデフォルト値を設定します。 AFPSCharacter(); protected: // ゲームの開始時またはスポーン時に呼び出します。 virtual void BeginPlay() override; public: // フレームごとに呼び出します。 virtual void Tick( float DeltaTime ) override; // Input に機能をバインドする際に呼び出します。 virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override; // 前方および後方への移動のための入力を処理します。 UFUNCTION() void MoveForward(float Value); // 右および左への移動のための入力を処理します。 UFUNCTION() void MoveRight(float Value); // キーが押された際にジャンプ フラグを設定します。 UFUNCTION() void StartJump(); // キーが放された際にジャンプ フラグをクリアします。 UFUNCTION() void StopJump(); // FPS カメラ UPROPERTY(VisibleAnywhere) UCameraComponent* FPSCameraComponent; // ファースト パーソン メッシュ (腕) で、所有しているプレイヤーにのみ表示されます。 UPROPERTY(VisibleDefaultsOnly, Category = Mesh) USkeletalMeshComponent* FPSMesh; // 発射物の発射を処理します。 UFUNCTION() void Fire(); };
Visual Studio の [Solution Explorer] に移動し、
FPSCharacter.cpp
クラス用の実装ファイルを開きます。Fire
関数をバインドするには、次のコードをFPSCharacter.cpp:
のSetupPlayerInputComponent
関数に追加します。PlayerInputComponent->BindAction("Fire", IE_Pressed, this, &AFPSCharacter::Fire);
次の関数定義を
FPSCharacter.cpp
に追加します。void AFPSCharacter::Fire() { }
FPSCharacter.cpp
は次のようになります。// Copyright Epic Games, Inc. All Rights Reserved. #include "FPSCharacter.h" // デフォルト値を設定します。 AFPSCharacter::AFPSCharacter() { // このキャラクターがフレームごとに Tick() を呼び出すように設定します。必要ない場合は、パフォーマンス向上のためにこれをオフにすることができます。 PrimaryActorTick.bCanEverTick = true; // 一人称視点カメラ コンポーネントを作成します。 FPSCameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("FirstPersonCamera")); check(FPSCameraComponent != nullptr); // カメラ コンポーネントをカプセル コンポーネントにアタッチします。 FPSCameraComponent->SetupAttachment(CastChecked<USceneComponent, UCapsuleComponent>(GetCapsuleComponent())); // カメラを目の少し上に位置づけます。 FPSCameraComponent->SetRelativeLocation(FVector(0.0f, 0.0f, 50.0f + BaseEyeHeight)); // ポーンがカメラの回転をコントロールできます。 FPSCameraComponent->bUsePawnControlRotation = true; // 所有するプレイヤー向けのファースト パーソン メッシュ コンポーネントを作成します。 FPSMesh = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("FirstPersonMesh")); check(FPSMesh != nullptr); // このメッシュは所有するプレイヤーにのみ表示されます。 FPSMesh->SetOnlyOwnerSee(true); // FPS メッシュを FPS カメラにアタッチします。 FPSMesh->SetupAttachment(FPSCameraComponent); // 単一のメッシュがあるかのような錯覚を維持するには、一部の環境シャドウイングを無効にします。 FPSMesh->bCastDynamicShadow = false; FPSMesh->CastShadow = false; // 所有するプレイヤーには通常の (サードパーソン) ボディ メッシュは表示されません。 GetMesh()->SetOwnerNoSee(true); } // ゲームの開始時またはスポーン時に呼び出します。 void AFPSCharacter::BeginPlay() { Super::BeginPlay(); check(GEngine != nullptr); // デバッグ メッセージを 5 秒間表示します。 // 「Key」値引数 -1 を指定すると、メッセージは更新またはリフレッシュされなくなります。 GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("We are using FPSCharacter.")); } // フレームごとに呼び出します。 void AFPSCharacter::Tick( float DeltaTime ) { Super::Tick( DeltaTime ); } // Input に機能をバインドする際に呼び出します。 void AFPSCharacter::SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) { Super::SetupPlayerInputComponent(PlayerInputComponent); // 「移動」バインディングをセットアップします。 PlayerInputComponent->BindAxis("MoveForward", this, &AFPSCharacter::MoveForward); PlayerInputComponent->BindAxis("MoveRight", this, &AFPSCharacter::MoveRight); // 「ルック」バインディングをセットアップします。 PlayerInputComponent->BindAxis("Turn", this, &AFPSCharacter::AddControllerYawInput); PlayerInputComponent->BindAxis("LookUp", this, &AFPSCharacter::AddControllerPitchInput); // 「アクション」バインディングをセットアップします。 PlayerInputComponent->BindAction("Jump", IE_Pressed, this, &AFPSCharacter::StartJump); PlayerInputComponent->BindAction("Jump", IE_Released, this, &AFPSCharacter::StopJump); PlayerInputComponent->BindAction("Fire", IE_Pressed, this, &AFPSCharacter::Fire); } void AFPSCharacter::MoveForward(float Value) { // どちらが「前方」なのかを調べ、プレイヤーがその方向に移動したがっていることを記録します。 FVector Direction = FRotationMatrix(Controller->GetControlRotation()).GetScaledAxis(EAxis::X); AddMovementInput(Direction, Value); } void AFPSCharacter::MoveRight(float Value) { // どちらが「右」なのかを調べ、プレイヤーがその方向に移動したがっていることを記録します。 FVector Direction = FRotationMatrix(Controller->GetControlRotation()).GetScaledAxis(EAxis::Y); AddMovementInput(Direction, Value); } void AFPSCharacter::StartJump() { bPressedJump = true; } void AFPSCharacter::StopJump() { bPressedJump = false; } void AFPSCharacter::Fire() { }
発射物のスポーン位置を定義する
FPSProjectile
アクタをスポーンする際に、OnFire
関数の実装に関して、次の 2 点の考慮事項があります。発射物をスポーンする場所。
発射物クラス (
FPSCharacter
とその派生ブループリントがスポーンする発射物の種類を認識するため)。
カメラ空間のオフセット ベクターを使用して、発射物のスポーン位置を決定します。このパラメータを編集可能にして、
BP_FPSCharacter
ブループリントで設定し調整できるようにします。最終的に、このデータに基づいて発射物の初期位置を計算できます。FPSCharacter.h
クラス ヘッダ ファイルに移動します。次のコードを
FPSCharacter.h
のpublic
アクセス指定子の下に追加します。// カメラ位置からの銃口のオフセット。 UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Gameplay) FVector MuzzleOffset;
EditAnywhere
により、ブループリント エディタのデフォルト モード内、またはキャラクターのいずれかのインスタンスの [Details (詳細)] タブで、銃口オフセットの値を変更できます。BlueprintReadWrite
指定子により、ブループリント内で銃口オフセットの値を取得、設定できます。次のコードを
FPSCharacter.h
のprotected
アクセス指定子の下に追加します。protected: // スポーンする発射物クラス。 UPROPERTY(EditDefaultsOnly, Category = Projectile) TSubclassOf<class AFPSProjectile> ProjectileClass;
EditDefaultsOnly
の意味は、発射物クラスをブループリントでデフォルトとして設定できるだけで、ブループリントの各インスタンスではできないということです。FPSCharacter.h
は次のようになります。// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "GameFramework/Character.h" #include "Camera/CameraComponent.h" #include "Components/CapsuleComponent.h" #include "FPSCharacter.generated.h" UCLASS() class FPSPROJECT_API AFPSCharacter : public ACharacter { GENERATED_BODY() public: // このキャラクターのプロパティのデフォルト値を設定します。 AFPSCharacter(); protected: // ゲームの開始時またはスポーン時に呼び出します。 virtual void BeginPlay() override; // スポーンする発射物クラス。 UPROPERTY(EditDefaultsOnly, Category = Projectile) TSubclassOf<class AFPSProjectile> ProjectileClass; public: // フレームごとに呼び出します。 virtual void Tick( float DeltaTime ) override; // Input に機能をバインドする際に呼び出します。 virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override; // 前方および後方への移動のための入力を処理します。 UFUNCTION() void MoveForward(float Value); // 右および左への移動のための入力を処理します。 UFUNCTION() void MoveRight(float Value); // キーが押された際にジャンプ フラグを設定します。 UFUNCTION() void StartJump(); // キーが放された際にジャンプ フラグをクリアします。 UFUNCTION() void StopJump(); // 発射物を発射する関数。 UFUNCTION() void Fire(); // FPS カメラ UPROPERTY(VisibleAnywhere) UCameraComponent* FPSCameraComponent; // ファースト パーソン メッシュ (腕) で、所有しているプレイヤーにのみ表示されます。 UPROPERTY(VisibleDefaultsOnly, Category = Mesh) USkeletalMeshComponent* FPSMesh; // カメラ位置からの銃口のオフセット。 UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Gameplay) FVector MuzzleOffset; };
コードをコンパイルしてチェックする
ここで、新しく実装した発射物コードをコンパイルしてチェックします。
すべてのヘッダおよび実装ファイルを Visual Studio で保存します。
[Solution Explorer] に移動して [FPSProject] を選択します。
[FPSProject] を右クリックして [Build (ビルド)] を選択し、プロジェクトをコンパイルします。
このステップの目的は、次のステップに進む前に、何らかのビルド エラーを検出することです。このチュートリアルの範囲を超えるビルド エラーや警告が発生した場合は、「
[](programming-and-scripting\coding-standard)」や「Unreal Engine API リファレンス」を参照してください。
3.2 - シューティングを実装する
ファースト パーソン シューター キャラクターのシューティングを実装する方法を習得します。
Fire 関数を実装する
FPSCharacter.h
クラス ヘッダ ファイルに移動します。次のコード行を
FPSCharacter.h
に追加します。#include "FPSProjectile.h"
FPSCharacter.cpp
クラス用の実装ファイルに移動します。次の
Fire
関数定義をFPSCharacter.cpp
に追加します。void AFPSCharacter::Fire() { // 発射物を発射するよう試行します。 if (ProjectileClass) { // カメラのトランスフォームを取得します。 FVector CameraLocation; FRotator CameraRotation; GetActorEyesViewPoint(CameraLocation, CameraRotation); // MuzzleOffset がわずかにカメラ正面に対して発射物をスポーンするように設定します。 MuzzleOffset.Set(100.0f, 0.0f, 0.0f); // MuzzleOffset をカメラ空間からワールド空間にトランスフォームします。 FVector MuzzleLocation = CameraLocation + FTransform(CameraRotation).TransformVector(MuzzleOffset); // 照準をわずかに上方に歪めます。 FRotator MuzzleRotation = CameraRotation; MuzzleRotation.Pitch += 10.0f; UWorld* World = GetWorld(); if (World) { FActorSpawnParameters SpawnParams; SpawnParams.Owner = this; SpawnParams.Instigator = GetInstigator(); // 発射物を銃口にスポーンします。 AFPSProjectile* Projectile = World->SpawnActor<AFPSProjectile>(ProjectileClass, MuzzleLocation, MuzzleRotation, SpawnParams); if (Projectile) { // 発射物の初期軌道を設定します。 FVector LaunchDirection = MuzzleRotation.Vector(); Projectile->FireInDirection(LaunchDirection); } } } }
FPSCharacter.h
は次のようになります。// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "GameFramework/Character.h" #include "Camera/CameraComponent.h" #include "Components/CapsuleComponent.h" #include "FPSProjectile.h" #include "FPSCharacter.generated.h" UCLASS() class FPSPROJECT_API AFPSCharacter : public ACharacter { GENERATED_BODY() public: // このキャラクターのプロパティのデフォルト値を設定します。 AFPSCharacter(); protected: // ゲームの開始時またはスポーン時に呼び出します。 virtual void BeginPlay() override; // スポーンする発射物クラス。 UPROPERTY(EditAnywhere, Category = Projectile) TSubclassOf<class AFPSProjectile> ProjectileClass; public: // フレームごとに呼び出します。 virtual void Tick(float DeltaTime) override; // Input に機能をバインドする際に呼び出します。 virtual void SetupPlayerInputComponent(class UIComponent* PlayerInputComponent) override; // 前方および後方への移動のための入力を処理します。 UFUNCTION() void MoveForward(float Value); // 右および左への移動のための入力を処理します。 UFUNCTION() void MoveRight(float Value); // キーが押された際にジャンプ フラグを設定します。 UFUNCTION() void StartJump(); // キーが放された際にジャンプ フラグをクリアします。 UFUNCTION() void StopJump(); // FPS カメラ UPROPERTY(VisibleAnywhere) UCameraComponent* FPSCameraComponent; // ファースト パーソン メッシュ (腕) で、所有しているプレイヤーにのみ表示されます。 UPROPERTY(VisibleDefaultsOnly, Category = Mesh) USkeletalMeshComponent* FPSMesh; // 発射物を発射する関数。 UFUNCTION() void Fire(); // カメラ位置からの銃口のオフセット。 UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Gameplay) FVector MuzzleOffset; };
FPSCharacter.cpp
は次のようになります。// Copyright Epic Games, Inc. All Rights Reserved. #include "FPSCharacter.h" // デフォルト値を設定します。 AFPSCharacter::AFPSCharacter() { // このキャラクターがフレームごとに Tick() を呼び出すように設定します。必要ない場合は、パフォーマンス向上のためにこれをオフにすることができます。 PrimaryActorTick.bCanEverTick = true; // 一人称視点カメラ コンポーネントを作成します。 FPSCameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("FirstPersonCamera")); check(FPSCameraComponent != nullptr); // カメラ コンポーネントをカプセル コンポーネントにアタッチします。 FPSCameraComponent->SetupAttachment(CastChecked<USceneComponent, UCapsuleComponent>(GetCapsuleComponent())); // カメラを目の少し上に位置づけます。 FPSCameraComponent->SetRelativeLocation(FVector(0.0f, 0.0f, 50.0f + BaseEyeHeight)); // ポーンがカメラの回転をコントロールできます。 FPSCameraComponent->bUsePawnControlRotation = true; // 所有するプレイヤー向けのファースト パーソン メッシュ コンポーネントを作成します。 FPSMesh = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("FirstPersonMesh")); check(FPSMesh != nullptr); // このメッシュは所有するプレイヤーにのみ表示されます。 FPSMesh->SetOnlyOwnerSee(true); // FPS メッシュを FPS カメラにアタッチします。 FPSMesh->SetupAttachment(FPSCameraComponent); // 単一のメッシュがあるかのような錯覚を維持するには、一部の環境シャドウイングを無効にします。 FPSMesh->bCastDynamicShadow = false; FPSMesh->CastShadow = false; // 所有するプレイヤーには通常の (サードパーソン) ボディ メッシュは表示されません。 GetMesh()->SetOwnerNoSee(true); } // ゲームの開始時またはスポーン時に呼び出します。 void AFPSCharacter::BeginPlay() { Super::BeginPlay(); check(GEngine != nullptr); // デバッグ メッセージを 5 秒間表示します。 // 「Key」値引数 -1 を指定すると、メッセージは更新またはリフレッシュされなくなります。 GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("We are using FPSCharacter.")); } // フレームごとに呼び出します。 void AFPSCharacter::Tick(float DeltaTime) { Super::Tick( DeltaTime ); } // Input に機能をバインドする際に呼び出します。 void AFPSCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) { Super::SetupPlayerInputComponent(PlayerInputComponent); // 「移動」バインディングをセットアップします。 PlayerInputComponent->BindAxis("MoveForward", this, &AFPSCharacter::MoveForward); PlayerInputComponent->BindAxis("MoveRight", this, &AFPSCharacter::MoveRight); // 「ルック」バインディングをセットアップします。 PlayerInputComponent->BindAxis("Turn", this, &AFPSCharacter::AddControllerYawInput); PlayerInputComponent->BindAxis("LookUp", this, &AFPSCharacter::AddControllerPitchInput); // 「アクション」バインディングをセットアップします。 PlayerInputComponent->BindAction("Jump", IE_Pressed, this, &AFPSCharacter::StartJump); PlayerInputComponent->BindAction("Jump", IE_Released, this, &AFPSCharacter::StopJump); PlayerInputComponent->BindAction("Fire", IE_Pressed, this, &AFPSCharacter::Fire); } void AFPSCharacter::MoveForward(float Value) { // どちらが「前方」なのかを調べ、プレイヤーがその方向に移動したがっていることを記録します。 FVector Direction = FRotationMatrix(Controller->GetControlRotation()).GetScaledAxis(EAxis::X); AddMovementInput(Direction, Value); } void AFPSCharacter::MoveRight(float Value) { // どちらが「右」なのかを調べ、プレイヤーがその方向に移動したがっていることを記録します。 FVector Direction = FRotationMatrix(Controller->GetControlRotation()).GetScaledAxis(EAxis::Y); AddMovementInput(Direction, Value); } void AFPSCharacter::StartJump() { bPressedJump = true; } void AFPSCharacter::StopJump() { bPressedJump = false; } void AFPSCharacter::Fire() { // 発射物を発射するよう試行します。 if (ProjectileClass) { // カメラのトランスフォームを取得します。 FVector CameraLocation; FRotator CameraRotation; GetActorEyesViewPoint(CameraLocation, CameraRotation); // MuzzleOffset がわずかにカメラ正面に対して発射物をスポーンするように設定します。 MuzzleOffset.Set(100.0f, 0.0f, 0.0f); // MuzzleOffset をカメラ空間からワールド空間にトランスフォームします。 FVector MuzzleLocation = CameraLocation + FTransform(CameraRotation).TransformVector(MuzzleOffset); // 照準をわずかに上方に歪めます。 FRotator MuzzleRotation = CameraRotation; MuzzleRotation.Pitch += 10.0f; UWorld* World = GetWorld(); if (World) { FActorSpawnParameters SpawnParams; SpawnParams.Owner = this; SpawnParams.Instigator = GetInstigator(); // 発射物を銃口にスポーンします。 AFPSProjectile* Projectile = World->SpawnActor<AFPSProjectile>(ProjectileClass, MuzzleLocation, MuzzleRotation, SpawnParams); if (Projectile) { // 発射物の初期軌道を設定します。 FVector LaunchDirection = MuzzleRotation.Vector(); Projectile->FireInDirection(LaunchDirection); } } } }
FPSCharacter.h
とFPSCharacter.cpp
を Visual Studio で保存します。[Solution Explorer] に移動して [FPSProject] を選択します。
[FPSProject] を右クリックして [Build] を選択し、プロジェクトをコンパイルします。
発射物メッシュをインポートする
先に進む前に、次のリンクからサンプル メッシュをダウンロードして展開してください。
Unreal Engine を開いて コンテンツ ブラウザ に移動し、「Content」フォルダを開きます。
コンテンツ ブラウザ のファイル ウィンドウ内を右クリックして、[Import Asset] ダイアログ ウィンドウを開きます。
右クリックのインポートについて説明しましたが、コンテンツをインポートするには 3 つの方法があります。コンテンツのインポート方法については、「アセットを直接インポートする」を参照してください。
[Import to /Game... (/Game にインポート...)] をクリックして、[Import (インポート)] ダイアログ ウィンドウを開きます。
「Sphere.fbx」メッシュ ファイルをダウンロードしたフォルダで見つけて選択します。
[Open (開く)] をクリックして、メッシュのプロジェクトへのインポートを開始します。
[FBX Import Options (FBX インポート オプション)] ダイアログ ウィンドウが表示されます。[Import All (すべてインポート)] をクリックすると、メッシュがプロジェクトに追加されます。
スムージング グループに関する次のエラーを無視します。
このメッシュではファースト パーソン メッシュ セットアップがまだ表示されます。後のセクションでセットアップするアニメーションと連動します。
[Main (メイン)] メニュー パネルで [File (ファイル)] をクリックし、[Save all (すべて保存)] を選択してインポートされたメッシュを保存します。
発射物のメッシュを追加する
Visual Studio を開き、[Solution Explorer] に移動します。
[Solution Explorer] で
FPSProjectile.h
クラス ヘッダ ファイルを開きます。次のコードを
FPSProjectile.h
のpublic
アクセス指定子の下に追加します。// 発射物メッシュ UPROPERTY(VisibleDefaultsOnly, Category = Projectile) UStaticMeshComponent* ProjectileMeshComponent;
Visual Studio の Solution Explorer に移動し、
FPSProjectile.cpp
クラス用の実装ファイルを開きます。次のコードを「
FPSProjectile.cpp
」のコンストラクタに追加します。if(!ProjectileMeshComponent) { ProjectileMeshComponent = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("ProjectileMeshComponent")); static ConstructorHelpers::FObjectFinder<UStaticMesh>Mesh(TEXT("[ADD STATIC MESH ASSET REFERENCE]")); if(Mesh.Succeeded()) { ProjectileMeshComponent->SetStaticMesh(Mesh.Object); } }
Unreal Editor を開いて コンテンツ ブラウザ に移動し、Sphere スタティックメッシュを右クリックして [Copy Reference (参照をコピー)] を選択します。
Visual Studio を開き、「
FPSProjectile.cpp
」のProjectileMeshComponent
コードに戻り、[ADD STATIC MESH ASSET REFERENCE]
をコピーしたリファレンスで置き換えます。コードは次のようになります。if(!ProjectileMeshComponent) { ProjectileMeshComponent = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("ProjectileMeshComponent")); static ConstructorHelpers::FObjectFinder<UStaticMesh>Mesh(TEXT("'/Game/Sphere.Sphere'")); if(Mesh.Succeeded()) { ProjectileMeshComponent->SetStaticMesh(Mesh.Object); } }
アセットのリファレンス パスは、コンテンツ ブラウザで Sphere メッシュを保存した場所により変わります。また、コピーしたアセットのリファレンスを貼り付けたとき、リファレンスには、アセットのリファレンス パスの前にアセットのタイプ名が含まれます。この場合は、StaticMesh'/Game/Sphere.Sphere' です。必ずアセットのタイプ名 (StaticMesh など) をリファレンス パスから削除します。
FPSProjectile.h
は次のようになります。// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "GameFramework/Actor.h" #include "Components/SphereComponent.h" #include "GameFramework/ProjectileMovementComponent.h" #include "FPSProjectile.generated.h" UCLASS() class FPSPROJECT_API AFPSProjectile : public AActor { GENERATED_BODY() public: // このアクタのプロパティのデフォルト値を設定します。 AFPSProjectile(); protected: // ゲームの開始時またはスポーン時に呼び出します。 virtual void BeginPlay() override; public: // フレームごとに呼び出します。 virtual void Tick(float DeltaTime) override; // 球体コリジョン コンポーネント UPROPERTY(VisibleDefaultsOnly, Category = Projectile) USphereComponent* CollisionComponent; // 発射物移動コンポーネント UPROPERTY(VisibleAnywhere, Category = Movement) UProjectileMovementComponent* ProjectileMovementComponent; // 発射物の発射方向の速度を初期化する関数。 void FireInDirection(const FVector& ShootDirection); // 発射物メッシュ UPROPERTY(VisibleDefaultsOnly, Category = Projectile) UStaticMeshComponent* ProjectileMeshComponent; };
FPSProjectile.cpp
は次のようになります。// Copyright Epic Games, Inc. All Rights Reserved. #include "FPSProjectile.h" // デフォルト値を設定します。 AFPSProjectile::AFPSProjectile() { // 各フレームでこのアクタが Tick() を呼び出すよう設定します。必要ない場合は、パフォーマンス向上のためにこれをオフにすることができます。 PrimaryActorTick.bCanEverTick = true; if (!RootComponent) { RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("ProjectileSceneComponent")); } if (!CollisionComponent) { // 単純なコリジョン表現として、球体を使用します。 CollisionComponent = CreateDefaultSubobject<USphereComponent>(TEXT("SphereComponent")); // 球体のコリジョン半径を設定します。 CollisionComponent->InitSphereRadius(15.0f); // コリジョン コンポーネントとなるルート コンポーネントを設定します。 RootComponent = CollisionComponent; } if (!ProjectileMovementComponent) { // このコンポーネントを使用し、この発射物の動きを駆動します。 ProjectileMovementComponent = CreateDefaultSubobject<UProjectileMovementComponent>(TEXT("ProjectileMovementComponent")); ProjectileMovementComponent->SetUpdatedComponent(CollisionComponent); ProjectileMovementComponent->InitialSpeed = 3000.0f; ProjectileMovementComponent->MaxSpeed = 3000.0f; ProjectileMovementComponent->bRotationFollowsVelocity = true; ProjectileMovementComponent->bShouldBounce = true; ProjectileMovementComponent->Bounciness = 0.3f; ProjectileMovementComponent->ProjectileGravityScale = 0.0f; } if (!ProjectileMeshComponent) { ProjectileMeshComponent = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("ProjectileMeshComponent")); static ConstructorHelpers::FObjectFinder<UStaticMesh>Mesh(TEXT("'/Game/Sphere.Sphere'")); if (Mesh.Succeeded()) { ProjectileMeshComponent->SetStaticMesh(Mesh.Object); } } } // ゲームの開始時またはスポーン時に呼び出します。 void AFPSProjectile::BeginPlay() { Super::BeginPlay(); } // フレームごとに呼び出します。 void AFPSProjectile::Tick(float DeltaTime) { Super::Tick(DeltaTime); } // 発射物の発射方向の速度を初期化する関数。 void AFPSProjectile::FireInDirection(const FVector& ShootDirection) { ProjectileMovementComponent->Velocity = ShootDirection * ProjectileMovementComponent->InitialSpeed; }
FPSProjectile.h
とFPSProjectile.cpp
を Visual Studio で保存します。[Solution Explorer] に移動して [FPSProject] を選択します。
[FPSProject] を右クリックして [Build (ビルド)] を選択し、プロジェクトをコンパイルします。
発射物のマテリアルを追加する
Visual Studio で
FPSProjectile.h
に移動し、次のコードをFPSProjectile.h
のpublic
アクセス指定子の下に追加します。// 発射物のマテリアル UPROPERTY(VisibleDefaultsOnly, Category = Movement) UMaterialInstanceDynamic* ProjectileMaterialInstance;
FPSProjectile.cpp
に移動し、次のコードをif (!ProjectileMeshComponent)
コンストラクタの下に追加します。static ConstructorHelpers::FObjectFinder<UMaterial>Material(TEXT("[ADD MATERIAL ASSET REFERENCE]")); if (Material.Succeeded()) { ProjectileMaterialInstance = UMaterialInstanceDynamic::Create(Material.Object, ProjectileMeshComponent); } ProjectileMeshComponent->SetMaterial(0, ProjectileMaterialInstance); ProjectileMeshComponent->SetRelativeScale3D(FVector(0.09f, 0.09f, 0.09f)); ProjectileMeshComponent->SetupAttachment(RootComponent);
Unreal Engine を開き、コンテンツ ブラウザ の「Content」フォルダに移動します。コンテンツ ブラウザ のファイル ウィンドウ内を右クリックし、[Material (マテリアル)] を選択します。
新しいマテリアルに「SphereMaterial」という名前を付けます。
新規マテリアルのノード グラフを次のようなプロパティで設定します。
Base Color: Base Color ピンからワイヤーを引き出すと表示されるウィンドウで Constant3Vector ノードを検索して選択し、「(R:1; G:0; B:0)」に設定します
Specular: Specular ピンからワイヤーを引き出すと表示されるウィンドウで Constant ノードを検索して選択し、値 を「0.5」に設定します
Emissive Color: Emissive Color からワイヤーを引き出すと表示されるウィンドウで Constant ノードを検索して選択し、値 を「0.05」に設定します
このステップでは、基本的なマテリアル アセットを作成します。複雑なマテリアルの作成方法を習得するには、マテリアルの使用と作成方法を参照してください。
新規マテリアルのノード グラフを設定した後、[Save (保存)] をクリックし、コンテンツ ブラウザ を開きます。
Sphere マテリアルを右クリックし、[Copy Reference] を選択します。
「
FPSProjectile.cpp
」のProjectileMeshComponent
コードに戻り、[ADD MATERIAL ASSET REFERENCE]
をコピーしたリファレンスで置き換えます。コードは次のようになります。static ConstructorHelpers::FObjectFinder<UMaterial>Material(TEXT("'/Game/SphereMaterial.SphereMaterial'")); if (Material.Succeeded()) { ProjectileMaterialInstance = UMaterialInstanceDynamic::Create(Material.Object, ProjectileMeshComponent); } ProjectileMeshComponent->SetMaterial(0, ProjectileMaterialInstance); ProjectileMeshComponent->SetRelativeScale3D(FVector(0.09f, 0.09f, 0.09f)); ProjectileMeshComponent->SetupAttachment(RootComponent);
アセットのリファレンス パスは、コンテンツ ブラウザで Sphere マテリアルを保存した場所により変わります。また、コピーしたアセットのリファレンスを貼り付けたとき、リファレンスには、アセットのリファレンス パスの前にアセットのタイプ名が含まれます。この場合は、Material'/Game/Sphere.Sphere' です。必ずアセットのタイプ名 (Material など) をリファレンス パスから削除します。
FPSProjectile.h
は次のようになります。// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "GameFramework/Actor.h" #include "Components/SphereComponent.h" #include "GameFramework/ProjectileMovementComponent.h" #include "FPSProjectile.generated.h" UCLASS() class FPSPROJECT_API AFPSProjectile : public AActor { GENERATED_BODY() public: // このアクタのプロパティのデフォルト値を設定します。 AFPSProjectile(); protected: // ゲームの開始時またはスポーン時に呼び出します。 virtual void BeginPlay() override; public: // フレームごとに呼び出します。 virtual void Tick(float DeltaTime) override; // 球体コリジョン コンポーネント UPROPERTY(VisibleDefaultsOnly, Category = Projectile) USphereComponent* CollisionComponent; // 発射物移動コンポーネント UPROPERTY(VisibleAnywhere, Category = Movement) UProjectileMovementComponent* ProjectileMovementComponent; // 発射物の発射方向の速度を初期化する関数。 void FireInDirection(const FVector& ShootDirection); // 発射物メッシュ UPROPERTY(VisibleDefaultsOnly, Category = Projectile) UStaticMeshComponent* ProjectileMeshComponent; // 発射物のマテリアル UPROPERTY(VisibleDefaultsOnly, Category = Movement) UMaterialInstanceDynamic* ProjectileMaterialInstance; };
FPSProjectile.cpp
は次のようになります。// Copyright Epic Games, Inc. All Rights Reserved. #include "FPSProjectile.h" // デフォルト値を設定します。 AFPSProjectile::AFPSProjectile() { // 各フレームでこのアクタが Tick() を呼び出すよう設定します。必要ない場合は、パフォーマンス向上のためにこれをオフにすることができます。 PrimaryActorTick.bCanEverTick = true; if (!RootComponent) { RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("ProjectileSceneComponent")); } if (!CollisionComponent) { // 単純なコリジョン表現として、球体を使用します。 CollisionComponent = CreateDefaultSubobject<USphereComponent>(TEXT("SphereComponent")); // 球体のコリジョン半径を設定します。 CollisionComponent->InitSphereRadius(15.0f); // コリジョン コンポーネントとなるルート コンポーネントを設定します。 RootComponent = CollisionComponent; } if (!ProjectileMovementComponent) { // このコンポーネントを使用し、この発射物の動きを駆動します。 ProjectileMovementComponent = CreateDefaultSubobject<UProjectileMovementComponent>(TEXT("ProjectileMovementComponent")); ProjectileMovementComponent->SetUpdatedComponent(CollisionComponent); ProjectileMovementComponent->InitialSpeed = 3000.0f; ProjectileMovementComponent->MaxSpeed = 3000.0f; ProjectileMovementComponent->bRotationFollowsVelocity = true; ProjectileMovementComponent->bShouldBounce = true; ProjectileMovementComponent->Bounciness = 0.3f; ProjectileMovementComponent->ProjectileGravityScale = 0.0f; } if (!ProjectileMeshComponent) { ProjectileMeshComponent = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("ProjectileMeshComponent")); static ConstructorHelpers::FObjectFinder<UStaticMesh>Mesh(TEXT("'/Game/Sphere.Sphere'")); if (Mesh.Succeeded()) { ProjectileMeshComponent->SetStaticMesh(Mesh.Object); } static ConstructorHelpers::FObjectFinder<UMaterial>Material(TEXT("'/Game/SphereMaterial.SphereMaterial'")); if (Material.Succeeded()) { ProjectileMaterialInstance = UMaterialInstanceDynamic::Create(Material.Object, ProjectileMeshComponent); } ProjectileMeshComponent->SetMaterial(0, ProjectileMaterialInstance); ProjectileMeshComponent->SetRelativeScale3D(FVector(0.09f, 0.09f, 0.09f)); ProjectileMeshComponent->SetupAttachment(RootComponent); } } // ゲームの開始時またはスポーン時に呼び出します。 void AFPSProjectile::BeginPlay() { Super::BeginPlay(); } // フレームごとに呼び出します。 void AFPSProjectile::Tick(float DeltaTime) { Super::Tick(DeltaTime); } // 発射物の発射方向の速度を初期化する関数。 void AFPSProjectile::FireInDirection(const FVector& ShootDirection) { ProjectileMovementComponent->Velocity = ShootDirection * ProjectileMovementComponent->InitialSpeed; }
UE を開き、コンテンツ ブラウザ の「Blueprints」フォルダに移動して「BP_FPSCharacter」ファイルを開きます。
フル ブループリント エディタ を開き (必要な場合)、[Components (コンポーネント)] パネルに移動して [BP_FPSCharacter (Self)] コンポーネントを選択します。
開いている ブループリント エディタ の[Details] パネルに移動します。
[Projectile (発射物)] セクションを見つけて、[Projectile Class] の隣にあるドロップダウンから [FPSProjectile] を選択します。
ドロップダウン メニューで [FPSProjectile] が見つからない場合は、Unreal Engine を再実行してください。
[Compile (コンパイル)] ボタンと [Save] ボタンを順にクリックします。
Visual Studio を開き、[Solution Explorer] に移動して [FPSProject] を選択します。
[FPSProject] を右クリックして [Build] を選択し、プロジェクトをコンパイルします。
ゲームを PIE モードで実行して、スタティックメッシュやマテリアルがシーンにスポーンされていることを確認します。
弾を発射すると、[Outliner (アウトライナー)] で発射物の数が増えていくのがわかります。これは、存続期間を定義していないためです。
次のセクションのチュートリアルでは、発射物の初期存続期間を定義する方法を示します。
3.3 - 発射物のコリジョンと存続期間を設定する
現時点で発射物は次のようになっています。
無限に存続 (シーン アウトライナーから消えない)
ワールドの他のオブジェクトと衝突しない
このステップでは、発射物のコリジョンと存続期間を設定します。
発射物の存続期間を制限する
Visual Studio を開き、[Solution Explorer] に移動します。
[Solution Explorer] で
FPSProjectile.cpp
クラス用の実装ファイルを開きます。次のコードを
FPSProjectile.cpp
のAFPSProjectile::AFPSProjectile()
コンストラクタに追加し、発射物の存続期間を設定します。// 3 秒後に発射物を削除します。 InitialLifeSpan = 3.0f;
FPSProjectile.cpp
は次のようになります。// Copyright Epic Games, Inc. All Rights Reserved. #include "FPSProjectile.h" // デフォルト値を設定します。 AFPSProjectile::AFPSProjectile() { // 各フレームでこのアクタが Tick() を呼び出すよう設定します。必要ない場合は、パフォーマンス向上のためにこれをオフにすることができます。 PrimaryActorTick.bCanEverTick = true; if (!RootComponent) { RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("ProjectileSceneComponent")); } if (!CollisionComponent) { // 単純なコリジョン表現として、球体を使用します。 CollisionComponent = CreateDefaultSubobject<USphereComponent>(TEXT("SphereComponent")); // 球体のコリジョン半径を設定します。 CollisionComponent->InitSphereRadius(15.0f); // コリジョン コンポーネントとなるルート コンポーネントを設定します。 RootComponent = CollisionComponent; } if (!ProjectileMovementComponent) { // このコンポーネントを使用し、この発射物の動きを駆動します。 ProjectileMovementComponent = CreateDefaultSubobject<UProjectileMovementComponent>(TEXT("ProjectileMovementComponent")); ProjectileMovementComponent->SetUpdatedComponent(CollisionComponent); ProjectileMovementComponent->InitialSpeed = 3000.0f; ProjectileMovementComponent->MaxSpeed = 3000.0f; ProjectileMovementComponent->bRotationFollowsVelocity = true; ProjectileMovementComponent->bShouldBounce = true; ProjectileMovementComponent->Bounciness = 0.3f; ProjectileMovementComponent->ProjectileGravityScale = 0.0f; } if (!ProjectileMeshComponent) { ProjectileMeshComponent = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("ProjectileMeshComponent")); static ConstructorHelpers::FObjectFinder<UStaticMesh>Mesh(TEXT("'/Game/Sphere.Sphere'")); if (Mesh.Succeeded()) { ProjectileMeshComponent->SetStaticMesh(Mesh.Object); } static ConstructorHelpers::FObjectFinder<UMaterial>Material(TEXT("'/Game/SphereMaterial.SphereMaterial'")); if (Material.Succeeded()) { ProjectileMaterialInstance = UMaterialInstanceDynamic::Create(Material.Object, ProjectileMeshComponent); } ProjectileMeshComponent->SetMaterial(0, ProjectileMaterialInstance); ProjectileMeshComponent->SetRelativeScale3D(FVector(0.09f, 0.09f, 0.09f)); ProjectileMeshComponent->SetupAttachment(RootComponent); } // 3 秒後に発射物を削除します。 InitialLifeSpan = 3.0f; } // ゲームの開始時またはスポーン時に呼び出します。 void AFPSProjectile::BeginPlay() { Super::BeginPlay(); } // フレームごとに呼び出します。 void AFPSProjectile::Tick(float DeltaTime) { Super::Tick(DeltaTime); } // 発射物の発射方向の速度を初期化する関数。 void AFPSProjectile::FireInDirection(const FVector& ShootDirection) { ProjectileMovementComponent->Velocity = ShootDirection * ProjectileMovementComponent->InitialSpeed; }
ファイルを保存します。
[Solution Explorer] に移動して [FPSProject] を選択します。
[FPSProject] を右クリックして [Build] を選択し、プロジェクトをコンパイルします。
発射物が 3 秒後に破棄されるかどうかを確認するために、ゲームを PIE モードで実行します。
[Outliner] で確認すると、スポーンされた発射物が 3 秒後にシーンから消えます。
発射物のコリジョン設定を編集する
Unreal Engine には複数のプリセット コリジョン チャンネルがパッケージされています。ただしエンジンには、ゲーム プロジェクトで利用できる、カスタマイズ可能なチャンネルも用意されています。
カスタム仕様のコリジョン チャンネルを作成するには、Unreal Engine に移動して [Project Settings] を開き、左側にある [Engine] セクションで [Collision (コリジョン)] を選択したら、右側にある [Preset (プリセット)] セクションを展開します。
[Object Channels (オブジェクト チャンネル)] に移動して [New Object Channel... (新規のオブジェクトチャンネル...)] を選択し、新しいコリジョン チャンネルを作成します。新規コリジョン チャンネルに「Projectile」という名前を付けて、[Default Response (デフォルト応答)] が「Block」に設定されていることを確認してから、[Accept (承認)] をクリックします。
[Preset] で [New... (新規...)] を選択し、新規プロファイルに「Projectile」という名前を付けます。次の画像を参照してコリジョン プリセットを設定し、[Accept] をクリックします。
このコリジョン プロファイルでは、発射物がスタティック アクタ、ダイナミック アクタ、物理シミュレーションを利用するアクタ、乗り物、被破壊アクタでブロックされるように指定します。また、このコリジョン プロファイルでは発射物がポーンとオーバーラップすることも指定します。
新規コリジョン チャンネルの設定を使用する
Visual Studio を開いて
FPSProjectile.cpp
クラス用の実装ファイルに移動します。FPSProjectile
コンストラクタで、次のコード行をif (!CollisionComponent)
ブロックのCreateDefaultSubobject<USphereComponent>(TEXT("SphereComponent"));
の後に追加します。// 球体のコリジョン プロファイル名を「Projectile」に設定します。 CollisionComponent->BodyInstance.SetCollisionProfileName(TEXT("Projectile"));
FPSProjectile.cpp
は次のようになります。// Copyright Epic Games, Inc. All Rights Reserved. #include "FPSProjectile.h" // デフォルト値を設定します。 AFPSProjectile::AFPSProjectile() { // 各フレームでこのアクタが Tick() を呼び出すよう設定します。必要ない場合は、パフォーマンス向上のためにこれをオフにすることができます。 PrimaryActorTick.bCanEverTick = true; if (!RootComponent) { RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("ProjectileSceneComponent")); } if (!CollisionComponent) { // 単純なコリジョン表現として、球体を使用します。 CollisionComponent = CreateDefaultSubobject<USphereComponent>(TEXT("SphereComponent")); // 球体のコリジョン プロファイル名を「Projectile」に設定します。 CollisionComponent->BodyInstance.SetCollisionProfileName(TEXT("Projectile")); // 球体のコリジョン半径を設定します。 CollisionComponent->InitSphereRadius(15.0f); // コリジョン コンポーネントとなるルート コンポーネントを設定します。 RootComponent = CollisionComponent; } if (!ProjectileMovementComponent) { // このコンポーネントを使用し、この発射物の動きを駆動します。 ProjectileMovementComponent = CreateDefaultSubobject<UProjectileMovementComponent>(TEXT("ProjectileMovementComponent")); ProjectileMovementComponent->SetUpdatedComponent(CollisionComponent); ProjectileMovementComponent->InitialSpeed = 3000.0f; ProjectileMovementComponent->MaxSpeed = 3000.0f; ProjectileMovementComponent->bRotationFollowsVelocity = true; ProjectileMovementComponent->bShouldBounce = true; ProjectileMovementComponent->Bounciness = 0.3f; ProjectileMovementComponent->ProjectileGravityScale = 0.0f; } if (!ProjectileMeshComponent) { ProjectileMeshComponent = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("ProjectileMeshComponent")); static ConstructorHelpers::FObjectFinder<UStaticMesh>Mesh(TEXT("'/Game/Sphere.Sphere'")); if (Mesh.Succeeded()) { ProjectileMeshComponent->SetStaticMesh(Mesh.Object); } static ConstructorHelpers::FObjectFinder<UMaterial>Material(TEXT("'/Game/SphereMaterial.SphereMaterial'")); if (Material.Succeeded()) { ProjectileMaterialInstance = UMaterialInstanceDynamic::Create(Material.Object, ProjectileMeshComponent); } ProjectileMeshComponent->SetMaterial(0, ProjectileMaterialInstance); ProjectileMeshComponent->SetRelativeScale3D(FVector(0.09f, 0.09f, 0.09f)); ProjectileMeshComponent->SetupAttachment(RootComponent); } // 3 秒後に発射物を削除します。 InitialLifeSpan = 3.0f; } // ゲームの開始時またはスポーン時に呼び出します。 void AFPSProjectile::BeginPlay() { Super::BeginPlay(); } // フレームごとに呼び出します。 void AFPSProjectile::Tick(float DeltaTime) { Super::Tick(DeltaTime); } // 発射物の発射方向の速度を初期化する関数。 void AFPSProjectile::FireInDirection(const FVector& ShootDirection) { ProjectileMovementComponent->Velocity = ShootDirection * ProjectileMovementComponent->InitialSpeed; }
ファイルを保存します。
[Solution Explorer] に移動して [FPSProject] を選択します。
[FPSProject] を右クリックして [Build] を選択し、プロジェクトをコンパイルします。
3.4 - 発射物をワールドとインタラクトさせる
ここで、発射物のコリジョン インタラクションを検出でき、コリジョンに対応する方法を決定できます。このステップでは、OnHit
関数をコリジョン イベントに対応する FPSProjectile
に追加します。
発射物をコリジョンに反応させる
FPSProjectile.h.
を開く次のコードを
FPSProjectile.h
のpublic
アクセス指定子の下に追加します。// 発射物が何かにヒットした場合に呼び出される関数。 UFUNCTION() void OnHit(UPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComponent, FVector NormalImpulse, const FHitResult& Hit);
FPSProjectile.h
は次のようになります。// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "GameFramework/Actor.h" #include "Components/SphereComponent.h" #include "GameFramework/ProjectileMovementComponent.h" #include "FPSProjectile.generated.h" UCLASS() class FPSPROJECT_API AFPSProjectile : public AActor { GENERATED_BODY() public: // このアクタのプロパティのデフォルト値を設定します。 AFPSProjectile(); protected: // ゲームの開始時またはスポーン時に呼び出します。 virtual void BeginPlay() override; public: // フレームごとに呼び出します。 virtual void Tick(float DeltaTime) override; // 球体コリジョン コンポーネント UPROPERTY(VisibleDefaultsOnly, Category = Projectile) USphereComponent* CollisionComponent; // 発射物移動コンポーネント UPROPERTY(VisibleAnywhere, Category = Movement) UProjectileMovementComponent* ProjectileMovementComponent; // 発射物の発射方向の速度を初期化する関数。 void FireInDirection(const FVector& ShootDirection); // 発射物メッシュ UPROPERTY(VisibleDefaultsOnly, Category = Projectile) UStaticMeshComponent* ProjectileMeshComponent; // 発射物のマテリアル UPROPERTY(VisibleDefaultsOnly, Category = Movement) UMaterialInstanceDynamic* ProjectileMaterialInstance; // 発射物が何かにヒットした場合に呼び出される関数。 UFUNCTION() void OnHit(UPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComponent, FVector NormalImpulse, const FHitResult& Hit); };
FPSProjectile.cpp
を開いて、次のコードを追加します。// 発射物が何かにヒットした場合に呼び出される関数。 void AFPSProjectile::OnHit(UPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComponent, FVector NormalImpulse, const FHitResult& Hit) { if (OtherActor != this && OtherComponent->IsSimulatingPhysics()) { OtherComponent->AddImpulseAtLocation(ProjectileMovementComponent->Velocity * 100.0f, Hit.ImpactPoint); } Destroy(); }
FPSProjectile
コンストラクタで、次のコード行をif (!CollisionComponent)
ブロックのBodyInstance.SetCollisionProfileName:
の後に追加します。// コンポーネントが何かにヒットしたときに呼び出されるイベント。 CollisionComponent->OnComponentHit.AddDynamic(this, &AFPSProjectile::OnHit);
FPSProjectile.cpp
は次のようになります。// Copyright Epic Games, Inc. All Rights Reserved. #include "FPSProjectile.h" // デフォルト値を設定します。 AFPSProjectile::AFPSProjectile() { // 各フレームでこのアクタが Tick() を呼び出すよう設定します。必要ない場合は、パフォーマンス向上のためにこれをオフにすることができます。 PrimaryActorTick.bCanEverTick = true; if (!RootComponent) { RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("ProjectileSceneComponent")); } if (!CollisionComponent) { // 単純なコリジョン表現として、球体を使用します。 CollisionComponent = CreateDefaultSubobject<USphereComponent>(TEXT("SphereComponent")); // 球体のコリジョン プロファイル名を「Projectile」に設定します。 CollisionComponent->BodyInstance.SetCollisionProfileName(TEXT("Projectile")); // コンポーネントが何かにヒットしたときに呼び出されるイベント。 CollisionComponent->OnComponentHit.AddDynamic(this, &AFPSProjectile::OnHit); // 球体のコリジョン半径を設定します。 CollisionComponent->InitSphereRadius(15.0f); // コリジョン コンポーネントとなるルート コンポーネントを設定します。 RootComponent = CollisionComponent; } if (!ProjectileMovementComponent) { // このコンポーネントを使用し、この発射物の動きを駆動します。 ProjectileMovementComponent = CreateDefaultSubobject<UProjectileMovementComponent>(TEXT("ProjectileMovementComponent")); ProjectileMovementComponent->SetUpdatedComponent(CollisionComponent); ProjectileMovementComponent->InitialSpeed = 3000.0f; ProjectileMovementComponent->MaxSpeed = 3000.0f; ProjectileMovementComponent->bRotationFollowsVelocity = true; ProjectileMovementComponent->bShouldBounce = true; ProjectileMovementComponent->Bounciness = 0.3f; ProjectileMovementComponent->ProjectileGravityScale = 0.0f; } if (!ProjectileMeshComponent) { ProjectileMeshComponent = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("ProjectileMeshComponent")); static ConstructorHelpers::FObjectFinder<UStaticMesh>Mesh(TEXT("'/Game/Sphere.Sphere'")); if (Mesh.Succeeded()) { ProjectileMeshComponent->SetStaticMesh(Mesh.Object); } static ConstructorHelpers::FObjectFinder<UMaterial>Material(TEXT("'/Game/SphereMaterial.SphereMaterial'")); if (Material.Succeeded()) { ProjectileMaterialInstance = UMaterialInstanceDynamic::Create(Material.Object, ProjectileMeshComponent); } ProjectileMeshComponent->SetMaterial(0, ProjectileMaterialInstance); ProjectileMeshComponent->SetRelativeScale3D(FVector(0.09f, 0.09f, 0.09f)); ProjectileMeshComponent->SetupAttachment(RootComponent); } // 3 秒後に発射物を削除します。 InitialLifeSpan = 3.0f; } // ゲームの開始時またはスポーン時に呼び出します。 void AFPSProjectile::BeginPlay() { Super::BeginPlay(); } // フレームごとに呼び出します。 void AFPSProjectile::Tick(float DeltaTime) { Super::Tick(DeltaTime); } // 発射物の発射方向の速度を初期化する関数。 void AFPSProjectile::FireInDirection(const FVector& ShootDirection) { ProjectileMovementComponent->Velocity = ShootDirection * ProjectileMovementComponent->InitialSpeed; } // 発射物が何かにヒットした場合に呼び出される関数。 void AFPSProjectile::OnHit(UPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComponent, FVector NormalImpulse, const FHitResult& Hit) { if (OtherActor != this && OtherComponent->IsSimulatingPhysics()) { OtherComponent->AddImpulseAtLocation(ProjectileMovementComponent->Velocity * 100.0f, Hit.ImpactPoint); } Destroy(); }
発射物のコリジョンをテストする
ファイルを Visual Studio で保存します。
[Solution Explorer] に移動して [FPSProject] を選択します。
[FPSProject] を右クリックして [Build] を選択し、プロジェクトをコンパイルします。
ビルドが終了したら、Unreal Editor の FPSProject に戻ります。
[Outliner] に移動して Floor スタティックメッシュを選択します。
選択した Floor メッシュをコピーして貼り付けます。
比率ロック ([Scale (スケール)] 行にある鍵型アイコン) が解除されていることを確認し、[Floor2] の [Detail] パネルにある [Transform (トランスフォーム)] セクションで、次のようにオプションを設定します。
Location (位置):(0.0, 0.0, 150)
Rotation (回転):(0.0, 0.0, 0.0)
Scale (スケール):(0.2, 0.2, 3.0)
[Physics (物理)] セクションにスクロールダウンし、[Simulate Physics (物理シミュレーション)] ボックスをオンにします。
メイン メニュー パネルで [File] をクリックし、[Save All] を選択してインポートされたメッシュを保存します。
レベル エディタのツールバー で [Play (プレイ)] ボタンをクリックします。
発射物とキューブとのコリジョンを確認するために、マウスの左ボタンをクリックして物体を発射し、レベルでキューブをいろいろな場所に移動させてみます。
[
Shift キーと Escape キー を押すか レベル エディタのツールバー にある [Stop (停止)] をクリックし、PIE モードを終了します。
3.5 - 照準線をビューポートに追加する
このステップでは、照準線 HUD 要素をゲームに追加して、狙いを定めて発射できるようにします。
照準アセットをインポートする
開始する前に、次のリンクからサンプル画像をダウンロードして展開してください。
コンテンツ ブラウザ に移動して「Content」フォルダを開きます。
コンテンツ ブラウザ のファイル ウィンドウ内を右クリックして、[Import Asset] ダイアログ ウィンドウを開きます。
[Import to /Game...] をクリックして、[Import] ダイアログ ウィンドウを開きます。
「crosshair.TGA」画像ファイルをダウンロードしたフォルダで見つけて選択します。
[Open] をクリックして、画像ファイルのプロジェクトへのインポートを開始します。
メイン メニュー パネルの [File] をクリックして、インポートされたメッシュを保存します。
新規 HUD クラスを追加する
[Tools] を メイン メニュー パネルでクリックし、[New C++ Class...] をクリックして新しい親クラスを選択します。
[Choose Parent Class] メニューが表示されます。[HUD] を親クラスとして選択し、[Next] をクリックします。
新規クラスに「FPSHUD」という名前を付けてから、[Create Class] をクリックします。
C++ クラスが作成されると、Visual Studio が自動的に、
FPSHUD.h
ヘッダ ファイルとFPSHUD.cpp
実装ファイルが開いた状態で表示されます。FPSHUD.h
クラス ヘッダ ファイルに移動し、次の変数をprotected
アクセス指定子の下に追加します。protected: // これは、画面中央に描画されます。 UPROPERTY(EditDefaultsOnly) UTexture2D* CrosshairTexture;
次の関数宣言を
FPSHUD.h
のpublic
アクセス指定子の下に追加します。public: // HUD のプライマリ ドロー コール virtual void DrawHUD() override;
次のヘッダ ファイルを
FPSHUD.h
に追加します。#include "Engine/Canvas.h"
FPSHUD.h
は次のようになります。// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "GameFramework/HUD.h" #include "Engine/Canvas.h" #include "FPSHUD.generated.h" /** * */ UCLASS() class FPSPROJECT_API AFPSHUD : public AHUD { GENERATED_BODY() protected: // これは、画面中央に描画されます。 UPROPERTY(EditDefaultsOnly) UTexture2D* CrosshairTexture; public: // HUD のプライマリ ドロー コール virtual void DrawHUD() override; };
FPSHUD.cpp
実装ファイルを開き、DrawHUD
関数を追加します。void AFPSHUD::DrawHUD() { Super::DrawHUD(); if (CrosshairTexture) { // キャンバスの中心を見つけます。 FVector2D Center(Canvas->ClipX * 0.5f, Canvas->ClipY * 0.5f); // テクスチャの半分の長さでオフセットを設定し、キャンバスの中心でテクスチャが揃うようにします。 FVector2D CrossHairDrawPosition(Center.X - (CrosshairTexture->GetSurfaceWidth() * 0.5f), Center.Y - (CrosshairTexture->GetSurfaceHeight() * 0.5f)); // 中心点に照準線を描画します。 FCanvasTileItem TileItem(CrossHairDrawPosition, CrosshairTexture->Resource, FLinearColor::White); TileItem.BlendMode = SE_BLEND_Translucent; Canvas->DrawItem(TileItem); } }
FPSHUD.h
とFPSHUD.cpp
を Visual Studio で保存します。[Solution Explorer] に移動して [FPSProject] を選択します。
[FPSProject] を右クリックして [Build] を選択し、プロジェクトをコンパイルします。
CPP HUD クラスのブループリントに拡張する
ここで、CPP HUD クラスをブループリントに拡張します。復習が必要な場合は、C++ とブループリント リファレンス ページで C++ クラスからブループリントへの拡張について確認してください。
FPSHUD
クラスを右クリックして、[C++ Class Actions (C++ クラスアクション)] メニューを開きます。[Create Blueprint class based on FPSHUD (FPSHUD に基づくブループリント クラスを作成します)] をクリックして、[Add Blueprint Class (ブループリントクラスを追加)] ダイアログ メニューを開きます。
新規ブループリント クラスに「BP_FPSHUD」という名前を付け、「Blueprints」フォルダを選択してから、[Create Blueprint Class (ブループリントクラスを作成)] ボタンをクリックします。
これで、「BluePrints」フォルダ内に BP_FPSHUD ブループリント クラスが新しく作成されます。
必ず BP_FPSHUD ブループリントを保存してから、ブループリント エディタ を閉じます。
デフォルトの HUD クラスを設定する
メイン メニュー パネルで [Edit] をクリックし、[Project Settings] を選択します。
[Project Settings] タブの左側にある [Project (プロジェクト)] セクションで、[Maps & Modes (マップ & モード)] を選択します。
[BP_FPSHUD] を [Default HUD] ドロップダウン メニューで選択します。
[Project Settings] メニューを閉じます。
戻って BP_FPSHUD を ブループリント エディタ で開きます。
次に、ブループリント エディタ の [FPSHUD] セクションにあるドロップダウン メニューをクリックし、照準線テクスチャを選択します。
最後に、BP_FPSHUD ブループリントを コンパイル して 保存 してから、ブループリント エディタ を閉じます。
HUD を確認する
レベル エディタのツールバー で [Play (プレイ)] ボタンをクリックします。新しく追加された照準線で発射物の狙いを定められます。
Shift キーと Escape キー を押すか レベル エディタのツールバー にある [Stop (停止)] をクリックし、PIE モードを終了します。
このセクションで完了したコード
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Components/SphereComponent.h"
#include "GameFramework/ProjectileMovementComponent.h"
#include "FPSProjectile.generated.h"
UCLASS()
class FPSPROJECT_API AFPSProjectile : public AActor
{
GENERATED_BODY()
public:
// このアクタのプロパティのデフォルト値を設定します。
AFPSProjectile();
protected:
// ゲームの開始時またはスポーン時に呼び出します。
virtual void BeginPlay() override;
public:
// フレームごとに呼び出します。
virtual void Tick(float DeltaTime) override;
// 球体コリジョン コンポーネント
UPROPERTY(VisibleDefaultsOnly, Category = Projectile)
USphereComponent* CollisionComponent;
// 発射物移動コンポーネント
UPROPERTY(VisibleAnywhere, Category = Movement)
UProjectileMovementComponent* ProjectileMovementComponent;
// 発射物の発射方向の速度を初期化する関数。
void FireInDirection(const FVector& ShootDirection);
// 発射物メッシュ
UPROPERTY(VisibleDefaultsOnly, Category = Projectile)
UStaticMeshComponent* ProjectileMeshComponent;
// 発射物のマテリアル
UPROPERTY(VisibleDefaultsOnly, Category = Movement)
UMaterialInstanceDynamic* ProjectileMaterialInstance;
// 発射物が何かにヒットした場合に呼び出される関数。
UFUNCTION()
void OnHit(UPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComponent, FVector NormalImpulse, const FHitResult& Hit);
};
// Copyright Epic Games, Inc. All Rights Reserved.
#include "FPSProjectile.h"
// デフォルト値を設定します。
AFPSProjectile::AFPSProjectile()
{
// 各フレームでこのアクタが Tick() を呼び出すよう設定します。必要ない場合は、パフォーマンス向上のためにこれをオフにすることができます。
PrimaryActorTick.bCanEverTick = true;
if (!RootComponent)
{
RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("ProjectileSceneComponent"));
}
if (!CollisionComponent)
{
// 単純なコリジョン表現として、球体を使用します。
CollisionComponent = CreateDefaultSubobject<USphereComponent>(TEXT("SphereComponent"));
// 球体のコリジョン プロファイル名を「Projectile」に設定します。
CollisionComponent->BodyInstance.SetCollisionProfileName(TEXT("Projectile"));
// コンポーネントが何かにヒットしたときに呼び出されるイベント。
CollisionComponent->OnComponentHit.AddDynamic(this, &AFPSProjectile::OnHit);
// 球体のコリジョン半径を設定します。
CollisionComponent->InitSphereRadius(15.0f);
// コリジョン コンポーネントとなるルート コンポーネントを設定します。
RootComponent = CollisionComponent;
}
if (!ProjectileMovementComponent)
{
// このコンポーネントを使用し、この発射物の動きを駆動します。
ProjectileMovementComponent = CreateDefaultSubobject<UProjectileMovementComponent>(TEXT("ProjectileMovementComponent"));
ProjectileMovementComponent->SetUpdatedComponent(CollisionComponent);
ProjectileMovementComponent->InitialSpeed = 3000.0f;
ProjectileMovementComponent->MaxSpeed = 3000.0f;
ProjectileMovementComponent->bRotationFollowsVelocity = true;
ProjectileMovementComponent->bShouldBounce = true;
ProjectileMovementComponent->Bounciness = 0.3f;
ProjectileMovementComponent->ProjectileGravityScale = 0.0f;
}
if (!ProjectileMeshComponent)
{
ProjectileMeshComponent = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("ProjectileMeshComponent"));
static ConstructorHelpers::FObjectFinder<UStaticMesh>Mesh(TEXT("'/Game/Sphere.Sphere'"));
if (Mesh.Succeeded())
{
ProjectileMeshComponent->SetStaticMesh(Mesh.Object);
}
static ConstructorHelpers::FObjectFinder<UMaterial>Material(TEXT("'/Game/SphereMaterial.SphereMaterial'"));
if (Material.Succeeded())
{
ProjectileMaterialInstance = UMaterialInstanceDynamic::Create(Material.Object, ProjectileMeshComponent);
}
ProjectileMeshComponent->SetMaterial(0, ProjectileMaterialInstance);
ProjectileMeshComponent->SetRelativeScale3D(FVector(0.09f, 0.09f, 0.09f));
ProjectileMeshComponent->SetupAttachment(RootComponent);
}
// 3 秒後に発射物を削除します。
InitialLifeSpan = 3.0f;
}
// ゲームの開始時またはスポーン時に呼び出します。
void AFPSProjectile::BeginPlay()
{
Super::BeginPlay();
}
// フレームごとに呼び出します。
void AFPSProjectile::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
// 発射物の発射方向の速度を初期化する関数。
void AFPSProjectile::FireInDirection(const FVector& ShootDirection)
{
ProjectileMovementComponent->Velocity = ShootDirection * ProjectileMovementComponent->InitialSpeed;
}
void AFPSProjectile::OnHit(UPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComponent, FVector NormalImpulse, const FHitResult& Hit)
{
if (OtherActor != nullptr && OtherActor != this && OtherComponent != nullptr && OtherComponent->IsSimulatingPhysics())
{
OtherComponent->AddImpulseAtLocation(ProjectileMovementComponent->Velocity * 100.0f, Hit.ImpactPoint);
}
Destroy();
}
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "Camera/CameraComponent.h"
#include "Components/CapsuleComponent.h"
#include "FPSProjectile.h"
#include "FPSCharacter.generated.h"
UCLASS()
class FPSPROJECT_API AFPSCharacter : public ACharacter
{
GENERATED_BODY()
public:
// このキャラクターのプロパティのデフォルト値を設定します。
AFPSCharacter();
protected:
// ゲームの開始時またはスポーン時に呼び出します。
virtual void BeginPlay() override;
// スポーンする発射物クラス。
UPROPERTY(EditAnywhere, Category = Projectile)
TSubclassOf<class AFPSProjectile> ProjectileClass;
public:
// フレームごとに呼び出します。
virtual void Tick(float DeltaTime) override;
// Input に機能をバインドする際に呼び出します。
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
// 前方および後方への移動のための入力を処理します。
UFUNCTION()
void MoveForward(float Value);
// 右および左への移動のための入力を処理します。
UFUNCTION()
void MoveRight(float Value);
// キーが押された際にジャンプ フラグを設定します。
UFUNCTION()
void StartJump();
// キーが放された際にジャンプ フラグをクリアします。
UFUNCTION()
void StopJump();
// FPS カメラ
UPROPERTY(VisibleAnywhere)
UCameraComponent* FPSCameraComponent;
// ファースト パーソン メッシュ (腕) で、所有しているプレイヤーにのみ表示されます。
UPROPERTY(VisibleDefaultsOnly, Category = Mesh)
USkeletalMeshComponent* FPSMesh;
// 発射物を発射する関数。
UFUNCTION()
void Fire();
// カメラ位置からの銃口のオフセット。
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Gameplay)
FVector MuzzleOffset;
};
// Copyright Epic Games, Inc. All Rights Reserved.
#include "FPSCharacter.h"
// デフォルト値を設定します。
AFPSCharacter::AFPSCharacter()
{
// このキャラクターがフレームごとに Tick() を呼び出すように設定します。必要ない場合は、パフォーマンス向上のためにこれをオフにすることができます。
PrimaryActorTick.bCanEverTick = true;
// 一人称視点カメラ コンポーネントを作成します。
FPSCameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("FirstPersonCamera"));
check(FPSCameraComponent != nullptr);
// カメラ コンポーネントをカプセル コンポーネントにアタッチします。
FPSCameraComponent->SetupAttachment(CastChecked<USceneComponent, UCapsuleComponent>(GetCapsuleComponent()));
// カメラを目の少し上に位置づけます。
FPSCameraComponent->SetRelativeLocation(FVector(0.0f, 0.0f, 50.0f + BaseEyeHeight));
// ポーンがカメラの回転をコントロールできます。
FPSCameraComponent->bUsePawnControlRotation = true;
// 所有するプレイヤー向けのファースト パーソン メッシュ コンポーネントを作成します。
FPSMesh = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("FirstPersonMesh"));
check(FPSMesh != nullptr);
// このメッシュは所有するプレイヤーにのみ表示されます。
FPSMesh->SetOnlyOwnerSee(true);
//FPS メッシュを FPS カメラにアタッチします。
FPSMesh->SetupAttachment(FPSCameraComponent);
//単一のメッシュがあるかのような錯覚を維持するには、一部の環境シャドウを無効にします。
FPSMesh->bCastDynamicShadow = false;
FPSMesh->CastShadow = false;
// 所有するプレイヤーには通常の (サードパーソン) ボディ メッシュは表示されません。
GetMesh()->SetOwnerNoSee(true);
}
// ゲームの開始時またはスポーン時に呼び出します。
void AFPSCharacter::BeginPlay()
{
Super::BeginPlay();
check(GEngine != nullptr);
// デバッグ メッセージを 5 秒間表示します。
// 「Key」値引数 -1 を指定すると、メッセージは更新またはリフレッシュされなくなります。
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("We are using FPSCharacter."));
}
// フレームごとに呼び出します。
void AFPSCharacter::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
// Input に機能をバインドする際に呼び出します。
void AFPSCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
// 「移動」バインディングをセットアップします。
PlayerInputComponent->BindAxis("MoveForward", this, &AFPSCharacter::MoveForward);
PlayerInputComponent->BindAxis("MoveRight", this, &AFPSCharacter::MoveRight);
// 「ルック」バインディングをセットアップします。
PlayerInputComponent->BindAxis("Turn", this, &AFPSCharacter::AddControllerYawInput);
PlayerInputComponent->BindAxis("LookUp", this, &AFPSCharacter::AddControllerPitchInput);
// 「アクション」バインディングをセットアップします。
PlayerInputComponent->BindAction("Jump", IE_Pressed, this, &AFPSCharacter::StartJump);
PlayerInputComponent->BindAction("Jump", IE_Released, this, &AFPSCharacter::StopJump);
PlayerInputComponent->BindAction("Fire", IE_Pressed, this, &AFPSCharacter::Fire);
}
void AFPSCharacter::MoveForward(float Value)
{
// どちらが「前方」なのかを調べ、プレイヤーがその方向に移動したがっていることを記録します。
FVector Direction = FRotationMatrix(Controller->GetControlRotation()).GetScaledAxis(EAxis::X);
AddMovementInput(Direction, Value);
}
void AFPSCharacter::MoveRight(float Value)
{
// どちらが「右」なのかを調べ、プレイヤーがその方向に移動したがっていることを記録します。
FVector Direction = FRotationMatrix(Controller->GetControlRotation()).GetScaledAxis(EAxis::Y);
AddMovementInput(Direction, Value);
}
void AFPSCharacter::StartJump()
{
bPressedJump = true;
}
void AFPSCharacter::StopJump()
{
bPressedJump = false;
}
void AFPSCharacter::Fire()
{
// 発射物を発射するよう試行します。
if (ProjectileClass)
{
// カメラのトランスフォームを取得します。
FVector CameraLocation;
FRotator CameraRotation;
GetActorEyesViewPoint(CameraLocation, CameraRotation);
// MuzzleOffset がわずかにカメラ正面に対して発射物をスポーンするように設定します。
MuzzleOffset.Set(100.0f, 0.0f, 0.0f);
// MuzzleOffset をカメラ空間からワールド空間にトランスフォームします。
FVector MuzzleLocation = CameraLocation + FTransform(CameraRotation).TransformVector(MuzzleOffset);
// 照準をわずかに上方に歪めます。
FRotator MuzzleRotation = CameraRotation;
MuzzleRotation.Pitch += 10.0f;
UWorld* World = GetWorld();
if (World)
{
FActorSpawnParameters SpawnParams;
SpawnParams.Owner = this;
SpawnParams.Instigator = GetInstigator();
// 発射物を銃口にスポーンします。
AFPSProjectile* Projectile = World->SpawnActor<AFPSProjectile>(ProjectileClass, MuzzleLocation, MuzzleRotation, SpawnParams);
if (Projectile)
{
// 発射物の初期軌道を設定します。
FVector LaunchDirection = MuzzleRotation.Vector();
Projectile->FireInDirection(LaunchDirection);
}
}
}
}
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/HUD.h"
#include "Engine/Canvas.h"
#include "FPSHUD.generated.h"
/**
*
*/
UCLASS()
class FPSPROJECT_API AFPSHUD : public AHUD
{
GENERATED_BODY()
protected:
// これは、画面中央に描画されます。
UPROPERTY(EditDefaultsOnly)
UTexture2D* CrosshairTexture;
public:
// HUD のプライマリ ドロー コール
virtual void DrawHUD() override;
};
// Copyright Epic Games, Inc. All Rights Reserved.
#include "FPSHUD.h"
void AFPSHUD::DrawHUD()
{
Super::DrawHUD();
if (CrosshairTexture)
{
// キャンバスの中心を見つけます。
FVector2D Center(Canvas->ClipX * 0.5f, Canvas->ClipY * 0.5f);
// テクスチャの半分の長さでオフセットを設定し、キャンバスの中心でテクスチャが揃うようにします。
FVector2D CrossHairDrawPosition(Center.X - (CrosshairTexture->GetSurfaceWidth() * 0.5f), Center.Y - (CrosshairTexture->GetSurfaceHeight() * 0.5f));
// 中心点に照準線を描画します。
FCanvasTileItem TileItem(CrossHairDrawPosition, CrosshairTexture->Resource, FLinearColor::White);
TileItem.BlendMode = SE_BLEND_Translucent;
Canvas->DrawItem(TileItem);
}
}
おつかれさまでした!ここでは、以下の方法について学習しました。
✓ 発射物をゲームに追加する ✓ シューティングを実装する ✓ 発射物のコリジョンと存続期間を設定する ✓ 発射物をワールドとインタラクトさせる ✓ 照準線をビューポートに追加する
次のセクションでキャラクターをアニメートする方法を学習する準備ができました。