UE3代码阅读需知
概述
本文档是为正在开始使用虚幻引擎进行开发的程序员提供的。它简单地讲解了基础代码的各个方面。它绝不是一个针对引擎的完整指南,而是一个在您编译引擎代码之后的一个很好的准备工作。如果您还没有编译您的项目,请访问首先访问: 入门指南或快速入门页面。
目录结构
当从Perforce上同步了UE3基础代码后,在您的Perforce depot(仓库)的根文件夹中应该有一个称为 UnrealEngine3 的目录。
在"UnrealEngine3"文件夹内有以下目录:
- Binaries - 这个文件中包含二进制对象。比如,编译后的 引擎/游戏 版本、UE3所需要的DLL文件(Direct X、wx 运行时系统等)、编辑器资源文件(图标图像)。
- Development -这是引擎的源码真正存在的地方,在这个文件夹中有:
- Build -这里存储着用于启动在我们的Continuous Integration Server(CIS)[连续集成服务器]上各种类型编译的批处理文件。CIS可以为我们检查编译并尽可能地捕获编译错误。
- Documentation -存储文档文件的文件夹。我们自动生成的windows帮助文档就存储在这里(UnrealEngine3.chm)。这个帮助文档是帮助您快速浏览Unreal源码的很好的资源。
- External -所有不是Epic公司的库源文件存储在这个文件夹中。比如libPNG、 wxWidgets、zlib、Cg等。
- Intermediate -所有的中间编译文件存储在这里。.obj文件、预编译头文件等。
- Src -源码文件和项目的makefiles。主要的UnrealEngine3解决方案存在于文件夹中。在这个解决方案中也包含了每个项目的文件夹。一个实例项目可以像这样进行细分目录:
- Game Folder(游戏文件夹)
- 在工程主文件夹中是工程的visual studio项目文件。
- Classes -保存.uc(UnrealScript)文件的文件夹。
- Inc -保存.h文件的文件夹。
- Src -保存.cpp文件的文件夹。
- Game Folder(游戏文件夹)
- Tools -保存各种引擎相关工具的所有源码的文件夹, 比如UDE、vistual studio macros(vistual studio宏)及一些命令行工具。
- Engine/Game folders -在Binaries 和 Development文件夹之后是文件夹Engine,然后是使用这个引擎的每个游戏的文件夹。
- Config -包含着用于保存那个游戏设置的.ini文件。
- Content -包含那个游戏内容包的文件夹。
- Localization - 包含那个游戏的任何本地化文本的文件夹。
- Logs -包含着当运行那个游戏时产生的日志文件的文件夹。
- Script -包含着那个游戏编译后的UnrealScript代码(.u文件)。
工程结构
为了分离系统并帮助维持跨平台的兼容性,UE3被分割成了多个工程。
基本的结构如下所示(从最层代码到高层层次代码):
- Core
- Engine
- Editor
- UnrealED
注意,所有的这些工程都要建立于彼此之上的,这意味着Engine依赖Core、Editor依赖Engine、UnrealED依赖Editor。
- Core: Core是最低级别的类,它是指一些类似于字符串类、数组类、内存管理器等东西。这里是核心脚本和加载代码所存在的地方。
- Engine: Engine文件夹是渲染和游戏性代码存在的地方。同时包括了和actors、碰撞检测以及很多其它的子系统。这里是大多数‘游戏界面’代码所在的地方。
- Editor: Editor由仅当编辑器运行时使用的类和程序组成。这些类允许用户操作多边形、画刷、地形及执行其它的通常在游戏中不能执行的动作。
- UnrealED: UnrealED是编辑器的真正UI实现,这是编辑器应用程序代码存在的地方。目前我们使用wxWidgets作为我们的UI工具集。任何需要书写的wxWidgets代码都要放到UnrealED中。这里的观念是'Editor'工程包含了所有编辑工具的功能性代码,而UnrealED仅是那些功能的UI封装。
也有很多针对其它平台的特定功能,比如D3DDrv(渲染的Direct 3D实现),WinDrv(创建视口、处理事件的Windows实现等)。如果一个工程是跨平台界面的特定平台实现,那么很可能它需要附加后缀Drv。
游戏/引擎 代码
虚幻引擎一般把游戏代码和引擎代码分割为2个不同的程序语言:
- 引擎代码:
- 使用C++语言书写。
- 使用".h" 和".cpp"扩展名,并分别存储在工程的"Inc" 和 "Src"文件夹中。
- 游戏代码:
- 使用Unreal Engine的独特语言:UnrealScript。这种语言在很多方面和Java类似。请参照UnrealScript 参考页面获得更多信息。
- 使用扩展名".uc",存储在工程的"Classes"文件夹中。
- 在执行前进行编译。
Unreal C++标准
- Unreal已经在整个引擎中定义了它自己的一组数据类型集合(
INT
、FLOAT
、UBOOL
等)。应该总是使用这些数据类型,而不是标准的C++数据类型,从而保证跨平台的兼容性。 - Unreal为字符串使用了宽字符(
TCHAR
)。所有的字符串文字都需要在TEXT()
宏进行封装,从而确保它们可以正确地被转换为宽字符。
示例:
const TCHAR* TestString = TEXT("My Test String");
关于虚幻编码规范的更多信息,请参照编码规范页面。
对象命名规则
在类和struct前附加一个字母前缀是很重要的,因为Unreal使用这些前缀让程序员知道应该如何处理一个对象。
- FClassName
- 实例: FString, FVector
- 应用: 任何带有字符前缀‘F’的对象是标准的C++的struct(结构体)和类,这些对象没有必须继承其它任何特殊东西。
- TClassName
- 实例: TArray, TMap
- 应用: 任何带有字符前缀‘T’的对象是模板类,这些对象没有必须继承其它任何特殊东西。
- UClassName
- AClassName
- 实例: AActor, ABrush, APointLight
- 应用: 任何带有字符前缀‘A’的对象是从AActor继承而来的类或struct(结构体)。AActor是基础'Actor'类。Actors和一般的UObject是不同的,因为它们在游戏世界中由实际的位置和真实的项目。仅actors可以在网络上进行复制以及使用UnrealScript状态。Actors也可以继承UObject,并且同样地遵循应用到带有U前缀的对象的不能删除规则。
UnrealScript 文件
本文档仅简要地略微谈到了一个新的程序员在使用虚幻引擎时需要处理的某些问题。
关于UnrealScript的全面描述,请参照UnrealScript参考指南页面。
类的选项修饰符
当声明类时,可以指定一些UnrealScript Classes的选项。关于这个功能的详细介绍,请参照UnrealScript参考指南: UnrealScriptReference#Class_overview。
在本讲解中,我们将简要地覆盖这些选项中的一个,因为它很容易使初识虚幻引擎的程序员犯错误。
使用 native 选项修饰符的自动生成头文件
如果为一个类指定了 native
选项,那么UnrealScript编译器将会自动地为那个UrealScript类生成相应的C++类声明。这些头文件具有后缀词"Classes",一般和工程的名称相关。比如"EngineClasses.h"、"EnginePhysicsClasses.h"、"EditorClasses.h"等。
因为当指定 native
时,UnrealScript将会自动生成C++类,所以程序员应该注意不要修改自动生成的头文件,而是在.uc文件中进行修改。
noexport选项修饰符
某些类在指定了 native
的同时也指定了 noexport
选项。这些类不能自动生成相应的C++部分,但是仍然期望C++声明存在。所以程序员必须自己书写一个头文件。在引擎中有很多这种情况的示例(请参照 Development/Src/Editor/Inc/Editor.h 文件中的UEditorEngine)。
noexport
UnrealScript 类需要和它们的C++对应部分保持‘同步’。因此,当修改UnrealScript文件时,程序员应该小心。
关于编译native 类的更多信息,请参照编译Native 类。
UObjects
因为UObjects是由引擎自动管理的,所以可能有一些新程序员不熟悉的独特的微小差别:
- UOject永远不要使用
new
进行创建, 而是用ConstructObject
模板函数。 - UOject永远不要使用
delete
进行删除。垃圾回收器将会自动地处理删除UObjects。 UObject::StaticClass
可以用于获得指向任何UObject静态实例的指针。
AActor
Unreal World对象包含了它内部所有的Actors列表。
- 不能使用
new
或ConstructObject
来创建AActors – 您应该使用SpawnActor
函数。 - 要想销毁一个Actor,请使用定义在UWorld中的
DestroyActor
函数。 - AActors可以包含一组UActorComponents。这些是提供较小的、正交单元的功能的有用对象。这可以使一个光源、粒子系统、声效等。
序列化
虚幻引擎的一个强大功能是它可以对定义在.uc文件中的UObjects的所有属性根据名称自动地进行序列化。这允许您改变属性的顺序、添加新的属性、在类层次中移动属性,并且这些操作不会破坏向后的兼容性。
因为Unreal垃圾回收系统使用序列化来决定哪个 UObjects 是 可获得的 ,所以您需要确保您具有到任何您不想清除的UObjects的序列化引用。在编辑器中创建工具时是可能导致问题的一种情况。我们一般从FSerializableObject接口继承工具,它提供了一个=Serialize= 函数,可以通过实现这个函数来保存到在工具中创建的UObjects的引用。
void FSomeTool::Serialize(FArchive& Ar) { Ar << SomeUObjectPointer; }
如果您使用native序列化工具来序列化永久性数据,您需要明确地支持老版本的序列化数据。要想启用这个功能,两个版本号存储在每个包中,一个供引擎使用,一个供授权用户使用。当授权用户在进行序列化改变时,可以更新 GPackageFileLicenseeVersion
,并且在反序列化过程中将会检查 Ar.LicenseeVer()
来决定保存的是哪个版本的序列换数据。
配置系统
UE3使用层次化的配置系统,意味着默认值将会从 base(基类) 的配置文件向下传递,并且子类可以覆盖这些基类的配置文件。
所有的配置文件都是.INI文件,并且遵循特殊的命名规则:
- 基类配置文件放置在 UnrealEngine3\Engine\Config文件夹中,文件名具有前缀单词 Base 。
- 每个文件都有它自己的 Config 文件夹和一组.INI配置文件。这些文件的前缀是拥有它们的游戏的名称。
比如,Editor配置文件的结构将是:
- Engine\Config\BaseEditor.ini
- "ExampleGame"的INI文件: ExampleGame\Config\DefaultEditor.ini
- "ExampleGame"的INI文件: ExampleGame\Config\ExampleGameEditor.ini
- "ExampleGame"的INI文件: ExampleGame\Config\DefaultEditor.ini
这个系统是强大的,因为它也用于为脚本中定义的属性填充数据,而这些脚本属性可以非常快速地配置游戏的任何部分。
主Tick(更新)循环
引擎的主Tick循环可以在LaunchEngineLoop.cpp文件中找到: FEngineLoop::Tick()
。这个函数调用UEngine类的 Tick
函数,这样它将会更新游戏世界(UWorld)并监控更新引擎的其它各种子系统。
如果您想获得关于Actor ticking(更新)循环函数是如何工作的,请参照Actor 更新页面
结论
虚幻引擎编程乍一看令人畏惧,但是随着时间的推移,您将会开始理解是某些复杂设计的决定使得它成为了今天的样子。既然您已经学习了虚幻引擎编程的第一个速成教程,那么请随便浏览UDN上提供给程序员的其它技术资源。
这里是为初学者提供的一些有用页面:
虚幻引擎编码规范:
编码规范
如何使用ExampleGame 创建一个新的游戏项目:
ExampleGame指南
如何跟踪ue3中您的内存应用情况:
Memory使用
actor更新循环是如何工作的:
Actor更新
UnrealScript参考指南(这是必读页面):
虚幻脚本概述:
关于编译标记为'native'类的更多信息:
编译Native类
虚幻引擎中常见的技术问题(FAQ):
常见技术问题
ClassificationForm | |
---|---|
Relevant topics | UnrealScriptTopics, GettingStartedTopics, TechnicalTopics |
Engine build | None/All |
Applicable platforms | |
Document availability | UE3Licensees |
Access required | |
Origin | CodeOrientation |
Revision | 1.11 |
Language | zh |