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

3.5 - 在视口中添加准星

在这步中,我们将在游戏中添加一个准星 HUD 元素,以便进行瞄准射击。

导入准星资源

开始之前,先从以下链接下载并提取样本模型:

  1. 在 Content Browser 文件框中单击右键打开 Import Asset 对话框。

  2. 点击 “Import to /Game...” 打开 Import 对话框。

    RightClickImport.png `

  3. 找到并选择 crosshair.TGA 图片文件。

  4. 点击 Open 开始将图片文件导入项目。

  5. 点击 Save 按钮保存导入的图片。

新增 HUD 类

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

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

    ChooseParentHUDClass.png

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

    NameHUDClass.png

  4. Solution Explorer 中找到 FPSHUD 类头文件并打开 FPSHUD.h 添加以下受保护变量:

    protected:
        // 这将在屏幕中央绘制。
        UPROPERTY(EditDefaultsOnly)
        UTexture2D* CrosshairTexture;
  5. FPSHUD.h 中添加以下函数声明:

    public:
        // HUD 的主绘制调用。
        virtual void DrawHUD() override;
  6. FPSHUD.cpp 中实现 DrawHUD 函数:

    void AFPSHUD::DrawHUD()
    {
        Super::DrawHUD();
    
        if (CrosshairTexture)
        {
            // 找到画布中心。
            FVector2D Center(Canvas->ClipX * 0.5f, Canvas->ClipY * 0.5f);
    
            // 纹理维度一半偏移,使纹理中心和画布中心对齐。
            FVector2D CrossHairDrawPosition(Center.X - (CrosshairTexture->GetSurfaceWidth() * 0.5f), Center.Y - (CrosshairTexture->GetSurfaceHeight() * 0.5f));
    
            // 在中心点绘制准星。
            FCanvasTileItem TileItem(CrossHairDrawPosition, CrosshairTexture->Resource, FLinearColor::White);
            TileItem.BlendMode = SE_BLEND_Translucent;
            Canvas->DrawItem(TileItem);
        }
    }
  7. 在 Visual Studio 中保存 FPSHUD.hFPSHUD.cpp

  8. Solution Explorer 中找到 FPSProject

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

    BuildFPSProject.png

将 CPP HUD 类扩展为蓝图

现在即可将 CPP HUD 类扩展为蓝图。在 C++和蓝图 参考页面中可了解 C++ 类扩展为蓝图的更多内容。

  1. 右键单击 FPSHUD 类打开 C++ Class Actions 菜单。

    CPPClassActionsMenu.png

  2. 点击 Create Blueprint class based on FPSHUD 打开 Add Blueprint Class 对话菜单。

    CreateDerivedBPClass.png

  3. 将新蓝图类命名为“BP_FPSHUD”,选择蓝图文件夹,然后点击 Create Blueprint Class 按钮。

    AddBPClass.png

  4. 现在,Blueprints 文件夹中便拥有了一个新建的 BP_FPSHUD 蓝图类。

    AddedBPClass.png

  5. 关闭蓝图编辑器之前须保存 BP_FPSHUD 蓝图。

设置默认 HUD 类

  1. Edit 菜单中点击 Project Settings

  2. Project Settings 标签左侧的 Project 标题下点击 Maps & Modes

  3. Default HUD 下拉菜单中选择 BP_FPSHUD

    ChooseHUDClass.png

  4. 关闭 Project Settings 菜单。

  5. 返回并打开 BP_FPSHUD 蓝图编辑器。

  6. 点击蓝图编辑器 FPSHUD 部分的下拉菜单,选择准星纹理。

    SelectCrosshairTexture.png

  7. 最后,在关闭蓝图编辑器前保存 BP_FPSHUD 蓝图。

验证 HUD

  1. 点击关卡编辑器工具栏中的 Play 按钮。现在即可通过新增的准星进行瞄准。

    CrosshairsInGame.png

  2. 点击关卡编辑器中的 Stop 按钮即可退出 Play in Editor(PIE)模式。

完成分段代码

FPSProjectile.h

// 版权所有 1998-2017 Epic Games, Inc. 保留所有权利。

#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);

    // 发射物命中物体时调用的函数。
    void OnHit(class AActor* OtherActor, class UPrimitiveComponent* OtherComponent, FVector NormalImpulse, const FHitResult& Hit);

};

FPSProjectile.cpp

// 版权所有 1998-2017 Epic Games, Inc. 保留所有权利。

#include "FPSProject.h"
#include "FPSProjectile.h"

// 设置默认值
AFPSProjectile::AFPSProjectile()
{
    // 将此 actor 设为每帧调用 Tick()。不需要时可将此关闭,以提高性能。
    PrimaryActorTick.bCanEverTick = true;

    // 使用球体代表简单碰撞。
    CollisionComponent = CreateDefaultSubobject<USphereComponent>(TEXT("SphereComponent"));
    CollisionComponent->BodyInstance.SetCollisionProfileName(TEXT("Projectile"));
    CollisionComponent->OnComponentHit.AddDynamic(this, &AFPSProjectile::OnHit);

    // 设置球体的碰撞半径。
    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;

    // 3 秒后消亡。
    InitialLifeSpan = 3.0f;
}

// 游戏开始时或生成时调用。
void AFPSProjectile::BeginPlay()
{
    Super::BeginPlay();
}

// 每帧调用。
void AFPSProjectile::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);

}

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

// 发射物命中物体时调用的函数。
void AFPSProjectile::OnHit(AActor* OtherActor, UPrimitiveComponent* OtherComponent, FVector NormalImpulse, const FHitResult& Hit)
{
    if (OtherActor != this && OtherComponent->IsSimulatingPhysics())
    {
        OtherComponent->AddImpulseAtLocation(ProjectileMovementComponent->Velocity * 100.0f, Hit.ImpactPoint);
    }
}

FPSCharacter.h

// 版权所有 1998-2017 Epic Games, Inc. 保留所有权利。

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

FPSCharacter.cpp

// 版权所有 1998-2017 Epic Games, Inc. 保留所有权利。

#include "FPSProject.h"
#include "FPSCharacter.h"
#include "FPSProjectile.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(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()
{
    // 尝试发射物体。
    if (ProjectileClass)
    {
        // 获取摄像机变换。
        FVector CameraLocation;
        FRotator CameraRotation;
        GetActorEyesViewPoint(CameraLocation, CameraRotation);

        // 将 MuzzleOffset 从摄像机空间变换到世界空间。
        FVector MuzzleLocation = CameraLocation + FTransform(CameraRotation).TransformVector(MuzzleOffset);
        FRotator MuzzleRotation = CameraRotation;
        // 将准星稍微上抬。
        MuzzleRotation.Pitch += 10.0f; 
        UWorld* World = GetWorld();
        if (World)
        {
            FActorSpawnParameters SpawnParams;
            SpawnParams.Owner = this;
            SpawnParams.Instigator = Instigator;
            // 在枪口处生成发射物。
            AFPSProjectile* Projectile = World->SpawnActor<AFPSProjectile>(ProjectileClass, MuzzleLocation, MuzzleRotation, SpawnParams);
            if (Projectile)
            {
                // 设置发射物的初始轨道。
                FVector LaunchDirection = MuzzleRotation.Vector();
                Projectile->FireInDirection(LaunchDirection);
            }
        }
    }
}

FPSHUD.h

// 版权所有 1998-2017 Epic Games, Inc. 保留所有权利。

#pragma once

#include "GameFramework/HUD.h"
#include "FPSHUD.generated.h"

/**
 * 
 */
UCLASS()
class FPSPROJECT_API AFPSHUD : public AHUD
{
    GENERATED_BODY()

public:
    // HUD 的主绘制调用。
    virtual void DrawHUD() override;

protected:  
    // 这将在屏幕中央绘制。
    UPROPERTY(EditDefaultsOnly)
    UTexture2D* CrosshairTexture;   
};

FPSHUD.cpp

// 版权所有 1998-2017 Epic Games, Inc. 保留所有权利。

#include "FPSProject.h"
#include "FPSHUD.h"

void AFPSHUD::DrawHUD()
{
    Super::DrawHUD();

    if (CrosshairTexture)
    {
        // 找到画布中心。
        FVector2D Center(Canvas->ClipX * 0.5f, Canvas->ClipY * 0.5f);

        // 纹理维度一半偏移,使纹理中心和画布中心对齐。
        FVector2D CrossHairDrawPosition(Center.X - (CrosshairTexture->GetSurfaceWidth() * 0.5f), Center.Y - (CrosshairTexture->GetSurfaceHeight() * 0.5f));

        // 在中心点绘制准星。
        FCanvasTileItem TileItem(CrossHairDrawPosition, CrosshairTexture->Resource, FLinearColor::White);
        TileItem.BlendMode = SE_BLEND_Translucent;
        Canvas->DrawItem(TileItem);
    }
}

恭喜!您已学会:

✓ 在游戏中添加发射物
✓ 实现射击
✓ 设置发射物碰撞和生命周期
✓ 使发射物和世界场景形成交互
✓ 在视口中添加准星

下一部分将学习如何为角色设置动画。