始める前に
前のセクション「入力アクションを使用してプレイヤー キャラクターを作成する」で次の目標を達成したことを確認します。
キャラクターの C++ クラスを作成しました。
入力アクションと入力マッピング コンテキストの仕組みを学んだ。
入力と移動をリンクする方法を学ぶ
サンプル キャラクター ブループリントで、入力アクション、入力マッピング コンテキスト、コードがどのように組み合わさって動きを生み出すのかを学びます。 その後、コードでその機能をレプリケートする方法を学びます。
ブループリントで入力を視覚化する
一人称視点テンプレートに含まれている BP_FirstPersonCharacter クラスは、ブループリントと入力アクションがどのようにインタラクトするのかがわかる好例です。
コンテンツ ブラウザのアセット ツリーで、「Content」 > 「FirstPerson」 > 「Blueprints」の順に移動します。 BP_FirstPersonCharacter クラスをダブルクリックしてブループリント エディタで開きます。
ブループリントのイベント グラフは、ブループリント エディタの中央にあります。 EventGraph は、イベントと関数呼び出しを使用して、ゲームプレイに応じて一連のアクションを順番に実行するノード グラフです。 このグラフには、Camera Input (カメラ入力)、Movement Input (移動入力)、Jump Input (ジャンプ入力) のノード グループがあります。
Jump Input のロジックを理解する
Jump Input グループにズームインします。 EnhancedInputAction IA_Jump ノードは、前の手順で確認した IA_Jump 入力アクション アセットを表します。
入力アクションがトリガーされると、Started (開始) イベントと Triggered (トリガー) イベントが開始されます。 ノードの Started イベントは、Jump という関数ノードにつながります。 このブループリントの親キャラクター クラスには組み込みのジャンプ機能があり、この関数はボタン押下によって IA_Jump がトリガーされるたびに呼び出されます。
ジャンプが完了すると、ノードが Completed (完了) イベントをトリガーします。 このイベントは、同じくキャラクター クラスから継承する別の関数ノード Stop Jumping (ジャンプを停止) につながります。
Jump Input ロジックもタッチ コントロールを追加しますが、このチュートリアルでは扱いません。
Movement Input のロジックを理解する
次に、Movement Input グループを見ていきます。 このグループも入力アクション IA_Move に対応するノードから始まります。
IA_Move ノードには、IA_Move にバインドされている任意のボタンが押されたときに開始する Triggered イベントがあります。
IA_Move にも、プレイヤーの入力によって生成される X 移動値と Y 移動値である、Action Value X (アクション値 X) と Action Value Y (アクション値 Y) が含まれています。 X 値と Y 値は別であるため、それぞれをキャラクターに個別に適用する必要があります。
Move ノードは、キャラクターに移動を適用するカスタム関数ノードです。 このノードとその[Details (詳細)] パネルで、「Left/Right」と「Forward/Backward」という名前の 2 つの入力を受け取ることと、IA_Move の X と Y の移動値がこれらの入力に渡されていることがわかります。
Move ノードをダブルクリックするか、グラフ上部の [Move (移動)] タブをクリックして、関数内のロジックを表示します。
この関数は、その入力値を持つ関数エントリ ノードから開始します。
Left/Right ノード グループには、World Direction (ワールド方向) と Scale Value (スケール値) という 2 つの値に基づいてキャラクターに移動を追加する、Add Movement Input 関数ノードが含まれています。
World Direction はキャラクターがワールドで向いている方向であり、Scale Value は適用する移動量です。 このノードは左/右移動を処理するため、Get Actor Right Vector 関数ノードを使用して、最初にワールドのキャラクターの位置の右ベクターを取得してから、Left / Right 入力 (または Input Action の X 値) をそのベクターに沿って動きを適用する Scale Value として使用します。
Left / Right が正の場合、キャラクターは X 軸を上方向 (右方向) に移動します。 Left / Right が負の場合、キャラクターは X 軸を下方向 (左方向) に移動します。
Forward/Backward グループの設定は Left/Right グループと同じですが、このグループは代わりに Forward / Backward 入力 (Input Action の Y 値) を使用し、アクタの Forward Vector (前方ベクター) に沿って Scale Value を決定します。
コードでこれらのノードをレプリケートするには、もう少し作業が必要ですが、レプリケートすれば、キャラクターがどこをどのように移動するのかを正確に制御できます。
PlayerController でプレイヤーに入力を割り当てる
入力マッピング コンテキストは、プレイヤーの入力を入力アクションにマッピングしますが、その入力コンテキストもプレイヤーにつなげる必要があります。 デフォルト プレイヤーは、PlayerController クラスと入力サブシステムでこれを行います。
PlayerController アセットは、人間のプレイヤーと人間のプレイヤーが制御するインゲームのポーン間の橋渡しをします。 このアセットがプレイヤーの入力を受け取って処理し、その入力をコマンドに変換すると、ポーンがそれらのコマンドを受け取ってゲーム ワールドでの移動の実行方法を割り出します。 同じ PlayerController を使用してさまざまなポーンを制御できます。
PlayerController では、以下を行うことも可能です。
カットシーンまたはメニューでの入力を無効にする。
スコアなどのプレイヤー データを追跡する。
UI 要素をスポーンするか非表示にする。
PlayerController とキャラクターを分離することにより、柔軟性とデータの永続性を確保できます。 たとえば、これは PlayerController 内に存在するため、(プレイヤーが死んだときなどに) プレイヤー データや入力処理ロジックを失うことなくキャラクターを切り替えることができます。
ブループリントでこれを設定する方法を確認するには、コンテンツ ブラウザで「Blueprints」フォルダに戻って BP_FirstPersonPlayerController ブループリントを開きます。
PlayerController クラスには、Enhanced Input Local Player サブシステムがあります。 これは、特定のローカル プレイヤーにアタッチされ、ランタイム時にそのプレイヤーの入力コンテキストとマッピングを管理するサブシステムです。 これを使用してどの入力がアクティブなのかを管理し、ランタイム時に入力コンテキストを切り替えます。 UE のサブシステムの詳細については、「プログラミング サブシステム」を参照してください。
ゲームの開始時に Enhanced Input Local Player サブシステムが有効な場合、Add Mapping Context が呼び出されて IMC_Default 入力マッピング コンテキストがプレイヤーの入力サブシステムにバインドされます。 つまり、このノードのグループはプレイヤーに対してこの入力セットをアクティブ化します。
この PlayerController ロジックは、他の移動ロジックとは別のブループリントにありますが、C++ では、2 つ目の C++ クラスが必要なくなるよう、そのすべてをキャラクター クラス内に実装します。
BP_FirstPersonCharacterのイベント グラフは、ポーンが所持されるまで待機するという、入力マッピング コンテキストを適用する別の方法を表示します。 この方法はこのチュートリアルでは取り上げていませんが、ご自身で試してみてください。
キャラクター クラスを設定する
ブループリントでの移動の仕組みがわかったら、コードでビルドを行ってレベルでのキャラクターの移動をテストします。 まず、必要なモジュールと #include 文のすべてを追加してから、キャラクターの移動を実装するのに必要なクラス、関数、プロパティを宣言します。
このチュートリアルのコードの例では、AdventureGame というプロジェクトと AdventureCharacter というキャラクター クラスを使用します。
Enhanced Input システムを追加する
Unreal Editor で Enhanced Input システムが有効になっていることはすでに確認しましたが、プロジェクトの Build.cs で手動で宣言し、 キャラクター クラスに特定のコンポーネントを追加する必要もあります。
C++ プロジェクトで Enhanced Input システムを使用するには、次の手順を実行します。
Visual Studio でプロジェクトを開いてから、
[ProjectName].Build.csを開きます (このファイルは、プロジェクトの他のクラス ファイルとともに「Source」フォルダにあります)。このファイルにより、プロジェクトをビルドする必要があるモジュールが Unreal Engine に伝えられます。
PublicDependencyModuleNames関数呼び出しで、モジュールのリストに“EnhancedInput”を追加します。C++PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "EnhancedInput" });保存して
Build.csファイルを閉じます。
キャラクター クラスに Enhanced Input システムのコンポーネントを追加するには、次の手順を実行します。
キャラクターの
.hファイルを開きます。 ファイルの上部の近くに次の include 文を追加します。#include “EnhancedInputComponent.h”は Enhanced Input コンポーネント モジュールを追加します。#include “InputActionValue.h”は、入力アクションによって生成された入力アクション値にアクセスできるようにします。#include “EnhancedInputSubsystems.h”は、ローカル プレイヤー サブシステムにアクセスできるようにします。
C++// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "GameFramework/Character.h" #include "EnhancedInputComponent.h" #include "EnhancedInputSubsystems.h" #include "InputActionValue.h" #include "AdventureCharacter.generated.h"追加するすべての
#include文がAdventureCharacter.generated.h文の前に来るようにします。 コードを適切に機能させるには、この文が入力のリストの最後になるようにする必要があります。#include文の後に、次の 3 つの新しいクラスを宣言します。UInputMappingContextUInputActionUInputComponent
これらのクラスは、すでに Enhanced Input モジュールに存在します。 このような既存のオブジェクトの宣言は前方宣言と呼ばれ、クラスが存在することとユーザーがそれを使用することをコンパイラに伝えます。
C++// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "GameFramework/Character.h" #include "EnhancedInputComponent.h" #include "EnhancedInputSubsystems.h" #include "InputActionValue.h" #include "AdventureCharacter.generated.h"
InputMappingContext ポインタを宣言する
キャラクターの .h ファイルの protected セクションで、 TObjectPtr を使って FirstPersonContext という名前の UInputMappingContext ポインタを追加します。 これは、入力アクションをボタン押下にリンクする入力マッピング コンテキストへのポインタです。
TObjectPtr は、UObject を継承した型を安全に参照するための Unreal Engine におけるスマート ポインタ ラッパーです。 これは、エディタ対応でガベージ コレクションにも安全な、生の UObject ポインタの代替手段です。 これはハード リファレンスであり、実行時にオブジェクトをロードされた状態に保ちます。 Unreal Engine でプログラミングする際は、この方法でポインタを宣言することを推奨します。
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input)
TObjectPtr<UInputMappingContext> FirstPersonContext;U プレフィックスは、InputMappingContext を UObject として識別します。
変数宣言の前に来る UPROPERTY() マクロは、Unreal Engine にその変数を伝えます。 Unreal ヘッダ ツールは、このマクロを使用してコードに関する情報を処理し、変数にアクセスできる場所やエディタでの変数の表示方法などを制御します。
このポインタには、次の UPROPERTY 値があります。
EditAnywhere:クラスの [Details] パネルで Unreal Editor にプロパティを公開します。BlueprintReadOnly:ブループリントはこのプロパティにアクセスできますが、編集はできません。Category = Input:このプロパティは、クラスの [Details] パネルの [Input (入力)] というセクションの下に表示されます。 カテゴリはコードを整理するのに役立ち、これによってエディタの操作を格段に容易にできます。
ジャンプおよび移動入力アクション ポインタを宣言する
また protected セクションで、MoveAction と JumpAction という 2 つの UInputAction ポインタを追加します。 これらは、IA_Jump 入力アクションと IA_Move 入力アクションへのポインタです。
これらに UInputMappingContext と同じ UPROPERTY() マクロを付与します。
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input)
TObjectPtr<UInputMappingContext> FirstPersonContext;
// Move Input Actions
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input)
TObjectPtr<UInputAction> MoveAction;
Move() 関数を宣言する
入力アクションによって入力アクション値が生成されたら、これらの値を使用してキャラクターに移動を適用する新しい関数にこれらの値を渡します。
ファイルの public セクションで、Value という定数 FInputActionValue の参照を取る Move() という新しい関数を宣言します。
// Handles 2D Movement Input
UFUNCTION()
void Move(const FInputActionValue& Value);関数宣言の前に来る UFUNCTION() マクロは、Unreal ヘッダ ツールにこの関数を認識させます。
ファイルを保存します。 キャラクターのヘッダ ファイルは次のようになります。
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "EnhancedInputComponent.h"
#include "EnhancedInputSubsystems.h"
#include "InputActionValue.h"
#include "AdventureCharacter.generated.h"
class UInputMappingContext;
移動関数を実装する
キャラクターの移動に必要なプロパティを宣言したら、デフォルト キャラクターのブループリントで見た機能を模倣する関数をキャラクターの .cppファイルで設計します。
Move() 関数を設定する
キャラクターの .cppクラスを開いて Move() の新しい関数定義を追加し、.hファイルで宣言したものを実装します。
void AAdventureCharacter::Move(const FInputActionValue& Value)
{
) デフォルト キャラクターの入力アクションを見ると、IA_Move に Axis2D (Vector2D) の値型があるため、トリガーされると FVector2D の値が返されます。
Move() 内で FInputActionValue の値を取得し、MovementValue という新しい FVector2D 内に保存します。
void AAdventureCharacter::Move(const FInputActionValue& Value)
{
// 2D Vector of movement values returned from the input action
const FVector2D MovementValue = Value.Get<FVector2D>();
}次に、if 文を追加してコントローラーが有効かどうかを確認します。 Controller は、このアクタを処理するコントローラーへのポインタであり、移動を機能させるには有効でなければなりません。
void AAdventureCharacter::Move(const FInputActionValue& Value)
{
// 2D Vector of movement values returned from the input action
const FVector2D MovementValue = Value.Get<FVector2D>();
// Check if the controller possessing this Actor is valid
if (Controller)
{
}
Move() を使用して 2D 移動入力を追加する
キャラクター ブループリントで左、右、前、後方向の移動を生成するために、イベント グラフで IA_Move の Action Value X と Action Value Y にアクタの右ベクターと前方ベクターを組み合わせて移動入力を追加しました。 これを Move() 関数内のコードで実装します。
if 文内で GetActorRightVector() を呼び出して、Right という新しい FVector にアクタの右ベクターを保存します。
const FVector Right = GetActorRightVector();その後、AddMovementInput() を呼び出してキャラクターに移動を追加し、Right と MovementValue.X を渡します。
AddMovementInput(Right, MovementValue.X);GetActorForwardVector() を使用して前方移動と後方移動でこのプロセスを繰り返し、今回は MovementValue.Y を渡します。
完成した Move() 関数は次のようになります。
void AAdventureCharacter::Move(const FInputActionValue& Value)
{
// 2D Vector of movement values returned from the input action
const FVector2D MovementValue = Value.Get<FVector2D>();
// Check if the controller posessing this Actor is valid
if (Controller)
{
// Add left and right movement
const FVector Right = GetActorRightVector();
SetupPlayerInputComponent を使用して入力に移動をバインドする
次に、Move 関数を、前に宣言した FirstPersonContext 入力マッピング コンテキストにリンクします。
これの関数 SetupPlayerInputComponent() は、ACharacter から継承されるため、キャラクターの .cpp ファイルですでに定義されています。 この関数は、UInputComponent を取って使用することにより、プレイヤーの入力を設定します。
Enhanced Input コンポーネントを確認する
デフォルトでは、この関数は、キャラクターに入力コンポーネントが存在するかどうかを確認する ACharacter からの SetupPlayerInputComponent() 関数への呼び出しから始まります。
// Called to bind functionality to input
void AAdventure::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
}これはキャラクターに通常の入力コンポーネントが存在するかどうかだけを確認するものであり、代わりに Enhanced Input コンポーネントがあるかどうかを確認する必要があるため、親クラスの SetupPlayerInputComponent() 関数へのこの呼び出しを削除します。
代わりに、if 文で EnhancedInputComponent という新しい UEnhancedInputComponent ポインタを宣言します。 これをこの関数に渡された PlayerInputComponent での CastChecked() の呼び出しの結果と同じにすると同時に、UEnhancedInputComponent にキャストします。
if (UEnhancedInputComponent* EnhancedInputComponent = CastChecked<UEnhancedInputComponent>(PlayerInputComponent))
{
}移動アクションをバインドする
if 文内で EnhancedInputComponent から BindAction() 関数を呼び出します。
関数に次の引数を渡します。
MoveAction:バインドする入力アクション (キャラクターの.hファイルで宣言されている)。ETriggeredEventからのTriggeredイベント:イベントのトリガーの型。this:バインド先のキャラクター。Move():バインドする関数への参照。
if (TObjectPtr<UEnhancedInputComponent> EnhancedInputComponent = CastChecked<UEnhancedInputComponent>(PlayerInputComponent))
{
// Bind Movement Actions
EnhancedInputComponent->BindAction(MoveAction, ETriggerEvent::Triggered, this, &AAdventureCharacter::Move);
}これで IA_Move がトリガーされると、Move() 関数が呼び出されてキャラクターに移動が追加されます。
ジャンプ アクションをバインドする
次に、2 つのバインディングを IA_Jump に追加します。1 つはジャンプを開始するバインディング、もう 1 つはジャンプを停止するバインディングです。
次の引数を使用します。
.hファイルで宣言した IA_Jump への入力アクション ポインタであるJumpAction。StartedおよびCompletedトリガー イベント。ACharacter 親クラスから継承してそこで定義された
JumpおよびStopJumping関数。
// Bind Jump Actions
EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Started, this, &ACharacter::Jump);
EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Completed, this, &ACharacter::StopJumping);SetupPlayerInputComponent() 関数は次のようになります。
// Called to bind functionality to input
void AAdventureCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
// Check the UInputComponent passed to this function and cast it to an UEnhancedInputComponent
if (UEnhancedInputComponent* EnhancedInputComponent = CastChecked<UEnhancedInputComponent>(PlayerInputComponent))
{
// Bind Movement Actions
EnhancedInputComponent->BindAction(MoveAction, ETriggerEvent::Triggered, this, &AAdventureCharacter::Move);
// Bind Jump Actions
キャラクターに入力マッピングをバインドする
関数に入力をバインドしましたが、キャラクターに入力マッピング コンテキストをバインドする必要があります。 ゲームの開始時に入力が設定されるよう、これはキャラクターの BeginPlay() 関数で行います。
BeginPlay() は、親 AActor クラスの仮想関数であり、ゲームが始まったときかアクタがワールドでスポーンされて完全に初期化されるときに呼び出されます。 これは、ゲームプレイの開始時にそのアクタに対して 1 回実行する必要があるロジックに使用します。
BeginPlay() で、続行する前にグローバル エンジン ポインタが null かどうかを確認します。
check(GEngine != nullptr);if 文で、PlayerController という名前の新しい APlayerController ポインタを作成します。 このポインタに、Controller を APlayerController にキャストした結果を設定します。
// Get the player controller for this character
if (APlayerController* PlayerController = Cast<APlayerController>(Controller))
{
}if 文は、ポインタが null でない場合にのみ実行が続行されるようにします。
ここで、Enhanced Input Local Player サブシステムを取得し、サブシステムに (.h ファイルで宣言した) FirstPersonContext 入力マッピング コンテキストを追加する必要があります。
別の if 文で ULocalPlayer::GetSubsystem() を呼び出して Subsystem という新しい UEnhancedInputLocalPlayerSubsystem ポインタを作成し、現在のプレイヤーを渡します。 現在のプレイヤーは、PlayerController->GetLocalPlayer() を呼び出すことで取得できます。
if (APlayerController* PlayerController = Cast<APlayerController>(Controller))
{
if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayerController->GetLocalPlayer()))
{
}
}AddMappingContext() を呼び出してサブシステムにマッピング コンテキストを追加し、マッピング コンテキストと優先度 0 を渡してこのマッピング コンテキストを最優先に設定します。
// Get the enhanced input local player subsystem and add a new input mapping context to it
if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayerController->GetLocalPlayer()))
{
Subsystem->AddMappingContext(FirstPersonContext, 0);
}最後に、カスタム キャラクター クラスが使用されていることを確認するための新しいデバッグ メッセージを追加します。
BeginPlay() 関数は次のようになります。
// Called when the game starts or when spawned
void AAdventureCharacter::BeginPlay()
{
Super::BeginPlay();
check(GEngine != nullptr);
// Get the player controller for this character
if (APlayerController* PlayerController = Cast<APlayerController>(Controller))
{
Visual Studio に .hヘッダ ファイルと .cpp実装ファイルを保存してから、[Build] をクリックしてプロジェクトをコンパイルします。
キャラクター ブループリントに変数を設定する
これらの移動制御の設定を完了するには、キャラクターのブループリントを使用して、コードで宣言した変数にアセットを割り当てます。
キャラクターの新しいプロパティにアセットを追加するには、次の手順を実行します。
まだ開いていない場合は、Unreal Editor のブループリント エディタでキャラクター ブループリントを開きます。
[Details] パネルの [Input] で、次のプロパティを設定します。
[First Person Context (一人称視点コンテキスト)] を
IMC_Adventureに設定します。[Move Action (移動アクション)] を
IA_Moveに設定します。[Jump Action (ジャンプアクション)] を
IA_Jumpに設定します。
ブループリントを保存し、[Compile] をクリックしてコンパイルします。
キャラクター移動をテストする
レベル エディタ ツールバーで [Play] をクリックし、プレイ イン エディタ モードを開始します。 ゲームが始まると、画面上に「Hello World!」と「We are using AdventureCharacter (AdventureCharacter を使用しています)」と表示されます。 WASD キーか矢印キーで動き回り、スペースバーでジャンプできるようになっているはずです。
次の内容
移動するキャラクターは完成しましたが、適切なメッシュとカメラはまだありません。 次のセクションでは、カメラ コンポーネントを作成してキャラクターにバインドし、スケルタルメッシュを追加して実際の一人称視点を取得する方法を学びます。
一人称視点のカメラ、メッシュ、アニメーションを追加する
C++ を使用して一人称視点のキャラクターにメッシュおよびカメラ コンポーネントをアタッチする方法を学びます。
完全なコード
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "EnhancedInputComponent.h"
#include "EnhancedInputSubsystems.h"
#include "InputActionValue.h"
#include "AdventureCharacter.generated.h"
class UInputMappingContext;
#include "AdventureCharacter.h"
// Sets default values
AAdventureCharacter::AAdventureCharacter()
{
// 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;
}
// Fill out your copyright notice in the Description page of Project Settings.
using UnrealBuildTool;
public class AdventureGame : ModuleRules
{
public AdventureGame(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;