3.5 - 在视口中添加准星

学习在第一人称射击游戏项目的视口中添加准星的方法。

Windows
MacOS
Linux

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

导入准星资源

开始之前先从以下链接下载并提取样本图像:

  1. 在内容浏览器的文件框中点击右键打开 导入资源(Import Asset) 对话框。

  2. 点击 导入到/游戏(Import to /Game) 打开 导入(Import) 对话框。

    RightClickImport.png `

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

  4. 点击 打开(Open) 开始将图像文件导入项目。

  5. 点击 保存(Save) 按钮保存导入的图像。

新增一个HUD类

  1. 在“文件(File)”菜单中,选择 新建C++类(New C++ Class...) 来选择新的父类。

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

    ChooseParentHUDClass.png

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

    NameHUDClass.png

  4. 解决方案浏览器 中找到 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++类操作(C++ Class Actions) 菜单。

    CPPClassActionsMenu.png

  2. 点击 基于FPSHUD创建蓝图类(Create Blueprint class based on FPSHUD) 打开 添加蓝图类(Add Blueprint Class) 对话菜单。

    CreateDerivedBPClass.png

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

    AddBPClass.png

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

    AddedBPClass.png

  5. 关闭蓝图编辑器前务必保存 BP_FPSHUD 蓝图。

设置默认HUD类

  1. 编辑(Edit) 菜单中点击 项目设置(Project Settings)

  2. 项目设置(Project Settings) 标签左侧的 项目(Project) 标题下点击 地图与模式(Maps & Modes)

  3. 默认HUD(Default HUD) 下拉菜单中选择 BP_FPSHUD

    ChooseHUDClass.png

  4. 关闭 项目设置(Project Settings) 菜单。

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

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

    SelectCrosshairTexture.png

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

验证HUD

  1. 点击“关卡编辑器工具栏(Level Editor Toolbar)”中的 运行(Play) 按钮。

    CrosshairsInGame.png

  2. 点击“关卡编辑器(Level Editor)”中的 停止(Stop) 按钮即可退出PIE模式。

完成的分段代码

FPSProjectile.h

// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.

#pragma once

#include "GameFramework/Actor.h"
#include "FPSProjectile.generated.h"

UCLASS()
class FPSPROJECT_API AFPSProjectile : public AActor
{
    GENERATED_BODY()

public: 
    // 设置该Aactor属性的默认值。
    AFPSProjectile();

protected:
    // 游戏开始时或生成时调用
    virtual void BeginPlay() override;

public:
    // 每帧调用。
    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(UPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComponent, FVector NormalImpulse, const FHitResult& Hit);

};

FPSProjectile.cpp

// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.

#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

// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.

#pragma once

#include "GameFramework/Character.h"
#include "FPSCharacter.generated.h"

UCLASS()
class FPSPROJECT_API AFPSCharacter : public ACharacter
{
    GENERATED_BODY()

public:
    // 设置该角色属性的默认值。
    AFPSCharacter();

protected:
    // 游戏开始时或生成时调用
    virtual void BeginPlay() override;

public:
    // 每帧调用。
    virtual void Tick( float DeltaSeconds ) override;

    // 调用以将功能绑定到输入。
    virtual void SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) 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

// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.

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

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

    // 创建一个第一人称相机组件。
    FPSCameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("FirstPersonCamera"));
    // 将相机组件附加到胶囊体组件。
    FPSCameraComponent->SetupAttachment(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->SetupAttachment(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* PlayerInputComponent)
{
    Super::SetupPlayerInputComponent(PlayerInputComponent);

    // 设置“移动”绑定。
    PlayerInputComponent->BindAxis("MoveForward", this, &AFPSCharacter::MoveForward);
    PlayerInputComponent->BindAxis("MoveRight", this, &AFPSCharacter::MoveRight);

    // 设置“观察”绑定。
    PlayerInputComponent->BindAxis("Turn", this, &AFPSCharacter::AddControllerYawInput);
    PlayerInputComponent->BindAxis("LookUp", this, &AFPSCharacter::AddControllerPitchInput);

    // 设置“动作”绑定。
    PlayerInputComponent->BindAction("Jump", IE_Pressed, this, &AFPSCharacter::StartJump);
    PlayerInputComponent->BindAction("Jump", IE_Released, this, &AFPSCharacter::StopJump);
    PlayerInputComponent->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

// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.

#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

// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.

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

恭喜!你已经学会了如何:

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

下一部分将学习设置角色动画的方法。

Select Skin
Light
Dark

欢迎来到全新虚幻引擎4文档站!

我们正在努力开发新功能,包括反馈系统,以便您能对我们的工作作出评价。但它目前还未正式上线。如果您对此页面有任何意见与在使用中遭遇任何问题,请前往文档反馈论坛告知我们。

新系统上线运行后,我们会及时通知您的。

发表反馈意见