4.结合使用Pawn和组件

将Pawn功能、输入配置和组件绑定起来。

Windows
MacOS
Linux
本页面的内容
  1. 为了使用自定义 Pawn移动组件,我们首先需要向 Pawn 类添加一个变量来进行跟踪。在CollidingPawn.h中的类定义底部,在添加了“OurParticleSystem”变量的附近位置,应该添加:

    class UCollidingPawnMovementComponent* OurMovementComponent;
  2. 当有了可以跟踪的位置后,需要创建一个 碰撞Pawn移动组件 来存储新变量,所以打开CollidingPawn.cpp并在文件顶部#include "GameFramework/Pawn.h"行下面添加以下内容,这样我们的代码才能引用新类:

    #include "CollidingPawnMovementComponent.h"

    确保列表中的最后一个#include是generated.h(在本例中为#include "CollidingPawn.generated.h"),否则会导致编译错误。

    创建Pawn移动组件并将其与Pawn关联起来是十分简单的操作。在 ACollidingPawn::ACollidingPawn 底部,可以添加下面的代码:

    // 创建移动组件实例,并告诉它更新根。
    OurMovementComponent = CreateDefaultSubobject<UCollidingPawnMovementComponent>(TEXT("CustomMovementComponent"));
    OurMovementComponent->UpdatedComponent = RootComponent;

    与我们目前看到的其他 组件 不同的是,不需要将该组件连接到我们自己的组件层级。这是因为其他组件都属于 场景组件 类型,本质上需要有物理位置。但 移动控制器 不是场景组件,不需要表示物理对象,因此存在于某个物理位置的概念或者能够在物理上连接到另一个组件的概念不适用于这类组件。

  3. Pawn调用了一个函数 GetMovementComponent,这个函数用来使引擎中的其他类能够访问Pawn当前正在使用的Pawn移动组件。我们将需要覆盖该函数,以便它返回我们的自定义Pawn移动组件。在CollidingPawn.h中的类定义中,需要添加:

    virtual UPawnMovementComponent* GetMovementComponent() const override;

    在CollidingPawn.cpp中,需要添加已覆盖函数的定义,如下所示:

    UPawnMovementComponent* ACollidingPawn::GetMovementComponent() const
    {
        return OurMovementComponent;
    }
  4. 设置好新Pawn移动组件后,创建代码来处理Pawn将接收的输入。首先在CollidingPawn.h中的类定义中声明新函数:

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

    在CollidingPawn.cpp中,添加这些函数的定义,如下所示:

        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) 按钮来加载更改。

编程工作已完成,我们现在可以将自定义Pawn放在场景中并四处移动。

完成的代码

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:
    // 设置该Pawn属性的默认值
    ACollidingPawn();

protected:
    // 游戏开始或产生时调用
    virtual void BeginPlay() override;

public:
    // 每一帧都调用
    virtual void Updatefloat DeltaSeconds override

    // 调用以将功能与输入绑定
    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"

// 设置默认值
ACollidingPawn::ACollidingPawn()
{
    // 设置该Pawn以在每一帧调用Tick()。如果不需要,可以将此设置关闭来提高性能。
    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;

    // 创建移动组件实例,并告诉它更新根组件。
    OurMovementComponent = CreateDefaultSubobject<UCollidingPawnMovementComponent>(TEXT("CustomMovementComponent"));
    OurMovementComponent->UpdatedComponent = RootComponent;
}

// 游戏开始或产生时调用
void ACollidingPawn::BeginPlay()
{
    Super::BeginPlay();

}

// 每一帧都调用
void ACollidingPawn::Tick( float DeltaTime )
{
    Super::Tick( DeltaTime );

}

// 调用以将功能与输入绑定
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);

    // 确保一切有效,然后我们能够移动。
    if (!PawnOwner || !UpdatedComponent || ShouldSkipUpdate(DeltaTime))
    {
        return;
    }

    // 获取(然后清除)我们在ACollidingPawn::Tick中设置的移动矢量
    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);
        }
    }
};

欢迎来到全新虚幻引擎4文档站!

我们正在努力开发新功能,包括反馈系统,以便您能对我们的工作作出评价。但它目前还未正式上线。如果您对此页面有任何意见与在使用中遭遇任何问题,请前往文档反馈论坛告知我们。

新系统上线运行后,我们会及时通知您的。

发表反馈意见