3 - 発射物を実装する

発射物をファースト パーソン シューター ゲームに実装する方法を学びます。

Choose your operating system:

Windows

macOS

Linux

前提トピック

このページは以下のトピックへの知識があることを前提にしています。まず以下のトピックの内容についてご確認をお願いします。

CrosshairsInGame.png

このセクションを終了したときに、このような画面になります。

目標

このセクションでは、ファースト パーソン シューター ゲームに発射物を実装する方法を説明します。

目的

このチュートリアルのこのセクションを完了すると、次のことができるようになります。

  • 発射物をゲームに追加する

  • シューティングを実装する

  • 発射物のコリジョンと存続期間を設定する

  • 発射物をワールドとインタラクトさせる

  • 照準線をビューポートに追加する

ステップ

  • 3.1 - 発射物をゲームに追加する

  • 3.2 - シューティングを実装する

  • 3.3 - 発射物のコリジョンと存続期間を設定する

  • 3.4 - 発射物をワールドとインタラクトさせる

  • 3.5 - 照準線をビューポートに追加する

3.1 - 発射物をゲームに追加する

キャラクターの設定が完了したので、発射される武器を実装します。手りゅう弾のような単純な発射物を画面の中央から発射し、ワールドに衝突するまで飛び続けるようにプログラムします。このステップでは、入力を追加し、発射物の新しいクラスのコードを作成します。

Fire アクション マッピングを追加する

  1. [Edit (編集)] メニューから [Project Settings (プロジェクト設定)] を選択します。

  2. [Project Settings] タブの左側にある [Engine] の見出しで、 [Input] をクリックします。

  3. [Bindings] で、 [Action Mappings] の隣にある [+] 記号をクリックします。

  4. [Action Mappings (アクションマッピング)] の左にある 矢印 をクリックします。

  5. 表示されたテキスト フィールドに「Fire」と入力し、テキストボックスの左にある矢印をクリックして、アクション バインディング オプションを展開します。

  6. ドロップダウン メニューで [Mouse] ドロップダウン リストから [Left Mouse Button] を選択します。

  7. 入力設定は以下のようになります。

    ActionMapping.png

  8. [Project Settings] メニューを閉じます。

Projectile クラスを追加する

  1. ファイル メニューで [New C++ Class... (新規 C++ クラス)] をクリックして、新しい親クラスを選択します。

  2. [Choose Parent Class (親クラスを選択)] メニューが開きます。下方にスクロールして、親クラスとして [Actor] を選択して、 [Next] をクリックします。

    AddProjectileClass.png

  3. 新規クラスに「FPSProjectile」と名前を付けてから、 [Create Class (クラスを作成)] をクリックします。

    CreateProjectileClass.png

USphere コンポーネントを追加する

  1. Solution Explorer で、 FPSProjectile クラス ヘッダーを探して、 FPSProjectile.h を開きます。

  2. SphereComponent ヘッダ ファイルを追加します。

    #include "Components/SphereComponent.h"
  3. FPSProjectile インターフェースで、. USphereComponent への参照を追加します。

    // Sphere collision component.
    UPROPERTY(VisibleDefaultsOnly, Category = Projectile)
    USphereComponent* CollisionComponent;
  4. 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: 
        // Sets default values for this actor's properties
        AFPSProjectile();
    
    protected:
        // Called when the game starts or when spawned
        virtual void BeginPlay() override;
    
    public:
        // Called every frame
        virtual void Tick( float DeltaTime ) override;
    
        // Sphere collision component
        UPROPERTY(VisibleDefaultsOnly, Category = Projectile)
        USphereComponent* CollisionComponent;
    };
  5. Solution Explorer (ソリューション エクスプローラー) で、 FPSProjectile class CPP ファイルを探して、 FPSProjectile.cpp を開きます。

  6. 次のコードを AFPSProjectile コンストラクタの PrimaryActorTick.bcanEverTick の後 ( FPSProjectile.cpp 内) に追加します。

    if(!RootComponent)
    {
    RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("ProjectileSceneComponent"));
    }
    if(!CollisionComponent)
    {
    // Use a sphere as a simple collision representation.
    CollisionComponent = CreateDefaultSubobject<USphereComponent>(TEXT("SphereComponent"));
    // Set the sphere's collision radius.
    CollisionComponent->InitSphereRadius(15.0f);
    // Set the root component to be the collision component.
    RootComponent = CollisionComponent;
    }

    シミュレーションで動かすため、 CollisionComponent RootComponent にしています。

  7. FPSProjectile.cpp は以下のようになります。

    // Copyright Epic Games, Inc.All Rights Reserved.
    
    #include "FPSProjectile.h"
    
    // Sets default values
    AFPSProjectile::AFPSProjectile()
    {
        // Set this actor to call Tick() every frame.You can turn this off to improve performance if you don't need it.
        PrimaryActorTick.bCanEverTick = true;
    
        if(!RootComponent)
        {
            RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("ProjectileSceneComponent"));
        }
    
        if(!CollisionComponent)
        {
            // Use a sphere as a simple collision representation.
            CollisionComponent = CreateDefaultSubobject<USphereComponent>(TEXT("SphereComponent"));
            // Set the sphere's collision radius.
            CollisionComponent->InitSphereRadius(15.0f);
            // Set the root component to be the collision component.
            RootComponent = CollisionComponent;
        }
    }
    
    // Called when the game starts or when spawned
    void AFPSProjectile::BeginPlay()
    {
        Super::BeginPlay();
    
    }
    
    // Called every frame
    void AFPSProjectile::Tick( float DeltaTime )
    {
        Super::Tick( DeltaTime );
    
    }

Projectile Movement コンポーネントを追加する

  1. Solution Explorer (ソリューション エクスプローラー) で、 FPSProjectile クラス ヘッダ ファイルを探して、 FPSProjectile.h を開きます。

  2. ProjectileMovementComponent ヘッダ ファイルを追加します。

    #include "GameFramework/ProjectileMovementComponent.h"
  3. 次のコードを FPSProjectile.cpp に追加します。

    // Projectile movement component.
    UPROPERTY(VisibleAnywhere, Category = Movement)
    UProjectileMovementComponent* ProjectileMovementComponent;
  4. 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: 
        // Sets default values for this actor's properties
        AFPSProjectile();
    
    protected:
        // Called when the game starts or when spawned
        virtual void BeginPlay() override;
    
    public:
        // Called every frame
        virtual void Tick( float DeltaTime ) override;
    
        // Sphere collision component.
        UPROPERTY(VisibleDefaultsOnly, Category = Projectile)
        USphereComponent* CollisionComponent;
    
        // Projectile movement component.
        UPROPERTY(VisibleAnywhere, Category = Movement)
        UProjectileMovementComponent* ProjectileMovementComponent;
    };
  5. FPSProjectile.cpp [Solution Explorer] で開きます。

  6. 以下のコードの行を、 FPSProjectile.cpp 内の FPSProjectile コンストラクタに追加します。

    if(!ProjectileMovementComponent)
    {
        // Use this component to drive this projectile's movement.
        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;
    }
  7. FPSProjectile.cpp は以下のようになります。

    // Copyright Epic Games, Inc.All Rights Reserved.
    
    #include "FPSProjectile.h"
    
    // Sets default values
    AFPSProjectile::AFPSProjectile()
    {
        // Set this actor to call Tick() every frame.You can turn this off to improve performance if you don't need it.
        PrimaryActorTick.bCanEverTick = true;
        if(!RootComponent)
        {
            RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("ProjectileSceneComponent"));
        }
    
        if(!CollisionComponent)
        {
            // Use a sphere as a simple collision representation.
            CollisionComponent = CreateDefaultSubobject<USphereComponent>(TEXT("SphereComponent"));
            // Set the sphere's collision radius.
            CollisionComponent->InitSphereRadius(15.0f);
            // Set the root component to be the collision component.
            RootComponent = CollisionComponent;
        }
    
        if(!ProjectileMovementComponent)
        {
            // Use this component to drive this projectile's movement.
            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;
        }
    }
    
    // Called when the game starts or when spawned
    void AFPSProjectile::BeginPlay()
    {
        Super::BeginPlay();
    
    }
    
    // Called every frame
    void AFPSProjectile::Tick( float DeltaTime )
    {
        Super::Tick( DeltaTime );
    
    }

発射物の初期べロシティを設定する

  1. FPSProjectile.h [Solution Explorer] で開きます。

  2. 次の関数宣言を「FPSProjectile.h」に追加します。

    // Function that initializes the projectile's velocity in the shoot direction.
    void FireInDirection(const FVector& ShootDirection);

    この関数は、発射物を発射します。

  3. 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: 
        // Sets default values for this actor's properties
        AFPSProjectile();
    
    protected:
        // Called when the game starts or when spawned
        virtual void BeginPlay() override;
    
    public:
        // Called every frame
        virtual void Tick( float DeltaTime ) override;
    
        // Sphere collision component.
        UPROPERTY(VisibleDefaultsOnly, Category = Projectile)
        USphereComponent* CollisionComponent;
    
        // Projectile movement component.
        UPROPERTY(VisibleAnywhere, Category = Movement)
        UProjectileMovementComponent* ProjectileMovementComponent;
    
        // Function that initializes the projectile's velocity in the shoot direction.
        void FireInDirection(const FVector& ShootDirection);
    };
  4. FPSProjectile.cpp [Solution Explorer] で開きます。

  5. 次の関数定義を FPSProjectile.cpp に追加します。

    // Function that initializes the projectile's velocity in the shoot direction.
    void AFPSProjectile::FireInDirection(const FVector& ShootDirection)
    {
        ProjectileMovementComponent->Velocity = ShootDirection * ProjectileMovementComponent->InitialSpeed;
    }

    発射物の速度は ProjectileMovementComponent によって定義されているため、指定するのは発射方向だけです。

  6. FPSProjectile.cpp は以下のようになります。

    // Copyright Epic Games, Inc.All Rights Reserved.
    
    #include "FPSProjectile.h"
    
    // Sets default values
    AFPSProjectile::AFPSProjectile()
    {
    // Set this actor to call Tick() every frame.You can turn this off to improve performance if you don't need it.
    PrimaryActorTick.bCanEverTick = true;
    
    if(!RootComponent)
    {
        // Use a sphere as a simple collision representation.
        CollisionComponent = CreateDefaultSubobject<USphereComponent>(TEXT("SphereComponent"));
        // Set the sphere's collision radius.
        CollisionComponent->InitSphereRadius(15.0f);
        // Set the root component to be the collision component.
        RootComponent = CollisionComponent;
    }
    
    if(!ProjectileMovementComponent)
    {
        // Use this component to drive this projectile's movement.
        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;
    }
    }
    
    // Called when the game starts or when spawned
    void AFPSProjectile::BeginPlay()
    {
    Super::BeginPlay();
    
    }
    
    // Called every frame
    void AFPSProjectile::Tick( float DeltaTime )
    {
    Super::Tick( DeltaTime );
    
    }
    
    // Function that initializes the projectile's velocity in the shoot direction.
    void AFPSProjectile::FireInDirection(const FVector& ShootDirection)
    {
    ProjectileMovementComponent->Velocity = ShootDirection * ProjectileMovementComponent->InitialSpeed;
    }

Fire 入力アクションをバインドする

  1. FPSCharacter.h [Solution Explorer] で開きます。

  2. FPSCharacter.h に以下の関数の宣言を追加します。

    // Function that handles firing projectiles.
    UFUNCTION()
    void Fire();
  3. 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:
        // Sets default values for this character's properties
        AFPSCharacter();
    
    protected:
        // Called when the game starts or when spawned
        virtual void BeginPlay() override;
    
    public:
        // Called every frame
        virtual void Tick( float DeltaTime ) override;
    
        // Called to bind functionality to input
        virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
    
        // Handles input for moving forward and backward.
        UFUNCTION()
        void MoveForward(float Value);
    
        // Handles input for moving right and left.
        UFUNCTION()
        void MoveRight(float Value);
    
        // Sets jump flag when key is pressed.
        UFUNCTION()
        void StartJump();
    
        // Clears jump flag when key is released.
        UFUNCTION()
        void StopJump();
    
        // Function that handles firing projectiles.
        UFUNCTION()
        void Fire();
    
        // FPS camera
        UPROPERTY(VisibleAnywhere)
        UCameraComponent* FPSCameraComponent;
    
        // First-person mesh (arms), visible only to the owning player.
        UPROPERTY(VisibleDefaultsOnly, Category = Mesh)
        USkeletalMeshComponent* FPSMesh;
    };
  4. FPSCharacter CPP ファイルを [Solution Explorer] で見つけて、 FPSCharacter.cpp を開きます。

  5. Fire 関数をバインドするには、次のコードを FPSCharacter.cpp SetupPlayerInputComponent 関数に追加します。

    PlayerInputComponent->BindAction("Fire", IE_Pressed, this, &AFPSCharacter::Fire);
  6. 次の関数定義を FPSCharacter.cpp に追加します。

    void AFPSCharacter::Fire()
    {
    }
  7. FPSCharacter.cpp は以下のようになります。

    // Copyright Epic Games, Inc.All Rights Reserved.
    
    #include "FPSCharacter.h"
    
    // Sets default values
    AFPSCharacter::AFPSCharacter()
    {
        // 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;
    
        // Create a first person camera component.
        FPSCameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("FirstPersonCamera"));
        check(FPSCameraComponent != nullptr);
    
        // Attach the camera component to our capsule component.
        FPSCameraComponent->SetupAttachment(CastChecked<USceneComponent, UCapsuleComponent>(GetCapsuleComponent()));
    
        // Position the camera slightly above the eyes.
        FPSCameraComponent->SetRelativeLocation(FVector(0.0f, 0.0f, 50.0f + BaseEyeHeight));
    
        // Enable the pawn to control camera rotation.
        FPSCameraComponent->bUsePawnControlRotation = true;
    
        // Create a first person mesh component for the owning player.
        FPSMesh = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("FirstPersonMesh"));
        check(FPSMesh != nullptr);
    
        // Only the owning player sees this mesh.
        FPSMesh->SetOnlyOwnerSee(true);
    
        // Attach the FPS mesh to the FPS camera.
        FPSMesh->SetupAttachment(FPSCameraComponent);
    
        // Disable some environmental shadowing to preserve the illusion of having a single mesh.
        FPSMesh->bCastDynamicShadow = false;
        FPSMesh->CastShadow = false;
    
        // The owning player doesn't see the regular (third-person) body mesh.
        GetMesh()->SetOwnerNoSee(true);
    }
    
    // Called when the game starts or when spawned
    void AFPSCharacter::BeginPlay()
    {
        Super::BeginPlay();
    
        if (GEngine)
        {
            // Display a debug message for five seconds. 
            // The -1 "Key" value argument prevents the message from being updated or refreshed.
            GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("We are using FPSCharacter."));
        }
    }
    
    // Called every frame
    void AFPSCharacter::Tick( float DeltaTime )
    {
        Super::Tick( DeltaTime );
    
    }
    
    // Called to bind functionality to input
    void AFPSCharacter::SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent)
    {
        Super::SetupPlayerInputComponent(PlayerInputComponent);
    
        // Set up "movement" bindings.
        PlayerInputComponent->BindAxis("MoveForward", this, &AFPSCharacter::MoveForward);
        PlayerInputComponent->BindAxis("MoveRight", this, &AFPSCharacter::MoveRight);
    
        // Set up "look" bindings.
        PlayerInputComponent->BindAxis("Turn", this, &AFPSCharacter::AddControllerYawInput);
        PlayerInputComponent->BindAxis("LookUp", this, &AFPSCharacter::AddControllerPitchInput);
    
        // Set up "action" bindings.
        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)
    {
        // Find out which way is "forward" and record that the player wants to move that way.
        FVector Direction = FRotationMatrix(Controller->GetControlRotation()).GetScaledAxis(EAxis::X);
        AddMovementInput(Direction, Value);
    }
    
    void AFPSCharacter::MoveRight(float Value)
    {
        // Find out which way is "right" and record that the player wants to move that way.
        FVector Direction = FRotationMatrix(Controller->GetControlRotation()).GetScaledAxis(EAxis::Y);
        AddMovementInput(Direction, Value);
    }
    
    void AFPSCharacter::StartJump()
    {
        bPressedJump = true;
    }
    
    void AFPSCharacter::StopJump()
    {
        bPressedJump = false;
    }
    
    void AFPSCharacter::Fire()
    {
    }

発射物のスポーン位置を定義する

  1. FPSProjectile アクタをスポーンする場合、 OnFire 関数を実装する場合に考慮すべきポイントが以下のように 2 つあります。

    • 発射物をスポーンする場所。

    • 発射物クラス ( FPSCharacter とその派生ブループリントがスポーンする発射物の種類を認識するため)。

    カメラ空間のオフセット ベクターを使用して、発射物のスポーン位置を決定します。このパラメータを編集可能にして、 BP_FPSCharacter ブループリントで設定し調整できるようにします。最終的に、このデータに基づいて発射物の初期位置を計算できます。

  2. FPSCharacter.h [Solution Explorer] で開きます。

  3. 次のコードを FPSCharacter.h に追加します。

    // Gun muzzle offset from the camera location.
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Gameplay)
    FVector MuzzleOffset;

    EditAnywhere により、ブループリント エディタのデフォルト モード内、またはキャラクターのいずれかのインスタンスの [Details (詳細)] タブで、銃口オフセットの値を変更できます。 BlueprintReadWrite 指定子により、ブループリント内で銃口オフセットの値を取得、設定できます。

  4. 次のコードを FPSCharacter.h の protected: アクセス指定子の下に追加します。

    // Projectile class to spawn.
    UPROPERTY(EditDefaultsOnly, Category = Projectile)
    TSubclassOf<class AFPSProjectile> ProjectileClass;

    EditDefaultsOnly の意味は、発射物クラスをブループリントでデフォルトとして設定できるだけで、ブループリントの各インスタンスではできないということです。

  5. 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:
    // Sets default values for this character's properties
    AFPSCharacter();
    
    protected:
    // Called when the game starts or when spawned
    virtual void BeginPlay() override;
    
    // Projectile class to spawn.
    UPROPERTY(EditDefaultsOnly, Category = Projectile)
    TSubclassOf<class AFPSProjectile> ProjectileClass;
    
    public:
    // Called every frame
    virtual void Tick( float DeltaTime ) override;
    
    // Called to bind functionality to input
    virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
    
    // Handles input for moving forward and backward.
    UFUNCTION()
    void MoveForward(float Value);
    
    // Handles input for moving right and left.
    UFUNCTION()
    void MoveRight(float Value);
    
    // Sets jump flag when key is pressed.
    UFUNCTION()
    void StartJump();
    
    // Clears jump flag when key is released.
    UFUNCTION()
    void StopJump();
    
    // Function that fires projectiles.
    UFUNCTION()
    void Fire();
    
    // FPS camera
    UPROPERTY(VisibleAnywhere)
    UCameraComponent* FPSCameraComponent;
    
    // First-person mesh (arms), visible only to the owning player.
    UPROPERTY(VisibleDefaultsOnly, Category = Mesh)
    USkeletalMeshComponent* FPSMesh;
    
    // Gun muzzle offset from the camera location.
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Gameplay)
    FVector MuzzleOffset;
    
    };

コードをコンパイルしチェックする

新規で実装した発射物のコードをコンパイルし、チェックします。

  1. Visual Studio にすべてのヘッダーと実装ファイルを保存します。

  2. [Solution Explorer (ソリューション エクスプローラ)] [FPSProject] を探します。

  3. [FPSProject] を右クリックして [Build] を選択してプロジェクトをコンパイルします。

    BuildFPSProject.png

    このステップの目的は、次のステップに進む前に、何らかのビルド エラーを検出することです。このチュートリアルの範囲を超えるビルドエラーや警告が発生した場合は「 Coding Standard (コーディング標準) 」や「 Unreal Engine API Reference (API リファレンス) 」を参照してください。

3.2 - シューティングを実装する

ファースト パーソン シューター キャラクターのシューティングを実装する方法を習得します。

Fire 関数を実装する

  1. 次のコード行を「FPSCharacter.h」に追加します。

    #include "FPSProjectile.h"
  2. 次の Fire 関数定義を「FPSCharacter.cpp」に追加します。

    void AFPSCharacter::Fire()
    {
    // Attempt to fire a projectile.
    if (ProjectileClass)
    {
    // Get the camera transform.
    FVector CameraLocation;
    FRotator CameraRotation;
    GetActorEyesViewPoint(CameraLocation, CameraRotation);
    
    // Set MuzzleOffset to spawn projectiles slightly in front of the camera.
    MuzzleOffset.Set(100.0f, 0.0f, 0.0f);
    
    // Transform MuzzleOffset from camera space to world space.
    FVector MuzzleLocation = CameraLocation + FTransform(CameraRotation).TransformVector(MuzzleOffset);
    
    // Skew the aim to be slightly upwards.
    FRotator MuzzleRotation = CameraRotation;
    MuzzleRotation.Pitch += 10.0f;
    
    UWorld* World = GetWorld();
    if (World)
    {
        FActorSpawnParameters SpawnParams;
        SpawnParams.Owner = this;
        SpawnParams.Instigator = GetInstigator();
    
        // Spawn the projectile at the muzzle.
        AFPSProjectile* Projectile = World->SpawnActor<AFPSProjectile>(ProjectileClass, MuzzleLocation, MuzzleRotation, SpawnParams);
        if (Projectile)
        {
            // Set the projectile's initial trajectory.
            FVector LaunchDirection = MuzzleRotation.Vector();
            Projectile->FireInDirection(LaunchDirection);
        }
    }
    }
    }
  3. FPSCharacter.h は以下のようになります。

    // Copyright Epic Games, Inc.All Rights Reserved.
    
    #pragma once
    
    #include "CoreMinimal.h"
    #include "GameFramework/Character.h"
    #include "Camera/CameraComponent.h"
    #include "Components/CapsultComponent.h"
    #include "FPSProjectile.h"
    #include "FPSCharacter.generated.h"
    
    UCLASS()
    class FPSPROJECT_API AFPSCharacter : public ACharacter
    {
        GENERATED_BODY()
    
    public:
        // Sets default values for this character's properties
        AFPSCharacter();
    
    protected:
        // Called when the game starts or when spawned
        virtual void BeginPlay() override;
    
       // Projectile class to spawn.
       UPROPERTY(EditAnywhere, Category = Projectile)
       TSubclassOf<class AFPSProjectile> ProjectileClass;
    
    public:
        // Called every frame
        virtual void Tick(float DeltaTime) override;
    
        // Called to bind functionality to input
       virtual void SetupPlayerInputComponent(class UIComponent* PlayerInputComponent) override;
    
        // Handles input for moving forward and backward.
        UFUNCTION()
        void MoveForward(float Value);
    
        // Handles input for moving right and left.
        UFUNCTION()
        void MoveRight(float Value);
    
        // Sets jump flag when key is pressed.
        UFUNCTION()
        void StartJump();
    
        // Clears jump flag when key is released.
        UFUNCTION()
        void StopJump();
    
        // Function that fires projectiles.
        UFUNCTION()
        void Fire();
    
        // FPS camera
        UPROPERTY(VisibleAnywhere)
        UCameraComponent* FPSCameraComponent;
    
        // First-person mesh (arms), visible only to the owning player.
        UPROPERTY(VisibleDefaultsOnly, Category = Mesh)
        USkeletalMeshComponent* FPSMesh;
    
        // Gun muzzle offset from the camera location.
        UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Gameplay)
        FVector MuzzleOffset;
    
    };
  4. FPSCharacter.cpp は以下のようになります。

    // Copyright Epic Games, Inc.All Rights Reserved.
    
    #include "FPSCharacter.h"
    
    // Sets default values
    AFPSCharacter::AFPSCharacter()
    {
        // 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;
    
        // Create a first person camera component.
        FPSCameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("FirstPersonCamera"));
        check(FPSCameraComponent != nullptr);
    
        // Attach the camera component to our capsule component.
        FPSCameraComponent->SetupAttachment(CastChecked<USceneComponent, UCapsuleComponent>(GetCapsuleComponent()));
    
        // Position the camera slightly above the eyes.
        FPSCameraComponent->SetRelativeLocation(FVector(0.0f, 0.0f, 50.0f + BaseEyeHeight));
    
        // Enable the Pawn to control camera rotation.
        FPSCameraComponent->bUsePawnControlRotation = true;
    
        // Create a first person mesh component for the owning player.
        FPSMesh = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("FirstPersonMesh"));
        check(FPSMesh != nullptr);
    
        // Only the owning player sees this mesh.
        FPSMesh->SetOnlyOwnerSee(true);
    
        // Attach the FPS mesh to the FPS camera.
        FPSMesh->SetupAttachment(FPSCameraComponent);
    
        // Disable some environmental shadowing to preserve the illusion of having a single mesh.
        FPSMesh->bCastDynamicShadow = false;
        FPSMesh->CastShadow = false;
    
        // The owning player doesn't see the regular (third-person) body mesh.
        GetMesh()->SetOwnerNoSee(true);
    }
    
    // Called when the game starts or when spawned
    void AFPSCharacter::BeginPlay()
    {
        Super::BeginPlay();
    
        if (GEngine)
        {
            // Display a debug message for five seconds. 
            // The -1 "Key" value argument prevents the message from being updated or refreshed.
            GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("We are using FPSCharacter."));
        }
    }
    
    // Called every frame
    void AFPSCharacter::Tick( float DeltaTime )
    {
        Super::Tick( DeltaTime );
    
    }
    
    // Called to bind functionality to input
    void AFPSCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
    {
        Super::SetupPlayerInputComponent(PlayerInputComponent);
    
        // Set up "movement" bindings.
        PlayerInputComponent->BindAxis("MoveForward", this, &AFPSCharacter::MoveForward);
        PlayerInputComponent->BindAxis("MoveRight", this, &AFPSCharacter::MoveRight);
    
        // Set up "look" bindings.
        PlayerInputComponent->BindAxis("Turn", this, &AFPSCharacter::AddControllerYawInput);
        PlayerInputComponent->BindAxis("LookUp", this, &AFPSCharacter::AddControllerPitchInput);
    
        // Set up "action" bindings.
        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)
    {
        // Find out which way is "forward" and record that the player wants to move that way.
        FVector Direction = FRotationMatrix(Controller->GetControlRotation()).GetScaledAxis(EAxis::X);
        AddMovementInput(Direction, Value);
    }
    
    void AFPSCharacter::MoveRight(float Value)
    {
        // Find out which way is "right" and record that the player wants to move that way.
        FVector Direction = FRotationMatrix(Controller->GetControlRotation()).GetScaledAxis(EAxis::Y);
        AddMovementInput(Direction, Value);
    }
    
    void AFPSCharacter::StartJump()
    {
        bPressedJump = true;
    }
    
    void AFPSCharacter::StopJump()
    {
        bPressedJump = false;
    }
    
    void AFPSCharacter::Fire()
    {
        // Attempt to fire a projectile.
        if (ProjectileClass)
        {
            // Get the camera transform.
            FVector CameraLocation;
            FRotator CameraRotation;
            GetActorEyesViewPoint(CameraLocation, CameraRotation);
    
            // Set MuzzleOffset to spawn projectiles slightly in front of the camera.
            MuzzleOffset.Set(100.0f, 0.0f, 0.0f);
    
            // Transform MuzzleOffset from camera space to world space.
            FVector MuzzleLocation = CameraLocation + FTransform(CameraRotation).TransformVector(MuzzleOffset);
    
            // Skew the aim to be slightly upwards.
            FRotator MuzzleRotation = CameraRotation;
            MuzzleRotation.Pitch += 10.0f;
    
            UWorld* World = GetWorld();
            if (World)
            {
                FActorSpawnParameters SpawnParams;
                SpawnParams.Owner = this;
                SpawnParams.Instigator = GetInstigator();
    
                // Spawn the projectile at the muzzle.
                AFPSProjectile* Projectile = World->SpawnActor<AFPSProjectile>(ProjectileClass, MuzzleLocation, MuzzleRotation, SpawnParams);
                if (Projectile)
                {
                    // Set the projectile's initial trajectory.
                    FVector LaunchDirection = MuzzleRotation.Vector();
                    Projectile->FireInDirection(LaunchDirection);
                }
            }
        }
    }
  5. FPSCharacter.h FPSCharacter.cpp を Visual Studio で保存します。

  6. [Solution Explorer (ソリューション エクスプローラ)] [FPSProject] を探します。

  7. [FPSProject] を右クリックして [Build] を選択してプロジェクトをコンパイルします。

    BuildFPSProject.png

発射物メッシュをインポートする

先に進む前に、「 発射物メッシュ 」から サンプル メッシュをダウンロードして展開してください。

  1. コンテンツ ブラウザのファイル ボックス内で右クリックして [Import Asset (インポート アセット)] ダイアログボックスを開きます。

    右クリックのインポートを説明しましたが、コンテンツをインポートするには 3 つの方法があります。コンテンツをインポートする方法については以下のドキュメントを参照してください。

  2. [Import to /Game...] をクリックして [Import (インポート)] ダイアログボックスを開きます。

    RightClickImport.png

  3. Sphere.fbx メッシュ ファイルを探して選択します。

  4. プロジェクトへのメッシュのインポートを開始するには、 [Open (開く)] をクリックします。

  5. コンテンツ ブラウザ に、 [FBX Import Options (FBXインポートオプション)] ダイアログ ボックスが表示されます。 [Import All (すべてインポート)] をクリックすると、メッシュがプロジェクトに追加されます。

    スムージング グループに関する以下のエラーは無視してください。

    FBXWarning.png

    このメッシュはファーストパーソン メッシュの設定を表していますが、この後のセクションで設定するアニメーションでも機能します。

  6. [Save] ボタンを クリック してインポートしたスタティックメッシュを保存します。

発射物のメッシュを追加する

  1. 次のコードを「FPSProjectile.h」に追加します。

    // Projectile mesh
    UPROPERTY(VisibleDefaultsOnly, Category = Projectile)
    UStaticMeshComponent* ProjectileMeshComponent;
  2. 次のコードを「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);
        }
    }
  3. コンテンツブラウザを開き、Sphere スタティック メッシュを右クリックして、 [Copy Reference (リファレンスをコピー)] を選択します。

    SphereCopyReference.png

  4. 「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 など) をリファレンス パスから削除します。

  5. 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: 
        // Sets default values for this actor's properties
        AFPSProjectile();
    
    protected:
        // Called when the game starts or when spawned
        virtual void BeginPlay() override;
    
    public: 
        // Called every frame
        virtual void Tick(float DeltaTime) override;
    
        // Sphere collision component
        UPROPERTY(VisibleDefaultsOnly, Category = Projectile)
        USphereComponent* CollisionComponent;
    
        // Projectile movement component
        UPROPERTY(VisibleAnywhere, Category = Movement)
        UProjectileMovementComponent* ProjectileMovementComponent;
    
        // Projectile mesh
        UPROPERTY(VisibleDefaultsOnly, Category = Projectile)
        UStaticMeshComponent* ProjectileMeshComponent;
    
        // Function that initializes the projectile's velocity in the shoot direction.
        void FireInDirection(const FVector& ShootDirection);
    
    };
  6. FPSProjectile.cpp は次のようになります。

    // Copyright Epic Games, Inc.All Rights Reserved.         
    
    #include "FPSProjectile.h"
    
    // Sets default values
    AFPSProjectile::AFPSProjectile()
    {
        // Set this actor to call Tick() every frame.You can turn this off to improve performance if you don't need it.
        PrimaryActorTick.bCanEverTick = true;
    
        if (!RootComponent)
        {
            RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("ProjectileSceneComponent"));
        }
    
        if (!CollisionComponent)
        {
            // Use a sphere as a simple collision representation.
            CollisionComponent = CreateDefaultSubobject<USphereComponent>(TEXT("SphereComponent"));
            // Set the sphere's collision radius.
            CollisionComponent->InitSphereRadius(15.0f);
            // Set the root component to be the collision component.
            RootComponent = CollisionComponent;
        }
    
        if (!ProjectileMovementComponent)
        {
            // Use this component to drive this projectile's movement.
            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);
            }
        }
    }
    
    // Called when the game starts or when spawned
    void AFPSProjectile::BeginPlay()
    {
        Super::BeginPlay();
    
    }
    
    // Called every frame
    void AFPSProjectile::Tick(float DeltaTime)
    {
        Super::Tick(DeltaTime);
    
    }
    
    // Function that initializes the projectile's velocity in the shoot direction.
    void AFPSProjectile::FireInDirection(const FVector& ShootDirection)
    {
        ProjectileMovementComponent->Velocity = ShootDirection * ProjectileMovementComponent->InitialSpeed;
    }
  7. コードを保存してビルドしてから、次のステップに進みます。次はメッシュのマテリアルとスケールを設定します。

発射物のマテリアルを追加する

  1. 次のコードを「FPSProjectile.h」に追加します。

    // Projectile material
    UPROPERTY(VisibleDefaultsOnly, Category = Movement)
    UMaterialInstanceDynamic* ProjectileMaterialInstance;
  2. 次のコードを「FPSProjectile.cpp」のコンストラクタに追加します。

    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);
  3. コンテンツブラウザ内を右クリックして M[Material (マテリアル)] を選択します。

    CreateBasicMaterial.png

  4. 新しいマテリアルに「SphereMaterial」と名前を付けます。

  5. 新規マテリアルのノード グラフを次のようなプロパティで設定します。 SetSphereNodes.png

  • Base Color: Constant2Vector ノードを (1,0,0) に設定

  • Specular: Constant ノードを 0.5 に設定 Emissive Color (エミッシブ カラー):Constant ノードを 0.05 に設定

  • Emissive Color: Constant ノードを 0.05 に設定

    このステップでは、基本的なマテリアル アセットを作成します。複雑なマテリアルの作成方法を習得するには、マテリアルの使用と作成方法を参照してください。

  1. 新規マテリアルのノード グラフを設定した後、[Save (保存)] をクリックして、コンテンツブラウザを開きます。

  2. Sphere マテリアルを右クリックして、[Copy Reference (リファレンスをコピー)] を選択します。

    SphereCopyReference.png

  3. 「FPSProjectile.cpp」の ProjectileMeshComponent コードに戻り、[ADD STATIC MESH 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 など) をリファレンス パスから削除します。

  4. 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: 
        // Sets default values for this actor's properties
        AFPSProjectile();
    
    protected:
        // Called when the game starts or when spawned
        virtual void BeginPlay() override;
    
    public: 
        // Called every frame
        virtual void Tick(float DeltaTime) override;
    
        // Sphere collision component
        UPROPERTY(VisibleDefaultsOnly, Category = Projectile)
        USphereComponent* CollisionComponent;
    
        // Projectile movement component
        UPROPERTY(VisibleAnywhere, Category = Movement)
        UProjectileMovementComponent* ProjectileMovementComponent;
    
        // Projectile mesh
        UPROPERTY(VisibleDefaultsOnly, Category = Projectile)
        UStaticMeshComponent* ProjectileMeshComponent;
    
        // Projectile material
        UPROPERTY(VisibleDefaultsOnly, Category = Movement)
        UMaterialInstanceDynamic* ProjectileMaterialInstance;
    
        // Function that initializes the projectile's velocity in the shoot direction.
        void FireInDirection(const FVector& ShootDirection);
    
    };
  5. FPSProjectile.cpp は次のようになります。

    // Copyright Epic Games, Inc.All Rights Reserved.     
    
    #include "FPSProjectile.h"
    
    // Sets default values
    AFPSProjectile::AFPSProjectile()
    {
    // Set this actor to call Tick() every frame.You can turn this off to improve performance if you don't need it.
    PrimaryActorTick.bCanEverTick = true;
    
    if (!RootComponent)
    {
        RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("ProjectileSceneComponent"));
    }
    
    if (!CollisionComponent)
    {
        // Use a sphere as a simple collision representation.
        CollisionComponent = CreateDefaultSubobject<USphereComponent>(TEXT("SphereComponent"));
        // Set the sphere's collision radius.
        CollisionComponent->InitSphereRadius(15.0f);
        // Set the root component to be the collision component.
        RootComponent = CollisionComponent;
    }
    
    if (!ProjectileMovementComponent)
    {
        // Use this component to drive this projectile's movement.
        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);
    }
        }
    
        // Called when the game starts or when spawned
        void AFPSProjectile::BeginPlay()
        {
    Super::BeginPlay();
    
        }
    
        // Called every frame
        void AFPSProjectile::Tick(float DeltaTime)
        {
    Super::Tick(DeltaTime);
    
        }
    
        // Function that initializes the projectile's velocity in the shoot direction.
        void AFPSProjectile::FireInDirection(const FVector& ShootDirection)
        {
    ProjectileMovementComponent->Velocity = ShootDirection * ProjectileMovementComponent->InitialSpeed;
        }
  6. コンテンツブラウザの「Blueprints」フォルダに移動して、「BP_FPSCharacter」ファイルを開きます。

    ContentBrowserBPFPSCharacter.png

  7. 必要であればフル エディタを開き、[Detail (詳細)] パネルに移動します。

  8. Projectile ヘッダを Projectile クラスの隣にあるドロップダウンから見つけて、[FPSProjectile] を選択します。

    DetailsSelectProjectile.png

  9. FPSProject をビルドし、ゲームを PIE モードで実行して、スタティック メッシュやマテリアルがシーンにスポーンされていることを確認します。

    PIEModeDemo.gif

    弾を発射すると、[World Outliner (アウトライナ)] で発射物の数が増えていくのがわかります。存続期間を定義していないからです。

    ProjectileTTLDemo.gif

    次のセクションでは、発射物の初期存続期間を定義する方法を示します。

3.3 - 発射物のコリジョンと存続期間を設定する

現時点で発射物は次のようになっています。

  • 無限に存続 (シーン アウトライナから消えない)

  • ワールドの他のオブジェクトと衝突しない

このステップでは、発射物のコリジョンとライフタイムをセットアップします。

発射物の存続期間を制限する

  1. 「FPSProjectile.cpp」を開きます。

  2. 次のコードを FPSProjectile コンストラクタに追加し、発射物の存続期間を設定します。

    // Delete the projectile after 3 seconds.
    InitialLifeSpan = 3.0f;
  3. FPSProjectile.cpp は次のようになります。

    // Copyright Epic Games, Inc.All Rights Reserved.
    
    #include "FPSProjectile.h"
    
    // Sets default values
    AFPSProjectile::AFPSProjectile()
    {
        // Set this actor to call Tick() every frame.You can turn this off to improve performance if you don't need it.
        PrimaryActorTick.bCanEverTick = true;
    
        if (!RootComponent)
        {
            RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("ProjectileSceneComponent"));
        }
    
        if (!CollisionComponent)
        {
            // Use a sphere as a simple collision representation.
            CollisionComponent = CreateDefaultSubobject<USphereComponent>(TEXT("SphereComponent"));
            // Set the sphere's collision radius.
            CollisionComponent->InitSphereRadius(15.0f);
            // Set the root component to be the collision component.
            RootComponent = CollisionComponent;
        }
    
        if (!ProjectileMovementComponent)
        {
            // Use this component to drive this projectile's movement.
            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);
        }
    
        // Delete the projectile after 3 seconds.
        InitialLifeSpan = 3.0f;
    }
    
    // Called when the game starts or when spawned
    void AFPSProjectile::BeginPlay()
    {
        Super::BeginPlay();
    
    }
    
    // Called every frame
    void AFPSProjectile::Tick(float DeltaTime)
    {
        Super::Tick(DeltaTime);
    
    }
    
    // Function that initializes the projectile's velocity in the shoot direction.
    void AFPSProjectile::FireInDirection(const FVector& ShootDirection)
    {
        ProjectileMovementComponent->Velocity = ShootDirection * ProjectileMovementComponent->InitialSpeed;
    }
  4. FPSProject を保存しビルドします。

  5. 発射物が 3 秒後に破棄されるかどうかを確認するために、ゲームを PIE モードで実行します。

    ProjectileWorldOutlinerDemo.gif

    [World Outliner (アウトライナ)] で確認すると、スポーンされた発射物が 3 秒後にシーンから消えます。

発射物のコリジョン設定を編集する

Unreal Engine には複数のプリセット コリジョン チャンネルがパッケージされています。ただしエンジンには、ゲーム プロジェクトで利用できる、カスタマイズ可能なチャンネルも用意されています。

  1. カスタム コリジョン チャンネルを作成するには、[Project Settings (プロジェクト設定)] を開き、[Engine - Collision (エンジン - コリジョン)] で、[Preset (プリセット)] を展開します。

    SettingCollisionChannels.png

  2. [Object Channels (オブジェクト チャンネル)] の [New Object Channel... (新規のオブジェクトチャンネル...)] を選択し、新しいコリジョン チャンネルを作成します。新規コリジョン チャンネルに「Projectile」と名前を付けて、[Default Response (デフォルト応答)] が「Block」に設定されていることを確認してから、[Accept (承認)] をクリックします。

    NewChannel.png

  3. [Preset (プリセット)] で [New... (新規...)] を選択して、新規プロファイルに「Projectile」と名前を付けます。次の画像を参照してコリジョン プリセットを設定します。[Accept (承認)] をクリックします。

    NewProfile.png

このコリジョン プロファイルでは、発射物がスタティック アクタ、ダイナミック アクタ、物理シミュレーションを利用するアクタ、乗り物、破壊可能なアクタでブロックされるように指定します。また、このコリジョン プロファイルでは発射物がポーンとオーバーラップすることも指定します。

新規コリジョン チャンネルの設定を使用する

  1. 「FPSProjectile.cpp」を開きます。

  2. FPSProjectile コンストラクタで、次のコード行を CreateDefaultSubobject の下に追加します

    // Set the sphere's collision profile name to "Projectile".
    CollisionComponent->BodyInstance.SetCollisionProfileName(TEXT("Projectile"));
  3. FPSProjectile.cpp は以下のようになります。

    // Copyright Epic Games, Inc.All Rights Reserved.
    
    #include "FPSProjectile.h"
    
    // Sets default values
    AFPSProjectile::AFPSProjectile()
    {
    // Set this actor to call Tick() every frame.You can turn this off to improve performance if you don't need it.
    PrimaryActorTick.bCanEverTick = true;
    
    if (!RootComponent)
    {
        RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("ProjectileSceneComponent"));
    }
    
    if (!CollisionComponent)
    {
        // Use a sphere as a simple collision representation.
        CollisionComponent = CreateDefaultSubobject<USphereComponent>(TEXT("SphereComponent"));
        // Set the sphere's collision profile name to "Projectile".
        CollisionComponent->BodyInstance.SetCollisionProfileName(TEXT("Projectile"));
        // Set the sphere's collision radius.
        CollisionComponent->InitSphereRadius(15.0f);
        // Set the root component to be the collision component.
        RootComponent = CollisionComponent;
    }
    
    if (!ProjectileMovementComponent)
    {
        // Use this component to drive this projectile's movement.
        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);
    }
    
    // Delete the projectile after 3 seconds.
    InitialLifeSpan = 3.0f;
    }
    
    // Called when the game starts or when spawned
    void AFPSProjectile::BeginPlay()
    {
    Super::BeginPlay();
    
    }
    
    // Called every frame
    void AFPSProjectile::Tick(float DeltaTime)
    {
    Super::Tick(DeltaTime);
    
    }
    
    // Function that initializes the projectile's velocity in the shoot direction.
    void AFPSProjectile::FireInDirection(const FVector& ShootDirection)
    {
    ProjectileMovementComponent->Velocity = ShootDirection * ProjectileMovementComponent->InitialSpeed;
    }
  4. FPSProject を保存しビルドします。

3.4 - 発射物をワールドとインタラクトさせる

ここで、発射物のコリジョン インタラクションを検出でき、コリジョンに対応する方法を決定できます。このステップでは、 OnHit 関数を、コリジョン イベントに対応する FPSProjectile に追加します。

発射物をコリジョンに反応させる

  1. FPSProjectile.h. を開きます。

  2. 次のコードを「 FPSProjectile.h 」に追加します。

    // Function that is called when the projectile hits something.
    UFUNCTION()
    void OnHit(UPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComponent, FVector NormalImpulse, const FHitResult& Hit);
  3. 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: 
    // Sets default values for this actor's properties
    AFPSProjectile();
    
    protected:
    // Called when the game starts or when spawned
    virtual void BeginPlay() override;
    
    public: 
    // Called every frame
    virtual void Tick(float DeltaTime) override;
    
    // Function that is called when the projectile hits something.
    UFUNCTION()
    void OnHit(UPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComponent, FVector NormalImpulse, const FHitResult& Hit);
    
    // Sphere collision component
    UPROPERTY(VisibleDefaultsOnly, Category = Projectile)
    USphereComponent* CollisionComponent;
    
    // Projectile movement component
    UPROPERTY(VisibleAnywhere, Category = Movement)
    UProjectileMovementComponent* ProjectileMovementComponent;
    
    // Projectile mesh
    UPROPERTY(VisibleDefaultsOnly, Category = Projectile)
    UStaticMeshComponent* ProjectileMeshComponent;
    
    // Projectile material
    UPROPERTY(VisibleDefaultsOnly, Category = Movement)
    UMaterialInstanceDynamic* ProjectileMaterialInstance;
    
    // Function that initializes the projectile's velocity in the shoot direction.
    void FireInDirection(const FVector& ShootDirection);
    
    };
  4. Solution Explorer で、 FPSProjectile class CPP ファイルを探して、 FPSProjectile.cpp を開いて以下のコードを追加します。

    // Function that is called when the projectile hits something.
    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();
    }
  5. FPSProjectile コンストラクタで、次のコード行を BodyInstance.SetCollisionProfileName: の後に追加します。

    // Event called when component hits something.
    CollisionComponent->OnComponentHit.AddDynamic(this, &AFPSProjectile::OnHit);
  6. FPSProjectile.cpp は以下のようになります。

    // Copyright Epic Games, Inc.All Rights Reserved.
    
    #include "FPSProjectile.h"
    
    // Sets default values
    AFPSProjectile::AFPSProjectile()
    {
    // Set this actor to call Tick() every frame.You can turn this off to improve performance if you don't need it.
    PrimaryActorTick.bCanEverTick = true;
    
    if (!RootComponent)
    {
        RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("ProjectileSceneComponent"));
    }
    
    if (!CollisionComponent)
    {
        // Use a sphere as a simple collision representation.
        CollisionComponent = CreateDefaultSubobject<USphereComponent>(TEXT("SphereComponent"));
        // Set the sphere's collision profile name to "Projectile".
        CollisionComponent->BodyInstance.SetCollisionProfileName(TEXT("Projectile"));
        // Event called when component hits something.
        CollisionComponent->OnComponentHit.AddDynamic(this, &AFPSProjectile::OnHit);
        // Set the sphere's collision radius.
        CollisionComponent->InitSphereRadius(15.0f);
        // Set the root component to be the collision component.
        RootComponent = CollisionComponent;
    }
    
    if (!ProjectileMovementComponent)
    {
        // Use this component to drive this projectile's movement.
        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);
    }
    
    // Delete the projectile after 3 seconds.
    InitialLifeSpan = 3.0f;
    }
    
    // Called when the game starts or when spawned
    void AFPSProjectile::BeginPlay()
    {
    Super::BeginPlay();
    
    }
    
    // Called every frame
    void AFPSProjectile::Tick(float DeltaTime)
    {
    Super::Tick(DeltaTime);
    
    }
    // Function that is called when the projectile hits something.
        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();
    }
    
    // Function that initializes the projectile's velocity in the shoot direction.
    void AFPSProjectile::FireInDirection(const FVector& ShootDirection)
    {
    ProjectileMovementComponent->Velocity = ShootDirection * ProjectileMovementComponent->InitialSpeed;
    }

発射物のコリジョンをテストする

  1. ビルドが完了したら、Unreal Editor に戻り、FPSProject を開きます。

  2. Floor スタティックメッシュを選択します。

  3. このフロア メッシュをコピーして貼り付けます。

  4. 比率ロック ([拡大・縮小] 行にある鍵型アイコン) が解除されていることを確認し、フロア メッシュのコピー (Floor2) を {0.2, 0.2, 3.0} にスケーリングします。

  5. フロア メッシュのコピーを {320, 0, 170} に配置します。

  6. [Physics (物理)] セクションにスクロールダウンして、[Simulate Physics] ボックスをオンにします。

    画像をクリックするとズームインします。

  7. マップを保存します。

  8. レベル エディタのツールバーで [Play (プレイ)] をクリックします。

  9. 発射物とキューブとのコリジョンを確認するために、マウスの左ボタンをクリックして物体を発射し、レベルでキューブをいろいろな場所に移動させてみます。

    [ CollisionTest.gif

    うまくいきました。発射物は完成です。

  10. Esc キーを押す、またはレベル エディタの [Stop (停止)] をクリックして、プレイインエディタ (PIE) モードから抜け出します。

3.5 - 照準線をビューポートに追加する

このステップでは、照準線 HUD 要素をゲームに追加して、狙いを定めて発射できるようにします。

照準アセットをインポートする

開始する前に、次のリンクからサンプル画像をダウンロードして展開してください。

  1. コンテンツ ブラウザのファイル ボックス内で右クリックして [Import Asset (インポート アセット)] ダイアログボックスを開きます。

  2. [Import to /Game...] をクリックして [Import (インポート)] ダイアログボックスを開きます。

    RightClickImport.png

    1. crosshair.TGA 」画像ファイルを探して選択します。

  1. [Open] をクリックして画像ファイルのプロジェクトへのインポートを開始します。

  2. [Save] ボタンをクリックしてインポートした画像を保存します。

新規 HUD クラスを追加する

  1. ファイル メニューで [New C++ Class... (新規 C++ クラス)] をクリックして、新しい親クラスを選択します。

  2. [Choose Parent Class (親クラスを選択)] メニューが開きます。下にスクロールして、親クラスとして [HUD] を選択して、 [Next] をクリック します。

    ChooseParentHUDClass.png

  3. 新規クラスに「FPSHUD」と名前を付けてから、 [Create Class (クラスを作成)] をクリックします。

    NameHUDClass.png

  4. [Solution Explorer] で、 FPSHUD クラス ヘッダ ファイルを探して、 FPSHUD.h を開きます。以下の protected 変数を追加します。

    protected:
        // This will be drawn at the center of the screen.
        UPROPERTY(EditDefaultsOnly)
        UTexture2D* CrosshairTexture;
  5. 次の関数宣言を FPSHUD.h に追加します。

    public:
        // Primary draw call for the HUD.
        virtual void DrawHUD() override;
  6. 次のヘッダ ファイルを「FPSHUD.h」に追加します。

    #include "Engine/Canvas.h" 
  7. 「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()
    
    public:
        // Primary draw call for the HUD.
        virtual void DrawHUD() override;
    
    protected:
        // This will be drawn at the center of the screen.
        UPROPERTY(EditDefaultsOnly)
        UTexture2D* CrosshairTexture;
    };
  8. FPSHUD.cpp で、 DrawHUD を実装します。

    void AFPSHUD::DrawHUD()
    {
        Super::DrawHUD();
    
        if (CrosshairTexture)
        {
            // Find the center of our canvas.
            FVector2D Center(Canvas->ClipX * 0.5f, Canvas->ClipY * 0.5f);
    
            // Offset by half of the texture's dimensions so that the center of the texture aligns with the center of the Canvas.
            FVector2D CrossHairDrawPosition(Center.X - (CrosshairTexture->GetSurfaceWidth() * 0.5f), Center.Y - (CrosshairTexture->GetSurfaceHeight() * 0.5f));
    
            // Draw the crosshair at the centerpoint.
            FCanvasTileItem TileItem(CrossHairDrawPosition, CrosshairTexture->Resource, FLinearColor::White);
            TileItem.BlendMode = SE_BLEND_Translucent;
            Canvas->DrawItem(TileItem);
        }
    }
  9. FPSHUD.h FPSHUD.cpp を Visual Studio に保存します。

  10. [Solution Explorer (ソリューション エクスプローラ)] [FPSProject] で見つけます。

  11. [FPSProject] を右クリックして [Build] を選択してプロジェクトをコンパイルします。

CPP HUD クラスをブループリントに拡張する

CPP HUD クラスをブループリントに拡張します。復習が必要な場合は、「 C++ とブループリント 」リファレンス ページで C++ クラスからブループリントへの拡張について 確認してください。

  1. FPSHUD クラスを右クリックして、 [C++ Class Actions] メニューを開きます。

  2. [Create Blueprint class based on FPSHUD] をクリックして [Add Blueprint Class] ダイアログ メニューを開きます。

    CPPClassActionsMenu.png

  3. 新しい Blueprint クラスに "BP_FPSHUD" と名前を付けて、「Blueprints」フォルダを選択してから、 [Create Blueprint Class] ボタンをクリックします。

    AddBPClass.png

  4. ここまでで、新規作成した BP_FPSHUD Blueprint クラスが「Blueprint」フォルダの中にあるはずです。

    AddedBPClass.png

  5. BP_FPSHUD ブループリントを保存してからブループリント エディタを閉じるようにします。

デフォルトの HUD クラスを設定する

  1. [Edit (編集)] メニューで、 [Project Settings (プロジェクト設定)] をクリックします。

  2. [Project Settings] タブの左側にある [Project (プロジェクト)] セクションで、 [Maps & Modes] をクリックします。

  3. [Default HUD] のドロップダウン メニューで [BP_FPSHUD] を選択します。

    ChooseHUDClass.png

  4. [Project Settings] メニューを閉じます。

  5. 戻って、 BP_FPSHUD ブループリント エディタを開きます。

  6. ブループリント エディタの FPSHUD セクションにあるドロップダウン メニューをクリックして、照準線のテクスチャを選択します。

    SelectCrosshairTexture.png

  7. 最後に、 BP_FPSHUD ブループリントを保存してからブループリント エディタを閉じるようにします。

HUD を確認する

  1. レベル エディタのツールバー で、 [Play (プレイ)] ボタンをクリックします。新規に追加した照準線で発射物の照準を合わせることができるようになります。

    CrosshairsInGame.png

  2. [PIE (Play In Editor)] モードを終了するには、レベル エディタで [Stop] ボタンを クリック します。

このセクションで完了したコード

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: 
        // Sets default values for this actor's properties
        AFPSProjectile();

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

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

        // Function that is called when the projectile hits something.
        UFUNCTION()
        void OnHit(UPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComponent, FVector NormalImpulse, const FHitResult& Hit);

        // Sphere collision component
        UPROPERTY(VisibleDefaultsOnly, Category = Projectile)
        USphereComponent* CollisionComponent;

        // Projectile movement component
        UPROPERTY(VisibleAnywhere, Category = Movement)
        UProjectileMovementComponent* ProjectileMovementComponent;

        // Projectile mesh
        UPROPERTY(VisibleDefaultsOnly, Category = Projectile)
        UStaticMeshComponent* ProjectileMeshComponent;

        // Projectile material
        UPROPERTY(VisibleDefaultsOnly, Category = Movement)
        UMaterialInstanceDynamic* ProjectileMaterialInstance;

        // Function that initializes the projectile's velocity in the shoot direction.
        void FireInDirection(const FVector& ShootDirection);

    };

FPSProjectile.cpp

    // Copyright Epic Games, Inc.All Rights Reserved.         

    #include "FPSProjectile.h"

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

        if (!RootComponent)
        {
            RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("ProjectileSceneComponent"));
        }

        if (!CollisionComponent)
        {
            // Use a sphere as a simple collision representation.
            CollisionComponent = CreateDefaultSubobject<USphereComponent>(TEXT("SphereComponent"));
            // Set the sphere's collision profile name to "Projectile".
            CollisionComponent->BodyInstance.SetCollisionProfileName(TEXT("Projectile"));
            // Event called when component hits something.
            CollisionComponent->OnComponentHit.AddDynamic(this, &AFPSProjectile::OnHit);
            // Set the sphere's collision radius.
            CollisionComponent->InitSphereRadius(15.0f);
            // Set the root component to be the collision component.
            RootComponent = CollisionComponent;
        }

        if (!ProjectileMovementComponent)
        {
            // Use this component to drive this projectile's movement.
            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);
        }

        // Delete the projectile after 3 seconds.
        InitialLifeSpan = 3.0f;
    }

    // Called when the game starts or when spawned
    void AFPSProjectile::BeginPlay()
    {
        Super::BeginPlay();

    }

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

    }

    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();
    }

    // Function that initializes the projectile's velocity in the shoot direction.
    void AFPSProjectile::FireInDirection(const FVector& ShootDirection)
    {
        ProjectileMovementComponent->Velocity = ShootDirection * ProjectileMovementComponent->InitialSpeed;
    }

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:
        // Sets default values for this character's properties
        AFPSCharacter();

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

        // Projectile class to spawn.
        UPROPERTY(EditAnywhere, Category = Projectile)
        TSubclassOf<class AFPSProjectile> ProjectileClass;

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

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

        // Handles input for moving forward and backward.
        UFUNCTION()
        void MoveForward(float Value);

        // Handles input for moving right and left.
        UFUNCTION()
        void MoveRight(float Value);

        // Sets jump flag when key is pressed.
        UFUNCTION()
        void StartJump();

        // Clears jump flag when key is released.
        UFUNCTION()
        void StopJump();

        // Function that fires projectiles.
        UFUNCTION()
        void Fire();

        // FPS camera
        UPROPERTY(VisibleAnywhere)
        UCameraComponent* FPSCameraComponent;

        // First-person mesh (arms), visible only to the owning player.
        UPROPERTY(VisibleDefaultsOnly, Category = Mesh)
        USkeletalMeshComponent* FPSMesh;

        // Gun muzzle offset from the camera location.
        UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Gameplay)
        FVector MuzzleOffset;
    };

FPSCharacter.cpp

    // Copyright Epic Games, Inc.All Rights Reserved.         

    #include "FPSCharacter.h"

    // Sets default values
    AFPSCharacter::AFPSCharacter()
    {
        // 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;

        // Create a first person camera component.
        FPSCameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("FirstPersonCamera"));
        check(FPSCameraComponent != nullptr);

        // Attach the camera component to our capsule component.
        FPSCameraComponent->SetupAttachment(CastChecked<USceneComponent, UCapsuleComponent>(GetCapsuleComponent()));

        // Position the camera slightly above the eyes.
        FPSCameraComponent->SetRelativeLocation(FVector(0.0f, 0.0f, 50.0f + BaseEyeHeight));

        // Enable the Pawn to control camera rotation.
        FPSCameraComponent->bUsePawnControlRotation = true;

        // Create a first person mesh component for the owning player.
        FPSMesh = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("FirstPersonMesh"));
        check(FPSMesh != nullptr);

        // Only the owning player sees this mesh.
        FPSMesh->SetOnlyOwnerSee(true);

        //Attach the FPS mesh to the FPS Camera.
        FPSMesh->SetupAttachment(FPSCameraComponent);

        //Disable some environmental shadows to preserve the illusion of having a single mesh.
        FPSMesh->bCastDynamicShadow = false;
        FPSMesh->CastShadow = false;

        // The owning player doesn't see the regular (third-person) body mesh.
        GetMesh()->SetOwnerNoSee(true);
    }

    // Called when the game starts or when spawned
    void AFPSCharacter::BeginPlay()
    {
        Super::BeginPlay();

        if (GEngine)
        {
            // Display a debug message for five seconds.
            // The -1 "Key" value argument prevents the message from being updated or refreshed.
            GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("We are using FPSCharacter."));
        }
    }

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

    }

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

        // Set up "movement" bindings.
        PlayerInputComponent->BindAxis("MoveForward", this, &AFPSCharacter::MoveForward);
        PlayerInputComponent->BindAxis("MoveRight", this, &AFPSCharacter::MoveRight);

        // Set up "look" bindings.
        PlayerInputComponent->BindAxis("Turn", this, &AFPSCharacter::AddControllerYawInput);
        PlayerInputComponent->BindAxis("LookUp", this, &AFPSCharacter::AddControllerPitchInput);

        // Set up "action" bindings.
        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)
    {
        // Find out which way is "forward" and record that the player wants to move that way.
        FVector Direction = FRotationMatrix(Controller->GetControlRotation()).GetScaledAxis(EAxis::X);
        AddMovementInput(Direction, Value);
    }

    void AFPSCharacter::MoveRight(float Value)
    {
        // Find out which way is "right" and record that the player wants to move that way.
        FVector Direction = FRotationMatrix(Controller->GetControlRotation()).GetScaledAxis(EAxis::Y);
        AddMovementInput(Direction, Value);
    }

    void AFPSCharacter::StartJump()
    {
        bPressedJump = true;
    }

    void AFPSCharacter::StopJump()
    {
        bPressedJump = false;
    }

    void AFPSCharacter::Fire()
    {
        // Attempt to fire a projectile.
        if (ProjectileClass)
        {
            // Get the camera transform.
            FVector CameraLocation;
            FRotator CameraRotation;
            GetActorEyesViewPoint(CameraLocation, CameraRotation);

            // Set MuzzleOffset to spawn projectiles slightly in front of the camera.
            MuzzleOffset.Set(100.0f, 0.0f, 0.0f);

            // Transform MuzzleOffset from camera space to world space.
            FVector MuzzleLocation = CameraLocation + FTransform(CameraRotation).TransformVector(MuzzleOffset);

            // Skew the aim to be slightly upwards. 
            FRotator MuzzleRotation = CameraRotation;
            MuzzleRotation.Pitch += 10.0f;

            UWorld* World = GetWorld();
            if (World)
            {
                FActorSpawnParameters SpawnParams;
                SpawnParams.Owner = this;
                SpawnParams.Instigator = GetInstigator();

                // Spawn the projectile at the muzzle.
                AFPSProjectile* Projectile = World->SpawnActor<AFPSProjectile>(ProjectileClass, MuzzleLocation, MuzzleRotation, SpawnParams);
                if (Projectile)
                {
                    // Set the projectile's initial trajectory.
                    FVector LaunchDirection = MuzzleRotation.Vector();
                    Projectile->FireInDirection(LaunchDirection);
                }

            }
        }
    }

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()

    public:
        // Primary draw call for the HUD.
        virtual void DrawHUD() override;

    protected:
        // This will be drawn at the center of the screen.
        UPROPERTY(EditDefaultsOnly)
        UTexture2D* CrosshairTexture;
    };

FPSHUD.cpp

    // Copyright Epic Games, Inc.All Rights Reserved.         

    #include "FPSHUD.h"

    void AFPSHUD::DrawHUD()
    {
        Super::DrawHUD();

        if (CrosshairTexture)
        {
            // Find the center of our canvas.
            FVector2D Center(Canvas->ClipX * 0.5f, Canvas->ClipY * 0.5f);

            // Offset by half of the texture's dimensions so that the center of the texture aligns with the center of the Canvas.
            FVector2D CrossHairDrawPosition(Center.X - (CrosshairTexture->GetSurfaceWidth() * 0.5f), Center.Y - (CrosshairTexture->GetSurfaceHeight() * 0.5f));

            // Draw the crosshair at the centerpoint.
            FCanvasTileItem TileItem(CrossHairDrawPosition, CrosshairTexture->Resource, FLinearColor::White);
            TileItem.BlendMode = SE_BLEND_Translucent;
            Canvas->DrawItem(TileItem);
        }
    }

これで終了です。ここでは、以下の方法について学習しました。

✓ ゲームに発射物を追加
✓ シューティングの実装
✓ プロジェクタイルのコリジョンとライフタイムのセットアップ
✓ 発射物がワールドと相互作用するようにする
✓ ビューポートに照準線 (照準) を追加

これで次のセクションでキャラクターのアニメートについて学ぶ準備が整いました。

Unreal Engine のドキュメントを改善するために協力をお願いします!どのような改善を望んでいるかご意見をお聞かせください。
調査に参加する
閉じる