Language:
Page Info
Skill Level:
Engine Version:
Share
此中文页面内容对应的英文页面有后续更新,如需浏览最新文档可切换至英文页面浏览。

3.1 - 为游戏添加发射物

设置好角色后,现在需要实现一个发射武器。开火时,类似于手雷的简单发射物将从屏幕中央射出、飞行,然后和世界场景发生碰撞。在这步中,我们将为发射物添加输入并创建新的代码类。

添加开火动作映射

  1. Edit 菜单中点击 Project Settings

  2. Project Settings 标签左侧的 Engine 标题下点击 Input

  3. Bindings 下点击 Action Mappings 旁的加号。

  4. 点击 Action Mappings 左侧的箭头。

  5. 在出现的文本框中输入“Fire”,然后点击文本框左侧的箭头展开动作绑定选项。

  6. 在下拉菜单中,从 Mouse 下拉列表中选择 Left Mouse Button

  7. 输入设置应与下图相似:

    ActionMapping.png

  8. 关闭 Project Settings 菜单。

添加发射物类

  1. 在 File 菜单中,选择 New C++ Class... 选择新的父类。

  2. Choose Parent Class 菜单将打开。向下滚动,将 Actor 选为父类并点击 Next

    AddProjectileClass.png

  3. 将新建的类命名为“FPSProjectile”,然后点击 Create

    CreateProjectileClass.png

添加一个 USphere 组件

  1. Solution Explorer 中找到 FPSProjectile 类头文件并打开 FPSProjectile.h

  2. FPSProjectile 接口中添加对 USphereComponent 的引用。

    // 球体碰撞组件。
    UPROPERTY(VisibleDefaultsOnly, Category = Projectile)
    USphereComponent* CollisionComponent;
  3. FPSProjectile.h 应与以下代码相似:

    // 在 Project Settings 的 Description 页面填入版权声明。
    
    #pragma once
    
    #include "GameFramework/Actor.h"
    #include "FPSProjectile.generated.h"
    
    UCLASS()
    class FPSPROJECT_API AFPSProjectile : public AActor
    {
        GENERATED_BODY()
    
    public: 
        // 设置该 actor 属性的默认值
        AFPSProjectile();
    
        // 游戏开始时或生成时调用
        virtual void BeginPlay() override;
    
        // 每帧调用
        virtual void Tick( float DeltaSeconds ) override;
    
        // 球体碰撞组件。
        UPROPERTY(VisibleDefaultsOnly, Category = Projectile)
        USphereComponent* CollisionComponent;
    };
  4. Solution Explorer 中找到 FPSProjectile 类 CPP 文件并打开 FPSProjectile.cpp

  5. 将以下代码添加到 FPSProjectile.cpp 中的 AFPSProjectile 构造函数:

    // 使用球体代表简单碰撞。
    CollisionComponent = CreateDefaultSubobject<USphereComponent>(TEXT("SphereComponent"));
    // 设置球体的碰撞半径。
    CollisionComponent->InitSphereRadius(15.0f);
    // 将碰撞组件设为根组件。
    RootComponent = CollisionComponent;

    模拟将驱动 CollisionComponent,所以将其设为 RootComponent

  6. FPSProjectile.cpp 应与以下代码相似:

    // 在 Project Settings 的 Description 页面填入版权声明。
    
    #include "FPSProject.h"
    #include "FPSProjectile.h"
    
    // 设置默认值
    AFPSProjectile::AFPSProjectile()
    {
        // 将此 actor 设为每帧调用 Tick()。不需要时可将此关闭,以提高性能。
        PrimaryActorTick.bCanEverTick = true;
    
        // 使用球体代表简单碰撞。
        CollisionComponent = CreateDefaultSubobject<USphereComponent>(TEXT("SphereComponent"));
        // 设置球体的碰撞半径。
        CollisionComponent->InitSphereRadius(15.0f);
        // 将碰撞组件设为根组件。
        RootComponent = CollisionComponent;
    }
    
    // 游戏开始时或生成时调用
    void AFPSProjectile::BeginPlay()
    {
        Super::BeginPlay();
    
    }
    
    // 每帧调用
    void AFPSProjectile::Tick( float DeltaTime )
    {
        Super::Tick( DeltaTime );
    
    }

添加发射物运动组件

  1. Solution Explorer 中找到 FPSProjectile 类头文件并打开 FPSProjectile.h

  2. 将以下代码添加到 FPSProjectile.h

    // 发射物运动组件。
    UPROPERTY(VisibleAnywhere, Category = Movement)
    UProjectileMovementComponent* ProjectileMovementComponent;
  3. FPSProjectile.h 应与以下代码相似:

    // 在 Project Settings 的 Description 页面填入版权声明。
    
    #pragma once
    
    #include "GameFramework/Actor.h"
    #include "FPSProjectile.generated.h"
    
    UCLASS()
    class FPSPROJECT_API AFPSProjectile : public AActor
    {
        GENERATED_BODY()
    
    public: 
        // 设置该 actor 属性的默认值
        AFPSProjectile();
    
        // 游戏开始时或生成时调用
        virtual void BeginPlay() override;
    
        // 每帧调用
        virtual void Tick( float DeltaSeconds ) override;
    
        // 球体碰撞组件。
        UPROPERTY(VisibleDefaultsOnly, Category = Projectile)
        USphereComponent* CollisionComponent;
    
        // 发射物运动组件。
        UPROPERTY(VisibleAnywhere, Category = Movement)
        UProjectileMovementComponent* ProjectileMovementComponent;
    };
  4. Solution Explorer 中找到 FPSProjectile CPP 文件并打开 FPSProjectile.cpp

  5. 将以下代码行添加到 FPSProjectile.cpp 中的 FPSProjectile 构造函数:

    // 使用此组件驱动该发射物的运动。
    ProjectileMovementComponent = CreateDefaultSubobject<UProjectileMovementComponent>(TEXT("ProjectileMovementComponent"));
    ProjectileMovementComponent->SetUpdatedComponent(CollisionComponent);
    ProjectileMovementComponent->InitialSpeed = 3000.0f;
    ProjectileMovementComponent->MaxSpeed = 3000.0f;
    ProjectileMovementComponent->bRotationFollowsVelocity = true;
    ProjectileMovementComponent->bShouldBounce = true;
    ProjectileMovementComponent->Bounciness = 0.3f;
  6. FPSProjectile.cpp 应与以下代码相似:

    // 在 Project Settings 的 Description 页面填入版权声明。
    
    #include "FPSProject.h"
    #include "FPSProjectile.h"
    
    // 设置默认值
    AFPSProjectile::AFPSProjectile()
    {
        // 将此 actor 设为每帧调用 Tick()。不需要时可将此关闭,以提高性能。
        PrimaryActorTick.bCanEverTick = true;
    
        // 使用球体代表简单碰撞。
        CollisionComponent = CreateDefaultSubobject<USphereComponent>(TEXT("SphereComponent"));
        // 设置球体的碰撞半径。
        CollisionComponent->InitSphereRadius(15.0f);
        // 将碰撞组件设为根组件。
        RootComponent = CollisionComponent;
    
        // 使用此组件驱动此发射物的运动。
        ProjectileMovementComponent = CreateDefaultSubobject<UProjectileMovementComponent>(TEXT("ProjectileMovementComponent"));
        ProjectileMovementComponent->SetUpdatedComponent(CollisionComponent);
        ProjectileMovementComponent->InitialSpeed = 3000.0f;
        ProjectileMovementComponent->MaxSpeed = 3000.0f;
        ProjectileMovementComponent->bRotationFollowsVelocity = true;
        ProjectileMovementComponent->bShouldBounce = true;
        ProjectileMovementComponent->Bounciness = 0.3f;
    }
    
    // 游戏开始时或生成时调用
    void AFPSProjectile::BeginPlay()
    {
        Super::BeginPlay();
    
    }
    
    // 每帧调用
    void AFPSProjectile::Tick( float DeltaTime )
    {
        Super::Tick( DeltaTime );
    
    }

设置发射物的初速度

  1. Solution Explorer 中找到 FPSProjectile 类头文件并打开 FPSProjectile.h

  2. FPSProjectile.h 中添加以下函数声明:

    // 在发射方向上设置发射物初速度的函数。
    void FireInDirection(const FVector& ShootDirection);

    此函数负责发射物体。

  3. FPSProjectile.h 应与以下代码相似:

    // 在 Project Settings 的 Description 页面填入版权声明。
    
    #pragma once
    
    #include "GameFramework/Actor.h"
    #include "FPSProjectile.generated.h"
    
    UCLASS()
    class FPSPROJECT_API AFPSProjectile : public AActor
    {
        GENERATED_BODY()
    
    public: 
        // 设置该 actor 属性的默认值
        AFPSProjectile();
    
        // 游戏开始时或生成时调用
        virtual void BeginPlay() override;
    
        // 每帧调用
        virtual void Tick( float DeltaSeconds ) override;
    
        // 球体碰撞组件。
        UPROPERTY(VisibleDefaultsOnly, Category = Projectile)
        USphereComponent* CollisionComponent;
    
        // 发射物运动组件。
        UPROPERTY(VisibleAnywhere, Category = Movement)
        UProjectileMovementComponent* ProjectileMovementComponent;
    
        // 在发射方向上设置发射物初速度的函数。
        void FireInDirection(const FVector& ShootDirection);
    };
  4. Solution Explorer 中找到 FPSProjectile CPP 文件并打开 FPSProjectile.cpp

  5. FPSProjectile.cpp 添加以下函数定义:

    // 在发射方向上设置发射物初速度的函数。
    void AFPSProjectile::FireInDirection(const FVector& ShootDirection)
    {
        ProjectileMovementComponent->Velocity = ShootDirection * ProjectileMovementComponent->InitialSpeed;
    }

    只需提供一个发射方向,因为发射物的速度由 ProjectileMovementComponent 定义。

  6. FPSProjectile.cpp 应与以下代码相似:

    // 在 Project Settings 的 Description 页面填入版权声明。
    
    #include "FPSProject.h"
    #include "FPSProjectile.h"
    
    // 设置默认值
    AFPSProjectile::AFPSProjectile()
    {
        // 将此 actor 设为每帧调用 Tick()。不需要时可将此关闭,以提高性能。
        PrimaryActorTick.bCanEverTick = true;
    
        // 使用球体代表简单碰撞。
        CollisionComponent = CreateDefaultSubobject<USphereComponent>(TEXT("SphereComponent"));
        // 设置球体的碰撞半径。
        CollisionComponent->InitSphereRadius(15.0f);
        // 将碰撞组件设为根组件。
        RootComponent = CollisionComponent;
    
        // 使用此组件驱动此发射物的运动。
        ProjectileMovementComponent = CreateDefaultSubobject<UProjectileMovementComponent>(TEXT("ProjectileMovementComponent"));
        ProjectileMovementComponent->SetUpdatedComponent(CollisionComponent);
        ProjectileMovementComponent->InitialSpeed = 3000.0f;
        ProjectileMovementComponent->MaxSpeed = 3000.0f;
        ProjectileMovementComponent->bRotationFollowsVelocity = true;
        ProjectileMovementComponent->bShouldBounce = true;
        ProjectileMovementComponent->Bounciness = 0.3f;
    }
    
    // 游戏开始时或生成时调用
    void AFPSProjectile::BeginPlay()
    {
        Super::BeginPlay();
    
    }
    
    // 每帧调用
    void AFPSProjectile::Tick( float DeltaTime )
    {
        Super::Tick( DeltaTime );
    
    }
    
    // 在发射方向上设置发射物初速度的函数。
    void AFPSProjectile::FireInDirection(const FVector& ShootDirection)
    {
        ProjectileMovementComponent->Velocity = ShootDirection * ProjectileMovementComponent->InitialSpeed;
    }

绑定开火输入操作

  1. Solution Explorer 中找到 FPSCharacter 类头文件并打开 FPSCharacter.h

  2. FPSCharacter.h 中添加以下函数声明:

    // 处理开火的函数。
    UFUNCTION()
    void Fire();
  3. FPSCharacter.h 应与以下代码相似:

    // 在 Project Settings 的 Description 页面填入版权声明。
    
    #pragma once
    
    #include "GameFramework/Character.h"
    #include "FPSCharacter.generated.h"
    
    UCLASS()
    class FPSPROJECT_API AFPSCharacter : public ACharacter
    {
        GENERATED_BODY()
    
    public:
        // 设置该角色属性的默认值
        AFPSCharacter();
    
        // 游戏开始时或生成时调用
        virtual void BeginPlay() override;
    
        // 每帧调用
        virtual void Tick( float DeltaSeconds ) override;
    
        // 调用后将功能绑定到输入
        virtual void SetupPlayerInputComponent(class UInputComponent* InputComponent) override;
    
        // 处理前后移动的输入。
        UFUNCTION()
        void MoveForward(float Value);
    
        // 处理左右移动的输入。
        UFUNCTION()
        void MoveRight(float Value);
    
        // 按下按键时设置跳跃标记。
        UFUNCTION()
        void StartJump();
    
        // 松开按键时清除跳跃标记。
        UFUNCTION()
        void StopJump();
    
        // 处理开火的函数。
        UFUNCTION()
        void Fire();
    
        // FPS 摄像机。
        UPROPERTY(VisibleAnywhere)
        UCameraComponent* FPSCameraComponent;
    
        // 第一人称模型(手臂),仅对拥有玩家可见。
        UPROPERTY(VisibleDefaultsOnly, Category = Mesh)
        USkeletalMeshComponent* FPSMesh;
    };
  4. Solution Explorer 中找到 FPSCharacter CPP 文件并打开 FPSCharacter.cpp

  5. FPSCharacter.cpp 中,将以下代码添加到 SetupPlayerInputComponent,绑定 OnFire 函数:

    InputComponent->BindAction("Fire", IE_Pressed, this, &AFPSCharacter::Fire);
  6. 将以下函数定义添加到 FPSCharacter.cpp

    void AFPSCharacter::Fire()
    {
    }
  7. FPSCharacter.cpp 应与以下代码相似:

    // 在 Project Settings 的 Description 页面填入版权声明。
    
    #include "FPSProject.h"
    #include "FPSCharacter.h"
    
    // 设置默认值
    AFPSCharacter::AFPSCharacter()
    {
        // 设置此角色每帧调用 Tick()。不需要时可将此关闭,以提高性能。
        PrimaryActorTick.bCanEverTick = true;
    
        // 创建一个第一人称摄像机组件。
        FPSCameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("FirstPersonCamera"));
        // 将摄像机组件附加到胶囊体组件。
        FPSCameraComponent->AttachTo(GetCapsuleComponent());
        // 将摄像机放置在眼睛上方不远处。
        FPSCameraComponent->SetRelativeLocation(FVector(0.0f, 0.0f, 50.0f + BaseEyeHeight));
        // 用 pawn 控制摄像机旋转。
        FPSCameraComponent->bUsePawnControlRotation = true;
    
        // 为拥有玩家创建一个第一人称模型组件。
        FPSMesh = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("FirstPersonMesh"));
        // 该模型仅对拥有玩家可见。
        FPSMesh->SetOnlyOwnerSee(true);
        // 将 FPS 模型添加到 FPS 摄像机。
        FPSMesh->AttachTo(FPSCameraComponent);
        // 禁用部分环境阴影,保留单一模型存在的假象。
        FPSMesh->bCastDynamicShadow = false;
        FPSMesh->CastShadow = false;
    
        // 拥有玩家无法看到普通(第三人称)身体模型。
        GetMesh()->SetOwnerNoSee(true);
    }
    
    // 游戏开始时或生成时调用
    void AFPSCharacter::BeginPlay()
    {
        Super::BeginPlay();
    
        if (GEngine)
        {
            // 显示调试信息五秒。-1“键”值(首个参数)说明我们无需更新或刷新此消息。
            GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("We are using FPSCharacter."));
        }
    }
    
    // 每帧调用
    void AFPSCharacter::Tick( float DeltaTime )
    {
        Super::Tick( DeltaTime );
    
    }
    
    // 调用后将功能绑定到输入
    void AFPSCharacter::SetupPlayerInputComponent(class UInputComponent* InputComponent)
    {
        Super::SetupPlayerInputComponent(InputComponent);
    
        // 设置“移动”绑定。
        InputComponent->BindAxis("MoveForward", this, &AFPSCharacter::MoveForward);
        InputComponent->BindAxis("MoveRight", this, &AFPSCharacter::MoveRight);
    
        // 设置“查看”绑定。
        InputComponent->BindAxis("Turn", this, &AFPSCharacter::AddControllerYawInput);
        InputComponent->BindAxis("LookUp", this, &AFPSCharacter::AddControllerPitchInput);
    
        // 设置“动作”绑定。
        InputComponent->BindAction("Jump", IE_Pressed, this, &AFPSCharacter::StartJump);
        InputComponent->BindAction("Jump", IE_Released, this, &AFPSCharacter::StopJump);
        InputComponent->BindAction("Fire", IE_Pressed, this, &AFPSCharacter::Fire);
    }
    
    void AFPSCharacter::MoveForward(float Value)
    {
        // 明确哪个方向是“前进”,并记录玩家试图向此方向移动。
        FVector Direction = FRotationMatrix(Controller->GetControlRotation()).GetScaledAxis(EAxis::X);
        AddMovementInput(Direction, Value);
    }
    
    void AFPSCharacter::MoveRight(float Value)
    {
        // 明确哪个方向是“向右”,并记录玩家试图向此方向移动。
        FVector Direction = FRotationMatrix(Controller->GetControlRotation()).GetScaledAxis(EAxis::Y);
        AddMovementInput(Direction, Value);
    }
    
    void AFPSCharacter::StartJump()
    {
        bPressedJump = true;
    }
    
    void AFPSCharacter::StopJump()
    {
        bPressedJump = false;
    }
    
    void AFPSCharacter::Fire()
    {
    }

定义发射物的生成位置

  1. 生成 FPSProjectile actor 时实现 OnFire 函数需考虑两点:

  • 在何处生成发射物

  • 发射物类(以便 FPSCharacter 和其派生蓝图了解生成何种发射物)

需要使用摄像机空间偏移矢量确定发射物的生成位置。 需将此参数设为可编辑,以便在 BP_FPSCharacter 蓝图中进行设置和调整。 最终,可基于此数据计算发射物的初始位置。

  1. Solution Explorer 中找到 FPSCharacter 类头文件并打开 FPSCharacter.h

  2. 将以下代码添加到 FPSCharacter.h

    // 从摄像机位置的枪口偏移。
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Gameplay)
    FVector MuzzleOffset;

    可通过 EditAnywhere 说明符在蓝图编辑器的默认模式或角色任意实例的 Details 标签中修改枪口偏移值。 可通过 BlueprintReadWrite 说明符获取并设置蓝图中的枪口偏移值。

  3. 将以下代码添加到 FPSCharacter.h

    // 生成的发射物类。
    UPROPERTY(EditDefaultsOnly, Category = Projectile)
    TSubclassOf<class AFPSProjectile> ProjectileClass;

    EditDefaultsOnly 说明符意味着发射物类只能在蓝图上被设为默认,而不能在蓝图的每个实例上设置。

  4. FPSCharacter.h 应与以下代码相似:

    // 在 Project Settings 的 Description 页面填入版权声明。
    
    #pragma once
    
    #include "GameFramework/Character.h"
    #include "FPSCharacter.generated.h"
    
    UCLASS()
    class FPSPROJECT_API AFPSCharacter : public ACharacter
    {
        GENERATED_BODY()
    
    public:
        // 设置该角色属性的默认值
        AFPSCharacter();
    
        // 游戏开始时或生成时调用
        virtual void BeginPlay() override;
    
        // 每帧调用
        virtual void Tick( float DeltaSeconds ) override;
    
        // 调用后将功能绑定到输入
        virtual void SetupPlayerInputComponent(class UInputComponent* InputComponent) override;
    
        // 处理前后移动的输入。
        UFUNCTION()
        void MoveForward(float Value);
    
        // 处理左右移动的输入。
        UFUNCTION()
        void MoveRight(float Value);
    
        // 按下按键时设置跳跃标记。
        UFUNCTION()
        void StartJump();
    
        // 松开按键时清除跳跃标记。
        UFUNCTION()
        void StopJump();
    
        // 处理开火的函数。
        UFUNCTION()
        void Fire();
    
        // FPS 摄像机。
        UPROPERTY(VisibleAnywhere)
        UCameraComponent* FPSCameraComponent;
    
        // 第一人称模型(手臂),仅对拥有玩家可见。
        UPROPERTY(VisibleDefaultsOnly, Category = Mesh)
        USkeletalMeshComponent* FPSMesh;
    
        // 从摄像机位置的枪口偏移。
        UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Gameplay)
        FVector MuzzleOffset;
    
        // 生成的发射物类。
        UPROPERTY(EditDefaultsOnly, Category = Projectile)
        TSubclassOf<class AFPSProjectile> ProjectileClass;
    };

编译并检查代码

现在即可编译并检查新实现的发射物代码。

  1. 在 Visual Studio 中保存所有头文件和实现文件。

  2. Solution Explorer 中找到 FPSProject

  3. 右键单击 FPSProject 并选择 Build 编译项目。

    BuildFPSProject.png

    此步骤的目的是在进入下一步之前找出版本中的错误。