プレイヤー制御のカメラ

プレイヤー入力でカメラとポーンを同時に操作する方法を説明します。

Windows
MacOS
Linux

このチュートリアルでは、カメラのアクティベート方法、またあるカメラから別のカメラへアクティブなカメラを切り替える方法を紹介します。

1. カメラをポーンにアタッチする

  1. まず「HowTo_PlayerCamera」という名前のスターター コンテンツを使用して、新規 Basic Code プロジェクトを作成します。まず、カスタムの Pawn クラスを作成します。このチュートリアルでは、新規 Pawn クラスに対して、PawnWithCamera という名前を使います。

    NamePawnClass.png

  2. Visual Studio で 「PawnWithCamera.h」 ファイルを開いて、以下のコードをクラス定義の下に追加します。

    protected:
        UPROPERTY(EditAnywhere)
        USpringArmComponent* OurCameraSpringArm;
        UCameraComponent* OurCamera;

    こうした変数を使って、端に CameraComponent をアタッチした SpringArmComponent を作成します。スプリングアームは、カメラ (または他のコンポーネント) をアタッチする単純な方法であり、ある程度の柔軟性を持って、滑らかな動きをすることができます。

  3. その後、コンストラクタで実際にコンポーネントを作成する必要があります。次のコードを APawnWithCamera::APawnWithCamera 内の PawnWithCamera.cpp に追加します。

    //Create our components (コンポーネントを作成)
    RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("RootComponent"));
    OurCameraSpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraSpringArm"));
    OurCameraSpringArm->SetupAttachment(RootComponent);
    OurCameraSpringArm->SetRelativeLocationAndRotation(FVector(0.0f, 0.0f, 50.0f), FRotator(-60.0f, 0.0f, 0.0f));
    OurCameraSpringArm->TargetArmLength = 400.f;
    OurCameraSpringArm->bEnableCameraLag = true;
    OurCameraSpringArm->CameraLagSpeed = 3.0f;

    これで基本的な空の SceneComponent がコンポーネント階層のルートとして作成されます。次に、SpringArmComponent を作成してアタッチします。Spring Arm はデフォルトのピッチ、 -60 度 (すなわち 60 度下向き) およびルートから 50 ユニット上の位置になります。SpringArmComponent クラス特有の値もいくつか設定します。こうした値でその長さと動きの滑らかさを決めます。設定したら、CameraComponent を作成して、SpringArmComponent の終端のソケットにアタッチします。

    OurCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("GameCamera"));
    OurCamera->SetupAttachment(OurCameraSpringArm, USpringArmComponent::SocketName);
  4. 最後に、以下のコードを追加することで、デフォルトのローカル プレイヤーによって自動的にポーンがスポーン時に制御されるように設定することができます。

    //Take control of the default Player (デフォルトプレイヤーをコントロール)
    AutoPossessPlayer = EAutoReceiveInput::Player0;

これでカメラを快適に制御可能にするシンプルなポーンが用意できました。次に アンリアル エンジン エディタで入力を設定し、それに反応するコードを作成します。

Work-In-Progress Code

PawnWithCamera.h

// Copyright 1998-2018 Epic Games, Inc.All Rights Reserved.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Pawn.h"
#include "PawnWithCamera.generated.h"

UCLASS()
class HOWTO_PLAYERCAMERA_API APawnWithCamera : public APawn
{
    GENERATED_BODY()

public:
    // Sets default values for this pawn's properties (このポーンのプロパティの既定値を設定)
    APawnWithCamera();

protected:
    // Called when the game starts or when spawned (ゲーム開始時またはスポーン時に呼び出されます)
    virtual void BeginPlay() override;
        public:

    // Called every frame (フレーム毎に呼び出されます)
    virtual void Tick( float DeltaSeconds ) override;

    // Called to bind functionality to input (機能を入力にバインドするために呼び出されます)
    virtual void SetupPlayerInputComponent(class UInputComponent* InputComponent) override;

protected:
    UPROPERTY(EditAnywhere)
    USpringArmComponent* OurCameraSpringArm;
    UCameraComponent* OurCamera;
};

PawnWithCamera.cpp

// Copyright 1998-2018 Epic Games, Inc.All Rights Reserved.

#include "HowTo_PlayerCamera.h"
#include "PawnWithCamera.h"

// Sets default values
APawnWithCamera::APawnWithCamera()
{
    // Set this pawn to call Tick() every frame. (フレーム毎に Tick() を呼び出すには、このポーンを設定します)You can turn this off to improve performance if you don't need it. (必要がなければパフォーマンスを向上させるためにオフにすることができます) 
    PrimaryActorTick.bCanEverTick = true;

    //Create our components (コンポーネントを作成)
    RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("RootComponent"));
    OurCameraSpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraSpringArm"));
    OurCameraSpringArm->SetupAttachment(RootComponent);
    OurCameraSpringArm->SetRelativeLocationAndRotation(FVector(0.0f, 0.0f, 50.0f), FRotator(-60.0f, 0.0f, 0.0f));
    OurCameraSpringArm->TargetArmLength = 400.f;
    OurCameraSpringArm->bEnableCameraLag = true;
    OurCameraSpringArm->CameraLagSpeed = 3.0f;
    OurCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("GameCamera"));
    OurCamera->SetupAttachment(OurCameraSpringArm, USpringArmComponent::SocketName);

    //Take control of the default Player (デフォルトプレイヤーをコントロール)
    AutoPossessPlayer = EAutoReceiveInput::Player0;
}

// Called when the game starts or when spawned (ゲーム開始時またはスポーン時に呼び出されます)
void APawnWithCamera::BeginPlay()
{
    Super::BeginPlay();
}
// Called every frame (フレーム毎に呼び出されます)
void APawnWithCamera::Tick( float DeltaTime )
{
    Super::Tick( DeltaTime );

}

// Called to bind functionality to input (機能を入力にバインドするために呼び出されます)
void APawnWithCamera::SetupPlayerInputComponent(class UInputComponent* InputComponent)
{
    Super::SetupPlayerInputComponent(InputComponent);

}

2.カメラを制御する入力の設定

  1. カメラの制御で何を行うかを決めてから、それに応じて入力をセットアップします。このプロジェクトでは、右マウスボタンを押したときに、距離を短くして FOV をズームできるようにします。これに加えてマウス操作による視野角の操作と、 WASD キー操作で ポーン の動作もコントロールしましょう。設定を行うために、 アンリアルエンジン エディタの [Edit (編集)] ドロップダウンメニューから [Project Settings (プロジェクト設定)] を開きます。

    EditProjectSettings.png

  2. | Action Mapping | | | | ZoomIn | Right Mouse Button | |

    Axis Mapping

    MoveForward

    W

    1.0

    S

    -1.0

    MoveRight

    A

    -1.0

    D

    1.0

    CameraPitch

    MouseY

    1.0

    CameraYaw

    MouseX

    1.0

    InputConfig.png

    入力マッピングの仕組みについて理解したい場合、プレイヤー入力と Pawn クラス チュートリアルをご覧ください。

これで入力を定義しました。それに反応させるにはコードを記述する必要があります。それを行うには、Visual Studio に戻ります。

3. 入力に反応する C++ コードの記述

  1. 使用可能な入力マッピングを設定したので、受信するデータを格納するメンバー変数をセットアップしましょう。更新中に、移動とマウスルックの軸それぞれの 2D ベクター値が必要です。ズームインまたはズームアウトのビューのどちらへ移動させるべきか、2 つのステートの間隔が現時点でどれくらいあるのかについても知る必要があります。これを行うには、以下のコードを PawnWithCamera.h のクラス定義に追加する必要があります。

    //Input variables (入力変数)
    FVector2D MovementInput;
    FVector2D CameraInput;
    float ZoomFactor;
    bool bZoomingIn;
  2. 入力を追跡するための関数を作成する必要があります。PawnWithCamera.h のクラス定義に以下のコードを追加します。

    //Input functions (入力関数)
    void MoveForward(float AxisValue);
    void MoveRight(float AxisValue);
    void PitchCamera(float AxisValue);
    void YawCamera(float AxisValue);
    void ZoomIn();
    void ZoomOut();

    以下のコードを使って PawnWithCamera.cpp にこうした関数を入れることができます。

    //Input functions (入力関数)
    void APawnWithCamera::MoveForward(float AxisValue)
    {
        MovementInput.X = FMath::Clamp<float>(AxisValue, -1.0f, 1.0f);
    }
    
    void APawnWithCamera::MoveRight(float AxisValue)
    {
        MovementInput.Y = FMath::Clamp<float>(AxisValue, -1.0f, 1.0f);
    }
    
    void APawnWithCamera::PitchCamera(float AxisValue)
    {
        CameraInput.Y = AxisValue;
    }
    
    void APawnWithCamera::YawCamera(float AxisValue)
    {
        CameraInput.X = AxisValue;
    }
    
    void APawnWithCamera::ZoomIn()
    {
        bZoomingIn = true;
    }
    
    void APawnWithCamera::ZoomOut()
    {
        bZoomingIn = false;
    }

    ZoomFactor については、まだ何もしていません。この変数はボタンのステートに基づいて時間の経過と共に常に変化するため、ポーンTick 関数実行時に更新されます。

  3. 入力データを格納するコードを作成したので、あとは Unreal Engine にコードを呼び出すタイミングを指示するのみです。ポーンの入力イベントに関数をバインドするには、以下のように APawnWithCamera::SetupPlayerInputComponent にバインディングコードを追加するのみのシンプルな作業です。

    //Hook up events for "ZoomIn" (ZoomIn の Hook up イベント)
    InputComponent->BindAction("ZoomIn", IE_Pressed, this, &APawnWithCamera::ZoomIn);
    InputComponent->BindAction("ZoomIn", IE_Released, this, &APawnWithCamera::ZoomOut);
    
    //Hook up every-frame handling for our four axes (4 つの軸に各フレーム処理を接続)
    InputComponent->BindAxis("MoveForward", this, &APawnWithCamera::MoveForward);
    InputComponent->BindAxis("MoveRight", this, &APawnWithCamera::MoveRight);
    InputComponent->BindAxis("CameraPitch", this, &APawnWithCamera::PitchCamera);
    InputComponent->BindAxis("CameraYaw", this, &APawnWithCamera::YawCamera);
  4. Tick 関数でこれらの値を使用して、ポーンと カメラ をフレーム毎に更新することができます。以下のコードブロックのすべてを「PawnWithCamera.cpp」ファイルの APawnWithCamera::Tick に追加します。

    //Zoom in if ZoomIn button is down, zoom back out if it's not (ZoomIn ボタンが押されたらズームイン、押されなければズームアウト)
    {
        if (bZoomingIn)
        {
            ZoomFactor += DeltaTime / 0.5f;         //Zoom in over half a second (0.5秒でズームイン)
        }
        else
        {
            ZoomFactor -= DeltaTime / 0.25f;        //Zoom out over a quarter of a second (0.25秒でズームアウト)
        }
        ZoomFactor = FMath::Clamp<float>(ZoomFactor, 0.0f, 1.0f);
        //Blend our camera's FOV and our SpringArm's length based on ZoomFactor (ZoomFactorに基づいてカメラのFOVとスプリングアームの長さをブレンド)
        OurCamera->FieldOfView = FMath::Lerp<float>(90.0f, 60.0f, ZoomFactor);
        OurCameraSpringArm->TargetArmLength = FMath::Lerp<float>(400.0f, 300.0f, ZoomFactor);
    }

    このコードはハードコードされた値をいくつか使用します。使用する値は、0.5 秒や 0.25 秒のズーム時間、90 度でズームアウト、60 度でズームインする FOV 値、またはカメラ距離の 400 ズームアウト、300 ズームインの値などです。このような値は、UPROPERTY(EditAnywhere) をタグ付けした変数として通常はエディタに公開し、プログラマー以外の人によるコードの変更、コードを再コンパイルしないでプログラマーが変更、またはエディタでゲームを再生中でも変更できるようにします。

    //Rotate our actor's yaw, which will turn our camera because we're attached to it (アクタのヨーを回転。ヨーに親子付けしているためカメラを回転します)
    {
        FRotator NewRotation = GetActorRotation();
        NewRotation.Yaw += CameraInput.X;
        SetActorRotation(NewRotation);
    }
    
    //Rotate our camera's pitch, but limit it so we're always looking downward (カメラのピッチを回転しますが、常に下向きになるようにこれを制限します)
    {
        FRotator NewRotation = OurCameraSpringArm->GetComponentRotation();
        NewRotation.Pitch = FMath::Clamp(NewRotation.Pitch + CameraInput.Y, -80.0f, -15.0f);
        OurCameraSpringArm->SetWorldRotation(NewRotation);
    }

    このコードブロックはマウスの X 軸でポーンのヨーを直接回転させますが、カメラシステムのみが、マウスの Y 軸からのピッチ変更に反応します。アクタ または アクタ のサブクラスを回転させると、実質的にルートレベルの コンポーネント を回転させます。これが間接的にコンポーネントに親子付けされているものすべてに影響を与えます。

    //Handle movement based on our "MoveX" and "MoveY" axes (MoveX と MoveY 軸に基づいて移動を処理)
    {
        if (!MovementInput.IsZero())
        {
            //Scale our movement input axis values by 100 units per second (移動入力軸の値を 1 秒あたり 100 単位でスケーリング)
            MovementInput = MovementInput.SafeNormal() * 100.0f;
            FVector NewLocation = GetActorLocation();
            NewLocation += GetActorForwardVector() * MovementInput.X * DeltaTime;
            NewLocation += GetActorRightVector() * MovementInput.Y * DeltaTime;
            SetActorLocation(NewLocation);
        }
    }

    GetActorForwardVectorGetActorRightVector を使用すると、アクタが向いている方向と相対的な動きになります。カメラはアクタと同じ方向を向いていいるため、これでフォワード キーはプレイヤーが見ているものに対して常に相対的に前方向になります。

  5. これでコーディングが終わりました。コードをコンパイルして、コンテンツ ブラウザ から新規クラスを Unreal Engine Editor の レベルエディタ ウィンドウにドラッグできるようになりました。

    ClassInContentBrowser.png

    スタティック メッシュ やその他のビジュアル コンポーネントを自由に追加したり、または何も追加しないでプレイをお楽しみください。カメラが操作に追随してレベル内で滑らかに加速、減速の動きをするのがわかりますが、回転はぎこちなく瞬間的に起こります。Camera Rotation Lag を追加したり Camera Lag を増減させるなど SpringArmComponent のプロパティをいくつか変更して、コントロールの操作性の違いを感じてみてください。

    SpringArmValues.png

  6. コーン メッシュを回転させると、下図のようになります。

    FinalScreen.png

完成コード

PawnWithCamera.h

// Copyright 1998-2018 Epic Games, Inc.All Rights Reserved.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Pawn.h"
#include "PawnWithCamera.generated.h"

UCLASS()
class HOWTO_PLAYERCAMERA_API APawnWithCamera : public APawn
{
    GENERATED_BODY()

public:
    // Sets default values for this pawn's properties (このポーンのプロパティの既定値を設定)
    APawnWithCamera();

protected:
    // Called when the game starts or when spawned (ゲーム開始時またはスポーン時に呼び出されます)
    virtual void BeginPlay() override;
        public:

    // Called every frame (フレーム毎に呼び出されます)
    virtual void Tick( float DeltaSeconds ) override;

    // Called to bind functionality to input (機能を入力にバインドするために呼び出されます)
    virtual void SetupPlayerInputComponent(class UInputComponent* InputComponent) override;

protected:
    UPROPERTY(EditAnywhere)
    USpringArmComponent* OurCameraSpringArm;
    UCameraComponent* OurCamera;

    //Input variables (入力変数)
    FVector2D MovementInput;
    FVector2D CameraInput;
    float ZoomFactor;
    bool bZoomingIn;

    //Input functions (入力関数)
    void MoveForward(float AxisValue);
    void MoveRight(float AxisValue);
    void PitchCamera(float AxisValue);
    void YawCamera(float AxisValue);
    void ZoomIn();
    void ZoomOut();
};

PawnWithCamera.cpp

// Copyright 1998-2018 Epic Games, Inc.All Rights Reserved.

#include "HowTo_PlayerCamera.h"
#include "PawnWithCamera.h"

// Sets default values
APawnWithCamera::APawnWithCamera()
{
    // Set this pawn to call Tick() every frame. (フレーム毎に Tick() を呼び出すには、このポーンを設定します)You can turn this off to improve performance if you don't need it. (必要がなければパフォーマンスを向上させるためにオフにすることができます) 
    PrimaryActorTick.bCanEverTick = true;

    //Create our components (コンポーネントを作成)
    RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("RootComponent"));
    OurCameraSpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraSpringArm"));
    OurCameraSpringArm->SetupAttachment(RootComponent);
    OurCameraSpringArm->SetRelativeLocationAndRotation(FVector(0.0f, 0.0f, 50.0f), FRotator(-60.0f, 0.0f, 0.0f));
    OurCameraSpringArm->TargetArmLength = 400.f;
    OurCameraSpringArm->bEnableCameraLag = true;
    OurCameraSpringArm->CameraLagSpeed = 3.0f;
    OurCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("GameCamera"));
    OurCamera->SetupAttachment(OurCameraSpringArm, USpringArmComponent::SocketName);

    //Take control of the default Player (デフォルトプレイヤーをコントロール)
    AutoPossessPlayer = EAutoReceiveInput::Player0;
}

// Called when the game starts or when spawned (ゲーム開始時またはスポーン時に呼び出されます)
void APawnWithCamera::BeginPlay()
{
    Super::BeginPlay();
}
// Called every frame (フレーム毎に呼び出されます)
void APawnWithCamera::Tick( float DeltaTime )
{
    Super::Tick(DeltaTime);

    //Zoom in if ZoomIn button is down, zoom back out if it's not (ZoomIn ボタンが押されたらズームイン、押されなければズームアウト)
    {
        if (bZoomingIn)
        {
            ZoomFactor += DeltaTime / 0.5f;         //Zoom in over half a second (0.5秒でズームイン)
        }
        else
        {
            ZoomFactor -= DeltaTime / 0.25f;        //Zoom out over a quarter of a second (0.25秒でズームアウト)
        }
        ZoomFactor = FMath::Clamp<float>(ZoomFactor, 0.0f, 1.0f);
        //Blend our camera's FOV and our SpringArm's length based on ZoomFactor (ZoomFactorに基づいてカメラのFOVとスプリングアームの長さをブレンド)
        OurCamera->FieldOfView = FMath::Lerp<float>(90.0f, 60.0f, ZoomFactor);
        OurCameraSpringArm->TargetArmLength = FMath::Lerp<float>(400.0f, 300.0f, ZoomFactor);
    }

    //Rotate our actor's yaw, which will turn our camera because we're attached to it (アクタのヨーを回転。ヨーに親子付けしているためカメラを回転します)
    {
        FRotator NewRotation = GetActorRotation();
        NewRotation.Yaw += CameraInput.X;
        SetActorRotation(NewRotation);
    }

    //Rotate our camera's pitch, but limit it so we're always looking downward (カメラのピッチを回転しますが、常に下向きになるようにこれを制限します)
    {
        FRotator NewRotation = OurCameraSpringArm->GetComponentRotation();
        NewRotation.Pitch = FMath::Clamp(NewRotation.Pitch + CameraInput.Y, -80.0f, -15.0f);
        OurCameraSpringArm->SetWorldRotation(NewRotation);
    }

    //Handle movement based on our "MoveX" and "MoveY" axes (MoveX と MoveY 軸に基づいて移動を処理)
    {
        if (!MovementInput.IsZero())
        {
            //Scale our movement input axis values by 100 units per second (移動入力軸の値を 1 秒あたり 100 単位でスケーリング)
            MovementInput = MovementInput.SafeNormal() * 100.0f;
            FVector NewLocation = GetActorLocation();
            NewLocation += GetActorForwardVector() * MovementInput.X * DeltaTime;
            NewLocation += GetActorRightVector() * MovementInput.Y * DeltaTime;
            SetActorLocation(NewLocation);
        }
    }
}

// Called to bind functionality to input (機能を入力にバインドするために呼び出されます)
void APawnWithCamera::SetupPlayerInputComponent(class UInputComponent* InputComponent)
{
    Super::SetupPlayerInputComponent(InputComponent);

    //Hook up events for "ZoomIn" (ZoomIn の Hook up イベント)
    InputComponent->BindAction("ZoomIn", IE_Pressed, this, &APawnWithCamera::ZoomIn);
    InputComponent->BindAction("ZoomIn", IE_Released, this, &APawnWithCamera::ZoomOut);

    //Hook up every-frame handling for our four axes (4 つの軸に各フレーム処理を接続)
    InputComponent->BindAxis("MoveForward", this, &APawnWithCamera::MoveForward);
    InputComponent->BindAxis("MoveRight", this, &APawnWithCamera::MoveRight);
    InputComponent->BindAxis("CameraPitch", this, &APawnWithCamera::PitchCamera);
    InputComponent->BindAxis("CameraYaw", this, &APawnWithCamera::YawCamera);
}

//Input functions (入力関数)
void APawnWithCamera::MoveForward(float AxisValue)
{
    MovementInput.X = FMath::Clamp<float>(AxisValue, -1.0f, 1.0f);
}

void APawnWithCamera::MoveRight(float AxisValue)
{
    MovementInput.Y = FMath::Clamp<float>(AxisValue, -1.0f, 1.0f);
}

void APawnWithCamera::PitchCamera(float AxisValue)
{
    CameraInput.Y = AxisValue;
}

void APawnWithCamera::YawCamera(float AxisValue)
{
    CameraInput.X = AxisValue;
}

void APawnWithCamera::ZoomIn()
{
    bZoomingIn = true;
}

void APawnWithCamera::ZoomOut()
{
    bZoomingIn = false;
}

4. 応用編

ここまでで学んだ知識を活かして、以下を行ってみましょう。

  • ポーンの 移動速度を速めるためにクリックする「run」キーをプレイヤーに提供します。

  • 自動および入力駆動のカメラ移動をミックスするために、さまざまな方法を実験します。これはゲーム開発における非常に奥深い分野であり、試すべきことが沢山あります。

  • カメラ全体の雰囲気にラグがどのくらい影響を与えるかの理解を深めるために Spring コンポーネント からラグを増加、削減、または取り除きます。

  • カメラに「手持ち」感覚を与えるために多少ランダム化させて、または Curve アセットを使用して、少量の定期的な動きを実装します。

  • 移動しているプレイヤー オブジェクトの背後へ徐々に移動して、プレイヤーの移動方向を向くように、カメラ を自動的にある程度回転させます。

以下はこのチュートリアルの内容の詳しい情報のリンク先です。

Select Skin
Light
Dark

新しい Unreal Engine 4 ドキュメントサイトへようこそ!

あなたの声を私たちに伝えるフィードバックシステムを含め、様々な新機能について開発をおこなっています。まだ広く使える状態にはなっていないので、準備ができるまでは、ドキュメントフィードバックフォーラムで、このページについて、もしくは遭遇した問題について教えていただけると助かります。

新しいシステムが稼働した際にお知らせします。

フィードバックを送信