虚幻4游戏引擎C++编程官网例程解析

  • 官网例程
  • 源代码
  • FloatingActor.h
  • FloatingActor.cpp
  • 内容说明
  • pragma once
  • 头文件
  • UClass宏
  • AActor类
  • Private私有部分
  • Public公有部分
  • protected保护部分
  • 源文件
  • 构造函数
  • CreateDefaultSubobject函数
  • TEXT()宏
  • SetupAttachment函数
  • ConstructorHelpers::FObjectFinder类
  • SetStaticMesh函数
  • SetRelativeLocation函数
  • FVector类
  • BeginPlay函数
  • Super类
  • Tick函数
  • 使物体在指定区间来回移动的巧妙办法:
  • FRotator类
  • SetActorLocationAndRotation函数


官网例程

创建首个代码项目,并添加新的C++类 官方源代码里,创建了一个叫FloatingActor的类。这个类的功能是创建一个可以旋转且上下浮动的模型

源代码

FloatingActor.h

// 版权所有 1998-2019 Epic Games, Inc。保留所有权利。

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Components/StaticMeshComponent.h"
#include "FloatingActor.generated.h"

UCLASS()
class QUICKSTART_API AFloatingActor : public AActor
{
    GENERATED_BODY()

public: 
    // 设置此Actor属性的默认值
    AFloatingActor();

    UPROPERTY(VisibleAnywhere)
    UStaticMeshComponent* VisualMesh;

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

public: 
    // 逐帧调用
    virtual void Tick(float DeltaTime) override;

};

FloatingActor.cpp

// 版权所有 1998-2019 Epic Games, Inc。保留所有权利。

#include "FloatingActor.h"

// 设置默认值
AFloatingActor::AFloatingActor()
{
    // 将此Actor设为逐帧调用Tick()。如无需此功能,可关闭以提高性能。
    PrimaryActorTick.bCanEverTick = true;

    VisualMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh"));
    VisualMesh->SetupAttachment(RootComponent);

    static ConstructorHelpers::FObjectFinder<UStaticMesh> CubeVisualAsset(TEXT("/Game/StarterContent/Shapes/Shape_Cube.Shape_Cube"));

    if (CubeVisualAsset.Succeeded())
    {
        VisualMesh->SetStaticMesh(CubeVisualAsset.Object);
        VisualMesh->SetRelativeLocation(FVector(0.0f, 0.0f, 0.0f));
    }
}

// 游戏开始时或生成时调用
void AFloatingActor::BeginPlay()
{
    Super::BeginPlay();

}

// 逐帧调用
void AFloatingActor::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);

    FVector NewLocation = GetActorLocation();
    FRotator NewRotation = GetActorRotation();
    float RunningTime = GetGameTimeSinceCreation();
    float DeltaHeight = (FMath::Sin(RunningTime + DeltaTime) - FMath::Sin(RunningTime));
    NewLocation.Z += DeltaHeight * 20.0f;       //Scale our height by a factor of 20
    float DeltaRotation = DeltaTime * 20.0f;    //Rotate by 20 degrees per second
    NewRotation.Yaw += DeltaRotation;
    SetActorLocationAndRotation(NewLocation, NewRotation);
}

内容说明

pragma once

#pragma once //告诉编译器,本头文件只引用一次,保证头文件只被编译一次

头文件

#include "CoreMinimal.h" //头文件包含一套来自UE4核心编程环境的普遍存在类型
#include "GameFramework/Actor.h"  //UE4 Actor类的声明头文件
#include "Components/StaticMeshComponent.h" //UE4 静态网格体组件头文件
#include "FloatingActor.generated.h" 
/*
	从UnrealHeaderTool导出的生成代码
	UE4将生成所有反射数据并放入该文件中。
	必须将该文件作为声明类型的标头文件中的最后一个包含语句,将其包含进去,不然会报错
*/
UCLASS() 
class QUICKSTART_API AFloatingActor : public AActor
{};

UClass宏

UCLASS 宏为 UObject 提供一个对 UCLASS 的引用,描述其是基于虚幻引擎的类型

AActor类

AActor类是当前类的父类。Actor是所有能被放置于关卡中的对象的基类。Actor is the base class for an Object that can be placed or spawned in a level.(Unreal Engine Document原文)

private:
    GENERATED_BODY()

public: 
    AFloatingActor(); //类默认构造函数

    UPROPERTY(VisibleAnywhere)
    UStaticMeshComponent* VisualMesh;
    // 逐帧调用函数,
    virtual void Tick(float DeltaTime) override;
    
protected:
    // 游戏开始时或生成时调用
    virtual void BeginPlay() override;

Private私有部分

GENERATED_BODY()宏用来产生某些预设方法。其产生的档案会放在$YouProject\Intermediate\Build\Win64\UE4\Inc\YouProject\xxx.generated.h里。

GENERATED_BODY()宏GENERATED_UCLASS_BODY()宏的区别:

  • 在4.6版本之前使用GENERATED_UCLASS_BODY() ,4.6版本之后使用GENERATED_BODY()
  • GENERATED_BODY()宏与构造函数相关 GENERATED_BODY macro参考资料

Public公有部分

AFloatingActor()函数是当前类的构造函数,函数名称与类名称相同,实现部分在cpp文件中。

UPROPERTY(VisibleAnywhere) UStaticMeshComponent* VisualMesh
这是一个类型是UStaticMeshComponent(静态网格体组件,俗称模型),叫做VisualMesh的对象指针,个人认为它叫做pVIsualMesh更合理一些。
UPRORERTY(VisibleAnywhere)宏将变量公开到蓝图和虚幻编辑器

参数

作用

VIsibleAnywhere

任何地方可见

AdvancedDisplay

属性在“详细信息”面板的高级下拉列表中

EditAnywhere

任何地方可编辑

Category = “CategoryPath”

在详细信息中显示的属性的类别,使用管道符定义嵌套层级

BluprintReadOnly

设置为蓝图只读,蓝图中只提供变量的get函数

BlueprintReadWrite

设置为蓝图读写,在蓝图中为被修饰的变量提供 Get 和 Set 函数

virtual void TIck(DeltaTIme) override函数
Tick的意思是滴答,在游戏代码中代表每帧执行一次的函数。参数为DeltaTime,类型为单精浮点,表示和上一帧之间的间隔。函数的返回值为空。virtual关键词修饰了函数是个虚函数,运行时会覆盖父类的TIck函数。override关键字确保该函数为虚函数并覆写来自基类的虚函数,在派生类的成员函数中使用override时,如果基类中无此函数,或基类中的函数并不是虚函数,编译器会给出相关错误信息。

protected保护部分

virtual void BeginPlay() override
类实例化生成时调用一次

源文件

#include "FloatingActor.h" //引用头文件

构造函数

//构造函数
AFloatingActor::AFloatingActor()
{
   // 将此Actor设为逐帧调用Tick()。如无需此功能,可关闭以提高性能。
   PrimaryActorTick.bCanEverTick = true;


   VisualMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh"));
   VisualMesh->SetupAttachment(RootComponent);

   static ConstructorHelpers::FObjectFinder<UStaticMesh> CubeVisualAsset(TEXT("/Game/StarterContent/Shapes/Shape_Cube.Shape_Cube"));

   if (CubeVisualAsset.Succeeded())
   {
       VisualMesh->SetStaticMesh(CubeVisualAsset.Object);
       VisualMesh->SetRelativeLocation(FVector(0.0f, 0.0f, 0.0f));
   }
}
CreateDefaultSubobject函数

功能:
该函数是个模板函数,用于创建组件或子对象,然后返回指向新建组件内存区域的指针
原型:

/**
	 * 创建一个组件或子对象.
	 * @param	TReturnType			函数返回类型
	 * @param	SubobjectName		新组件名称
	 * @param	bTransient			如果将组件分配给瞬态属性,则为True。
	 * 								这不会使组件本身瞬态,但会阻止其继承父默认值,默认为false
	 */
	template<class TReturnType>
	TReturnType* CreateDefaultSubobject(FName SubobjectName, bool bTransient = false)
	{
		UClass* ReturnType = TReturnType::StaticClass();
		return static_cast<TReturnType*>(CreateDefaultSubobject(
			SubobjectName, 
			ReturnType, 
			ReturnType, 
			/*bIsRequired =*/ true, 
			bTransient));
	}

有关其中的TReturnType::StaticClass()说明:

StaticsClass()实质上检索了所述类型的反射数据,它是虚幻引擎内置的反射系统的一部分。 当一个类包含UClass(),GENERATED_UCLASS_BODY()等时,这些宏将使用Unreal Header Tool将在编译时处理的反射系统。调用StaticClass()函数仅使用instance->GetClass()函数返回UObject实例的类型。

Messiah游戏引擎 游戏引擎官网_游戏

TEXT()宏

功能:
将原本的字符串最前端加上L,将原本多字节字符转换为统一的Unicode编码,根据平台的不同会在字符串前面加u或者L,宏代码中“##“表示连接两个字符串
注意:宏替换是在预编译阶段,此时的字符串和真正的字符串是有差异的,TEXT(“Mesh”)会变成L"Mesh"或者u"Mesh",而不是"LMesh"或者"uMesh"。
原型:

// 如果我们没有针对TEXT宏的特定于平台的定义,请立即进行定义。
#if !defined(TEXT) && !UE_BUILD_DOCS
	#if PLATFORM_TCHAR_IS_CHAR16
		#define TEXT_PASTE(x) u ## x
	#else
		#define TEXT_PASTE(x) L ## x
	#endif
		#define TEXT(x) TEXT_PASTE(x)
#endif

CSDN的代码着色有点问题,下面这张是VS里的截图

Messiah游戏引擎 游戏引擎官网_Messiah游戏引擎_02

SetupAttachment函数

功能:将调用的类附加到指定组件下
原型:

/** 
	* 初始化注册组件时要附加的所需的Attach Parent和SocketName。
	* 通常打算从其所属Actor的构造函数中调用,
	* 并且在未注册组件时,应优先于AttachToComponent进行调用
	* @param  InParent				依附到的父组件
	* @param  InSocketName			要附加到父项上的可选套接字,默认NAME_None
	*/
	void SetupAttachment(USceneComponent* InParent, FName InSocketName = NAME_None);

Messiah游戏引擎 游戏引擎官网_c++_03

ConstructorHelpers::FObjectFinder类
static ConstructorHelpers::FObjectFinder<UStaticMesh> CubeVisualAsset(TEXT("/Game/StarterContent/Shapes/Shape_Cube.Shape_Cube"));

   if (CubeVisualAsset.Succeeded())
   {
       pVisualMesh->SetStaticMesh(CubeVisualAsset.Object);
       pVisualMesh->SetRelativeLocation(FVector(0.0f, 0.0f, 0.0f));
   }

顾名思义,这是一个帮助寻找资源的类。UStaticMesh表示要寻找的是静态网格体。添加这行代码会让FloatingActor拥有默认的静态网格体,当然也可以在属性菜单里修改。调用的构造函数有一个Unicode字符串参数,传递在磁盘上的资源路径。创建出来的变量名叫CubeVisualAsset,是一个static变量,这里有个重要的点,为什么要让这个变量声明称static?
static是静态局部变量:用于函数体内部修饰变量,这种变量的生存期长于该函数,变量存在于全局区,变量的生命周期是定义后直到程序结束运行,而不是这个函数返回时就释放。我们需要让这个Cube在游戏运行时一直显示,但这个变量又是在函数内部创建的,这时候就需要使用static修饰,来确保变量的内容一直存在。

原型:

struct COREUOBJECT_API ConstructorHelpers
{
public:
	template<class T>
	struct FObjectFinder : public FGCObject
	{
		T* Object; //这个结构不禁让我联想到string
		...
	};
	...
};

接下来就是将创建的资源传递给pVIsualMesh,if语句来确保传递资源前资源已正确加载(假设磁盘上的资源因为意外情况移除,程序也能正常运行)

如果从VS开启调试

打开UE4的时候会报警告

Messiah游戏引擎 游戏引擎官网_游戏_04

SetStaticMesh函数

功能:
更改此实例使用的静态网格体
原型:

/** Change the StaticMesh used by this instance. */
	UFUNCTION(BlueprintCallable, Category="Components|StaticMesh")
	virtual bool SetStaticMesh(class UStaticMesh* NewMesh);

UFUNCTION宏修饰函数,BlueprintCallable表示BP脚本可调用这个函数,Category="Components|StaticMesh"表示函数在BP内的目录

SetRelativeLocation函数

功能:
设置静态网格体的相对位置

FORCEINLINE_DEBUGGABLE void USceneComponent::SetRelativeLocation(FVector NewLocation, bool bSweep=false, FHitResult* OutSweepHitResult=nullptr, ETeleportType Teleport = ETeleportType::None)
{
	SetRelativeLocationAndRotation(NewLocation, RelativeRotationCache.RotatorToQuat(GetRelativeRotation()), bSweep, OutSweepHitResult, Teleport);
}
FVector类

是由浮点精度的分量(X,Y,Z)组成的3-D空间中的向量结构体

struct FVector 
{
public:
	/** Vector's X component. */
	float X;
	/** Vector's Y component. */
	float Y;
	/** Vector's Z component. */
	float Z;
};

有如下静态变量可供调用

CORE_API const FVector FVector::ZeroVector(0.0f, 0.0f, 0.0f);
CORE_API const FVector FVector::OneVector(1.0f, 1.0f, 1.0f);
CORE_API const FVector FVector::UpVector(0.0f, 0.0f, 1.0f);
CORE_API const FVector FVector::DownVector(0.0f, 0.0f, -1.0f);
CORE_API const FVector FVector::ForwardVector(1.0f, 0.0f, 0.0f);
CORE_API const FVector FVector::BackwardVector(-1.0f, 0.0f, 0.0f);
CORE_API const FVector FVector::RightVector(0.0f, 1.0f, 0.0f);
CORE_API const FVector FVector::LeftVector(0.0f, -1.0f, 0.0f);

BeginPlay函数

作用:游戏开始时或生成时调用的代码

Super类

Super是UE进行层次结构升级的方式,具体的类型取决于父类,在这个项目中它的定义如下:
typedef TArray<T, TInlineAllocator> Super;

Tick函数

作用:每帧执行一次
参数DeltaTime,跟上一帧的间隔时间
程序源代码与思路:

// 逐帧调用
void AFloatingActor::Tick(float DeltaTime)
{
	//调用父类的Tick函数
    Super::Tick(DeltaTime);
	//创建一个FVector变量存储当前位置信息
    FVector NewLocation = GetActorLocation();
    //创建一个FRotator变量存储当前旋转信息
    FRotator NewRotation = GetActorRotation();
    //创建一个单精浮点存储程序运行时间
    float RunningTime = GetGameTimeSinceCreation();
    //变化的比例=sin(当前时间)-sin(上一帧时间)
    float DeltaHeight = (FMath::Sin(RunningTime + DeltaTime) - FMath::Sin(RunningTime));
    //新的Z轴坐标=变化的比例*范围系数
    NewLocation.Z += DeltaHeight * 20.0f;       //Scale our height by a factor of 20
    //变化的角度=变化时间*系数
    float DeltaRotation = DeltaTime * 20.0f;    //Rotate by 20 degrees per second
    NewRotation.Yaw += DeltaRotation;
    //设置Actor的坐标和角度
    SetActorLocationAndRotation(NewLocation, NewRotation);
}
使物体在指定区间来回移动的巧妙办法:

利用sin函数的特性 y∈[-1,1],而且是连续的函数,可以计算出每次的距离,而不受帧数的影响

Messiah游戏引擎 游戏引擎官网_API_05

FRotator类
struct FRotator
{
public:
	/** Rotation around the right axis (around Y axis), Looking up and down (0=Straight Ahead, +Up, -Down) */
	float Pitch; 
	/** Rotation around the up axis (around Z axis), Running in circles 0=East, +North, -South. */
	float Yaw; 
	/** Rotation around the forward axis (around X axis), Tilting your head, 0=Straight, +Clockwise, -CCW. */
	float Roll;
};

分别对应绕Y轴,Z轴,X轴旋转(PS:我真怀疑写UE4的程序员以前是开飞机的)
按照正视方向为x,建立三维坐标系,想象自己是一架飞机


旋转

说明

正方向

X

Roll

滚筒

右倾(飞机向右滚动)

Y

Pitch

俯仰

仰起(机头向上)

Z

Yaw

偏航

向右(飞机向右转动)

Messiah游戏引擎 游戏引擎官网_游戏_06

SetActorLocationAndRotation函数

功能:
设置Actor位置和旋转
原型:

bool SetActorLocationAndRotation(FVector NewLocation, FRotator NewRotation, bool bSweep=false, FHitResult* OutSweepHitResult=nullptr, ETeleportType Teleport = ETeleportType::None);

和之前的SetActorRelativeLocation类似,不过这个设置的是绝对坐标和绝对旋转