虚幻四引擎学习之C++编程(一)

最近因为ACM省赛以及参加笔试面试等原因,许久没有更新博客,由于实习公司的原因,最近开始看虚幻四引擎的官方文档,这篇博客是对官方文档中C++编程部分第一人称FPS部分的代码分析和总结。

一、GameMode

GameMode又称游戏模式(其实就是翻译过来),它存在的意义是制定游戏的规则,比如龟兔赛跑,其规则就是谁先到达终点谁就获胜,它仅存在于服务器上。

在官方文档的第一人称FPS教程部分,有稍微提到过GameMode,代码如下:
FPSProjectGameMode.h

#pragma once

#include "GameFramework/GameMode.h"
#include "FPSProjectGameMode.generated.h"

/**
 * GameMode类用于构建游戏规则
 */
UCLASS()
class FPSPROJECT_API AFPSProjectGameMode : public AGameMode
{
    GENERATED_BODY()

    virtual void StartPlay() override;//重写AGameMode基类的虚函数StartPlay


};

FPSProjectGameMode.cpp

#include "FPSProject.h"
#include "FPSProjectGameMode.h"


void AFPSProjectGameMode::StartPlay()
{
    Super::StartPlay();//Super类即为AGameMode类

    if (GEngine)
    {
        // 显示调试信息五秒。 
        // -1“键”值(首个参数)说明我们无需更新或刷新此消息。
        GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Yellow, TEXT("Hello World, this is FPSGameMode!"));

    }
}

二、Pawn与Character

Pawn可由玩家或 AI 控制的所有 Actors 的基础类。而Actor可以简单认为是游戏世界中的任何可以动作的物体(我暂时这么理解)。Character是类人的Pawn,它自带许多组件,比如CharacterMovementComponent(移动组件)、CapsuleComponent(胶囊体组件)和SkeletalMesh(骨骼网格组件)。

官方教程在这里给出了FPS基本的动作比如:移动、跳跃、开火)
代码如下:
FPSCharacter.h

#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;//重写Actor的BeginPlay函数

    // 每帧调用。
    virtual void Tick(float DeltaSeconds) override;//重写Actor的Tick函数

    // 调用后将功能绑定到输入。
    virtual void SetupPlayerInputComponent(UInputComponent* InputComponent) override;//重写Character的SetupPlayerInputComponent函数

    // 处理前后移动的输入。UFUNCTION表示将该函数注册到蓝图中
    UFUNCTION()
        void MoveForward(float Value);

    // 处理左右移动的输入。
    UFUNCTION()
        void MoveRight(float Value);

    // 按下按键时设置跳跃标记。
    UFUNCTION()
        void StartJump();

    // 松开按键时清除跳跃标记。
    UFUNCTION()
        void StopJump();

    // 处理开火的函数。
    UFUNCTION()
        void Fire();

    // FPS 摄像机。UPROPERTY表示将一个类成员注册到蓝图中
    UPROPERTY(VisibleAnywhere)
        UCameraComponent* FPSCameraComponent;//定义一个UCameraComponent——相机组件

    // 第一人称模型(手臂),仅对拥有玩家可见。
    UPROPERTY(VisibleDefaultsOnly, Category = Mesh)
        USkeletalMeshComponent* FPSMesh;//定义一个USkeletalMeshComponent——骨骼网格组件

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

    // 生成的发射物类。
    UPROPERTY(EditDefaultsOnly, Category = Projectile)
        TSubclassOf<class AFPSProjectile> ProjectileClass;//AFPSProjectile类及其子类的类型引用,在蓝图中会使其可以通过下拉菜单来详细指定其类型
};

FPSCharacter.cpp

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

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

    // 创建一个第一人称摄像机组件。
    //CreateDefaultSubobject创建一个组件或者子对象
    FPSCameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("FirstPersonCamera"));
    // 将摄像机组件附加到胶囊体组件。
    //Character类在Pawn基础上含有CharacterMovementComponent、CapsuleComponent和SkeletalMesh组件
    //GetCapsuleComponent得到character类中胶囊提组件的指针
    FPSCameraComponent->AttachTo(GetCapsuleComponent());
    // 将摄像机放置在眼睛上方不远处。
    //BaseEyeHeight是Pawn类中的成员
    //SetRelativeLocation设置局部空间中的位置
    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是character类中的函数,返回绑定在character类(基类)中的骨骼模型
    GetMesh()->SetOwnerNoSee(true);
}

// 游戏开始时或生成时调用。
void AFPSCharacter::BeginPlay()
{
    Super::BeginPlay();//调用基类的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)
{
    // 明确哪个方向是“前进”,并记录玩家试图向此方向移动。
    //Controller是Pawn定义的成员即为控制该Pawn的控制器,GetControlRotation获得控制器的旋转角,GetScaledAxis的到某一个轴的值(这里是矩阵的第一列即X轴)
    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;
        //如果存在Controller或它的Pawn,这个函数则返回Controller或他的Pawn的视角。基本上,它会返回玩家从哪个位置和方向观看。
        GetActorEyesViewPoint(CameraLocation, CameraRotation);

        // 将 MuzzleOffset 从摄像机空间变换到世界空间。
        FVector MuzzleLocation = CameraLocation + FTransform(CameraRotation).TransformVector(MuzzleOffset);
        FRotator MuzzleRotation = CameraRotation;
        // 将准星稍微上抬。
        MuzzleRotation.Pitch += 10.0f;
        UWorld* World = GetWorld();
        if (World)
        {
            //FActorSpawnParameters是world类中的结构体,表示游戏世界中的一个pawn。。。(大概是这样)
            FActorSpawnParameters SpawnParams;
            SpawnParams.Owner = this;
            //Instigator是可以对pawn造成伤害的actor
            SpawnParams.Instigator = Instigator;
            // 在枪口处生成发射物。
            //SpawnActor用来创建Actor类型的物体(物体类型,生成地点,生成方向,和世界中的一个物体??)
            AFPSProjectile* Projectile = World->SpawnActor<AFPSProjectile>(ProjectileClass, MuzzleLocation, MuzzleRotation, SpawnParams);
            if (Projectile)
            {
                // 设置发射物的初始轨道。
                FVector LaunchDirection = MuzzleRotation.Vector();
                Projectile->FireInDirection(LaunchDirection);
            }
        }
    }
}

三、发射物的实现

这里,因为发射物是一个不需要AI或玩家控制的物体,但是其也会进行运动,所以该类继承自Actor类即可。
我们只需计算它的运动状态(碰撞啊之类的)即可。
FPSProjectile.h

#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(UPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComponent, FVector NormalImpulse, const FHitResult& Hit);

};

FPSProjectile.cpp

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

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

    // 使用球体代表简单碰撞。
    CollisionComponent = CreateDefaultSubobject<USphereComponent>(TEXT("SphereComponent"));
    //BodyInstance存储这个组件的物理信息,并持有一个刚体
    CollisionComponent->BodyInstance.SetCollisionProfileName(TEXT("Projectile"));
    //OnComponentHit检测碰撞,当发生碰撞后调用某个函数
    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 = 10.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;
}

// 发射物命中物体时调用的函数。
//UPrimitiveComponent具有某种形式的表现,如网格,粒子等可见的东西;
//HitComponent撞击物体
//OtherActor被撞物体
//OtherComponent被撞物体的组件
//FHitResult存储碰撞结果(发出射线进行碰撞检测然后存储结果)
void AFPSProjectile::OnHit(UPrimitiveComponent* HitComponent,AActor* OtherActor, UPrimitiveComponent* OtherComponent, FVector NormalImpulse, const FHitResult& Hit)
{
    //IsSimulatingPhysics判断是否遵循物理现象
    //注意父组件与子组件是两个实体,如果有二者满足碰撞条件,注意不要让二者重叠,
    //否则一旦游戏中产生这个Actor,两个组件就会以一定的速度相互排斥开来。
    //所以不要随意开启组件的simulate physics并且注意各个组件之间的关系。
    //而这里添加OtherActor != this这句话也可以防止该实体自身的组件间的发生碰撞
    if (OtherActor != this && OtherComponent->IsSimulatingPhysics())
    {
        //ImpactPoint碰撞点
        //AddImpulseAtLocation(冲量,位置)——在某一位置增加一个冲量
        OtherComponent->AddImpulseAtLocation(ProjectileMovementComponent->Velocity * 1000000.0f, Hit.ImpactPoint);
    }
}

四、HUD准星的实现

FPSHUD.h

#pragma once

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

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

protected:
    // 这将在屏幕中央绘制。
    //UTexture2D——2D纹理类型
    UPROPERTY(EditDefaultsOnly)
        UTexture2D* CrosshairTexture;
public:
    // HUD 的主绘制调用。
    virtual void DrawHUD() override;


};

FPSHUD.cpp

#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);
    }
}
  • 2
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值