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

来源:互联网 发布:c语言入门视频教程 编辑:程序博客网 时间:2024/05/16 10:31

最近因为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);    }}
原创粉丝点击