组件和碰撞

学习利用组件将Pawn与物理交互、使用粒子效果等方法。

创建 Pawn 子类

为了能对 Actor 进行控制, 创建 Pawn 子类并命名为 CollidingPawn

添加粒子系统组件

创建组件

为了实现碰撞, 需要创建具有物理碰撞性质的组件, 球体组件是一种可与游戏场景交互并碰撞的物理实体, 本例子使用球体组件。同时本例子还需要负责显示形态的静态网格体组件, 可附加的摄像机组件, 控制游戏视角的弹簧臂组件以及可控制的粒子系统组件. 有些组件需要包含头文件才能编译成功:

#include "UObject/ConstructorHelpers.h"
#include "Particles/ParticleSystemComponent.h"
#include "Components/SphereComponent.h"
#include "Camera/CameraComponent.h"
#include "GameFramework/SpringArmComponent.h"

同时在类中加入粒子系统组件类型的成员变量:

UPROPERTY()
    class UParticleSystemComponent* OurParticleSystem;
  • 构造函数
      // 创建球体组件并将其设置为根组件
      USphereComponent* SphereComponent = CreateDefaultSubobject(TEXT("RootComponent"));
      RootComponent = SphereComponent;
      SphereComponent->InitSphereRadius(40.0f);
      SphereComponent->SetCollisionProfileName(TEXT("Pawn"));
    
      // 创建并放置网格体组件,以便查看球体位置
      UStaticMeshComponent* SphereVisual = CreateDefaultSubobject(TEXT("VisualRepresentation"));
      SphereVisual->SetupAttachment(RootComponent);
      static ConstructorHelpers::FObjectFinder 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(TEXT("MovementParticles"));
      OurParticleSystem->SetupAttachment(SphereVisual);
      OurParticleSystem->bAutoActivate = false;
      OurParticleSystem->SetRelativeLocation(FVector(-20.0f, 0.0f, 20.0f));
      static ConstructorHelpers::FObjectFinder ParticleAsset(TEXT("/Game/StarterContent/Particles/P_Fire.P_Fire"));
      if (ParticleAsset.Succeeded())
      {
          OurParticleSystem->SetTemplate(ParticleAsset.Object);
      }
    
      // 使用弹簧臂给予摄像机平滑自然的运动感。
      USpringArmComponent* SpringArm = CreateDefaultSubobject(TEXT("CameraAttachmentArm"));
      SpringArm->SetupAttachment(RootComponent);
      SpringArm->SetRelativeRotation(FRotator(-45.f, 0.f, 0.f));
      SpringArm->TargetArmLength = 400.0f;
      SpringArm->bEnableCameraLag = true;
      SpringArm->CameraLagSpeed = 3.0f;
    
      // 创建摄像机并附加到弹簧臂
      UCameraComponent* Camera = CreateDefaultSubobject(TEXT("ActualCamera"));
      Camera->SetupAttachment(SpringArm, USpringArmComponent::SocketName);
    
      // 控制默认玩家
      AutoPossessPlayer = EAutoReceiveInput::Player0;
    

配置游戏输入

在编辑器中,编辑->项目设置->输入, 在绑定中添加:

操作映射
ParticleToggle 空格
轴映射
MoveForward W 1.0
S -1.0
MoveRight A -1.0
D 1.0
Turn Mouse X 1.0

创建PawnMovementComponent子类

PawnMovementComponent (Pawn移动组件) 拥有部分强大内置功能,以便使用常见物理功能,同时便于在大量Pawn类型间共享移动代码。使用组件分隔不同功能是上佳方法,可在项目增大、Pawn越加复杂时减少杂乱。创建PawnMovementComponent子类并将其命名为 CollidingPawnMovementComponent

  • 编写Pawn移动组件代码
    为自定义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);
            }
        }
    };
    

    此 TickComponent 函数使用 UPawnMovementComponent 类提供的几种强大功能ConsumeInputVector 报告并清空用于存储移动输入的内置变量值。
    SafeMoveUpdatedComponent 利用虚幻引擎物理移动Pawn移动组件,同时考虑固体障碍。移动导致碰撞时, SlideAlongSurface 会处理沿墙壁和斜坡等碰撞表面平滑滑动所涉及的计算和物理,而非直接停留原地,粘在墙壁或斜坡上。
    Pawn移动组件中还包含众多值得探究的功能,但本例子范围中暂时无需使用。

同时使用Pawn和组件

在 CollidingPawn.h中添加我们自定义的Pawn移动组件

UPROPERTY()
    class UCollidingPawnMovementComponent* OurMovementComponent;

添加头文件

#include "CollidingPawnMovementComponent.h"

须确保列最后一个头文件是 "generated.h",否则会造成编译错误。

  • 在构造函数中创建移动组件的实例

      // 创建移动组件的实例,并要求其更新根。
      OurMovementComponent = CreateDefaultSubobject(TEXT("CustomMovementComponent"));
      OurMovementComponent->UpdatedComponent = RootComponent;
    

    PawnMovementComponent 组件和场景组件的后代不同, 只有场景组件(及其子类)才需要和可以彼此附加, 相反的是他需要设置 UpdatedComponent, 用来告知物理效果应该更新到哪个组件.

  • 重写 GetMovementComponent 函数
    Pawn拥有名为 GetMovementComponent 的函数,用于提供引擎中其他类访问该Pawn当前所用Pawn移动组件的权限. 因此在类中添加

    virtual UPawnMovementComponent* GetMovementComponent() const override;
    

    并实现该方法

    UPawnMovementComponent* ACollidingPawn::GetMovementComponent() const
    {
      return OurMovementComponent;
    }
    
  • 最后设置输入事件的响应
    类中添加声明响应事件的成员函数

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

    实现这些函数

     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();
          }
      }
    

    最后绑定事件

    PlayerInputComponent->BindAction("ParticleToggle", IE_Pressed, this, &ACollidingPawn::ParticleToggle);
    
    PlayerInputComponent->BindAxis("MoveForward", this, &  ACollidingPawn::MoveForward);
    PlayerInputComponent->BindAxis("MoveRight", this, &  ACollidingPawn::MoveRight);
    PlayerInputComponent->BindAxis("Turn", this, &ACollidingPawn::Turn);
    

你可能感兴趣的:(组件和碰撞)