この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
前回はActorについて時間に応じて位置を変化させるオブジェクトについて確認しました。今回は、ユーザーの入力に応じて動くオブジェクトについて確認します。
Pawn とは
今回写経する資料はこちらになります。
ポーンは人間のプレイヤーまたは AI による制御が設定されている アクタ の一種です。
簡単に言うと、プレイヤーとして操作することができる Actor
の派生クラスになります。思考 *1によってなされる入力に応じて動きや形状などが変化します。
下準備
ゴールとして以下の様な構成を目指します。
入力によってオブジェクトを操作するのですが、ゲームであれば当然、操作するキャラクターに追随してカメラもついてきてくれなければなりません。そのため、 Pawn の RootComponent
以下に表示するオブジェクトと追随するカメラの情報をぶら下げてやる必要があります。
RootComponent
は予め定義されています。表示させるオブジェクトのインスタンスである OurVisibleComponent
と カメラオブジェクトのインスタンス OurCamera
を結びつけます。
クラスを追加
新規C++ファイル
を選択します。
ポーン
を選択します。
クラスの名称は MyPawn
でクラスを作成します。ここから Visual Studio(もしくはXcode)での作業になります。作業環境を移動しましょう(勝手に開発環境が起動してると思いますが)
#pragma once
#include "GameFramework/Pawn.h"
#include "MyPawn.generated.h"
UCLASS()
class TUTORIALPROJECT_API AMyPawn : public APawn
{
GENERATED_BODY()
public:
// Sets default values for this pawn's properties
AMyPawn();
// Called when the game starts or when spawned
virtual void BeginPlay() override;
// Called every frame
virtual void Tick( float DeltaSeconds ) override;
// Called to bind functionality to input
virtual void SetupPlayerInputComponent(class UInputComponent* InputComponent) override;
}
ヘッダファイルだけ抜粋しました。 Actor
と同じメソッドが確認できます。
virtual void BeginPlay()
virtual void Tick( float DeltaSeconds )
上記2つは Actor
から継承されたメソッドです。
virtual void SetupPlayerInputComponent(class UInputComponent* InputComponent)
これは Pawn
で定義された入力値を処理するためのセットアップメソッドになります。
RootComponent 以下に紐付けるオブジェクトを定義
冒頭で説明したように、 RootComponent
以下には OurVisibleComponent
と OurCamera
が必要です。
まず OurVisibleComponent
は ヘッダファイルに定義します。
public:
// Sets default values for this pawn's properties
AMyPawn();
// Called when the game starts or when spawned
virtual void BeginPlay() override;
// Called every frame
virtual void Tick( float DeltaSeconds ) override;
// Called to bind functionality to input
virtual void SetupPlayerInputComponent(class UInputComponent* InputComponent) override;
UPROPERTY(EditAnywhere) USceneComponent* OurVisibleComponent;
}
USceneComponet
のポインタとして定義します。 UPROPERTY
を利用し、UNREAL ENGINE上で編集可能なように変数を公開します。
OurCamera
はヘッダファイルには定義しません。特に外部に公開するべきオブジェクトではないので、実装ファイル内の定義にとどめます。
RootComponent にオブジェクトをひも付け
コンストラクタで変数の初期化と各オブジェクト同士の関係付を行います。以下は実装ファイル(MyPawn.cpp) へ記載します
// Sets default values
AMyPawn::AMyPawn()
{
// Set this pawn to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
// 親子付可能なダミーのルートコンポーネント
RootComponent = CreateDefaultSubobject(TEXT("RootComponent"));
// カメラオブジェクト作成
UCameraComponent* OurCamera = CreateDefaultSubobject(TEXT("OurCamera"));
// 可視オブジェクトを作成
OurVisibleComponent = CreateDefaultSubobject(TEXT("OurVisibleComponent"));
// カメラをRootComponent以下へぶら下げる
OurCamera->AttachTo(RootComponent);
// カメラ位置を調整
OurCamera->SetRelativeLocation(FVector(-250.0f, 0.0f, 250.0f));
OurCamera->SetRelativeRotation(FRotator(-45.0f, 0.0f, 0.0f));
// 可視オブジェクトをRootComponentにぶら下げ
OurVisibleComponent->AttachTo(RootComponent);
}
オブジェクトの作成とオブジェクト同士の関係付け(とカメラの位置調整)を行っています。この設定でカメラは情報斜め45度くらいから見下ろした形になるようです。
CreateDefaultSubobject
を利用して様々なオブジェクトの作成を行います。ダイヤモンド演算子で作成したいクラスを指定することができるようです。
入力を受け付けるプレイヤーの指定
どのプレイヤーの入力を受け付けるのかを設定します。これもコンストラクタで設定。
AMyPawn::AMyPawn()
{
// Set this pawn to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
// このポーンが最小値のプレイヤーで制御されるように設定。0番目のプレイヤー
AutoPossessPlayer = EAutoReceiveInput::Player0;
...(snip)
AutoProcessPlayer
という変数が定義されています。ここに EAutoReceiveInput
で定義されている Enum を指定します。
入力のキーバインドを定義
UNREAL ENGINEに戻ります。
編集 > プロジェクト設定
を選択します。
インプット
を選択します。
Bindings
内にある Axis Mapping
にキーの設定を追加します。
「MoveX」 は縦移動、「MoveY」は横移動です。いわゆるFPSゲームの移動と同じキーマッピングですね。以下のように設定します。
これでキーのマッピングが完了しました。中心軸からの変化量を取得できるようになります。
キー入力の変化をオブジェクトの移動量へ設定する
Visual Studio へ戻ります。先ほど設定した「MoveX」「MoveY」のキーバインドをオブジェクトを移動させるためのデリゲートメソッドに紐付けます。
まずはMoveX, MoveY それぞれに対応するメソッドを定義していきましょう。 ヘッダファイル(MyPawn.h)に以下を追記します。
public:
// Sets default values for this pawn's properties
AMyPawn();
// Called when the game starts or when spawned
virtual void BeginPlay() override;
// Called every frame
virtual void Tick( float DeltaSeconds ) override;
// Called to bind functionality to input
virtual void SetupPlayerInputComponent(class UInputComponent* InputComponent) override;
UPROPERTY(EditAnywhere) USceneComponent* OurVisibleComponent;
// X方向への移動
void Move_XAxis(float AxisValue);
// Y方向への移動
void Move_YAxis(float AxisValue);
// 入力変数
FVector CurrentVelocity;
}
それぞれ、 float AxisValue
で変化量を入力値として持つメソッドです。ここで入力値を記録する変数も定義しておきます。x, y, z(zは今回使わないけど)の入力値を管理するために FVector
で宣言しているようです。
続いて実装ファイル(MyPawn.cpp) にメソッドの実装を行います。
void AMyPawn::Move_XAxis(float AxisValue) {
// 1秒間に前後へ100移動。 AxisValue は -1.0fから1.0fの間
CurrentVelocity.X = FMath::Clamp(AxisValue, -1.0f, 1.0f) * 100.0f;
}
void AMyPawn::Move_YAxis(float AxisValue) {
// 1秒間に左右へ100移動。 AxisValue は -1.0fから1.0fの間
CurrentVelocity.Y = FMath::Clamp(AxisValue, -1.0f, 1.0f) * 100.0f;
}
floatAxis
は変化量ですが、 FMath::Clamp
によって、 -1.0f〜1.0f の範囲に制限されます。
X方向の移動の場合は、 CurrentVelocity.X
に変化量を入力します。Y方向の移動の場合は、CurrentVelocity.Y
に変化量を入力します。
ユーザーの入力に応じて各メソッドを実行するためのセットアップは、 void AMyPawn::SetupPlayerInputComponent(class UInputComponent* InputComponent)
に記載します。
// Called to bind functionality to input
void AMyPawn::SetupPlayerInputComponent(class UInputComponent* InputComponent)
{
Super::SetupPlayerInputComponent(InputComponent);
InputComponent->BindAxis("MoveX", this, &AMyPawn::Move_XAxis);
InputComponent->BindAxis("MoveY", this, &AMyPawn::Move_YAxis);
}
UInputComponent#BindAxis
は指定したラベルのキー入力を検知し、デリゲートに指定されたメソッドを実行します。
今回実装したコードでは、 MoveX
ラベルで指定された W, S の入力があった場合は、 Move_XAxis
が実行されます。同じく MoveY
ラベルで指定された A, D の入力があった場合は、 Move_YAxis
が実行されます。
これで入力値の記録、キーバインドによるユーザー入力の設定が完了しました。残りは入力された値にもとづいてオブジェクトを移動させましょう。
入力値に応じてオブジェクトを移動させる
ここからは、前回Actorの時にも行った作業と変わりません。 AMyPawn::Tick
が1フレームごとに呼ばれるので、ここにオブジェクトの位置を変化させるコードを追記します。
void AMyPawn::Tick( float DeltaTime )
{
Super::Tick( DeltaTime );
// CurrentVelocityに値があるかどうか
if (!CurrentVelocity.IsZero())
{
FVector NewLocation = GetActorLocation() + (CurrentVelocity * DeltaTime);
SetActorLocation(NewLocation);
}
}
GetActorLocation()
は自分自身のLocation情報を FVector
の形で取得します。入力値 CurrentVelocity
のx, y, zそれぞれに対して、経過時間 DeltaTime
を掛けあわせ、ベクトル同士の加算を行ったものが、オブジェクトの新たな位置情報になります。この新しい位置情報を SetActorLocation()
で新たに設定してやることで、オブジェクトの新しい位置が決定されます。
これでオブジェクトの移動は完成。レベルに配置してみましょう。
配置する
UNREAL ENGINEへ戻ります。 MyPawn をレベルにドラッグして適当な位置に配置します。
アウトライナ で MyPawn1 が選択されていることを確認します。
表示するオブジェクトは OurVisibleComponent に設定します。このままだと何も紐付いてないので再生しても特に何もオブジェクトは表示されません。適当なメッシュを選択します。サンプル通りシリンダーを選んでみましょう。
スタティックメッシュ の 「なし」をクリックするとメッシュのリストが表示されます。 ShapeCylinder を選択します。
メッシュを設定するとレベルには以下のようにシリンダーが表示されています。
このシリンダーのオブジェクトは、キーボードの「WSAD」で操作が可能です。実行してみます。
実行
[SWF]https://www.youtube.com/watch?v=UKJCeE4gTbg,560,315[/SWF]
まとめ
今回はユーザーの入力などを受け付けることの出来る Pawn
クラスについて黙々と写経しながら確認しました。少オブジェクトの親子関係などの設定が面倒ですが、作業としてはそこまで多くは無さそうです。(今のところ)
これらの実装を見てみると、UNREAL ENGINEにプログラマーが変数などを公開することで、ノンプログラマーでも簡単に作成したオブジェクトを扱うことが出来るようになっており、プログラマーとそうでない方々ががとてもうまい具合に協業できるようになっていると感じました。
参照
脚注
- 演算か人間の思考かはさておき ↩