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

xiaoxiao2021-02-28  77

最近因为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); } }
转载请注明原文地址: https://www.6miu.com/read-62697.html

最新回复(0)