UE4 C++编程基础记录

Hello ,I am Edwin


首先谢谢大家的支持,其次如果你碰到什么其他问题的话,欢迎来 我自己的一个 讨论群559666429来,大家一起找答案,共同进步

一:前言

 有很多人是从UE3 接触到Unreal,如果你也对UE3非常了解,便能很快的上手UE4。但是,UE4的开发模式还是有所不同的。


1. 谈谈过往,UE1和UE2.

  我知道在那个时候咱们一样挥霍美好的童年在玩游戏而不是做游戏,当然做游戏更挥霍你的青春XD

  UE1和UE2是为FPS设计的,使用UnrealScript来进行编程。说实话UnrealScript是最好的学习面向对象编程的语言。

2.UE3

  添加了kismet。更多的模块类。但是仍旧以FPS为核心设计的

3.UE4

  UnrealScript被下岗了,恭喜你咱们下岗了~。变成了Blueprint,这时候关卡设计师们大声叫好:”不再需要求着愚蠢的程序员帮我们实现系统了,哈哈“。

  游戏类型随意,Paper2D让你轻松制作2维游戏。

  还有许多工程实例供你学习。

  好了咱们向前看~

5.UnrealScript vs.C++ vs.Blueprint

  这是许多人的疑问,看好戏吧

6.UnrealScript

  ①面向对象编程,和C,C++,Java比较像。但是还是有区别的

  ②虚拟机编译,这和java一样,方便移植,缺点是速度慢效率低

  ③添加了许多有用的特性,State(说实话状态写AI简直是最完美的方案,我的游戏AI便是强依赖State).Timers(非常方便延迟执行),Delegates(容易引起崩溃,注意使用哦)

7.Blueprint

  ①对艺术家和设计师来说就是逃离恶心程序员”这不可能实现“的最有力武器,你可以做到”你行你上啊~“。Kismet的伙计应该很容易上手

  ②还是虚拟机,和UnrealScript是一样的。因此,如果你很重视一个环节的执行效率,那么就是用C++

  ③和UnrealScript一样,但一些方面比UnrealScript强。例如你在添加Component中时可以不用关闭编辑器直接修改模型。实在是工农大解放啊…

8.C++

  ①UE编程始终可以基于C++,除非你用UDK,你这个穷鬼XD

  ②紧密结合虚拟机,因为Blueprint变量和方法有时是需要和C++交互的

  ③为了替换UnrealScript为开发者已经大幅提升了?

9.UE4基础元素

    ①Actor

      我们又见面了Actor,Actor是在一个关卡中持续存在的,通常他包含几个Actor组件。支持网络复制和多人游戏。
     Actor不包含位置,方向。这些东西在Root Component中存储。对于UE3 中的Pawn也由PlayerCharacter继承了,因为他有MovementComponent包含跳跃,速度等属性
 
       由SpawnActor() 生成
       必须由Destroy来销毁
       在游戏中不能被垃圾回收
 
   ②什么是ActorComponent?

    能被复用的功能可以添加进入Actor
    包含一些最有趣的函数和事件
    能被Blueprint访问~

   ③组件例子

   Scene Component 添加形状和连接
   Primitive Component 添加碰撞和渲染
   UAudioComponent,UArrowComponent,UInputComponent,ULightComponent,UMeshComponent,UParticleSystemComponent等等…

   写过UnrealScript会对组件深有体会

   ④PrimitiveComponent组件事件举例

   Hit- 再碰到强的时候调用
   Begin/EndOverlap -进出一个Trigger
   Begin/EndCursorOver 没用过
   Clicked/Released 不解释
   InputTouchBegin/End
   Begin/EndTouchOver
 

10.你就只认得个Pawn

   Pawn就是你的阿凡达
   这个傀儡被Controller操纵着
   通常处理运动和输入控制
   实现HP的好位置
   通常没有运动或是输入反馈的代码,你可以在Controller中写

11.来见见老朋友Controller

   控制Pawn的傀儡师
   可以理解做就是玩家
   AIController就是控制AI的
   一次只能Possess一个Pawn
   当Pawn死的时候可以继续存在,在我的游戏中让pawn死亡,游戏假重置,就是controller直接Possess一个新Pawn的

   PlayerControlelr : 玩家阿凡达的接口
   处理点击,手柄,键盘
   显示和隐藏鼠标指针
   不需要Pawn表示的好地方
   菜单,语音聊天…
   一些别的有用选项  

12.Character

   这是使用过UE3的人学习UE4最困惑的地方,有了Pawn为什么还要扩展PlayerCharacter?

   这是一种能走的Pawn,因为包含了MovementComponent和上边一样,添加了一些有用的组件
   处理碰撞
   处理客户端角色运动
   比UE3有更大的升级

13.HUD

   还有一些绘制API
   没有构建HUD的工具
   UMG直接提供所有的东西,我讨厌老旧UI系统。UMG让人解放了

14.GameMode

   还记得GameInfo吗,实现游戏规则用的
   配置Pawn,Controller和HUD是谁
   能在任何地方被访问,我来告诉你API GetGameMode()
   只在服务器和单机实例中存在
   GameState是被用来在客户端复制状态用的
   默认游戏类型可以在Project Settings中被设置
   每个地图都有自己的GameMode,需要你亲手设置

15.Input

   这是我对UE4最好的改进之一,你直接在编辑器中添加Bind而不是让你眼花的配置文件中
   在Project Settings中设置去吧
   获取指令  

   PlayerController
   Level Blueprint
   Possessed Pawn

16.碰撞

   有多种碰撞处理的方法
   线性检测 Line Trace
    体积扫过 Geometry Sweeps
   重叠测试 Overlap tests
     简单的碰撞
     Box,胶囊,球,多边形
   在运动和物理模拟的时候都需要
     复杂碰撞
     实际用于多边形
   武器和反向动力


二:代码基础知识

我记性不怎么好。在学习UE4的碰到一些新的知识,记录在这里,算是学习笔记。都是写基础的,但是频繁使用的语句

在这里再发个牢骚就是。UE4比起U3D ,那简直规矩太多了,他好像什么都想给客户弄好,但是最后弄的臃肿繁琐。记不住的就很容易犯错。这难道就是U3D 能火的原因之一。就像中文和英文,中文每个字差不多就代表一种具象或者抽象的事物,合起来有可以代表其他各种事物,但是这样下来就有好多好多字得记,真佩服古代造字人的智慧,比起智慧中文完胜英文。但是比起易学,易用!有可数数量的字母,相互组合,就能拼接成各种单词。就像给了你基本的框架,你用这些搭建就好了。这样在 学习还有传播 方便 占据 优势。所以,UE4像不像中文?博大而精神,高深而智慧? U3D 像不像 英文? 极简归一,实用易懂?


类命名前缀

虚幻引擎为您提供在构建过程中生成代码的工具。这些工具拥有一些类命名规则。如命名与规则不符,将触发警告或错误。下方的类前缀列表说明了命名的规则。

  • 派生自 Actor 的类前缀为 A,如 AController。
  • 派生自 对象 的类前缀为 U,如 UComponent。
  • 枚举 的前缀为 E,如 EFortificationType。
  • 接口 类的前缀通常为 I,如 IAbilitySystemInterface。
  • 模板 类的前缀为 T,如 TArray。
  • 派生自 SWidget(Slate UI)的类前缀为 S,如 SButton。
  • 其余类的前缀均为 字母 F ,如 FVector。

数据类型
  • 数字类型
    因为不同平台基础类型的尺寸不同,如 short、int 和 long,UE4 提供了以下类型,可用作替代品:

    int8/uint8 :8 位带符号/不带符号 整数
    int16/uint16 :16 位带符号/不带符号 整数
    int32/uint32 :32 位带符号/不带符号 整数
    int64/uint64 :64 位带符号/不带符号整数
    标准 浮点 (32-bit) 和 双倍(64-bit)类型也支持浮点数。

    虚幻引擎拥有一个模板 TNumericLimits,用于找到数值类型支持的最小和最大范围。如需了解详情,请查阅此 链接 。

  • 字符串

    • FString
      FString 是一个可变字符串,类似于 std::string。FString 拥有许多方法,便于简单地使用字符串。使用 TEXT() 宏可新建一个 FString:
      FString MyStr = TEXT("Hello, Unreal 4!")

    • FText
      FText 与 FString 相似,但用于本地化文本。使用 NSLOCTEXT 宏可新建一个 FText。此宏拥有默认语言的命名空间、键和一个数值。
      FText MyText = NSLOCTEXT("Game UI", "Health Warning Message", "Low Health!")
      也可使用 LOCTEXT 宏,只需要在每个文件上定义一次命名空间。确保在文件底层取消它的定义

    // 在 GameUI.cpp 中
    
    #define LOCTEXT_NAMESPACE "Game UI"
    
    
    //...
    FText MyText = LOCTEXT("Health Warning Message", "Low Health!")
    //...
    
    
    #undef LOCTEXT_NAMESPACE
    
    // 文件末端
    • FName
      FName 将经常反复出现的字符串保存为辨识符,以便在对比时节约内存和 CPU 时间。FName 不会在引用完整字符串的每个对象间对其进行多次保存,而是使用一个映射到给定字符串的较小存储空间 索引。这会单次保存字符串内容,在字符串用于多个对象之间时节约内存。检查 NameA.Index 是否等于 NameB.Index 可对两个字符串进行快速对比,避免对字符串中每个字符进行相等性检查。

    • TCHAR
      TCHARs 用于存储不受正在使用的字符集约束的字符。平台不同,它们也可能存在不同。UE4 字符串在后台使用 TCHAR 阵列将数据保存在 UTF-16 编码中。使用返回 TCHAR 的重载解引用运算符可以访问原始数据。


  • 容器
说明 对应U3D 的
TArray 常用的主要容器。作用与std::vector类似 List
TMap 键值对的合集,与 std::map 相似 Dictionary
TSet 保存唯一值的合集,与 std::set 相似 HashSet

虚幻C++部分代码解释
  • C++和 蓝图
    虚幻提供两种创建游戏:C++ 和蓝图。程序员可以通过C++ 来添加基础游戏性体统。设计师(美术人员)即可在这个系统上创建关卡或者游戏的自定义游戏性。简单来说,程序员编写的C++是一个游戏的主要关键功能的构建方。而设计师所掌握的蓝图,则起到游戏功能的辅助作用。

  • 类向导
    而在创建一个UE4C++对象的时候。类中的会有一下结构。

#include "GameFramework/Actor.h"
#include "MyActor.generated.h"

UCLASS()
class AMyActor : public AActor
{
    GENERATED_BODY()

public: 
    // 设置该 actor 属性的默认值
    AMyActor();
    // 游戏开始时或生成时调用
    virtual void BeginPlay() override;

    // 每帧调用
    virtual void Tick( float DeltaSeconds ) override;
};
字段 作用
BeginPlay() 和U3D 的 Start()方法一样。在进入游戏状态的时候运行
Tick() 没帧调用和U3D 的 Update() 一样。如果不需要此功能。必须在构造函数中说明。如下代码
AMyActor::AMyActor()

{

    // 将此 actor 设为每帧调用 Tick()。不需要时可将bCanEverTick设置为false以关闭此功能,来提高性能。

    PrimaryActorTick.bCanEverTick = true;

}

  • 使属性出现在编辑器中
    类创建好之后。现在可以创建一些属(设计师可是虚幻编辑器中设置这些属性)。可以使用 特殊宏 UPROPERTY() 来轻松的将属性公开至编辑器中可视。比如:UPROPERTY(EditAnywhere) 宏即可。
UCLASS()
class AMyActor : public AActor
{
    GENERATED_BODY()

    UPROPERTY(EditAnywhere)
    int32 TotalDamage;

    ...
};

之后。你就可以在编辑器中看到变量TotalDamage对应的数值。并且可以对数值进行调节。
除此之外还可以加入其他的属性。

属性 作用 用法
EditAnywhere 使这个变量出现在编辑器中可编辑 EditAnywhere
Category 使此属性与相关属性出现在一个部分 Category=”Damage”
BlueprintReadWrite 使属性为可读取和可编写状态 BlueprintReadWrite
BlueprintReadOnly 使属性为可只读取状态 BlueprintReadOnly
VisibleAnywhere 意味着属性在虚幻编辑器中为可见状态,但是不可编辑 VisibleAnywhere
Transient 意味着无法从硬盘对其进行保存或加载 Transient
更多属性修饰符

除此之外,还可以通过宏定义来是一些属性在其关联属性被更改的时候也做出相应的改变:

void AMyActor::PostInitProperties()
{
    Super::PostInitProperties();

    CalculateValues();
}

void AMyActor::CalculateValues()
{
    DamagePerSecond = TotalDamage / DamageTimeInSeconds;
}
//通过WITH_EDITOR定义使这个函数的目标对象在编辑器中被更改时引擎将通知和运行这个函数。
#if WITH_EDITOR
void AMyActor::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
    CalculateValues();

    Super::PostEditChangeProperty(PropertyChangedEvent);
}
#endif
  • 蓝图调用C++中的函数
    上面说了如果对蓝图公开属性。那函数呢?对呀函数我们又得需要另一个宏来定义。
UFUNCTION(BlueprintCallable, Category="Damage")
void CalculateValues();

如上。 可以通过 UFUNCTION 宏来把C++函数对反射系统公开。 而 BlueprintCallable将其对蓝图虚拟机公开。这样在在蓝图中,右键点击快捷键菜单就可以使用这个函数。

UE4 C++编程基础记录_第1张图片

  • AActor简单介绍
    AActor 这个 类,被作为游戏场景中一个基本的对象。所以可以放在关卡场景中的对象都可延展自这个类,比如:AStaticMeshActor、ACameraActor 和 APointLight 等等 actor。
    这个类派生在 UObject。
    AActor 的生命周期为:
函数 Are
BeginPlay() 和U3D 的 Start()方法一样。在进入游戏状态的时候运行
Tick() 每帧调用和U3D 的 Update() 一样。如果不需要此功能。必须在构造函数中说明。
EndPlay 对象离开游戏进程时调用
  • 虚幻反射系统
    再用UE4 C++ 编程的时候。同会看到好多非C++ 语句的字段。例如,他会在一个类上加上UCLASS() 等等。这些都是UE4使用其自身的反射实现,来启动动态功能,比如,垃圾回收,序列化,网络复制和蓝图/C++通信。这些功能你可以根据需求选择的加入,只要为相应的类型添加正确的标记就可生成反射数据。 一下是一些基础标记:
标记 说明
UCLASS() 告知虚幻引擎生成类的反射数据。类必须派生自 UObject。
USTRUCT() 告知虚幻引擎生成结构体的反射数据。
GENERATED_BODY() UE4 使用它替代为类型生成的所有必需样板文件代码。就是说这个类不直接使用父类的声明。但是,你必须得去实现,自己去声明,否则就会报错。更多信息
UPROPERTY() 使 UCLASS 或 USTRUCT 的成员变量可用作 UPROPERTY。UPROPERTY 用途广泛。它允许变量被复制、被序列化,并可从蓝图中进行访问。垃圾回收器还使用它们来追踪对 UObject 的引用数。
UFUNCTION() 使 UCLASS 或 USTRUCT 的类方法可用作 UFUNCTION。UFUNCTION 允许类方法从蓝图中被调用,并在其他资源中用作 RPC

eg:

#include "MyObject.generated.h" //这个头文件包含虚幻引擎所有反射数据。必须在声明类型的头文件中将此文件作为最后的 include 包含。

UCLASS(Blueprintable)
class UMyObject : public UObject
{
    GENERATED_BODY()

public:
    MyUObject();

    UPROPERTY(BlueprintReadOnly, EditAnywhere)
    float ExampleProperty;

    UFUNCTION(BlueprintCallable)
    void ExampleFunction();
};

您还会注意到,可以在标记上添加额外的说明符。此处已添加部分常用说明符用于展示。通过说明符可对类型拥有的特定行为进行说明。

Blueprintable - 此类可由蓝图延展。
BlueprintReadOnly - 此属性只可从蓝图读取,不可写入。
Category - 定义此属性出现在编辑器 Details 视图下的部分。用于组织。
BlueprintCallable - 可从蓝图调用此函数。
说明符太多,无法一一列举于此,以下链接可用作参考:

UCLASS 说明符列表

UPROPERTY 说明符列表

UFUNCTION 说明符列表

USTRUCT 说明符列表

  • 对象/Actor 迭代器
    就是 对应 U3D 的 for 循环。先查找关卡中所以的派生自UObject的 类的实例。然后遍历他们。
// 将找到当前所有的 UObjects(此处可以写入你自定义的类的类名) 实例
for (TObjectIterator It; It; ++It)
{
    UObject* CurrentObject = *It;
    UE_LOG(LogTemp, Log, TEXT("Found UObject named:%s"), *CurrentObject.GetName());
}

但是这个在PIE(Play In Editor) 中使用的话可能产生意外后果。 因为编辑器已被加载,除编辑器正在使用的对象外,对象迭代器还将返回为游戏世界实例创建的全部 UObject。

而 Actor 迭代器 呢还和 对象迭代器的使用不一样,但套路就那么多。

APlayerController* MyPC = GetMyPlayerControllerFromSomewhere();
UWorld* World = MyPC->GetWorld();

// 和对象迭代器一样,您可提供一个特定类,只获取为该类的对象
// 或从该类派生的对象
for (TActorIterator It(World); It; ++It)
{
    // ...
}

这个迭代只能用于派生自 AActor 对象。

创建 actor 迭代器时,需要为其赋予一个指向 UWorld 实例的指针。许多 UObject 类(如 APlayerController)会提供 GetWorld 方法,助您一臂之力。如不确定,可在 UObject 上检查 ImplementsGetWorld 方法,确认其是否应用 GetWorld 方法。

  • Actors 和垃圾回收
    Actor 通常不会被垃圾自动回收。他在生成之后,必须使用Destroy() 来删除。而且他们不会立刻删除,会在垃圾回收阶段被清理。

常见情况下 actors 带有 Uobject 属性。

UCLASS()
class AMyActor : public AActor
{
    GENERATED_BODY()

public:
    UPROPERTY()
    MyGCType* SafeObject;

    MyGCType* DoomedObject;

    AMyActor(const FObjectInitializer& ObjectInitializer)
        :Super(ObjectInitializer)
    {
    // 创建两个对象。他们自动成为根集的一部分。
    // 这个指定了UPROPERTYSafeObjet将不会被垃圾回收,因为他从根集对象出到达。
        SafeObject = NewObject<MyGCType>();
//DoomedObject 因为未将标记为 UPROPERTY,因此回收器并不知道其正在被引用,而会将它逐渐销毁。
        DoomedObject = NewObject<MyGCType>();
    }
};

void SpawnMyActor(UWorld* World, FVector Location, FRotator Rotation)
{
    World->SpawnActor<AMyActor>(Location, Rotation);
}

由上面代码。可知,UObject 被垃圾回收时,对其的所有 UPROPERTY 引用将被设为 nullptr。那么 上面的 属性 SafeObject 将会为 nullptr。 所以为了安全的检测一个对象是否被垃圾回收。我们应该在使用的时候进行判断:

if (MyActor->SafeObject != nullptr)
{
    // 使用 SafeObject
}

你可能感兴趣的:(UE4,C++,虚幻引擎,笔记)