Language:
Page Info
Engine Version:
The translation of this page is out of date. Please see the English version for the latest version of the page.

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-2017 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-2017 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;

    // ルート コンポーネントは物理に反応するスフィア (球体) になります。
    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);
    }

    // カメラにスムーズで自然な動きを与えるためにスプリング アームを使用
    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);

    // デフォルト プレイヤーをコントロール
    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-2017 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-2017 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);

    // 全てがまだ有効であり、移動可能であることを確認
    if (!PawnOwner || !UpdatedComponent || ShouldSkipUpdate(DeltaTime))
    {
        return;
    }

    // ACollidingPawn::Tick で設定した移動ベクターを Get (その後クリア) 
    FVector DesiredMovementThisFrame = ConsumeInputVector().GetClampedToMaxSize(1.0f) * DeltaTime * 150.0f;
    if (!DesiredMovementThisFrame.IsNearlyZero())
    {
        FHitResult Hit;
        SafeMoveUpdatedComponent(DesiredMovementThisFrame, UpdatedComponent->GetComponentRotation(), true, Hit);

        //何かとぶつかった場合は、ぶつかった対象物に沿ってスライド
        if (Hit.IsValidBlockingHit())
        {
            SlideAlongSurface(DesiredMovementThisFrame, 1.f - Hit.Time, Hit.Normal, Hit);
        }
    }
};