组件和碰撞

学习利用组件将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);
    
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容