《大象无形:虚幻引擎程序设计浅析》读书总结

        建德若偷,质真若渝。大方无隅,大器晚成。
        大音希声,大象无形。夫唯道善贷且成。

                                                                                                                          ——老子,《道德经》

 

----------------------------------------------------------------------------------------------------------------------------------

注解

      “无声”、“无形”本来是虚空的东西,谈不上“大音”、“大象”。所谓“大音希声,大象无形”,应是大音若无声,大象若无形。至美的乐音,至美的形象已经到了和自然融为一体的境界,反倒给人以无音、无形的感觉。“大音”、“大象”至少有一个负载它们的实体,才能显示其“大”。

 

    “大音希声,大象无形”出自《老子》(又名《道德经》),是老子对“道”的阐释,应解释为“最美的声音就是听起来无声响,最美的形象就是看不见行迹”。大音若无声,大象若无形,至美的乐音、至美的形象已经到了和自然融为一体的境界,反倒给人以无音、无形的感觉。

 

        在现代,“大音希声,大象无形”则更代表一种将美融入生活的智慧;情感热烈深沉而不矫饰喧嚣,智慧隽永明快而不邀宠于形。拥有这种智慧的人不用刻意地去想什么、做什么,便自然无形地把情感使用到最值得、最有意义的地方去,从而使自己更好地享受生活!

-----------------------------------------------------------------------------------------------------------------------------摘自百度知道

 

学习虚幻引擎引擎,抓住最核心的五个类:UObject、Actor、Pawn、Controller、Character

一、UObject类提供以下功能:

1、垃圾回收

    c++内存由程序员完成,不需要对象a的时候,该不该释放该内存?如果释放,万一其他对象引用了,释放后就产生野指针,当另一个对象访问时,会看到空空如也,如果释放,如果我已经是最后一个使用的人,这片内存就永远无法被回收。

    对此虚幻这样解决:

    (1)继承UObject类,同时指向UObject类实例对象的指针成员变量,使用UPROPERTY宏标记,虚幻引擎的UObject架构会自动被UProperty标记的变量考虑到垃圾回收系统中,自动进行对象生命周期管理。

     (2)采用智能指针:只有非UObject类型才能使用智能指针进行内存释放。

2、反射

   反射的主要作用在于:运行时如何获取一个类,如何获取成员变量、成员函数。

c++没有提供这样一套机制,虚幻实现了这样一套机制。

3、序列化

    希望把一个类的对象保存到磁盘,下次无损加载,需要继承UObject类。

4、和引擎编辑器的交互

    如果希望类的变量能被虚幻引擎编辑器Editor简单编辑,需要继承这个类。

5、运行时类型识别

    虚幻引擎中无法使用c++标准的dynamic_cast可以继承UObject类,然后Cast<>函数完成

6、网络复制

    网络游戏中服务器、客户端能够自动处理变量的同步,被宏标记的变量能自动完成网络复制功能,从服务器端复制变量到客户端。

二、Actor类

    它能够被挂载组件,在Actor中,坐标和旋转量只是一个Scene Component组件,想让Actor被渲染,挂载一个静态网格组件,想让Actor有骨骼动画,挂载一个骨架网格物体组件,希望Actor能移动,挂在一个Movement组件,也即需要挂载组件的时候,才应该继承Actor类。

三、Pawn类

    提供了被操控的特性能被一个Controller操纵,这个Controller可以是玩家Player Controller类,也可以是AI Controller类,Pawn类一旦脱离Controller就是无法行动的肉体。

四、Character

    Character类继承Pawn类,它提供了一个特殊的组件,CharacterMovement,该组件提供了角色移动、跳跃等功能,如果不需要移动逻辑,可以不继承Character类只继承Pawn类。

五、Controller

    既然它是灵魂,通过Possess/UnPossess来控制/卸载一个肉体(Pawn)

六、创建C++类

工程目录下的Source文件夹下,找到和你游戏名称一致的文件夹,会发现Public文件夹、Private文件夹、.build.cs文件

我们将.h文件放在public文件夹中,.cpp文件放到private文件夹中

在.h中声明你的类,如果类继承UObject,类名上方加入UCLASS()宏,同时在类的第一行加GENERATED_UCLASS_BODY()宏,或者GENERATED_BODY()宏,前者需要手动实现一个带有const FObjectInitializer&参数的构造函数,后者手动实现一个无参构造函数,

七、虚幻引擎类命名规则

F:纯c++类

U:继承UObject但不继承Actor

A:继承Actor

S:Slate控件相关类

H:HitResult相关类

八、类对象的产生

如果类是一个纯c++(F开头),通过new产生对象

如果继承UObject但不继承Actor,需要通过NewObject函数产生出对象,NewObject<T>()会返回一个指向该类的对象,通过UWorld(可以通过GetWorld()获得)的SpawnActor产生出对象。这样调用:GetWorld()->SpawnActor<AYourActorClass>()

如果继承AActor,通过SpawnActor函数产生出对象

九、类对象的销毁

1、如果纯c++类在函数体中创建,而且不是通过new来分配内存,如下:

FYourClass YourObject=FYourClass();   //通过函数拿到类

那么类对象在函数调用结束后随函数的释放而释放,不需手动

2、如果纯c++类使用new来分配内存,而且直接传递类的指针,除非手动删除,否则内存永远不会释放。如果忘记了,造成内存泄露。

3、如果纯c++类使用new来分配内存,而且使用智能指针TSharedPtr/TShared-Ref来管理,不应该再手动释放,智能指针会使用引用计数来自动释放内存,使用MakeShareable函数将普通指针转化为智能指针。

十、UObject类

   借助NewObject<T>函数产生,无法通过标准New操作符产生 

    无法使用智能指针管理对象,它采取自动垃圾回收机制,当类成员变量包含UObject对象,带有UPROPERTY宏,这个成员变量会触发引用计数机制。

    垃圾回收期会定时从根节点Root开始检查,当一个UObject没有被别的任何UObject引用就会被垃圾回收。可以通过AddToRoot函数让一个UObject一直不被回收。

十一、Actor类

通过Destroy函数销毁

十二、UPROPERTY宏

将一个UObject类的子类成员变量注册到蓝图中,需要该宏,比如注册一个变量到蓝图中:

UPROPERTY(BlueprintReadWrite,VisibleAnywhere,Category="Object")

十三、UFUNCTION宏

注册函数到蓝图中

UFUNCTION(BlueprintCallable,Category="Test")

BlueprintCallable表示函数可以被蓝图调用,可选的还有BlueprintImplementEvent表示这个成员函数由蓝图子类实现,不该在C++中给出函数的实现,BlueprintNativeEvent表示成员函数提供一个C++的默认实现,同时可以被蓝图重载,需要提供一个"函数名_Implementation"为名字的函数实现,放在.cpp中。

十四、行为树

虚幻4中状态机被行为树代替,状态机在动画蓝图中保留,因为行为树更简化,更接近人的思维。

十五、同步

客户端是对服务端的拙劣模仿:客户端自己也运行着一个世界,不断预测服务端的行为,从而不断更新当前世界,最大程度接近服务端的世界,在延迟情况下,客户端不在试图同步服务器,而是模仿服务器,客户端根据同步数据发送时的当前对象位置和速度,猜测出当前对象在服务端的可能位置,并且通过修正当前世界,去模仿服务端的位置,如果服务端客户端差距太大,强行闪现修正。

十六、预编译

c++中只编译.cpp文件,编译成*.cpp文件(\debug目录下)c++中宏展开:代码中出现的宏用宏实体代替

#define 定义常量、函数宏

#undef 结束常量、函数宏定义

c++的预处理器是在编译器之前运行,将宏定义、头文件加载到.cpp也即#include文件,将该文件的代码全部拷贝替换了#include语句,理解为将头文件的内容粘贴到#include处

c++中头文件是不被编译的。cpp引用头文件是当预编译时将头文件插入到cpp中,因此变量的定义、函数定义不写在头文件中,因为头文件可能被多个cpp引用,连接的时候可能出现重复定义的情况

为了防止头文件被重复引用,应当使用#if ndef #define ... #endif结构

十七、预编译头

一个工程中总有一大堆头文件,几乎所有cpp都必须包含,可不可以把这些头文件提取出来,只编译一遍然后其他cpp都能使用呢?这就是预编译头的由来。

在Debug或Release目录中有一个很大的.pch文件,这就是编译之后的预编译头

十八、模块机制

1、快速完成模块创建文件结构:

在c++工程的Source文件夹下,创建一个新的模块文件夹,结构如下:

1、模块名.Build.cs

2、模块名.h

3、模块名.cpp

模块名.Build.cs,该文件来告知UBT如何配置编译和构建环境,如下可见,这里添加了Core,CoreUobject,Engine,InputCore这四个模块,.Build.cs模块告知UBT如何配置编译和构建环境

using UnrealBuildTool;
public class pluginDev : ModuleRules
{
    public pluginDev(TargetInfo Target)
    {
        publicDependencyModuleNames.AddRange(new string[]{"Core","CoreUObject","Engine","InputCore"});
        PrivateDependencyModuleNames.AddRange(new string[]{});
    }
}

模块名.h

#pragma once
#include "Engine.h"

模块名.cpp

#include "pluginDev.h"   //由于.h文件为预编译头文件,预编译头文件可以加速代码的编译,只要是.cpp文件都要包含预编译头文件
IMPLEMENT_PRIMARY_GAME_MODULE(FDefaultGameModuleImpl, pluginDev, "pluginDev")

2、模块卸载、加载函数

在.h和.cpp中覆写StartupModule和ShutdownModule函数,来书写模块启动和卸载时需要执行的内容。

3、预编译头文件

预编译头文件能加速代码的编译,头文件的标准命名方式为:“模块名PrivatePCH.h”放置于Private文件夹中,当前模块所有.cpp文件都要包含预编译头文件

4、引入模块

引入当前模块的方式是在目录下的Source文件夹中,找到工程名.Target.cs

public override void SetupBinaries(TargetInfo Target,ref List<UEBuildBinaryConfiguration>OutBuildBinaryConfigurations,ref List<string> OutExtraModuleNames)
{
    OutExtraModuleNames.AddRange(new string[]{"pluginDev"});   //在这里添加引入的模块名称
}

5、虚幻引擎模块加载顺序

十九、内存分配

虚幻引擎是通过宏来控制,在几个内存分配器中选择的

二十、垃圾回收

1、引用计数法:

优点:不需要程序暂停,垃圾回收的过程分配到运行中。

缺点:

(1)如果成千上万个对象生成,那么频繁修改数值开销很大

(2)环形引用:有相互引用的对象会出错

2、标记-清扫算法(追踪式GC)

和引用计数法只关注单个对象的思路相反,遍历每一个对象,看有没有被引用,没有就回收

优点:没有环形引用问题

缺点:

(1)执行该算法需要程序暂停,这会导致系统有明显延迟

(2)只是丢掉垃圾而不整理,导致可用空间越来越细碎,导致大型对象无法被分配

虚幻引用的智能指针采用引用计数算法,使用弱指针解决环形引用。

二十一、UBT和UHT

UBT(Unreal Build Tool):

UHT(Unreal Header Tool):

一个引擎独立应用程序,UBT会通过命令行参数告诉UHT,游戏模块对应的定义文件在哪,包含了编译的相关信息,然后UHT开始了自己的三道编译:Public Classes Headers、Public Headers、Private Headers

经过三道编译,最终生成.generated.cpp和generated.h两种文件,

同时对含有UProperty和UFunction字样的数据类型进行登记。

二十二、虚幻引擎的垃圾回收机制

二十三、Actor对象

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值