美文网首页
组件和碰撞

组件和碰撞

作者: wjundong | 来源:发表于2021-07-20 23:31 被阅读0次

    学习利用组件将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<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->SetRelativeRotation(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;
      

    配置游戏输入

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

    操作映射
    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<UCollidingPawnMovementComponent>(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);
      

    相关文章

      网友评论

          本文标题:组件和碰撞

          本文链接:https://www.haomeiwen.com/subject/hlizpltx.html