【UE4 C++】第三人称人物创建的基本代码(上)

这个是我的知乎账号,以后文章更新在这里啦,希望大家能够对我的文章多提点意见,指出我的错误,非常感谢!

效果图

【UE4 C++】第三人称人物创建的基本代码(上)_第1张图片
【UE4 C++】第三人称人物创建的基本代码(上)_第2张图片
【UE4 C++】第三人称人物创建的基本代码(上)_第3张图片
【UE4 C++】第三人称人物创建的基本代码(上)_第4张图片
【UE4 C++】第三人称人物创建的基本代码(上)_第5张图片
【UE4 C++】第三人称人物创建的基本代码(上)_第6张图片
【UE4 C++】第三人称人物创建的基本代码(上)_第7张图片

前言

本篇文章将会教你如何写出关于自己的第三人称人物,如何绑定角色,操控角色的移动。

第三人称人物代码实现过程

  • 创建C++文件

找到C++ Classes 文件夹,右键点击 New C++ Classes, 点击我们要继承的类:Character,点击Next, 命名之后点击 Create Class(在这里我的类名就是默认的名字:MyCharacter)。

创建之后,我们会发现VS已经给我们写好了一些东西。
我们对系统默认提供的代码进行分析:

<.h文件>
CoreMinimal.h 包含了我们经常使用的一些类型,例如:FString、FName、TArray等。感觉就是类似于我们刚学C++的时候接触到的 iostream 头文件一样。
GameFramework/Character.h 引入父类头文件,包含了父类的功能。
MyCharacter.generated.h" 引入反射系统,为反射提供一些功能,例如我们下面会看到的一些宏:UCLASS()、UPROPERTY等。需要注意的是,这个头文件要放在我们声明头文件的最后一行,否者编译器会报错。(我不知道为什么,如果有大佬看到了这个,能在评论区告诉我嘛?)

接下来就是类声明语法:

UCLASS([specifier, specifier, ...], [meta(key=value, key=value, ...)])
class ClassName : public ParentName
{
    GENERATED_BODY()
}

让编译器生成这个类反射所需要的代码,存入的位置就是我引入头文件的位置。
GENERATED_BODY() 宏必须被放置在类体的最前方。

这个前面还会有一个_API的定义, 这个是一个导入导出的一个标志,那么也就说明这个类可以在其他的模块中使用。

构造函数就不用说了,和C++的类是一样的性质,
virtual void BeginPlay() override; 就是调用这个类开始的时候调用的函数。
virtual void Tick(float DeltaTime) override;每一帧都会用到这个函数。
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;这个就是输入设置的一个函数,我们人物的运动与键盘的绑定就是在这个函数里面实现的。

<.cpp文件>
构造函数中PrimaryActorTick.bCanEverTick = true;说明每一帧都要调用Tick()函数,如果不需要的话,改成false即可。

  • .h文件中函数的声明

void MoveForward(float Value);控制人物的向前向后操作,当 V a l u e < 0 Value < 0 Value<0的时候就是向后。

void MoveRight(float Value);控制人物的向左向右操作,当 V a l u e < 0 Value < 0 Value<0的时候就是向左。

声明摇臂组件,相比于没有加上这个摇臂的优势在于,当我们的摄像机要撞墙的时候就会缩短与人物之间的距离,让我们视角更加合理,镜头移动不会那么死板。

UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera)
	class USpringArmComponent* CameraBoom;

声明摄像机组件

UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera)
	class UCameraComponent* FollowCamera;

在这个里面我们会发现多出来一个东西UPROPERTY(),与UCLASS()类似,UCLASS是将C++类变成UE4特有功能的类,那么这个就是变成UE4特有功能的变量(比如说垃圾收集),括号中可以加一些属性说明符。
基本格式为:

UPROPERTY([specifier, specifier, ...], [meta(key=value, key=value, ...)])
	Type VariableName;

我们的代码最终都会变成二进制码,就根本不知道谁是谁,这时候就需要反射,这些宏都是来帮助建立反射的,有了反射的话,我们就能知道某个类的名字以及他的一些属性。
写C++的时候我们需要自己在和合适的时候释放内存,忘记释放了就会造成内存泄漏,释放多了那麻烦就更大了,所以会很不友好,UE4它就帮我们实现了这个垃圾收集(GC)功能。
UE4 中还有一个工具就是 Unreal Header Tool (UHT), 这个工具就是作为我们C++代码和蓝图的一个桥梁,例如我们的宏属性说明符中包含BlueprintReadWrite 时,我们就可以在蓝图中对这个变量进行修改和查询。

UCLASS 里面的一些属性说明符我不太熟悉,UPROPERTY中一些属性说明符我和大家讲一下吧。

属性说明符 作用
BlueprintReadOnly 该变量放在在蓝图中只能看不能修改
BlueprintReadWrite 蓝图中可以查看和修改, 也就是我们在蓝图编辑中的Set 和 Get。
Category=“TopCategory|SubCategory | …” 指定在蓝图编辑器中的属性类别,将变量区分开来,便于我们查找
EditAnywhere 可以通过属性窗口在原型和实例上进行编辑,原型属性就是双击文件之后能够进行蓝图编辑的那个窗口看到的细节面板中的东西,那么实例属性就是我们点击实例之后细节面板中的属性。
EditDefaultsOnly 只能在原型上面修改属性。
EditInstanceOnly 只能在实例上修改属性
VisibleAnywhere 在所有属性窗口中都可见,无法编辑
VisibleDefaultsOnly 只能在原型的属性窗口可见, 无法编辑
VisibleInstanceOnly 只能在实例的窗口上可见, 无法编辑

它后面还有一个元数据说明符,暂时不知道他的用处在哪里。

  • .cpp文件中的函数实现

<构造函数的实现>

  1. 创建摇臂

添加头文件,因为摇臂组件是属于游戏框架里面的东西,所以我们要在游戏框架里面寻找它。
(个人觉得没必要记,有些东西用多了自然就熟了,但是前提是不是复制粘贴的那种。)
#include "GameFramework/SpringArmComponent.h"

CameraBoom = CreateDefaultSubobject(TEXT("CameraBoom"));
创建一个摇臂的实例,并且给他一个名字,使用宏TEXT("")可以将其变成FName变量。

CameraBoom->SetupAttachment(RootComponent);
设置依附关系,将摇臂依附在根组件上。

CameraBoom->TargetArmLength = 300.0f;
设置摇臂与我们依附的组件的距离。

CameraBoom->bUsePawnControlRotation = true;
只要它是能动的物体,都会有控制器来进行操控,我们设置摇臂的旋转根据控制器的旋转来。

  1. 创建摄影机

添加头文件,摄影机组件自然是摄影机里面的,所以我们要在摄影机中寻找他。
#include "Camera/CameraComponent.h"

FollowCamera = CreateDefaultSubobject(TEXT("FollowCamera"));
创建一个摄影机实例,创建一个实例一般都是 CreateDefaultSubobject<>()的形式。

FollowCamera->SetupAttachment(CameraBoom, USpringArmComponent::SocketName);
将摄影机依附在摇臂的插槽上面。

FollowCamera->bUsePawnControlRotation = false;
我们摄影机不用跟着控制器来移动,只要保持和摇臂一样就行。

  1. 设置碰撞

添加头文件:胶囊组件是属于组件中的,所以我们要在组件中寻找它。
#include "Components/CapsuleComponent.h"

我们要给我们的角色添加碰撞,要不然就会导致穿模的现象出现。
因为我们只是新手,碰撞体没必要搞那么复杂,给一个胶囊体就可以了。
GetCapsuleComponent()->InitCapsuleSize(42.f, 96.f);

  1. 设置角色移动

添加头文件:因为角色移动是属于游戏框架中的内容,所以我们在游戏框架中寻找它。
#include "GameFramework/CharacterMovementComponent.h"

GetCharacterMovement()->bOrientRotationToMovement = true;
我们需要设置我们人物的朝向要和我们运动的方向一致,要不然玩起来就很变扭。

GetCharacterMovement()->RotationRate = FRotator(0.f, 540.f, 0.f);
(那旋转速度怎么样呢?程序员是很注重细节的,一点都不能放过。) 我们旋转的时候是绕着Z轴旋转的,所以我们只需要设置Z轴的转速就行。
其中出现了一个新东西:FRotator。 它是存储三维变量的结构体,Pitch,Yaw, Roll, 分别代表的是 Y, Z, X。(Pitch代表抛,我们抛物线都是从Y轴的某个点开始抛,代表Y,Yaw,发音:压,那肯定是从Z轴的某个点压到它下面的位置,代表Z, Roll,原地打滚,原地打滚肯定是在X上运动,代表X。储存顺序就是抛了(Y)一个东西压(Z)到了一个球开始滚(X)。)

GetCharacterMovement()->JumpZVelocity = 600.f;
角色会跳跃吧,那一次跳多高呢?这个就是设置我们角色最大能跳多高。(这么说我运动速度还没设置,,,逃())

GetCharacterMovement()->AirControl = 0.2f;
设置能在空中掌控速度的程度。

  1. 设置角色不跟随控制器旋转
bUseControllerRotationPitch = false;
bUseControllerRotationRoll = false;
bUseControllerRotationYaw = false;

<向前后运动函数实现>

if (Controller && Value) {
	const FRotator Rotation = Controller->GetControlRotation();
	const FRotator YawRotation(0, Rotation.Yaw, 0);
	const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
	
	AddMovementInput(Direction, Value);
}

首先我们要获得它的方向,也就是Dirction,(FVector是储存空间向量的结构体),如何获得呢?先将控制器的Z轴拿到,然后根据这个Z轴获得一个旋转矩阵,得到他X方向的单位向量。
然后给这个方向一个值,放入我们移动输入函数中即实现了前或者后移动。

<向左右移动函数实现>

if (Controller && Value) {
	const FRotator Rotation = Controller->GetControlRotation();
	const FRotator YawRotation(0, Rotation.Yaw, 0);

	const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
	AddMovementInput(Direction, Value);
}

和上面那个函数差不多,只是方向变了,要得到Y轴的空间向量。

<绑定按键>

首先先按照这个步骤进行设置:
【UE4 C++】第三人称人物创建的基本代码(上)_第8张图片【UE4 C++】第三人称人物创建的基本代码(上)_第9张图片
【UE4 C++】第三人称人物创建的基本代码(上)_第10张图片

然后再回到函数中,进行绑定。

void ARole::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);
	check(PlayerInputComponent); // 检查是否为空指针

	// 空格绑定Action, 按一次跳跃一次,而不是持续跳。 这个是Character类中有的。
	PlayerInputComponent->BindAction("Jump", IE_Pressed, this, &ACharacter::Jump);
	PlayerInputComponent->BindAction("Jump", IE_Released, this, &ACharacter::StopJumping);

	// 绑定Axis, 绑定向前向后移动,这个是持续性的。绑定的是我们自己刚设置的函数。
	PlayerInputComponent->BindAxis("MoveForward", this, &ARole::MoveForward);
	PlayerInputComponent->BindAxis("MoveRight", this, &ARole::MoveRight);

	// 绑定移动方向, 这个是Pawn类中有的。
	PlayerInputComponent->BindAxis("Turn", this, &APawn::AddControllerYawInput);
	PlayerInputComponent->BindAxis("LookUp", this, &APawn::AddControllerPitchInput);
}

为角色选择人物模型

打开UE4,右击我们创建的C++角色文件
【UE4 C++】第三人称人物创建的基本代码(上)_第11张图片
创建以它为父类的蓝图。
【UE4 C++】第三人称人物创建的基本代码(上)_第12张图片
选择任务模型完毕。

玩家操控角色

回到C++ Classes文件夹,会看到有一个GameMode,右击创建蓝图。
【UE4 C++】第三人称人物创建的基本代码(上)_第13张图片
打开蓝图。
【UE4 C++】第三人称人物创建的基本代码(上)_第14张图片
Default Pawn Class 选择我们刚刚创建的人物BP。
回到主界面,打开世界设置,GameMode选择我们刚创建的游戏模式,然后开始游戏就是我们自己建造的这个人物啦。

但是这个人物还处于很呆板的样子,下一篇文章我将告诉你如何绑定人物动画~下章见!

技巧分享

头文件实在是太难记了,老是容易搞混怎么办?
推荐使用visual Assist X插件,首先按住Ctrl + 鼠标左键,然后他就会自动帮我们跳转到头文件的位置,这里就是我们要的东西。

你可能感兴趣的:(游戏开发,c++,游戏开发,游戏,visualstudio)