3.编写Pawn移动组件行为的代码

编写代码来定义Pawn移动组件如何确定它所控制的Pawn每一帧的运动。

Windows
MacOS
Linux
On this page
  1. 我们现在可以回到 Visual Studio,并编写自定义 Pawn移动组件 的代码。我们需要编写的是 TickComponent 函数(类似于 Actor的 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);
    
        // 确保一切有效,然后我们能够移动。
        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);
            }
        }
    };

    该代码将在场景中平滑地移动 Pawn,在适当的情况下从表面滑落。我们没有对Pawn应用重力,它的最大速度硬编码为 150**虚幻单位**/秒。

    该TickComponent函数利用 UPawnMovementComponent 类提供的一些功能强大的功能。

    ConsumeInputVector 报告并清除我们用来存储移动输入的内置变量的值。

    SafeMoveUpdatedComponent 使用 虚幻引擎 物理来移动Pawn移动组件,同时考虑固体障碍物的存在。

    SlideAlongSurface 处理在移动导致碰撞时,沿着碰撞表面(如墙壁和坡道)平滑移动所涉及的计算和物理,而不仅仅是停在原位并靠着墙壁或粘附在坡道上。

    Pawn移动组件中包含更多值得尝试的功能,但本教程不需要使用这些功能。而其他一些类,如 浮动Pawn移动旁观者Pawn移动角色移动组件,可能会提供更多有用示例和想法。

定义了我们的Pawn移动组件的行为后,现在可以编写代码,将所有定义整合到自定义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;
};

CollidingPawn.cpp

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

#include "HowTo_Components.h"
#include "CollidingPawn.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;
}

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

}

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

}

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

    // 确保一切有效,然后我们能够移动。
    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);
        }
    }
};
Select Skin
Light
Dark

Welcome to the new Unreal Engine 4 Documentation site!

We're working on lots of new features including a feedback system so you can tell us how we are doing. It's not quite ready for use in the wild yet, so head over to the Documentation Feedback forum to tell us about this page or call out any issues you are encountering in the meantime.

We'll be sure to let you know when the new system is up and running.

Post Feedback