2021-05-30【UE】初学者计划

Pro Unreal Engine Game Coding (puegc.com)
C++ 常量 | 菜鸟教程 (runoob.com)
反重力赛车之路 (shimo.im)
UE4 C++ CookBook (shimo.im)
【UE4】Rider For Unreal体验报告 - 知乎 (zhihu.com)
博客 – 第5页 – UnrealBook's Blog
Action RPG Game | Unreal Engine Documentation


【Day1】 内容熟悉&学习计划

为什么要用虚幻:对于一名游戏程序员,引擎不应该成为限制,而是工具。
后面还是希望有机会能用unreal进行项目的开发。

目前对虚幻的初步了解:
1.开源&魔改c++
c++的语法略懂,语言的用法都差不多,c++多了一个头文件,类似.cpp的目录。
通过include引用这些目录,就可以调用其中的内容方法。

UE5引擎目录

2.c++&蓝图&众多做好的完备功能模块
官方ue5的ancientworld使用的好像都是蓝图。我想从c++开始看起。
初步计划就是从这个官方示例工程中,拷贝代码出来,到一个新工程完成运行。
后续的计划一步一步来,代码方面的目标是能把代码思路及代码搞出来用到unity中。
具体的功能模块,包括输入、动画&ik、镜头相关等。
过程中也希望能学习到优秀代码的编写思路。

开始

ue5的界面简化过了(比我一开始见到ue4的时候好多了),有点blender内味。



右侧面板和unity类似。
点击场景中物体,可以看到它的细节,和unity不同的是,场景中每个物体,内部的层级关系只能在细节中查看。
点击添加,可以添加组件,和unity类似。



然而它内置的组件,虽然有些东西有点“多管闲事”的意思,但是可以看出来功能还是很强大的。
变换(最基本的,场景中物体都应该有)

渲染
复制(居然有是否同步网络的选项,虽然不知道干嘛的,而且觉得网络本身应该和它分开处理)
...
跳过这个,进入下一个场景
打开场景ancientworld,卡的要死。先删掉很多没用的模型就好很多了。



一个石头卡半天。点击编辑。



内存不足,卡死崩溃。牛逼!
这么多组件功能,跟unity类似,缺点肯定也和unity类似。
如果想做出更可控的东西,那么就不应该在场景中挂载太多的组件,基本应全交付给代码去实现。
首先先不让它卡,然后看下一步操作。

【Day2】AncientWorld 代码结构初探

不卡暂时是做不到了。利用早上的时间先看一下这个工程的代码结构,然后直接开始学习课程。



AncientGame.Target & AncientGameEditor.Target

这俩玩意都继承自TargetRules,具体是啥点不进去。大概是一个绑定Target配置项。
三个参数:Type(可选择目标是编辑器或是游戏) DefaultBuildSettings(一个配置项文件吧) ExtraModuleNames(额外的模块?"AncientGame")
根据这个额外模块名字,看这个类:AncientGame
然后...语法盲区了(忘记了)
C++ 教程 | 菜鸟教程 (runoob.com)

【Day3】进度比较慢,直接开始看教程

Unreal Engine C++ Developer: Learn C++ and Make Video Games | Udemy

1.1 介绍和安装 PPT

1.做完所有的挑战练习
2.访问他们的网站(no)
3.多去论坛交流(no)
4.解决问题
5.积极且努力

1.8 介绍和安装 PPT

安装vscode
支持unreal



自动打开vscode

vscode 终端 cl

cmd

选择默认终端:cmd

【Day4】正式开启coding

1.上一堂课的东西起了作用。必须先启动

然后输入code,从vs的这个面板中启动vscode。然后在vscode的终端中,cl命令才会起作用。

2.

执行cl命令,会生成obj和exe文件。输入第二条命令,会执行这个文件在终端进入程序。
第二章节跳过,基础语法

【Day5】UE基本操作

GitHub - UnrealCourse2019/BullCowGame
第三章节介绍了一个小型UE项目,首先下载资源,然后跟着教程搞。走起。
1.ctrl+space打开项目资源目录
2.鼠标右键单击会有很多放置选项等各种选项
3.上面的窗口按钮有很多设置的东西:
3.1 视口:可打开多个相机的视角窗口(unity的new scene窗口)
3.2 世界场景设置:(unity的project setting)
3.3 细节:(unity的detail)
3.4 世界分区:未知
3.5 数据层:未知
3.6 图层:未知
3.7 本地化控制面板


3.8 加载布局:很常规(unity的layout)

【Day6】UE资源导入及目录结构

unity关键目录:assets,packages,projectsettintgs
ue关键目录:Content,Source,Config
content里面是资源目录。尝试将已有content目录直接拷贝到空工程。
打开场景,mesh全丢失。
附加上去几个mesh。设置材质球。
双击材质球:发现有可视化编辑面板



【Day6】UE基本操作

介绍了一下地形笔刷。这玩意儿我真的有一天会用到吗?红豆泥?
介绍如何从默认的vs项目切到vscode。
编辑->编辑器偏好设置->源代码->vscode
工具->生成vscode项目
然后需要新建两个c++类,才能打开vscode工程。
问题来了,每当新建一个类,都要开始编译很久。




放弃vscode,切回vs。over。

【Day7】
找到一个udemy的课程,讲师是codemonkey。
【中文字幕】Unreal Engine虚幻引擎专业游戏编程大师级视频教程-CGTSJ-ZH25812

class15
基于版本4.25.4Release版本进行引擎的编译和开发。
入门15课,他讲了对于一个这么大的引擎的代码版本控制。由于他用的是Perforce,我就不跟着做了。后面如果转UE的话,会回来复习的。

【Day8】
我改主意了。完全跟随教程,包括版本控制。
class12
1.编译引擎,生成一堆文件。
2.Download Helix Core (P4D) | Perforce
这个是下载服务器,需要注册AWS亚马逊云服务账号,需要信用卡。
3.Helix Visual Client (P4V) | Perforce
下载客户端。
class13
把引擎加入自己本地服务器



将一堆文件映射为可写,可拷贝,可执行。
因为,exe,dll,需要每次都重新生成。将它们存储一个文件的历史拷贝,可以在服务器上节省大量的磁盘空间(是不是指,它会上传,但是是可覆盖的,永远只留存一份)
workspace设置:映射,并过滤没必要的垃圾文件,类似.gitignore

第一行:将仓库的路由分配给自己工作区的路由
其他行,都是过滤选项,是排除法,排除一些文件
如第二行,不要在引擎文件夹中添加任何.pdb类型的文件
因为它们太大,而且没什么用。它用于诊断崩溃。
Intermediate文件夹,是中间文件夹,不需要发布出去。
obj文件 ,是编译引擎时暂时需要的
Saved文件夹,也只存在于本地。

然后,由于一次性上传大批量文件会崩溃,要自己一小块一小块的提交。
pending:已添加,但是未提交。

搜索.dll文件,发现是Binary+Sw,与上面的配置匹配。

【Day9】
class16
作者喜欢c++,瞧不起蓝图,认为蓝图只能作为一部分跟主体无关的小功能,像胶水一样黏在主体上。
class17 编程规范 CODING STANDARD
https://docs.unrealengine.com/en-US/Programming/Development/CodingStandard/index.html

1.头文件的注释声明
2.变量命名
3.标准库和ue封装库
4.注释
5.const的使用
...
6.命名空间
epicu有一个工具,不需要执行,可以用它来操作反射类型,比如UCLASS。所以尽量不使用命名空间,除非一些无关的封装好的库。

使用auto关键字(var)
自己定义自己的编码规范,不用ue的(上文链接)
团队尽量遵循同一套编码规范

【Day10】
1.创建自己的项目
2.游戏模式,编译索引





3.Properties
什么是UPROPERTY
反射是非常有用的,它是ue引擎的一项基础技术,为许多系统提供动力,比如details panels in Editor, serialization, garbage collection, network replication and blueprint c++ communication.
但是c++本身并不支持 任何形式的反射,所以虚幻引擎自己实现了一套,来获取,查询,处理有关c++类,结构体,函数,成员变量等信息。
它是虚幻自己实现的反射系统的一部分。
如果想在编辑器和蓝图编辑一个变量,它必须是UPROPERTY
需要序列化,需要是UPROPERTY
多人游戏同步,需要是UPROPERTY
如果一个数据成员指向某种形式的UOBJECT,它需要是UPROPERTY。

Archetypes:原型;是c++和蓝图。可以理解为unity的prefabs
Instances:实例化出的实例


智能指针,垃圾回收


【Day11】今天圣诞节 2021.12.25
class2-006 游戏模式
保存场景,设置玩家初始位置
创建一个 gamemode BP


代码部分:
弹簧,相机,输入绑定

class2-009 Debugging HUD

打算直接cv看代码了。不打算跟着写一遍了。
拷贝导致引用丢失。这个问题暂时无法解决,重建工程重新拷贝吧。

【Day12】
friend 类:仅用于DebugUI展示,否则永远不要使用。破坏结构。



重新拷贝,研究一下结构。
1.两个.cs的target文件,跳过,字符串不要弄错即可。
2.module名.h module名.cpp model名.build
之前编译一直失败应该是这个.build文件被我删掉了。
它继承自ModuleRules,应该就是没有这个导致的失败。



这次生成成功了

class2-10 array
ue中的数组是动态长度数组,TArray。类似列表。
放弃所有其他的数组,只用他提供的。


image.png

Emplace:避免创建一个临时变量,这在对于FString来说效率会更好。




Empty:清空内存 Reset:保留内存

最下面的做法更好。

class2-11 actor life-cycle
Actor Lifecycle | Unreal Engine Documentation
跟unity类似。
对于开发者:



如果出问题,直接看源码。提示:此处群龙盘,怯者自避远。


class2-12 the goal of the game
触发器


触发器调用重置球位置方法:



作者的观点:为什么不为它写一个cpp脚本?因为这个东西只是重置位置,它自成一体,没有任何的维护问题,没必要为了它重写脚本。
我的看法:首先触发器这个东西,以及真实物理相关的东西,我是很不喜欢用的。我喜欢用射线检测。如果我用射线检测实现触发器,就比如人走到一个区域,我可能也是通过把这个区域设置为某种碰撞盒的方式实现,角色的射线检测到某种碰撞盒即为触发。
不过,作者这部分观点,是非常可取的,我也许太执着于代码实现了。后面会有很多非网络的游戏,可以用触发器 物理等。

在c++和蓝图之间需要有一个权衡。因为蓝图主要是给非编程人员配置用。

【Day13】2022年1.2日晚上九点 学一点吧,今天不想当废物。
c++和蓝图之间的互相使用:
c++基本上无法调用蓝图,获取蓝图内容。真要使用也不是不行:


image.png

3-1 数学
很多数学书籍资料是很枯燥的,它是离散的知识但是很少说明在具体情况中的种种应用。所以需要自己探索实践,知行结合。
不能让它变成空洞的知识,需要变成饱满的有用的知识。
3-2 c++编译器运算顺序
基本运算符
3-3 角度弧度
FMath1::Sin...
3-4 比率
3-5三角函数


image.png

用于烟雾特效等
image.png

3-6 abs sign -1 0 1
3-7 pow

3-8 插值lerp


image.png

图片像素点颜色插值
image.png

3-9 easing in out
3-10 Time-Based smoothing



3-11 Random Number Generation

推荐使用自己的随机数
3-12 Vectors
在3d游戏里,vector3用的最多,最满足需求。
3-13 Dots点乘

衡量向量之间平行程度的标准
向量 之间角度的余弦(投影)
根据点乘获得角度

前后


根据点乘获得射线和平面的交点:


3-14 Cross Products叉乘


根据一个轴,以及一个朝向,获得三个垂直的向量

3-15 Quaternions 四元数
表示一个方向。


球面差值:最近方向

3-16 Rotators
ue提供的旋转器
FRotator 和四元数一样,但是它更容易理解。
类似EulerAngle?roll pitch yaw




FMath::LerpRange
FMath::RInterpTo


3-17 Transforms
旋转,位置,缩放的集合


3-18 coordinate spaces 坐标空间
地球为参考系:经纬度
太阳为参考系:太阳系
银河为参考系:银河系


3-19 matrices 矩阵
在3d游戏革命的早期,开始很普遍的使用矩阵。
但是虚幻现在已经不需要太多使用矩阵了。
如果是一个老派编码员,但是是ue新手,不用管这些。
可能第一反应还是直接使用矩阵来完成transform的工作。但是要明白这是ue,需要真正确定自己是否要这样做。
现在用FTransform。



数量很大的话,不使用transform,自己用矩阵运算,会快30%。



计算旋转量

3-20 总结
vs,perforce,excel。
038 C-Operator-Precedence
https://en.cppreference.com/w/cpp/language/operator_precedence

056 Microsoft-Mathematics
https://www.microsoft.com/en-gb/download/details.aspx?id=15702

不会数学就谷歌。不影响做东西。

【Day14】
第四个文件夹了。
先把代码过一遍,然后快速浏览所有视频。
1.MetalInMotion.Target.cs
添加模块:MetalInMotion
2.MetalInMotion.Build.cs : ModuleRules
这就是模块,初始化模块内容:
{ "Core", "CoreUObject", "Engine", "InputCore" }
3.创建自己的游戏模式AMetalInMotionGameModeBase : AGameMode
初始化:背景音频,结束音频 通过拖拽配置音频
初始化:自定义HUD
BeginPlay:播放背景音乐
Tick:获取场景中所有物体,强转成ABallBearingGoal : ATriggerSphere
如果不满足HasBallBearing则break。
否则,累加计时,到达时间,播放结束音频,重启游戏。
4.ABallBearingGoal : ATriggerSphere
触发器:有物体进入和移出事件,成功触发,则往自己范围内球列表添加移除ABallBearing : APawn
前提:是能强转,然后不是玩家。
Tick:计算距离,给自己范围内的球一个牵引力。
5.球 ABallBearing : APawn
构造创建预制体。
6.玩家球 APlayerBallBearing : ABallBearing
SpringArm 弹簧系统绑定球
Camera 绑定弹簧
绑定按键
friend class HUD

7.HUD
hud不是手动绑定的。
是在游戏模式里设定了一下。HUDClass = ABallBearingHUD::StaticClass();
里面是一些看不懂语法的代码。课程说是反射。

BallBearingHUD:ADebugHUD
APlayerBallBearing* ballBearing = Cast<APlayerBallBearing>(GetOwningPawn());
在这里完成了对象的绑定。

APawn* AHUD::GetOwningPawn() const
{
    return PlayerOwner ? PlayerOwner->GetPawn() : NULL;
}

获取了玩家,玩家操作的pawn对象,强转后赋值。




4-2 蓝图方法UFUNCTION


4-4 console
用途:、可以通过命令行,在运行时调用方法。
stat units:查看帧率
自定义命令行:
在自测阶段,它非常有用。
方法1




方法2


4-5 Static Class
只要创建了一个UClass,UE的反射系统就会通过反射为它创建一个StaticClass Identifier标识符

【Day15】
4-7 Build Configurations 编译配置
https://docs.unrealengine.com/en-US/Programming/Development/BuildConfigurations/index.html
UE4 C++ CookBook (shimo.im)

Perforce版本管理。添加,设置可读写保存,还原。

【Day16】
5-4 Create second project 创建第二个工程
首先打开物理配置
修改重力



修改允许穿透的最大速度,防止车辆弹来弹去



又修改了一些其他的设置,作者也不知道为啥,但是跟nvida的建议有关。

Rendering 禁用静态光照

后处理关闭自定义深度模板

修改collisions




配置了一堆这个玩意,现在还不知道啥意思。比unity那个层级感觉更加的复杂。
明天开始魔改引擎。

【Day17】
5-5 Unreal Engine Modifications 修改引擎
1.版本对比软件:WinMerge




2.复制粘贴代码
3.简单讲述粘贴的代码
Stand tall and figure it out, or skulk away and discard the changes.
用于适当平衡车辆


to avoid player criticism by implementing this stuff.
用于计算local bounds



需要获取碰撞信息,知道具体发生了什么,做处理,不然的话,车辆会在随便的小碰撞中发生旋转。这个方法,是在碰撞修改过程中的一点胶水。




需要调整车辆展示的console面板





现在我们知道了,什么是box volume of space collision shaped converts(空间碰撞体的包围盒)
我们需要知道这个的原因,是之后我们需要知道把轮胎接触传感器放在哪。(the contact sensors for our tires)
相对于车的水平方向,这样它们就在碰撞体积内。(inside of the collision volume)
我们不需要任何侧面的contacts,只需要上下的。



重写了惯性张量(inertia tensor),都是与平衡车有关的。

做这些操作的原因,是因为需要能明确指出车辆的质心。原游戏做不到。都是封装起来的,并且看不到。
必须要有一个明确的质心,才能进行明确的物理计算。这里我们通过集中质量来解决这个问题(centralizing the mass)
同样的,物体的惯性张量的力,也是很随机的,作者做了处理。
以上就是处理的代码。
钩子:重写,角色对碰撞做出的反馈

新增碰撞方法,因为chaos出了,它做破碎效果不错,但是不是一个完备的物理系统。
Epic在两套物理上做了同样的接口,这是好事,但是不能随意的调用代码了必须走通用接口。

提交更改:
提交的时候出了问题,我的本地没提示有代码的修改。

作者提交了两次,第一次提交代码我无法提交。
作者进行了第二个步骤的提交操作:depot右键engine目录,选择offline。然后再提交,我比他多出了代码。
我怀疑是我代码也被识别成了跟dll一样的东西。所以虽然察觉到更改但是不让我提交?



反正提交上去了。

【Day18】开始造车!
5-8 We hold our fate in our own hands here!
导入模型Dreamnought,梦想零 ,无畏号。
碰撞信息:



This is a physics body, comprised of several component shapes that define the body.
这么设计的原因之一:不想和地面有太多的碰撞,很容易飞起来。
其他原因:有很多,虽然官方说明尽量不要让碰撞体有重叠,但是这就是经过实践测试适合这个项目的 。
Nvdia官方给的建议是使用一个cube,在99的情况,cube都还不错,但是在很小的情况下,这种做法表现的更好。
做游戏,细节很重要。
One bad apple, can spoil a whole barrel.
Modify Engine:
在UE中,有一个center of mass offset,允许你对质心进行位移计算,但是不允许你直接将它设置为一个绝对值。它是一个相对于其计算位置的偏移量,这个位置永远不会显示在任何地方。
因此,我们在PhysicsBody中添加了一个centralized mass集中质量,将中心设置为0。



尽量不要修改引擎,除非不得不改。
重量:9000kilo
Liner damping线性阻尼:0 因为我们将在车辆动力学代码中应用非常具体的力
Max Depenetrantion 最大穿透速度:200 有助于减少可能从物理模拟中获得 的碰撞反应。200cm,2m/s。
强制集中质心:为了车辆平衡。

重写了惯性张量:通过自己计算,更加可控。
碰撞的反馈:勾选
碰撞材质球:消除了所有摩擦和碰撞的阻力。
总结:加了更多可控的东西,因为在物理模拟中,车辆越是向环境开放 ,就越无法驾驶。有时候一个小小的摩擦力就会卡住玩家。

【Day19】Back to code
5-9 Exploration of the GRIP GRIP探险
作者提供了代码。我打算,把这些文件一个一个一行一行拷过来。
跟着第一个视频。
1.Grip.Build.cs
添加了众多模块。
2.Grip.cpp
用到了宏定义,添加自定义log。
宏定义,预处理:
C++ 预处理器 | 菜鸟教程 (runoob.com)
预处理器,本质上,是在编译之前,对固定格式的文本进行文本替换。所以它可以理解为一种语法糖。
' #和##:# 运算符会把 replacement-text 令牌转换为用引号引起来的字符串。## 运算符用于连接两个令牌。
一个宏定义,就麻麻的了。
1)定义一个拼接的缩略写法


2)全麻

    /** 
     * A macro to declare a logging category as a C++ "extern", usually declared in the header and paired with DEFINE_LOG_CATEGORY in the source. Accessible by all files that include the header.
     *一个宏来声明一个日志类别为c++的“extern”,通常在头文件中声明,并在源代码中与DEFINE_LOG_CATEGORY配对。 所有包含头文件的文件都可以访问。  
     * @param CategoryName, category to declare
     * @param DefaultVerbosity, default run time verbosity
     * @param CompileTimeVerbosity, maximum verbosity to compile into the code
     **/
    #define DECLARE_LOG_CATEGORY_EXTERN(CategoryName, DefaultVerbosity, CompileTimeVerbosity) \
        extern struct FLogCategory##CategoryName : public FLogCategory<ELogVerbosity::DefaultVerbosity, ELogVerbosity::CompileTimeVerbosity> \
        { \
            FORCEINLINE FLogCategory##CategoryName() : FLogCategory(TEXT(#CategoryName)) {} \
        } CategoryName;

【Day20】Back to code2 炎喵加油!!
学习c++,初步目标,看懂ue的宏定义,就是上面那段代码。
创建一个空的c++控制台工程,进行宏定义拷贝学习。
拷贝这块Log日志系统!
1.GenericPlatform.h


自定义类型字节及比特:在C语言中,double、long、unsigned、int、char类型数据所占字节数_善于总结-CSDN博客_long类型数据占字节

声明模板类:



使用模板类的typedef,进行typedef套typedef定义:



2.宏定义:

【Day21】Back to code3 炎喵加油!!
【C++】define宏定义中的#,##,@#及\符号_Trey_CSDN 的CSDN博客-CSDN博客
\不能切断单词,只能在空格地方加。
extern关键字:告诉编译器,存在这样一个东西,你就先用就完事了,不用管它在哪,就是有!
const修饰函数:梳理c++ const 修饰函数 - 知乎 (zhihu.com)


前面的const很好理解,修饰返回值,防止引用获得的返回值被修改。
后面的const,是修饰成员函数的。
上文中,提到了一个函数签名概念,

它把this,作为参数,传到第一个位置,这个让我想到了c#的扩展方法。我可以猜想,其实原理,都是变成扩展方法那个造型的,把this作为参数传入。整体是一个static的函数。所以,这里,const修饰了this指针,也就是,所有this调用的,也必须是const函数,并且不能修改数据成员:


出了点小问题。代码无法运行,是一段废代码,虽然我还不知道问题出在哪。
继续看书
c++类型强转:
static_cast:常规类型转换
const_cast:常量类型转换,可以去掉const属性
函数指针:
bool TestFunc(const string & ,const string &) 可以写成:
bool (*pf)(const string & ,const string &) pf就是一个指针,指向辣么一个函数:返回值为bool,参数为两个string引用
预处理宏,是从c语言继承过来的。c++本已经很少使用了。
但是,UELog这个地方,可以理解为,通过传参方式自动生成部分代码类。(也就是,自己传入一个名字,根据这个名字,动态的创建一个继承自Log基类的名字类。)
预处理不运行了,我放弃了。跳过,下一个!!!
跟着视频先看一遍吧。太多代码了。

5-9
介绍了整体结构。
项目配置,车辆,车轮(检测器),AI,相机,拾取道具,路点系统,其他杂七杂八
作者用c#写了一个分离器,根据开关,逐步分离代码,确保每一部分都是可以独立编译 。
第一课的代码,只有大概的骨架支持,没有填写太多的内容。(已经吐血了)

【Day22】
5-10
调整物理模拟速率,因为低速率模拟,弹簧系统等系统会出问题。epic提供的默认速率也是不够的。
我们自己的代码,是每渲染帧调用,而物理计算,是每次物理演算,都需要获得交互点。尤其是我们的suspension spring弹簧悬挂系统,
需要和物理模拟同步进行。
重点!!物理模拟,是在独立线程中运行的 。
为了做这件事情,做了一个类,进行单一复杂功能的打包处理。不需要知道怎么做的,只要知道为什么这么做。
VehicleMeshComponent:
保证线程安全。重写了很多SkeletalMeshComponent的方法,但是原方法很多传参方式是值传递不是引用传递这会造成性能影响。
我们将把所有方法,通过参数声明为常量引用(const &),来进行修改。
但是对于重写的虚函数,只能忍受。
IdleLock:当车速很慢时halt(空转),计算就会非常不稳定erratic。
写的跟物理相关代码,需要包裹起来,不管是读取还是写入(作者加了洋葱)。
这实现了在多线程中,调用ue底层物理子系统的线程安全。
重写:we need extra functionality over the stock classes:我们需要比本来的类有更多的功能。Its derived from origin class.
弹簧系统很重要:It pretty much had to be all or nothing.v
到目前位置,啥都没干,单纯介绍,作者让我不要慌。
As and when we start introducing features and functionality to the vehicle, that's when we're going to get in deeper and understand how all of that is happening.


【Day23】
Part1
新的同步计划:创建一个自己的模块,把Chaos物理系统的碰撞检测功能,和Actor基础移动的检测代码,剥离出来,实现自己的简化版本actor和chaos。
UBT和UHT
UBT:Unreal Build Tool
收集阶段:收集环境变量,源码目录,引擎目录
参数解析阶段:根据传入的命令行参数,确定自己需要生成的目标类型(.exe,.dll,.lib..)
实际生成阶段:根据环境和参数,生成makefile,确定c++各种目录的位置。最终开始构建整个项目,此时编译工作交给标准c++编译器。
关联文件:.target.cs
UHT:Unreal Head Tool
编译,解析头文件定义,并生成c++代码
关联文件:.generated.cpp .generated.h
用途及原理:
查户口。因为,UE中的UClass,是对原装Class的一层封装,UClass相当于一张户口表,记录了每个真正Class的位置,以及一些额外信息。
但是有一个问题,如果直接用指针地址获取位置,每一次编译,函数地址可能会发生变化,每一次运行,函数映射到内存的位置也会不同。因此不能直接获取地址。
因此,UE做了一套操作,它在户口本,给每个类都创建了一个,起了个独一无二的名字的静态全局变量实例,放在.generated.cpp中(在IMPLEMENT_CLASS宏中)
因为静态全局变量的初始化,在main函数之前,它会在main之前把类的信息注册到户口表中,在main调用的时候就能获得正确的地址了。
这样,就可以根据类名等东西,找到对象,也就是UE的反射机制。


Part2
5-11 diagnostic checks && asserts诊断检查,和断言
young and naive 不要年轻又天真,以为自己写的东西不会出bug
Wrapping code in CHECK will only execute that code and assert with a halt dialog if it fails in non-shipping builds
用check来封装代码,会执行那段代码,并且会通过停止对话来断言,在非运维构建中失败的情况。(失败时执行)
Wrapping code in CHECKSLOW macro will only execute that code and assert with a halt dialog if it fails in debug builds.
用checkslow 宏 来封装代码,会执行那段代码,并且会通过停止对话来断言,在debug构建失败的情况。(失败时执行)
VERIFY宏:永远会执行代码,仅仅nonshipping构建失败时,会停止对话来断言
VERIFYSLOW:debug模式,其他同上
ENSURE:会一直执行代码,但是会在失败时打印信息,而不是停止对话,在所有的模式下。



check和verify,会kill the program entirely
这些东西存在的意义,主要是为了说明,这里的代码不能是怎样的,可能会崩溃,错误,等。

【Day24】
Part1
编译4.27版本引擎,按照官方说明开启Chaos,编译失败。
混沌崩溃 4.25 - UE4 答案 (unrealengine.com)
现在按照这种方式重新编译。


Part2
5-12 车轮碰撞
1.车轮sweep检测,防止和多边形交叠
so that it doesn't intersect any geometry, but sits upon it, assuming it's close enough to touch it.
2.弹簧悬挂系统
Implement a spring model to simulate suspension and make the vehicle body react in a realistic manner,
the more, a will mounting point on the vehicle body, tries to push down towards the nearest surface, the more the spring react to push it away, just as in real life.
VehicleContactSensor:接触传感器
This is critical not only to what we want to accomplish here today, but also in getting our vehicle handling well.
Suspension played a crucial part in taming the handling of the game.
And in order to do that, we had to control our suspension forces much more tightly than you might expect.
重大问题:车辆反弹

扫描算法:GetCollision
调用物理,只有一个轴是物理检测计算,其他的是自己写的模拟计算,为了减少计算量。
Broadly, though, when we are estimating we simply remember the last real contact point from the last
real sweep, and create a plane from that and then determine the intersection point between the contact
sense of direction and plane ,much, much more cheaply.
一个平面,是一个无限长,无限平的表面,位于空间的一个点上,在ue中以一个特定的方向定向。平面通常被描述为一个位置或点,与平面的法线。
在真实中,车辆的碰撞三角面,不可能是一个无限的概念面。但在一帧过程中,车辆不可能移动那么远。
所以我们假设三角形比实际情况大一点。And our assumption is fine in practice.It works seamlessly.
在起点检测到,为0,在终点检测到,为1。几乎总在中间的某个地方。
弹簧算法:ComputeNewSpringCompressionAndForce


Part3 文件夹内容更新对比
071->075
资源:



代码:
1.FlippableSpringArmComponent.cpp
pragma region VehicleSpringArm
2.LightStreakComponent.cpp
预制体材质球设置
3.BaseVehicle.cpp
预制体材质球,声音设置
pragma region VehicleSpringArm
4.!!!!!!!!新大陆!!!



075->077
接下来,开始自己进行局部代码抄写,而不是文件夹替换。
资源:无
代码:

1.StrippingConfiguration.h



2.VehicleContactSensor.h

【Day25】
Part1
断点查看了一下chaos的源码;
人物移动,是很复杂的,最普通的移动也调用了一层又一层的方法,包括各种优化的过滤算法。
最终,才会调用到物理世界,物理运行在主线程外另外的线程。
这块的东西不是很快能吃下来的;
接下来的目标,就是根据下面的类图,以及源码,在ue中创建单独的模块,走主线程,尝试胶囊体的移动检测。每天一上午吧。
《Exploring in UE4》移动组件详解[原理分析](2019.7.14更新) - 知乎 (zhihu.com)
UE4 Chaos框架解析 - 知乎 (zhihu.com)


Part2
车车能跑了,Rider也很好用不卡了。
回退到最开始的代码版本,重新开始逐次叠加代码及细节分析。



根据上一个工程,初次版本提交,需要提交这些文件。



第二次,作者增加了一堆资源文件,包括新的车预制体。在代码初步组装的基础上,创建了子蓝图,给它配置了车轮,特效,等。

代码部分:
我知道为什么之前不能提交代码了,必须把这个客户端先关掉,再整体替换代码,它才能在workspace中提交。不然只能像之前那种方式操作(好像是断开depot连接等系列操作)

找到了新的操作,不用关客户端,在depot中点击checkout,然后就能提交了。



添加了 GRIP_VEHICLE_SPRING_ARM。是车辆的弹簧臂,接下来依次查看做了哪些事情。
首先看一下预制体:

代码修改1:FlippableSpringArmComponent.h FlippableSpringArmComponent.cpp

设置了数据结构,相机的初始偏移量,依赖x,z轴向。ue里,x轴是unity的z轴(前),z轴是unity的y轴(上)

用于碰撞测试的查询参数,防止相机裁剪到几何图形里。

与帧率无关的平滑算法
这两个脚本,从TickComponent驱动,根据Vehicle当前状态,不断更新相机Spring轴的属性值。具体的写法,还是很复杂的,内容比较多。

代码修改2:BaseVehicle.h BaseVehicle.cpp
绑定了相机按键
在Tick中更新了物理和时钟

小结:作者在编写这部分代码的时候,除了通用的辅助方法和工具类,其他部分是标准的面向对象编程方式。车,相机,物理,时钟,护盾...
以最直接的方式编写每个部分。数据结构也都是非常清晰明确的,每个变量都有用。目前为止车还是不会动的,只是相机已就绪。
下一章节,查看车辆是如何离开地面的。

【Day26】
先看Hud脚本,了解作者想要做的事情。然后看Vehicle及相关的Wheel,ContactSensor等脚本。


查看代码,物理更新是通过BaseVehicle.OnCalculateCustomPhysics引擎回调函数,进行更新。
回调函数的使用分两部分:
因为物理是在多线程中执行,在渲染帧Tick时调用PhysicsBody->AddCustomPhysics(OnCalculateCustomPhysics);执行上锁(不知道为啥上锁);回调会执行多次,需要分批处理。
在初始化Vehicle时绑定回调:OnCalculateCustomPhysics.BindUObject(this, &ABaseVehicle::SubstepPhysics);
核心方法:UpdateContactSensors,在上述回调中调用。传入车辆速度朝向等信息,获得碰撞信息。

会更新车轮的传感器,一个车轮有两个传感器,一个检测上方,一个检测下方。
传感器,FVehicleContactSensorTick方法:
CalculateContactPoint会进行射线扫描检测,确定上下方的物体,物体材质,距离,等等,这个步骤不是物理,这是单纯的碰撞检测。


根据四个车轮的检测信息,确定车辆的物理状态,空中,地面,曾经空中。
接下来的一大段代码,是复杂的情况判断,防止车辆有不确定的状态,可以说是大量的补丁。
最后,是给每个车轮,施加力,最终是给车辆施加具体的力。这里使用了物理,进行了物理写入。(最上面那个物理上锁,没有施加确定的力,这里是确定的力)

前后轮,高度不一致,悬挂系统压力大小不一致,最终计算受力不一致,所以车一开始会往前跑。


作者要求,深度挖掘源码,因为这部分代码都还算简单的,趁还能看懂,趁还有很好的注释,趁他还在讲解。不然,遇到真正的代码库 ,人直接麻。

【Day27】
Part1
响应作者的要求,自己开始创建一个新的项目,手抄代码。
声明log,创建gamemode,创建gamestate,gamestate中包含了各种游戏设置,如声音,输入,分辨率,抗锯齿,存储和读取,以及瞬时状态:fps,玩家名称等信息。代码并不难,新鲜的是写法。


Part2
5-12.15:47
作者进行扫描优化,本来是四个轮子,每帧上下共扫描八次,比较耗。
优化第一步:简化成四次扫描,根据第一个扫描点是上面还是下面。
优化第二步:根据第一个扫描点,估算出一个平面位置,然后其他轮子位置根据这个估算出的平面进行估算。
假如,这一帧用了前轮估算,下一帧就用后轮估算。It's an estimate.
因为一帧时间内,车不会跑太远,这种计算方式是没什么问题的。
estimate time:0-1,代表是接触上下。
作者说最终是从八次变成两次。待细看。
27min00

【Day28】
Part1


Part2
VehicleContactSensor->Tick

【Day29】
不会继续看代码了,先把它的ue代码结构都看下来,包括具体写法。
1.UWorld final
代码用了很多GetWorld,获得什么都走GetWorld。World里面,有八千多行API代码,基本上可以很齐全的调用游戏引擎的功能设置。
2.ABaseGameMode: AGameMode
UE中有一个游戏模式的概念。
游戏模式做的功能:
加载地图场景,音量设置,输入设置,更新时钟,休眠/唤醒组件。
AGameMode : AGameModeBase
多人游戏匹配等
AGameModeBase : AInfo
只在服务端有一个单例。
管理游戏规则,分数,游戏中都有哪些Actor,等。

游戏模式会和游戏状态密切相关,所以持有了一个UGlobalGameState

3.UGlobalGameState : UGameInstance
声明了很多游戏状态结构体,并在该类中有一个引用,和各种调用方法。
如,通用设置,音频设置,图形设置,瞬时状态
保存设置,读取设置
UE也自带保存的类,需要继承自USaveGame

UGameInstance : UObject
游戏运行中,高层次管理的一个单例。
游戏启动时自动创建,游戏结束时销毁。
但是在play-in-editor中,多少个PIE多少个这个东西。

4.CommonTypes
放了一些常用枚举类

5.GameConfigurations
log声明,及其他的一些宏定义



初步这些脚本构成了项目基础结构,拥有一个游戏的基础功能:游戏模式,游戏状态。

【Day30】
接下来,是把车弄进来。然后一行一行把车的代码写出来。

第一个坑:工程编译,Slate报错,提示无法解析的符号。
解决方法:把Slate模块的引用,从private挪到public。

问题2:如何创建基础预制体
首先从外部导入带骨骼的模型,这时候会生成一个SkeletalMesh。
然后选中该模型,右键,创建Skeleton骨骼,和PhysicsAsset物理。
物理性状的创建:点一下下面按钮是自动创建,但是车辆的这个自定义物理性状是怎么创建及导入的,不清楚。



现在的方式,是,一个ue来替换代码,查看工程的变化;另一个ue,在基础框架上,用手实现资源和代码的叠加。接下来目标:创建类PlayGameMode,以及车辆Actor,和蓝图。然后相机部分也会带入。这部分结束之后,就可以开始做小项目练手了。


【Day31】
UI类


【Day32】
官方案例ActionRPG学习
它基本上全用的蓝图。

但是先打开代码。
自定义模块:



其他的,有一些支持安卓ios平台的设置。

有一个RPGGameMode脚本。
指定了游戏模式的控制器,和游戏状态。
根据这个类,先创建蓝图,然后通过蓝图做一些设置(替代代码)
包含,控制器代码,控制器蓝图;游戏状态代码,游戏状态蓝图。
创建好蓝图后,在游戏模式中配置蓝图。


蓝图的编写

游戏模式里,Event蓝图,是游戏流程蓝图
有怪物生成的蓝图

游戏模式,游戏保存,数值系统,是在代码里写的 都会开放出蓝图接口。
大部分在蓝图里配置。

计划:
首先,明白这个项目是怎么打包装到手机上。
然后,把关键的项目配置代码,拷贝到新的工程成功打包安装到手机上。

研究我们自己的需求,需要什么,就去找什么,一步一步实现我们的功能。
需求:
游戏模式,控制游戏开始和结束,先随便写一个;
需要有一个起始ui面板,可以点击开始。
在点击开始的时候,我们有地图块,随机生成地图快;
玩家属性:金钱,血量
怪物:能寻路。(后面可把寻路去掉)

【Day33】
现在是上午9:40。
蓝图项目:需要研究他搭建游戏的流程,怪物生成,寻路等多种细节。
代码项目:需要继续抄代码,看视频解释。
现在是下午6:00。
很难想象,到现在为止我只是打开了项目。八个小时在做一些乱七八糟的事情中就过去了。总比学习有意思。但是我真的要开始学习了。

PlayGameMode中,用到了一个SingleHUDWidget,是全局的UI。
所以去搞HUDWidget。
HUDWidget用到了System.TimeSmoothing.h,用到了System.MathHelpers.h。所以搞这两个。

HUDWidget

TimeSmoothing
针对时间,和常用操作的值列表
高度优化的一个简易Queue,老的TArray替换远不如它快。
用了环形队列
缓存下来时间,可以求和,求平均值,这样可以隔一段时间请求一次而不是实时请求,以节约性能。

MathHelper
GetSmoothingRatio:根据当前deltaTime,和目标帧率,得到恒定速率的平滑,而不管帧率是多少。


image.png


【Day34】
Grip:



ActionARPG:
1.表:


2.预制体创建,蓝图配置:



蓝图界面配置已经通了。

3.创建资源DataAssets
首先自己写一个代码继承自这个东西
然后右键


【Day35】
UE事件系统:(委托)
UE4中的委托 – UnrealBook's Blog
单播委托,多播委托,动态委托,动态多播委托...
多播委托的意思,就是可以有多个参数。
动态委托的意思,就是需要把变量的命名也要填充。为了序列化到面板上给蓝图用。

大概看了看ARPG英文文档和代码。
一些基本的类是能直接用的,不需要自己重新抽象。
这一套主要实现了基础简易的结构;背包和道具,数据表。
代码提供了基础框架,很多真正的编程,是在蓝图中完成的。

计划:
明天,把ARPG的UI部分扒下来。事件系统,蓝图流程,扒一遍。

【Day36】
现在是十二点十五分,
需求:游戏模式初始化配置,游戏状态初始化配置,起始ui及场景跳转,游戏开始生成地图数据及摆放
1.初始BP_MainMenuGameMode
就是从引擎自带的GameMode派生出的蓝图
第一个问题:自定义ObjectType



第一步,点开这个东西,学习拼UI。
跟着拼。比较人性化,很好用。

按钮点击,需要触发一些蓝图方法。蓝图一部分是继承自代码,然后蓝图实现了一部分方法。


BP_FuncLibrary:
用来获得全局的一些对象,比如获得游戏单例,获得游戏模式对象,获得玩家控制器,获得玩家角色,获得鼠标指针等。

GlobalOptionsSaveGame:
配套一个自定义结构体,存储一些自定义的属性值。

GameInstance:
可以获取游戏设置,设置游戏设置。

GetGlobalOptions:
保存和加载。首先创建蓝图资源
这是蓝图创建的结构体和蓝图对象。



第一个蓝图,获得存储的数据,没有的话就先创建,放到指定槽位。



SetGlobalOptions
这里面,全局的属性只是一些音频参数。所以设置全局属性也是设置了声音相关的内容。
下面这个按钮不要勾选,不然会少很多节点。

蓝图难用的一笔。但是我没办法。先用这个捋清思路吧。
目前,这个Get和Set,是保存一些数据选项,以及获取数据选项应用到游戏的具体内容。
数据结构能用代码写还是用代码更好。这里先不管。

游戏基本蓝图


阶段小结:
蓝图:节点可以直接从原工程拷贝过来,有一些节点会拷贝和编译失败。这时候需要自己手动搞。
蓝图最麻烦的是,原工程有这个节点,而自己没有。这时候可以看这个节点是属于谁,有一些是在其他的蓝图中创建的变量,
有一些是从代码中创建的变量;有一些是其他蓝图中创建出的结构体,有一些是代码中的,按ALT可以查看这个节点细节,找到它的源头。
还有一些东西是创建出的资源实例。
UI:UI是很方便的编辑器,落unity不知道多少条街。包括序列化也落unity一大截。

==========================================

进入起始页面,鼠标一直是隐藏的。不知道它原项目怎么显示的,我在gamemode中加了一个显示鼠标



在后面的WB_Title找到了隐藏鼠标的地方,就是focus那块。

==========================================
战斗场景配置,战斗游戏模式配置,玩家配置

【Day37】
今天把c#的数据结构和算法抄到ue中来。

============================================
【Day N】

已经可以进行c++代码编写了。
UE4_最全FString字符串与各格式转换 输出 - 知乎 (zhihu.com)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 193,446评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,450评论 2 369
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 140,740评论 0 318
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 51,962评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,842评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 45,982评论 1 271
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,408评论 3 380
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,123评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,348评论 1 287
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,472评论 2 307
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,248评论 1 324
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,117评论 3 310
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,494评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,834评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,109评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,364评论 2 340
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,560评论 2 335

推荐阅读更多精彩内容

  • -----整理有关UE4的一些基本内容,作为以后一直更新的内容。 UE4来源于什么年代? 早在2005年8月时,E...
    Numdia阅读 3,322评论 1 2
  • Unity技术经理Sam Dogantimur为Unity初学者精心挑选和推荐了5款入门必备资源插件,涵盖游戏开...
    付老师的海角阅读 282评论 0 0
  • 想从事游戏开发,1 年内能精通 C++ 吗,还需要学习什么? 【MiloYip的回答(383票)】: 本人大约从2...
    踩在浪花上00阅读 23,319评论 18 204
  • 表情是什么,我认为表情就是表现出来的情绪。表情可以传达很多信息。高兴了当然就笑了,难过就哭了。两者是相互影响密不可...
    Persistenc_6aea阅读 123,052评论 2 7
  • 16宿命:用概率思维提高你的胜算 以前的我是风险厌恶者,不喜欢去冒险,但是人生放弃了冒险,也就放弃了无数的可能。 ...
    yichen大刀阅读 6,014评论 0 4