读《虚幻引擎程序设计浅析》笔记
Posted cartzhang
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了读《虚幻引擎程序设计浅析》笔记相关的知识,希望对你有一定的参考价值。
本文章由cartzhang编写,转载请注明出处。 所有权利保留。
文章链接: https://blog.csdn.net/cartzhang/article/details/88427849
作者:cartzhang
有两年没有怎么写东西了,
希望今年有时间和精力,多读书,多写些文字。
一、 c++编程
1.运行时类型识别
UE4不能直接使用dynamic_cast,因为虚幻引擎开启了/GR-编译器参数。
具体参考:https://msdn.microsoft.com/zh-cn/library/we6hfdy0.aspx
可以继承UObject,然后使用Cast<>来实现。UE自己实现了一套运行时的识别方案。
2.对象
Actor: 可以被挂组件。
Pawn:卒子,可被Controller操纵。
Chracter:继承自Pawn,添加了CharacterMovement。
Controller:就是控制器,可以Posses/UnPosses控制一个Pawn.
- new
纯C++类可以使用智能指针来管理。TSharedPtr/TShared_Refl来管理。
TSharedPtr<YourClass> ClassPtr = MakeShareable(new YourClass());
- 字符处理
FName : 无法修改的字符串,大小写不敏感。是借助哈希表个,字符串到FName转换,根据Key查询,整个表里只会存放一个。
FText,用于显示的字符串。这个不支持修改操作。对于被显示的字符串来说,修改是一个非常不安全的操作。
slate文字参数往往是FText,这个是为了本地化要求。
FString,这个是常用的,可以修改,消耗也大于FName和FText.
二、核心系统
1. 内存分配。
window 下提供了三种内存分别方式,分别为Malloc(ANSI),Intel TBB 内存分配器,和Binned 内存分配器。
对于内存分配,主要有两个方面,
一个是通过内存池降低Malloc 消耗。
一个是通过对齐,降低缓存命中失败消耗。
2. 引擎初始化过程
不同的平台,文件都存放在Engine\\Source\\Runtime\\Launch\\Private 文件夹下面。
Linux 平台下更容易看一点:
int main(int argc, char *argv[])
FPlatformMisc::SetGracefulTerminationHandler();
int ErrorLevel = 0;
// read the command line file
// 初始化命令行
InitCommandLine();
FPlatformMisc::LowLevelOutputDebugStringf(TEXT("Final commandline: %s\\n"), FCommandLine::Get());
#if !UE_BUILD_SHIPPING
GAlwaysReportCrash = true; // set by default and reverse the behavior
if (FParse::Param(FCommandLine::Get(), TEXT("nocrashreports")) || FParse::Param(FCommandLine::Get(), TEXT("no-crashreports")))
GAlwaysReportCrash = false;
#endif
if (!IncreasePerProcessLimits())
fprintf(stderr, "Could not set desired per-process limits, consider changing system limits.\\n");
ErrorLevel = 1;
return ErrorLevel;
// if (!FPlatformMisc::IsDebuggerPresent() || GAlwaysReportCrash)
//
// // Use default crash handler, but we still need to set this up to register signal handlers
// FPlatformMisc::SetCrashHandler(nullptr);
//
// ErrorLevel = GuardedMain(FCommandLine::Get());
// initialize the engine
//预初始化引擎
GEngineLoop.PreInit(0, NULL, FCommandLine::Get());
// initialize HMDs
// @todo Lumin: I guess we don't need this?
// InitHMDs();
UE_LOG(Logandroid, Display, TEXT("Passed PreInit()"));
GLog->SetCurrentThreadAsMasterThread();
//引擎初始化
GEngineLoop.Init();
UE_LOG(LogAndroid, Log, TEXT("Passed GEngineLoop.Init()"));
#if !UE_BUILD_SHIPPING
if (FParse::Param(FCommandLine::Get(), TEXT("Messaging")))
// initialize messaging subsystem
FModuleManager::LoadModuleChecked<IMessagingModule>("Messaging");
TSharedPtr<ISessionService> SessionService = FModuleManager::LoadModuleChecked<ISessionServicesModule>("SessionServices").GetSessionService();
SessionService->Start();
// Initialize functional testing
FModuleManager::Get().LoadModule("FunctionalTesting");
#endif
// tick until done
while (!GIsRequestingExit)
// FAppEventManager::GetInstance()->Tick();
// if(!FAppEventManager::GetInstance()->IsGamePaused())
// 主循环Tick
GEngineLoop.Tick();
// else
//
// // use less CPU when paused
// FPlatformProcess::Sleep(0.10f);
//
#if !UE_BUILD_SHIPPING
// show console window on next game tick
// if (GShowConsoleWindowNextTick)
//
// GShowConsoleWindowNextTick = false;
// AndroidThunkCpp_ShowConsoleWindow();
//
#endif
// FAppEventManager::GetInstance()->TriggerEmptyQueue();
UE_LOG(LogAndroid, Log, TEXT("Exiting"));
// exit out!
// 引擎退出循环
GEngineLoop.Exit();
// 应用退出
FEngineLoop::AppExit();
FPlatformMisc::LowLevelOutputDebugString(TEXT("Exiting is over"));
if (ErrorLevel)
printf("Exiting abnormally (error code: %d)\\n", ErrorLevel);
return ErrorLevel;
1)预初始化
主要工作有:
1)设置路径:包括当前程序路径,工作目录路径,游戏工程路径。
2)设置输出标准: 设置GLog 系统输出设备,是输出到命令行还是其他。
初始化一部分系统包括:
初始化主线程 GameThread.
初始化随机数系统
初始化TaskGraph,并按照当前平台的核心数量来设置工作线程数量。
判断引擎的启动模式,是游戏模式还是服务器模式。
完成后加载核心模块,PreInitModules 启动。
包括有引擎模块,渲染模块,动画蓝图,Slate渲染模块,Slate核心模块,贴图压缩,和地形模块。
2)初始化
若模块中有PostEngineInit函数,则会通过IProjectManager 来调用完成初始化。
3)主循环
引擎中Tick更新顺序:
更新控制台变量
请求渲染线程更新当前帧率文字
更新当前应用程序时间,也就是App::DeltaTime.
更新内存分配器的状态
请求渲染线程刷新当前底层绘制资源
等待slate 程序输入状态捕获
更新引擎GEngine,调用GEnigne->Tick
更新SlateApplication.
更新RHI
收集下一帧需要清理的对象
3.对象模型
1). 垃圾回收
UObject 分两个遍历步骤
首先,通知Uclass父类进行析构,然后是收回UOjbect占用的内存。
垃圾回收算法大致有两种,一种是引用计数发。一种是标记-清扫算法。
分类 | 项目 | 描述 |
---|---|---|
引用计数 | 中等文本 | 稍微长一点的文本 |
中等文本 | 稍微长一点的文本 | |
保守/精确 | 短文本 | 中等文本 |
分类 | 项目 | 描述 |
---|---|---|
引用计数/追踪式GC | 引用计数 | 通过额外的计数来对单个对象的引用计数进行计算,当引用计数为零时候,回收对象 |
追踪式 | 扫描系统对象引用网络,寻找被引用对象,留下的即没有被引用的为垃圾 | |
保守/精确 | 保守式 | 可不使用额外信息,不修改现有框架。释放绝对不可能被引用的对象。原则:不求全部回收,但求不错删。 |
精确式 | 需要额外信息来辅助识别指针字段,但是能够精确识别每个被引用对象。 | |
搬迁式/非搬迁式 | 搬迁式 | 在GC过程中,内存位置会移动。 |
非搬迁式 | 在GC过程中,内存不发生移动。 |
虚幻系统的垃圾回收机制,采用智能指针,用引用计数法来进行智能指针处理,
引用计数的缺点在于指针开销和环形引用。
虚幻使用弱指针方法来避免环形引用。
2)UObject 标记清扫算法
虚幻引擎使用了基于簇的垃圾回收算法,用于加速Cook 后对象的回收。
三、参考:
- 《大象无形-虚幻引擎程序设计浅析》
- https://msdn.microsoft.com/zh-cn/library/we6hfdy0.aspx
以上是关于读《虚幻引擎程序设计浅析》笔记的主要内容,如果未能解决你的问题,请参考以下文章
虚幻4引擎源码学习笔记:主循环LaunchEngineLoop