コンポーネントとコリジョン

コンポーネントでポーンを有効にして物理と相互作用し、パーティクル エフェクトを使用する方法などについて学習します。

Windows
MacOS
Linux

このチュートリアルでは、コンポーネントの作成、コンポーネントを階層へ配置、ゲームプレイ中のコントロール、またソリッドなオブジェクトが存在するワールド内でコンポーネントを使用してポーンを移動させる方法を紹介します。

1. コンポーネントを作成しアタッチする

Unreal Engine 4 にまだ慣れていない場合は、最初に プログラミング クイック スタート ガイド tutorial をご覧いただくとよいでしょう。このチュートリアルの対象者は、プロジェクトを作成、プロジェクトに C++ コードを追加、および Unreal Editor で入力値の設定ができる方を想定しています。独自の Pawn クラス作成や入力設定に不慣れなユーザーは、 プレイヤー入力と Pawn クラス tutorial から始めてください。

  1. まず「HowTo_Components」という名前のスターター コンテンツを使用して、Basic Code (基本コード) プロジェクトを作成します。まず必要なのは、コンポーネント を保持、レベル内を移動、そしてソリッドなオブジェクトと衝突するカスタマイズしたポーンをプロジェクトに追加することです。このチュートリアルでは「CollidingPawn」と名前を付けます。

    ChooseParentClass.png

    NamePawnClass.png

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

    UParticleSystemComponent *OurParticleSystem;

    この変数を使って、後で作成する Particle System Component を追跡します。変数で追跡を行わないコンポーネントも作成できますが、コードでこのコンポーネントを使用する場合は、以下のようにクラスメンバ変数に保存しなくてはいけません。

  3. CollidingPawn.cpp を開いて、さまざまな用途に役立つコンポーネントをスポーンし、それらを階層に配置するコードを追加することにより、コンストラクタ関数、ACollidingPawn::ACollidingPawn の編集が可能になります。物理ワールドとインタラクションを行う Sphere コンポーネント、コリジョン形状を視覚的に表現する Static Mesh コンポーネント 、意図的にオン/オフに出来る Particle System コンポーネント 、またインゲーム パースペクティブをコントロールするために Camera コンポーネント を親子付けして使用できる Spring Arm コンポーネント を作成します。

  4. 最初に階層のルートとなるコンポーネントを決めます。このチュートリアルは、ゲームワールドとのインタラクションや衝突が可能な物理プレゼンスである Sphere コンポーネントをルートに使用します。アクタ は複数の物理が有効なコンポーネントを階層に設定できますが、チュートリアルでは 1 つのみを有効にします。

        // Our root component will be a sphere that reacts to physics (ルートコンポーネントは物理に反応するスフィアになります)
        USphereComponent* SphereComponent = CreateDefaultSubobject<USphereComponent>(TEXT("RootComponent"));
        RootComponent = SphereComponent;
        SphereComponent->InitSphereRadius(40.0f);
        SphereComponent->SetCollisionProfileName(TEXT("Pawn"));
  5. 次に半径 50 の Static Mesh アセットから目に見えるスフィアを作成してこれを親子付けします。直前に作成した半径 40 の Sphere コンポーネントと完全にそろわないので、80 %に縮小します。さらに、中心を Sphere コンポーネントの中心と並べるために 40 単位移動させなくてはいけません。

        // 球体の位置が分かるようにメッシュ コンポーネントを作成して配置します
        UStaticMeshComponent* SphereVisual = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("VisualRepresentation"));
        SphereVisual->SetupAttachment(RootComponent);
        static ConstructorHelpers::FObjectFinder<UStaticMesh> SphereVisualAsset(TEXT("/Game/StarterContent/Shapes/Shape_Sphere.Shape_Sphere"));
        if (SphereVisualAsset.Succeeded())
        {
            SphereVisual->SetStaticMesh(SphereVisualAsset.Object);
            SphereVisual->SetRelativeLocation(FVector(0.0f, 0.0f, -40.0f));
            SphereVisual->SetWorldScale3D(FVector(0.8f));
        }

    Static Mesh アセットのアセット位置をコードで確認することができますが、アセットのパスのハードコーディングは一般的にアセットのロードに最適な方法とは言えません。クラスでコンポーネントを使用するには、コードにコンポーネント自体を作成して、Unreal Editor でアセットを選択することが一般的に好まれます。一方でコードで直接アセットをロードすることも可能で、プログラマーが行うデバッグ作業や新規機能の作成を高速に処理できます。

  6. 階層に非アクティブな Particle System コンポーネントを親子付けが出来るようになりました。このコンポーネントはコードで操作可能で、後にオン/オフを切り替える入力を設定出来ます。Particle System コンポーネントはルートにではなく Static Mesh コンポーネントに直接親子付けできることに注目してください。プレイ中の表示をより見やすくするために、メッシュの下部中央から少しだけオフセットしています。

        // アクティブまたは非アクティブにできるパーティクル システムを作成
        OurParticleSystem = CreateDefaultSubobject<UParticleSystemComponent>(TEXT("MovementParticles"));
        OurParticleSystem->SetupAttachment(SphereVisual);
        OurParticleSystem->bAutoActivate = false;
        OurParticleSystem->SetRelativeLocation(FVector(-20.0f, 0.0f, 20.0f));
        static ConstructorHelpers::FObjectFinder<UParticleSystem> ParticleAsset(TEXT("/Game/StarterContent/Particles/P_Fire.P_Fire"));
        if (ParticleAsset.Succeeded())
        {
            OurParticleSystem->SetTemplate(ParticleAsset.Object);
        }
  7. Spring Arm コンポーネントは、追跡しているポーンよりもゆっくりとカメラを加速したり減速させるため、よりスムーズな固定点を作成します。サードパーソン ゲームでプレイヤーがコーナーへ追いつめられる状況など、カメラがソリッドなオブジェクトを通り抜けてしまうことを防止する内蔵機能も備わっています。必須ではありませんが、ゲーム内のカメラワークにスムースな感覚を作成する迅速かつ簡単な方法です。

        // Use a spring arm to give the camera smooth, natural-feeling motion.
        USpringArmComponent* SpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraAttachmentArm"));
        SpringArm->SetupAttachment(RootComponent);
        SpringArm->RelativeRotation = FRotator(-45.f, 0.f, 0.f);
        SpringArm->TargetArmLength = 400.0f;
        SpringArm->bEnableCameraLag = true;
        SpringArm->CameraLagSpeed = 3.0f;
  8. 実際の Camera コンポーネントは作成が簡単で、特別な設定は必要ありません。Spring Arm には、ベース以外に親子付けが出来る特別な内蔵ソケットがあります。

        // カメラを作成してスプリング アームに親子付け
        UCameraComponent* Camera = CreateDefaultSubobject<UCameraComponent>(TEXT("ActualCamera"));
        Camera->SetupAttachment(SpringArm, USpringArmComponent::SocketName);
  9. コンポーネントを作成および親子付けしたので、このポーンをデフォルト プレイヤーがコントロールするように設定します。以下が必要なコードの全てです:

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

新規のポーンに便利なコンポーネントのコレクションを親子付けしました。また、ユーザーによるコントロール設定の準備も整いました。ここで Unreal Editor に戻ります。

Work-In-Progress Code

CollidingPawn.h

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

#pragma once

#include "GameFramework/Pawn.h"
#include "CollidingPawn.generated.h"

UCLASS()
class HOWTO_COMPONENTS_API ACollidingPawn : public APawn
{
    GENERATED_BODY()

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

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;

    UParticleSystemComponent* OurParticleSystem;
};

CollidingPawn.cpp

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

#include "HowTo_Components.h"
#include "CollidingPawn.h"

// Sets default values
ACollidingPawn::ACollidingPawn()
{
    // 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;

    // Our root component will be a sphere that reacts to physics (ルートコンポーネントは物理に反応するスフィアになります)
    USphereComponent* SphereComponent = CreateDefaultSubobject<USphereComponent>(TEXT("RootComponent"));
    RootComponent = SphereComponent;
    SphereComponent->InitSphereRadius(40.0f);
    SphereComponent->SetCollisionProfileName(TEXT("Pawn"));

    // 球体の位置が分かるようにメッシュ コンポーネントを作成して配置します
    UStaticMeshComponent* SphereVisual = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("VisualRepresentation"));
    SphereVisual->SetupAttachment(RootComponent);
    static ConstructorHelpers::FObjectFinder<UStaticMesh> SphereVisualAsset(TEXT("/Game/StarterContent/Shapes/Shape_Sphere.Shape_Sphere"));
    if (SphereVisualAsset.Succeeded())
    {
        SphereVisual->SetStaticMesh(SphereVisualAsset.Object);
        SphereVisual->SetRelativeLocation(FVector(0.0f, 0.0f, -40.0f));
        SphereVisual->SetWorldScale3D(FVector(0.8f));
    }

    // アクティブまたは非アクティブにできるパーティクル システムを作成
    OurParticleSystem = CreateDefaultSubobject<UParticleSystemComponent>(TEXT("MovementParticles"));
    OurParticleSystem->SetupAttachment(SphereVisual);
    OurParticleSystem->bAutoActivate = false;
    OurParticleSystem->SetRelativeLocation(FVector(-20.0f, 0.0f, 20.0f));
    static ConstructorHelpers::FObjectFinder<UParticleSystem> ParticleAsset(TEXT("/Game/StarterContent/Particles/P_Fire.P_Fire"));
    if (ParticleAsset.Succeeded())
    {
        OurParticleSystem->SetTemplate(ParticleAsset.Object);
    }

    // Use a spring arm to give the camera smooth, natural-feeling motion.
    USpringArmComponent* SpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraAttachmentArm"));
    SpringArm->SetupAttachment(RootComponent);
    SpringArm->RelativeRotation = FRotator(-45.f, 0.f, 0.f);
    SpringArm->TargetArmLength = 400.0f;
    SpringArm->bEnableCameraLag = true;
    SpringArm->CameraLagSpeed = 3.0f;

    // カメラを作成してスプリング アームに親子付け
    UCameraComponent* Camera = CreateDefaultSubobject<UCameraComponent>(TEXT("ActualCamera"));
    Camera->SetupAttachment(SpringArm, USpringArmComponent::SocketName);

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

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

}

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

}

2. 入力設定と Pawn Movement コンポーネントの作成

  1. Unreal Editor へ戻って、プロジェクトの入力設定を行います。設定値は [Edit (編集)] ドロップダウン メニューの [Project Settings (プロジェクト設定)] にあります。

    EditProjectSettings.png

    設定メニューに移動したら、左パネルの [Engine (エンジン)] セクションから [Input (入力)] を選択します。パーティクル システムの切り替えを設定する Action Mapping (アクションマッピング)ポーン を動かす 2 つの Axis Mappings 、そして ポーン を回転するためにもう一つの Axis Mapping を設定しなくてはいけません。

    Action Mapping

    ParticleToggle (パーティクルの切替)

    スペース バー

    Axis Mapping

    MoveForward

    W

    1.0

    S

    -1.0

    MoveRight

    A

    -1.0

    D

    1.0

    Turn (ターン)

    Mouse X

    1.0

    InputSettings.png

  2. Pawn クラスですべての動きを直接処理する代わりに、動きを管理する Movement コンポーネント を作成します。このチュートリアルでは、 Pawn Movement コンポーネント クラスを拡張します。最初に [File (ファイル)] のドロップダウン メニューから [Add Code to Project (コードをプロジェクトに追加)] コマンドを選択します。

    AddCPPCode.png

    Pawn クラスとは異なり、 Pawn Movement コンポーネントはデフォルトで表示されません。このコマンドを探すには、[Show All Classes (すべてのクラスを表示)] オプションにチェックをいれなくてはいけません。

    ShowAllClasses.png

    検索バーに「movement」と入力するとリストの検索範囲を絞り込めます。

    ChooseParentClass2.png

    Pawn Movement コンポーネントにはパワフルな内蔵機能がいくつかあり一般的な物理機能に役立ちます。また、多数ある Pawn タイプの間で移動コードを共有する最適な方法です。プロジェクトが大きくなるにつれて乱雑な状態になったり、ポーンがより複雑になることを防ぐために、機能の分割にコンポーネントを使用することはグッドプラクティスです。

    Pawn クラスを "CollidingPawn" と呼ぶため、このクラスを "CollidingPawnMovement コンポーネント" と呼びましょう。

    NameComponent.png

これで入力設定を定義して、カスタムの Pawn Movement コンポーネントを作成しました。Visual Studio に戻って、Pawn Movement Component を使用時の ポーンの移動方法を定義するためにコードを記述しましょう。

3. Pawn Movement コンポーネントの挙動のコーディング

  1. Visual Studio に戻り、カスタマイズした Pawn Movement コンポーネント を記述することができます。実際に記述が必要なのは、各フレームでどのように動くかを示す TickComponent 関数 (アクタTick 関数に類似)のみです。CollidingPawnMovementComponent.h で、クラス定義の TickComponent をオーバーライドする必要があります。

    public:
        virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction) override;  

    CollidingPawnMovementComponent.cpp ファイルにこの関数を定義します:

    void UCollidingPawnMovementComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction)
    {
        Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
    
        // Make sure that everything is still valid, and that we are allowed to move.
        if (!PawnOwner || !UpdatedComponent || ShouldSkipUpdate(DeltaTime))
        {
            return;
        }
    
        // Get (and then clear) the movement vector that we set in ACollidingPawn::Tick
        FVector DesiredMovementThisFrame = ConsumeInputVector().GetClampedToMaxSize(1.0f) * DeltaTime * 150.0f;
        if (!DesiredMovementThisFrame.IsNearlyZero())
        {
            FHitResult Hit;
            SafeMoveUpdatedComponent(DesiredMovementThisFrame, UpdatedComponent->GetComponentRotation(), true, Hit);
    
            // If we bumped into something, try to slide along it
            if (Hit.IsValidBlockingHit())
            {
                SlideAlongSurface(DesiredMovementThisFrame, 1.f - Hit.Time, Hit.Normal, Hit);
            }
        }
    };

    このコードは、適切な箇所でサーフェスをスライドしながら、ワールド内で ポーン をスムーズに動かします。重力はポーンに適用されず、ポーンの最大速度は 1 秒当たり 150 の アンリアル単位 でハードコーディングされています。

    この TickComponent 関数は UPawnMovementComponent クラスのパワフルな機能をいくつか利用します。 ConsumeInputVector は、移動入力値の保存に使用するビルドイン変数の値をレポートしてクリアします。

    SafeMoveUpdatedComponent はソリッドなバリアを尊重しながら Pawn Movement コンポーネントの移動に Unreal Engine の物理を使用します。

    SlideAlongSurface は、その場に停止したり壁や斜面にくっつくのではなく、壁や斜面に最終的に衝突した時のコリジョンサーフェスに沿ったスムーズなスライドの計算および物理を処理します。

    Pawn Movement コンポーネントは、その他にも検討に値する機能がありますが、このチュートリアルの範囲では必要ありません。Floating Pawn MovementSpectator Pawn MovementCharacter Movement Component などその他のクラスもさらなる使用法やアイデアを与えてくれるかもしれません。

Pawn Movement Component の挙動を定義したら、カスタマイズした Pawn クラスにすべてをまとめるコードを記述していきます。

Work-In-Progress Code

CollidingPawn.h

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

#pragma once

#include "GameFramework/Pawn.h"
#include "CollidingPawn.generated.h"

UCLASS()
class HOWTO_COMPONENTS_API ACollidingPawn : public APawn
{
    GENERATED_BODY()

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

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;

    UParticleSystemComponent* OurParticleSystem;
};

CollidingPawn.cpp

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

#include "HowTo_Components.h"
#include "CollidingPawn.h"

// Sets default values
ACollidingPawn::ACollidingPawn()
{
    // 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;

    // Our root component will be a sphere that reacts to physics (ルートコンポーネントは物理に反応するスフィアになります)
    USphereComponent* SphereComponent = CreateDefaultSubobject<USphereComponent>(TEXT("RootComponent"));
    RootComponent = SphereComponent;
    SphereComponent->InitSphereRadius(40.0f);
    SphereComponent->SetCollisionProfileName(TEXT("Pawn"));

    // 球体の位置が分かるようにメッシュ コンポーネントを作成して配置します
    UStaticMeshComponent* SphereVisual = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("VisualRepresentation"));
    SphereVisual->SetupAttachment(RootComponent);
    static ConstructorHelpers::FObjectFinder<UStaticMesh> SphereVisualAsset(TEXT("/Game/StarterContent/Shapes/Shape_Sphere.Shape_Sphere"));
    if (SphereVisualAsset.Succeeded())
    {
        SphereVisual->SetStaticMesh(SphereVisualAsset.Object);
        SphereVisual->SetRelativeLocation(FVector(0.0f, 0.0f, -40.0f));
        SphereVisual->SetWorldScale3D(FVector(0.8f));
    }

    // アクティブまたは非アクティブにできるパーティクル システムを作成
    OurParticleSystem = CreateDefaultSubobject<UParticleSystemComponent>(TEXT("MovementParticles"));
    OurParticleSystem->SetupAttachment(SphereVisual);
    OurParticleSystem->bAutoActivate = false;
    OurParticleSystem->SetRelativeLocation(FVector(-20.0f, 0.0f, 20.0f));
    static ConstructorHelpers::FObjectFinder<UParticleSystem> ParticleAsset(TEXT("/Game/StarterContent/Particles/P_Fire.P_Fire"));
    if (ParticleAsset.Succeeded())
    {
        OurParticleSystem->SetTemplate(ParticleAsset.Object);
    }

    // Use a spring arm to give the camera smooth, natural-feeling motion.
    USpringArmComponent* SpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraAttachmentArm"));
    SpringArm->SetupAttachment(RootComponent);
    SpringArm->RelativeRotation = FRotator(-45.f, 0.f, 0.f);
    SpringArm->TargetArmLength = 400.0f;
    SpringArm->bEnableCameraLag = true;
    SpringArm->CameraLagSpeed = 3.0f;

    // カメラを作成してスプリング アームに親子付け
    UCameraComponent* Camera = CreateDefaultSubobject<UCameraComponent>(TEXT("ActualCamera"));
    Camera->SetupAttachment(SpringArm, USpringArmComponent::SocketName);

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

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

}

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

}

CollidingPawnMovementComponent.h

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

#pragma once

#include "GameFramework/PawnMovementComponent.h"
#include "CollidingPawnMovementComponent.generated.h"

/**
    * 
    */
UCLASS()
class HOWTO_COMPONENTS_API UCollidingPawnMovementComponent : public UPawnMovementComponent
{
    GENERATED_BODY()
public:

    virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction) override;  
};

CollidingPawnMovementComponent.cpp

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

#include "HowTo_Components.h"
#include "CollidingPawnMovementComponent.h"

void UCollidingPawnMovementComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction)
{
    Super::TickComponent(DeltaTime, TickType, ThisTickFunction);

    // Make sure that everything is still valid, and that we are allowed to move.
    if (!PawnOwner || !UpdatedComponent || ShouldSkipUpdate(DeltaTime))
    {
        return;
    }

    // Get (and then clear) the movement vector that we set in ACollidingPawn::Tick
    FVector DesiredMovementThisFrame = ConsumeInputVector().GetClampedToMaxSize(1.0f) * DeltaTime * 150.0f;
    if (!DesiredMovementThisFrame.IsNearlyZero())
    {
        FHitResult Hit;
        SafeMoveUpdatedComponent(DesiredMovementThisFrame, UpdatedComponent->GetComponentRotation(), true, Hit);

        // If we bumped into something, try to slide along it
        if (Hit.IsValidBlockingHit())
        {
            SlideAlongSurface(DesiredMovementThisFrame, 1.f - Hit.Time, Hit.Normal, Hit);
        }
    }
};

4. ポーンとコンポーネントの併用

  1. カスタム仕様の Pawn Movement コンポーネント を使用するために、まずコンポーネントを追跡する Pawn クラスを追加します。"OurParticleSystem" 変数を追加した付近の、CollidingPawn.h のクラス定義下部に以下のコードを追加します。

    class UCollidingPawnMovementComponent* OurMovementComponent;
  2. 追跡する場所を用意したら、新規変数を格納する Colliding Pawn Movement コンポーネント を作成しなくてはいけません。CollidingPawn.cpp を開いて、コードで新規クラスの参照ができるようにファイル上部にある #include" 行の下に以下を追加します。

    #include "CollidingPawnMovementComponent.h"

    リストの最後の #include は、generated.h (この場合は、#include "CollidingPawn.generated.h") になるようにしてください。そうしないと、コンパイル エラーが発生します。

    Pawn Movement コンポーネントの作成と、このコンポーネントと Pawn の関連付けはシンプルな作業です。ACollidingPawn::ACollidingPawn の下に以下のコードを追加します。

    // Movement コンポーネントのインスタンスを作成し、これにルートの更新を伝えます。
    OurMovementComponent = CreateDefaultSubobject<UCollidingPawnMovementComponent>(TEXT("CustomMovementComponent"));
    OurMovementComponent->UpdatedComponent = RootComponent;

    これまで見てきた コンポーネント と違って、このコンポーネントを独自の Component 階層に親子付けする必要はありません。理由は、その他のコンポーネントは本質的に物理ロケーションを必要とするあらゆる種類の Scene コンポーネント だからです。しかし Movement Controllers は Scene コンポーネントではないため物理オブジェクトを表現しません。そのため、物理位置に存在したり、別のコンポーネントに物理的にアタッチするという概念があてはまりません。

  3. Pawns には GetMovement コンポーネント という関数があります。この関数はエンジン内のその他のクラスが、Pawn が現在使用中の Pawn Movement コンポーネントにアクセスできるようにします。カスタムの Pawn Movement コンポーネント を返すようにこの関数をオーバーライドする必要があります。CollidingPawn.h のクラス定義に以下のコードを追加します。

    virtual UPawnMovementComponent* GetMovementComponent() const override;

    CollidingPawn.cpp にオーバーライドされた関数の定義を以下のように追加します。

    UPawnMovementComponent* ACollidingPawn::GetMovementComponent() const
    {
        return OurMovementComponent;
    }
  4. 新規 Pawn Movement コンポーネントをセットアップしたので、Pawn が受け取る入力を処理するコードを作成します。CollidingPawn.h のクラス定義にいくつかの関数を宣言することからスタートします。

    void MoveForward(float AxisValue);
    void MoveRight(float AxisValue);
    void Turn(float AxisValue);
    void ParticleToggle();

    In CollidingPawn.cpp, we will add the definitions of those functions as follows:

        void ACollidingPawn::MoveForward(float AxisValue)
        {
            if (OurMovementComponent && (OurMovementComponent->UpdatedComponent == RootComponent))
            {
                OurMovementComponent->AddInputVector(GetActorForwardVector() * AxisValue);
            }
        }
    
        void ACollidingPawn::MoveRight(float AxisValue)
        {
            if (OurMovementComponent && (OurMovementComponent->UpdatedComponent == RootComponent))
            {
                OurMovementComponent->AddInputVector(GetActorRightVector() * AxisValue);
            }
        }
    
        void ACollidingPawn::Turn(float AxisValue)
        {
            FRotator NewRotation = GetActorRotation();
            NewRotation.Yaw += AxisValue;
            SetActorRotation(NewRotation);
        }
    
        void ACollidingPawn::ParticleToggle()
        {
            if (OurParticleSystem && OurParticleSystem->Template)
            {
                OurParticleSystem->ToggleActive();
            }
        }
  5. あとは関数と入力イベントをバインドするのみです。次のコードを ACollidingPawn::SetupPlayerInputComponent に追加します。

    InputComponent->BindAction("ParticleToggle", IE_Pressed, this, &ACollidingPawn::ParticleToggle);
    
    InputComponent->BindAxis("MoveForward", this, &ACollidingPawn::MoveForward);
    InputComponent->BindAxis("MoveRight", this, &ACollidingPawn::MoveRight);
    InputComponent->BindAxis("Turn", this, &ACollidingPawn::Turn);
  6. プログラミングが終了したので、 アンリアルエディタ に戻って Compile ボタンを押して変更をロードします。

プログラミング作業は終了です。これでカスタム ポーンをワールドに配置してあちこちへ移動させることができます。

完成したコード

CollidingPawn.h

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

#pragma once

#include "GameFramework/Pawn.h"
#include "CollidingPawn.generated.h"

UCLASS()
class HOWTO_COMPONENTS_API ACollidingPawn : public APawn
{
    GENERATED_BODY()

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

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;

    UParticleSystemComponent* OurParticleSystem;
    class UCollidingPawnMovementComponent* OurMovementComponent;

    virtual UPawnMovementComponent* GetMovementComponent() const override;

    void MoveForward(float AxisValue);
    void MoveRight(float AxisValue);
    void Turn(float AxisValue);
    void ParticleToggle();
};

CollidingPawn.cpp

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

#include "HowTo_Components.h"
#include "CollidingPawn.h"
#include "CollidingPawnMovementComponent.h"

// Sets default values
ACollidingPawn::ACollidingPawn()
{
    // 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;

    // Our root component will be a sphere that reacts to physics (ルートコンポーネントは物理に反応するスフィアになります)
    USphereComponent* SphereComponent = CreateDefaultSubobject<USphereComponent>(TEXT("RootComponent"));
    RootComponent = SphereComponent;
    SphereComponent->InitSphereRadius(40.0f);
    SphereComponent->SetCollisionProfileName(TEXT("Pawn"));

    // 球体の位置が分かるようにメッシュ コンポーネントを作成して配置します
    UStaticMeshComponent* SphereVisual = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("VisualRepresentation"));
    SphereVisual->SetupAttachment(RootComponent);
    static ConstructorHelpers::FObjectFinder<UStaticMesh> SphereVisualAsset(TEXT("/Game/StarterContent/Shapes/Shape_Sphere.Shape_Sphere"));
    if (SphereVisualAsset.Succeeded())
    {
        SphereVisual->SetStaticMesh(SphereVisualAsset.Object);
        SphereVisual->SetRelativeLocation(FVector(0.0f, 0.0f, -40.0f));
        SphereVisual->SetWorldScale3D(FVector(0.8f));
    }

    // アクティブまたは非アクティブにできるパーティクル システムを作成
    OurParticleSystem = CreateDefaultSubobject<UParticleSystemComponent>(TEXT("MovementParticles"));
    OurParticleSystem->SetupAttachment(SphereVisual);
    OurParticleSystem->bAutoActivate = false;
    OurParticleSystem->SetRelativeLocation(FVector(-20.0f, 0.0f, 20.0f));
    static ConstructorHelpers::FObjectFinder<UParticleSystem> ParticleAsset(TEXT("/Game/StarterContent/Particles/P_Fire.P_Fire"));
    if (ParticleAsset.Succeeded())
    {
        OurParticleSystem->SetTemplate(ParticleAsset.Object);
    }

    // Use a spring arm to give the camera smooth, natural-feeling motion.
    USpringArmComponent* SpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraAttachmentArm"));
    SpringArm->SetupAttachment(RootComponent);
    SpringArm->RelativeRotation = FRotator(-45.f, 0.f, 0.f);
    SpringArm->TargetArmLength = 400.0f;
    SpringArm->bEnableCameraLag = true;
    SpringArm->CameraLagSpeed = 3.0f;

    // カメラを作成してスプリング アームに親子付け
    UCameraComponent* Camera = CreateDefaultSubobject<UCameraComponent>(TEXT("ActualCamera"));
    Camera->SetupAttachment(SpringArm, USpringArmComponent::SocketName);

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

    // Movement コンポーネントのインスタンスを作成し、ルート コンポーネントの更新を指示します。
    OurMovementComponent = CreateDefaultSubobject<UCollidingPawnMovementComponent>(TEXT("CustomMovementComponent"));
    OurMovementComponent->UpdatedComponent = RootComponent;
}

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

}

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

    InputComponent->BindAction("ParticleToggle", IE_Pressed, this, &ACollidingPawn::ParticleToggle);

    InputComponent->BindAxis("MoveForward", this, &ACollidingPawn::MoveForward);
    InputComponent->BindAxis("MoveRight", this, &ACollidingPawn::MoveRight);
    InputComponent->BindAxis("Turn", this, &ACollidingPawn::Turn);
}

UPawnMovementComponent* ACollidingPawn::GetMovementComponent() const
{
    return OurMovementComponent;
}

void ACollidingPawn::MoveForward(float AxisValue)
{
    if (OurMovementComponent && (OurMovementComponent->UpdatedComponent == RootComponent))
    {
        OurMovementComponent->AddInputVector(GetActorForwardVector() * AxisValue);
    }
}

void ACollidingPawn::MoveRight(float AxisValue)
{
    if (OurMovementComponent && (OurMovementComponent->UpdatedComponent == RootComponent))
    {
        OurMovementComponent->AddInputVector(GetActorRightVector() * AxisValue);
    }
}

void ACollidingPawn::Turn(float AxisValue)
{
    FRotator NewRotation = GetActorRotation();
    NewRotation.Yaw += AxisValue;
    SetActorRotation(NewRotation);
}

void ACollidingPawn::ParticleToggle()
{
    if (OurParticleSystem && OurParticleSystem->Template)
    {
        OurParticleSystem->ToggleActive();
    }
}

CollidingPawnMovementComponent.h

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

#pragma once

#include "GameFramework/PawnMovementComponent.h"
#include "CollidingPawnMovementComponent.generated.h"

/**
    * 
    */
UCLASS()
class HOWTO_COMPONENTS_API UCollidingPawnMovementComponent : public UPawnMovementComponent
{
    GENERATED_BODY()
public:

    virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction) override;  
};

CollidingPawnMovementComponent.cpp

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

#include "HowTo_Components.h"
#include "CollidingPawnMovementComponent.h"

void UCollidingPawnMovementComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction)
{
    Super::TickComponent(DeltaTime, TickType, ThisTickFunction);

    // Make sure that everything is still valid, and that we are allowed to move.
    if (!PawnOwner || !UpdatedComponent || ShouldSkipUpdate(DeltaTime))
    {
        return;
    }

    // Get (and then clear) the movement vector that we set in ACollidingPawn::Tick
    FVector DesiredMovementThisFrame = ConsumeInputVector().GetClampedToMaxSize(1.0f) * DeltaTime * 150.0f;
    if (!DesiredMovementThisFrame.IsNearlyZero())
    {
        FHitResult Hit;
        SafeMoveUpdatedComponent(DesiredMovementThisFrame, UpdatedComponent->GetComponentRotation(), true, Hit);

        // If we bumped into something, try to slide along it
        if (Hit.IsValidBlockingHit())
        {
            SlideAlongSurface(DesiredMovementThisFrame, 1.f - Hit.Time, Hit.Normal, Hit);
        }
    }
};

5. Playing in Editor (PIE)

  1. Unreal EditorCompile (コンパイル) ボタンを押して変更したコードをロードします。

    CompileFromEditor.png

  2. ワールドに CollidingPawn のインスタンスをドロップします。コンテンツ ブラウザ の "C++ Classes/(Our Project Name)/CollidingPawn" の配下にクラスがあります。

    ClassInContentBrowser.png

    LevelEditorBeforePlay.png

  3. Play (再生) を押すと、スフィア (球体) は WASD キーで移動、マウス操作で回転、そして例にあるテーブルやチェアやワールドに配置したいその他の物理オブジェクトなど、ワールドオブジェクトと平行に「衝突してスライド」します。スペースバーで球体に火を付ける (または火を消す) こともできます。

    CollidingAndOnFire.png

Unreal Engine 4 は幅広い用途向けの コンポーネント を提供しています。これまでは一般的なコンポーネントをいくつか紹介しただけですが、その他にもたくさんのコンポーネントがあります。ビルトイン コンポーネントを試してみたり、独自のコンポーネントを記述してみてください!柔軟性に優れたパワフルな機能を提供するだけでなく、プロジェクトコードの管理と再利用に役立ちます。

6. 応用編

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

  • 親の周りを自動的に回る コンポーネント を作成します。

  • 最大 3 個までの子をスポーンするコンポーネントをビルドします。各コンポーネントは設定された時間後に独自にスポーンします。

  • コンポーネントを用いて完全な アクタ を別の アクタ へ親子付けする方法を学びます。

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

Select Skin
Light
Dark

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

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

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

フィードバックを送信