[C++] Unreal Engine 4ゲームプログラミング for Ultra Beginners [基本][Pawn]

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

前回はActorについて時間に応じて位置を変化させるオブジェクトについて確認しました。今回は、ユーザーの入力に応じて動くオブジェクトについて確認します。

Pawn とは

今回写経する資料はこちらになります。

ポーンは人間のプレイヤーまたは AI による制御が設定されている アクタ の一種です。

簡単に言うと、プレイヤーとして操作することができる Actor の派生クラスになります。思考 *1によってなされる入力に応じて動きや形状などが変化します。

下準備

ゴールとして以下の様な構成を目指します。

UE4_Pawn

入力によってオブジェクトを操作するのですが、ゲームであれば当然、操作するキャラクターに追随してカメラもついてきてくれなければなりません。そのため、 Pawn の RootComponent 以下に表示するオブジェクトと追随するカメラの情報をぶら下げてやる必要があります。

RootComponent は予め定義されています。表示させるオブジェクトのインスタンスである OurVisibleComponent と カメラオブジェクトのインスタンス OurCamera を結びつけます。

クラスを追加

新規C++ファイル を選択します。

2015-11-04 22_09_30-

ポーン を選択します。

2015-11-04 22_09_42-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 以下には OurVisibleComponentOurCamera が必要です。

まず 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<USceneComponent>(TEXT("RootComponent"));
    // カメラオブジェクト作成
    UCameraComponent* OurCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("OurCamera"));
    // 可視オブジェクトを作成
    OurVisibleComponent = CreateDefaultSubobject<UStaticMeshComponent>(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に戻ります。

編集 > プロジェクト設定 を選択します。

2015-11-05 00_50_26-

インプット を選択します。

2015-11-05 01_16_15-TutorialProject - アンリアルエディタ

Bindings 内にある Axis Mapping にキーの設定を追加します。

「MoveX」 は縦移動、「MoveY」は横移動です。いわゆるFPSゲームの移動と同じキーマッピングですね。以下のように設定します。

2015-11-05 01_17_03-

これでキーのマッピングが完了しました。中心軸からの変化量を取得できるようになります。

キー入力の変化をオブジェクトの移動量へ設定する

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 をレベルにドラッグして適当な位置に配置します。

2015-11-05 23_39_58-TutorialProject - アンリアルエディタ

アウトライナMyPawn1 が選択されていることを確認します。

2015-11-05 23_40_47-TutorialProject - アンリアルエディタ

表示するオブジェクトは OurVisibleComponent に設定します。このままだと何も紐付いてないので再生しても特に何もオブジェクトは表示されません。適当なメッシュを選択します。サンプル通りシリンダーを選んでみましょう。

2015-11-05 23_40_59-TutorialProject - アンリアルエディタ

スタティックメッシュ の 「なし」をクリックするとメッシュのリストが表示されます。 ShapeCylinder を選択します。

2015-11-05 23_41_11-

メッシュを設定するとレベルには以下のようにシリンダーが表示されています。

2015-11-05 23_41_25-TutorialProject - アンリアルエディタ

このシリンダーのオブジェクトは、キーボードの「WSAD」で操作が可能です。実行してみます。

実行

This movie requires Flash Player 9

まとめ

今回はユーザーの入力などを受け付けることの出来る Pawn クラスについて黙々と写経しながら確認しました。少オブジェクトの親子関係などの設定が面倒ですが、作業としてはそこまで多くは無さそうです。(今のところ)

これらの実装を見てみると、UNREAL ENGINEにプログラマーが変数などを公開することで、ノンプログラマーでも簡単に作成したオブジェクトを扱うことが出来るようになっており、プログラマーとそうでない方々ががとてもうまい具合に協業できるようになっていると感じました。

参照

脚注

  1. 演算か人間の思考かはさておき