Choose your operating system:
Windows
macOS
Linux
このセクションを終了したときに、このような画面になります。
目標
このセクションでは、ファースト パーソン シューター ゲームに発射物を実装する方法を説明します。
目的
このチュートリアルのこのセクションを完了すると、次のことができるようになります。
発射物をゲームに追加する
シューティングを実装する
発射物のコリジョンと存続期間を設定する
発射物をワールドとインタラクトさせる
照準線をビューポートに追加する
ステップ
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」と入力し、テキストボックスの左にある矢印をクリックして、アクション バインディング オプションを展開します。
ドロップダウン メニューで [Mouse] ドロップダウン リストから [Left Mouse Button] を選択します。
入力設定は以下のようになります。
[Project Settings] メニューを閉じます。
Projectile クラスを追加する
ファイル メニューで [New C++ Class... (新規 C++ クラス)] をクリックして、新しい親クラスを選択します。
[Choose Parent Class (親クラスを選択)] メニューが開きます。下方にスクロールして、親クラスとして [Actor] を選択して、[Next] をクリックします。
新規クラスに「FPSProjectile」と名前を付けてから、[Create Class (クラスを作成)] をクリックします。
USphere コンポーネントを追加する
Solution Explorer で、
FPSProjectile
クラス ヘッダーを探して、FPSProjectile.h
を開きます。SphereComponent ヘッダ ファイルを追加します。
#include "Components/SphereComponent.h"
FPSProjectile
インターフェースで、.USphereComponent
への参照を追加します。// Sphere collision component. 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: // 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; };
Solution Explorer (ソリューション エクスプローラー) で、
FPSProjectile
class CPP ファイルを探して、FPSProjectile.cpp
を開きます。次のコードを
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
にしています。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 コンポーネントを追加する
Solution Explorer (ソリューション エクスプローラー) で、
FPSProjectile
クラス ヘッダ ファイルを探して、FPSProjectile.h
を開きます。ProjectileMovementComponent ヘッダ ファイルを追加します。
#include "GameFramework/ProjectileMovementComponent.h"
次のコードを
FPSProjectile.cpp
に追加します。// Projectile movement component. 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: // 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; };
FPSProjectile.cpp
を [Solution Explorer] で開きます。以下のコードの行を、
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; }
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 ); }
発射物の初期べロシティを設定する
FPSProjectile.h
を [Solution Explorer] で開きます。次の関数宣言を「FPSProjectile.h」に追加します。
// Function that initializes the projectile's velocity in the shoot direction. 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: // 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); };
FPSProjectile.cpp
を [Solution Explorer] で開きます。次の関数定義を
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
によって定義されているため、指定するのは発射方向だけです。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 入力アクションをバインドする
FPSCharacter.h
を [Solution Explorer] で開きます。FPSCharacter.h
に以下の関数の宣言を追加します。// Function that handles firing projectiles. 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: // 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; };
FPSCharacter
CPP ファイルを [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" // 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() { }
発射物のスポーン位置を定義する
FPSProjectile
アクタをスポーンする場合、OnFire
関数を実装する場合に考慮すべきポイントが以下のように 2 つあります。発射物をスポーンする場所。
発射物クラス (
FPSCharacter
とその派生ブループリントがスポーンする発射物の種類を認識するため)。
カメラ空間のオフセット ベクターを使用して、発射物のスポーン位置を決定します。このパラメータを編集可能にして、
BP_FPSCharacter
ブループリントで設定し調整できるようにします。最終的に、このデータに基づいて発射物の初期位置を計算できます。FPSCharacter.h
を [Solution Explorer] で開きます。次のコードを
FPSCharacter.h
に追加します。// Gun muzzle offset from the camera location. UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Gameplay) FVector MuzzleOffset;
EditAnywhere
により、ブループリント エディタのデフォルト モード内、またはキャラクターのいずれかのインスタンスの [Details (詳細)] タブで、銃口オフセットの値を変更できます。BlueprintReadWrite
指定子により、ブループリント内で銃口オフセットの値を取得、設定できます。次のコードを
FPSCharacter.h
の protected: アクセス指定子の下に追加します。// Projectile class to spawn. 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: // 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; };
コードをコンパイルしチェックする
新規で実装した発射物のコードをコンパイルし、チェックします。
Visual Studio にすべてのヘッダーと実装ファイルを保存します。
[Solution Explorer (ソリューション エクスプローラ)] で [FPSProject] を探します。
[FPSProject] を右クリックして [Build] を選択してプロジェクトをコンパイルします。
このステップの目的は、次のステップに進む前に、何らかのビルド エラーを検出することです。このチュートリアルの範囲を超えるビルドエラーや警告が発生した場合は「Coding Standard (コーディング標準)」や「Unreal Engine API Reference (API リファレンス)」を参照してください。
3.2 - シューティングを実装する
ファースト パーソン シューター キャラクターのシューティングを実装する方法を習得します。
Fire 関数を実装する
次のコード行を「FPSCharacter.h」に追加します。
#include "FPSProjectile.h"
次の
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); } } } }
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 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; };
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); } } } }
FPSCharacter.h
とFPSCharacter.cpp
を Visual Studio で保存します。[Solution Explorer (ソリューション エクスプローラ)] で [FPSProject] を探します。
[FPSProject] を右クリックして [Build] を選択してプロジェクトをコンパイルします。
発射物メッシュをインポートする
先に進む前に、「発射物メッシュ」から サンプル メッシュをダウンロードして展開してください。
コンテンツ ブラウザのファイル ボックス内で右クリックして [Import Asset (インポート アセット)] ダイアログボックスを開きます。
右クリックのインポートを説明しましたが、コンテンツをインポートするには 3 つの方法があります。コンテンツをインポートする方法については以下のドキュメントを参照してください。
[Import to /Game...] をクリックして [Import (インポート)] ダイアログボックスを開きます。
Sphere.fbx メッシュ ファイルを探して選択します。
プロジェクトへのメッシュのインポートを開始するには、[Open (開く)] をクリックします。
コンテンツ ブラウザ に、[FBX Import Options (FBXインポートオプション)] ダイアログ ボックスが表示されます。[Import All (すべてインポート)] をクリックすると、メッシュがプロジェクトに追加されます。
スムージング グループに関する以下のエラーは無視してください。
このメッシュはファーストパーソン メッシュの設定を表していますが、この後のセクションで設定するアニメーションでも機能します。
[Save] ボタンを クリック してインポートしたスタティックメッシュを保存します。
発射物のメッシュを追加する
次のコードを「FPSProjectile.h」に追加します。
// Projectile mesh UPROPERTY(VisibleDefaultsOnly, Category = Projectile) UStaticMeshComponent* ProjectileMeshComponent;
次のコードを「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); } }
コンテンツブラウザを開き、Sphere スタティック メッシュを右クリックして、[Copy Reference (リファレンスをコピー)] を選択します。
「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: // 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); };
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; }
コードを保存してビルドしてから、次のステップに進みます。次はメッシュのマテリアルとスケールを設定します。
発射物のマテリアルを追加する
次のコードを「FPSProjectile.h」に追加します。
// Projectile material UPROPERTY(VisibleDefaultsOnly, Category = Movement) UMaterialInstanceDynamic* ProjectileMaterialInstance;
次のコードを「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);
コンテンツブラウザ内を右クリックして M[Material (マテリアル)] を選択します。
新しいマテリアルに「SphereMaterial」と名前を付けます。
新規マテリアルのノード グラフを次のようなプロパティで設定します。
Base Color: Constant2Vector ノードを (1,0,0) に設定
Specular: Constant ノードを 0.5 に設定 Emissive Color (エミッシブ カラー):Constant ノードを 0.05 に設定
Emissive Color: Constant ノードを 0.05 に設定
このステップでは、基本的なマテリアル アセットを作成します。複雑なマテリアルの作成方法を習得するには、マテリアルの使用と作成方法を参照してください。
新規マテリアルのノード グラフを設定した後、[Save (保存)] をクリックして、コンテンツブラウザを開きます。
Sphere マテリアルを右クリックして、[Copy Reference (リファレンスをコピー)] を選択します。
「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 など) をリファレンス パスから削除します。
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); };
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; }
コンテンツブラウザの「Blueprints」フォルダに移動して、「BP_FPSCharacter」ファイルを開きます。
必要であればフル エディタを開き、[Detail (詳細)] パネルに移動します。
Projectile ヘッダを Projectile クラスの隣にあるドロップダウンから見つけて、[FPSProjectile] を選択します。
FPSProject をビルドし、ゲームを PIE モードで実行して、スタティック メッシュやマテリアルがシーンにスポーンされていることを確認します。
弾を発射すると、[World Outliner (アウトライナ)] で発射物の数が増えていくのがわかります。存続期間を定義していないからです。
次のセクションでは、発射物の初期存続期間を定義する方法を示します。
3.3 - 発射物のコリジョンと存続期間を設定する
現時点で発射物は次のようになっています。
無限に存続 (シーン アウトライナから消えない)
ワールドの他のオブジェクトと衝突しない
このステップでは、発射物のコリジョンとライフタイムをセットアップします。
発射物の存続期間を制限する
「FPSProjectile.cpp」を開きます。
次のコードを FPSProjectile コンストラクタに追加し、発射物の存続期間を設定します。
// Delete the projectile after 3 seconds. InitialLifeSpan = 3.0f;
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; }
FPSProject を保存しビルドします。
発射物が 3 秒後に破棄されるかどうかを確認するために、ゲームを PIE モードで実行します。
[World Outliner (アウトライナ)] で確認すると、スポーンされた発射物が 3 秒後にシーンから消えます。
発射物のコリジョン設定を編集する
Unreal Engine には複数のプリセット コリジョン チャンネルがパッケージされています。ただしエンジンには、ゲーム プロジェクトで利用できる、カスタマイズ可能なチャンネルも用意されています。
カスタム コリジョン チャンネルを作成するには、[Project Settings (プロジェクト設定)] を開き、[Engine - Collision (エンジン - コリジョン)] で、[Preset (プリセット)] を展開します。
[Object Channels (オブジェクト チャンネル)] の [New Object Channel... (新規のオブジェクトチャンネル...)] を選択し、新しいコリジョン チャンネルを作成します。新規コリジョン チャンネルに「Projectile」と名前を付けて、[Default Response (デフォルト応答)] が「Block」に設定されていることを確認してから、[Accept (承認)] をクリックします。
[Preset (プリセット)] で [New... (新規...)] を選択して、新規プロファイルに「Projectile」と名前を付けます。次の画像を参照してコリジョン プリセットを設定します。[Accept (承認)] をクリックします。
このコリジョン プロファイルでは、発射物がスタティック アクタ、ダイナミック アクタ、物理シミュレーションを利用するアクタ、乗り物、破壊可能なアクタでブロックされるように指定します。また、このコリジョン プロファイルでは発射物がポーンとオーバーラップすることも指定します。
新規コリジョン チャンネルの設定を使用する
「FPSProjectile.cpp」を開きます。
FPSProjectile コンストラクタで、次のコード行を CreateDefaultSubobject
の下に追加します // Set the sphere's collision profile name to "Projectile". CollisionComponent->BodyInstance.SetCollisionProfileName(TEXT("Projectile"));
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; }
FPSProject を保存しビルドします。
3.4 - 発射物をワールドとインタラクトさせる
ここで、発射物のコリジョン インタラクションを検出でき、コリジョンに対応する方法を決定できます。このステップでは、OnHit
関数を、コリジョン イベントに対応する FPSProjectile
に追加します。
発射物をコリジョンに反応させる
FPSProjectile.h.
を開きます。次のコードを「
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);
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); };
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(); }
FPSProjectile
コンストラクタで、次のコード行をBodyInstance.SetCollisionProfileName:
の後に追加します。// Event called when component hits something. CollisionComponent->OnComponentHit.AddDynamic(this, &AFPSProjectile::OnHit);
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; }
発射物のコリジョンをテストする
ビルドが完了したら、Unreal Editor に戻り、FPSProject を開きます。
Floor スタティックメッシュを選択します。
このフロア メッシュをコピーして貼り付けます。
比率ロック ([拡大・縮小] 行にある鍵型アイコン) が解除されていることを確認し、フロア メッシュのコピー (Floor2) を {0.2, 0.2, 3.0} にスケーリングします。
フロア メッシュのコピーを {320, 0, 170} に配置します。
[Physics (物理)] セクションにスクロールダウンして、[Simulate Physics] ボックスをオンにします。
画像をクリックするとズームインします。
マップを保存します。
レベル エディタのツールバーで [Play (プレイ)] をクリックします。
発射物とキューブとのコリジョンを確認するために、マウスの左ボタンをクリックして物体を発射し、レベルでキューブをいろいろな場所に移動させてみます。
[
うまくいきました。発射物は完成です。
Esc キーを押す、またはレベル エディタの [Stop (停止)] をクリックして、プレイインエディタ (PIE) モードから抜け出します。
3.5 - 照準線をビューポートに追加する
このステップでは、照準線 HUD 要素をゲームに追加して、狙いを定めて発射できるようにします。
照準アセットをインポートする
開始する前に、次のリンクからサンプル画像をダウンロードして展開してください。
コンテンツ ブラウザのファイル ボックス内で右クリックして [Import Asset (インポート アセット)] ダイアログボックスを開きます。
[Import to /Game...] をクリックして [Import (インポート)] ダイアログボックスを開きます。
「crosshair.TGA」画像ファイルを探して選択します。
[Open] をクリックして画像ファイルのプロジェクトへのインポートを開始します。
[Save] ボタンをクリックしてインポートした画像を保存します。
新規 HUD クラスを追加する
ファイル メニューで [New C++ Class... (新規 C++ クラス)] をクリックして、新しい親クラスを選択します。
[Choose Parent Class (親クラスを選択)] メニューが開きます。下にスクロールして、親クラスとして [HUD] を選択して、[Next] をクリック します。
新規クラスに「FPSHUD」と名前を付けてから、[Create Class (クラスを作成)] をクリックします。
[Solution Explorer] で、
FPSHUD
クラス ヘッダ ファイルを探して、FPSHUD.h
を開きます。以下の protected 変数を追加します。protected: // This will be drawn at the center of the screen. UPROPERTY(EditDefaultsOnly) UTexture2D* CrosshairTexture;
次の関数宣言を
FPSHUD.h
に追加します。public: // Primary draw call for the 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() 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
で、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); } }
FPSHUD.h
とFPSHUD.cpp
を Visual Studio に保存します。[Solution Explorer (ソリューション エクスプローラ)] で [FPSProject] で見つけます。
[FPSProject] を右クリックして [Build] を選択してプロジェクトをコンパイルします。
CPP HUD クラスをブループリントに拡張する
CPP HUD クラスをブループリントに拡張します。復習が必要な場合は、「C++ とブループリント」リファレンス ページで C++ クラスからブループリントへの拡張について 確認してください。
FPSHUD
クラスを右クリックして、[C++ Class Actions] メニューを開きます。[Create Blueprint class based on FPSHUD] をクリックして [Add Blueprint Class] ダイアログ メニューを開きます。
新しい Blueprint クラスに "BP_FPSHUD" と名前を付けて、「Blueprints」フォルダを選択してから、[Create Blueprint Class] ボタンをクリックします。
ここまでで、新規作成した
BP_FPSHUD
Blueprint クラスが「Blueprint」フォルダの中にあるはずです。BP_FPSHUD
ブループリントを保存してからブループリント エディタを閉じるようにします。
デフォルトの HUD クラスを設定する
[Edit (編集)] メニューで、 [Project Settings (プロジェクト設定)] をクリックします。
[Project Settings] タブの左側にある [Project (プロジェクト)] セクションで、[Maps & Modes] をクリックします。
[Default HUD] のドロップダウン メニューで [BP_FPSHUD] を選択します。
[Project Settings] メニューを閉じます。
戻って、
BP_FPSHUD
ブループリント エディタを開きます。ブループリント エディタの
FPSHUD
セクションにあるドロップダウン メニューをクリックして、照準線のテクスチャを選択します。最後に、
BP_FPSHUD
ブループリントを保存してからブループリント エディタを閉じるようにします。
HUD を確認する
レベル エディタのツールバー で、[Play (プレイ)] ボタンをクリックします。新規に追加した照準線で発射物の照準を合わせることができるようになります。
[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);
}
}
これで終了です。ここでは、以下の方法について学習しました。
✓ ゲームに発射物を追加
✓ シューティングの実装
✓ プロジェクタイルのコリジョンとライフタイムのセットアップ
✓ 発射物がワールドと相互作用するようにする
✓ ビューポートに照準線 (照準) を追加
これで次のセクションでキャラクターのアニメートについて学ぶ準備が整いました。