http://blog.csdn.net/noahzuo/article/details/51887205

这篇博客讲解了在UE4中涉及到版本之间识别、切换的工作原理以及一些可优化项的介绍。

本博客翻译自Robert Throughton的UE4: Package Versioning… How It Works… And An Optimization,传送门。

这篇博客的翻译已征得原作者同意。

This post is translated from English. You can find the original English language version here: http://coconutlizard.co.uk/blog/ue4/ue4-package-versioning


UE4的版本迭代

Unreal Engine 在确保Editor以及cooked内容可以在多个不同的引擎版本之间通用这一块有很不错的表现。而且为了让程序员能够在这些不同的版本之间很方便的更改数据结构,Unreal Engine也提供了一些机制。这篇博客将通过GetLinkerUE4Version()及其相关的函数来分析版本之间相关的内容。

首先,我们需要了解引擎有哪些不同种类的版本:

  • UE4Version: 每当Epic在原来的代码之上做了一些修改后,这个数字便会增加。

    • 这个版本号只能由Epic来进行改动。
  • UE4LicenseeVersion: 当代码改动后,这个数字同样会增加。但是这是一个“安全的”版本号,这意味着你(开发者)可以在这个基础之上进行代码的更改。 
    • 这个数字可以通过开发者(Licensees)进行更改。
  • CustomVersion: 这个版本号可以用于在不同的工程师之间进行迭代而不引起冲突。

如何使用UE4的版本迭代

通常来说,我们通常使用GetLinkerUE4Version()函数和GetLinkerLicenseeUE4Version()来处理不同版本之间的问题。

接下来我将给出一个Epic使用GetLinkerUE4Version()函数来修正一些内容的例子:

在这段时间内,某个人增加了一个用于设定Visibility的Blueprint变量。但是很蛋疼的是,他们犯了一个错误,丢失了一个visibility中的第三个’i’。

后来,某个人发现了这个问题,因此他们在UWidgetBlueprint::PostLoad()函数中加入如下的函数,用于修正这个错误:

    if ( GetLinkerUE4Version() < VER_UE4_RENAME_WIDGET_VISIBILITY ){static const FName Visiblity(TEXT("Visiblity"));static const FName Visibility(TEXT("Visibility"));for ( FDelegateEditorBinding& Binding : Bindings ){if ( Binding.PropertyName == Visiblity ){Binding.PropertyName = Visibility;}}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

然后他们在ObjectVersion.h头文件中增加了一个针对于这个修改的宏:

    // Rename Visiblity on widgets to VisibilityVER_UE4_RENAME_WIDGET_VISIBILITY,
  • 1
  • 2
  • 1
  • 2

因此,整个的工作流程如下:

  1. 当一个比较老版本的有拼写错误的包被载入时,这个包的版本号会比VER_UE4_RENAME_WIDGET_VISIBILITY更小。
  2. 此时,上面的代码会被激活,用于修正拼写错误。
  3. 如果这个包被保存了,它会使用正确的拼写,并且把版本号设定为最新的版本号。

这种做法在很多地方都有使用,例如:

  • 在类里面增加/废除一些新的变量。
  • 增加一些我们想要改变的Patch.

版本号对应宏

在头文件ObjectVersion.h中,记录了Epic是如何处理版本迭代的,首先是EUnrealEngineObjectUE4Version


enum EUnrealEngineObjectUE4Version
{VER_UE4_OLDEST_LOADABLE_PACKAGE = 214,// Removed restriction on blueprint-exposed variables from being read-onlyVER_UE4_BLUEPRINT_VARS_NOT_READ_ONLY,// Added manually serialized element to UStaticMesh (precalculated nav collision)VER_UE4_STATIC_MESH_STORE_NAV_COLLISION,// Changed property name for atmospheric fogVER_UE4_ATMOSPHERIC_FOG_DECAY_NAME_CHANGE,
...// -----<new versions can be added before this line>-------------------------------------------------// - this needs to be the last line (see note below)VER_UE4_AUTOMATIC_VERSION_PLUS_ONE,VER_UE4_AUTOMATIC_VERSION = VER_UE4_AUTOMATIC_VERSION_PLUS_ONE - 1
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

同样的,还有EUnrealEngineObjectLicenseeUE4Version,也在同一个头文件中:

enum EUnrealEngineObjectLicenseeUE4Version
{VER_LIC_NONE = 0,// - this needs to be the last line (see note below)VER_LIC_AUTOMATIC_VERSION_PLUS_ONE,VER_LIC_AUTOMATIC_VERSION = VER_LIC_AUTOMATIC_VERSION_PLUS_ONE - 1
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

EUnrealEngineObjectLicenseeUE4Version有如下的注意事项:

  • 就如同上面提到的,licensees**只能**在EUnrealEngineObjectLicenseeUE4Version列表中进行迭代
  • 只能在该列表的最下面进行添加
  • 当使用例如Perforce之类的代码管理工具时,务必注意针对于包被保存后进行处理,否则这个包容易被玩坏。针对于这种情况,最简单的解决方案就是当版本号改变的时候,不允许check in

我们为何只能用UE4LicenseeVersion呢?考虑如下的状况:

  • 一个项目使用UE4.11开始
  • 我们对于UE4Version进行了迭代,加了一些我们觉得很酷的功能
  • 我们merge了UE4.12,然后发现我们出现了一个冲突 —— Epic也在4.12迭代了这一块的功能…在此情况下,我们只能: 
    • 把Epic的新版本号插入到列表里面的最后而将我们自己的迭代版本号设为不变 - 这样就意味着我们可以使用我们自己的内容,但是Epic提供的一些新的功能应该都用不了了。
    • 把我们的版本号移到最后 - 但是这样先前所做的功能便会出很大问题。

以上所出现的问题很可能是致命的,我们当初在UE3里面也犯过这种错误,我们花了好一阵时间进行re-patch。所以我再次提醒 —— 只使用UE4LicenseeVersion.


当版本号出现问题

在这么久的开发生涯中,我经历了不少的由于版本号冲突而导致的崩溃,我希望这些问题能够带给读者一些启发。

要弄懂这些问题到底从何二来,我们首先要了解版本号是如何运作的。现在假如说我们有一个包使用了很久以前的版本号256来进行储存,而现在的版本号是259.当这个包被载入的时候,它需要经过257、258和259三个版本的迭代处理。

以下是我总结出的最可能导致版本号出现问题的状况:

  • Merge了其他的迭代逻辑,但是还没有提交版本控制的逻辑。而团队的其他程序员还没有merge其他的迭代逻辑。

    • 在本地测试,没有任何问题。
    • 对于其他的开发者,就开始抱怨崩溃了。 
      • 其他开发者已经更新了迭代的逻辑。
      • 在其他开发者的版本中,这个迭代就有了一个不同的版本号。
      • 但是你已经将自己的迭代放到了list的末尾,系统现在出现混乱。
      • 当这个包被载入的时候,引擎认为你自己的迭代已经载入(但是实际上没有)。
  • 你从Epic那里integrate了一个新的版本迭代,并且在版本列表中加入了一个新的宏定义。 
    • 然而,在Epic的ObjectVersion.h头文件中,在你integrate的地方之前之后,Epic做了一些新的改动。
    • 你之后进行了一个full integration,把Epic添加的额外宏定义进行了添加。 
      • Crashes/bugs开始出现,因为有一些patch现在的顺序已经乱了。
    • 看来似乎唯一“正确”的方法只有添加那些你integrate之后的东西了。 
      • 但是这依然会出事,只要Epic提供的官方内容在其他的地方被保存了,那么这个包依然可能出现问题。

我们发现的一些可优化项

当项目的内容被cook完后,Saved文件夹下的内容中,所有的包的版本号都会被设为最大。因此我才有了这个优化的想法。

即使游戏是在载入已被cooked的内容,引擎也不会默认它是最新的。这样可以使得当你改变一些包的版本号时,不需要重新cook所有的东西。但是,频繁的检查内容是否需要更新的操作是很昂贵的。尤其对于Shipping的包,这也并非必要。所以我们进行一些假设:

  • Shipping包只在被cooked的内容下工作。
  • Shipping包中只会有完全被cooked到最新版本的内容。

如果这些假设成立,那么我们便可以进行优化,告诉编译器我们不需要去运行patching代码。

也就是说,我们需要告诉编译器让它的每一次检查例如:if ( GetLinkerUE4Version() < VER_…之类的代码都会失败,而且每一次检查例如:if ( GetLinkerUE4Version() >= VER_…之类的代码都会成功。因此最简单的方法就是让每个Get—Version()类型的函数都返回当前最新的版本号。

因此我们在头文件中进行处理,来确保编译器可以将其设为内联。

所以……我们是这样进行操作的:

我们在UObjectBaseUtility.h头文件的最开头: 
#define ASSUME_UE4VERSIONS_ARE_LATEST (UE_BUILD_SHIPPING && !WITH_EDITORONLY_DATA)

需要注意的是,我们在项目中的设定是Shipping包只能在运行cooked builds里面运行,针对于不同的项目你需要进行调整。

GetLinkerUE4Version()前,你应该加入如下代码:

#if ASSUME_UE4VERSIONS_ARE_LATESTFORCEINLINE int32 GetLinkerUE4Version() const { return VER_LATEST_ENGINE_UE4; }FORCEINLINE int32 GetLinkerLicenseeUE4Version() const { return VER_LATEST_ENGINE_LICENSEEUE4; }FORCEINLINE int32 GetLinkerCustomVersion(FGuid CustomVersionKey) const  { return MAX_int32; }
#else // ASSUME_UE4VERSIONS_ARE_LATEST/*** Returns the UE4 version of the linker for this object.
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

GetLinkerCustomVersion()后面,加入如下代码:

      int32 GetLinkerCustomVersion(FGuid CustomVersionKey) const;#endif // ASSUME_UE4VERSIONS_ARE_LATEST
  • 1
  • 2
  • 1
  • 2

现在我们开始重写版本控制函数的cpp代码,我们需要在ObjectBaseUtility.cpp文件开头,在include 语句之后加入如下代码:

    #include "CoreUObjectPrivate.h"#if !ASSUME_UE4VERSIONS_ARE_LATEST
  • 1
  • 2
  • 1
  • 2

在文件末尾加入:

#endif // !ASSUME_UE4VERSIONS_ARE_LATEST
  • 1
  • 1

这些就是所有的了,现在编译器应该能够完全去掉patching代码,这样一来整个代码简洁很多。


其他的意见(给Epic)

我见过了太多licensees会直接在EUnrealEngineObjectUE4Version中加入自己的宏定义了… 还是尽量避免这样吧。如果是我的话我会加入一些注释,把:

     // -----<new versions can be added before this line>-------------------------------------------------// - this needs to be the last line (see note below)VER_UE4_AUTOMATIC_VERSION_PLUS_ONE,VER_UE4_AUTOMATIC_VERSION = VER_UE4_AUTOMATIC_VERSION_PLUS_ONE - 1};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

改为:

     // LICENSEES SHOULD NOT ADD CODE HERE!// PLEASE USE EUnrealEngineObjectLicenseeUE4Version INSTEAD!// ADDING CODE HERE WILL MAKE FUTURE INTEGRATIONS HARD!// -----<new versions can be added before this line>-------------------------------------------------VER_UE4_AUTOMATIC_VERSION_PLUS_ONE,VER_UE4_AUTOMATIC_VERSION = VER_UE4_AUTOMATIC_VERSION_PLUS_ONE - 1};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

另外,如下的代码也可以更改,让其从:

    enum EUnrealEngineObjectLicenseeUE4Version{VER_LIC_NONE = 0,// - this needs to be the last line (see note below)VER_LIC_AUTOMATIC_VERSION_PLUS_ONE,VER_LIC_AUTOMATIC_VERSION = VER_LIC_AUTOMATIC_VERSION_PLUS_ONE - 1};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

改为:

    enum EUnrealEngineObjectLicenseeUE4Version{VER_LIC_NONE = 0,// -----<new versions can be added before this line>-------------------------------------------------VER_LIC_AUTOMATIC_VERSION_PLUS_ONE,VER_LIC_AUTOMATIC_VERSION = VER_LIC_AUTOMATIC_VERSION_PLUS_ONE - 1};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

还有可以尝试对版本控制之类的名字估计可以从EUnrealEngineObjectUE4Version重命名为EUnrealEngineObjectPleasePleaseOnlyForEpicChangesUE4Version之类的。请务必让用户了解到直接更改这一块的危害!我甚至见过非常有经验的开发者在这里栽过跟头。

<全文完>

Unreal Engine 4 —— 版本兼容的工作原理以及一些可优化项相关推荐

  1. SEO学习(SEO是什么,SEO的工作原理,SEO如何优化)

    文章目录 一.SEO是什么? 二.SEO的工作原理 三.SEO如何优化 内部优化 外部优化 一.SEO是什么? SEO(Search Engine Optimization),意思是搜索引擎优化,是一 ...

  2. 解密百度等搜索引擎的工作原理 SEO培训SEO优化课程

    课程目录: 课时1 seo培训第一课 解密百度等搜索引擎的工作原理 了解SEO推广 课时2 seo培训第一课续 解密百度等搜索引擎的工作原理 了解SEO推广 课时3 SEO培训第二讲页面标题标签优化 ...

  3. unreal engine各个版本网盘离线下载

    为方便访问国外网站有困难的同学,整理了各个版本网盘下载,有需要的请笑纳 下载地址: 链接: https://pan.baidu.com/s/1i4LEmA5   提取码:prcp 本文转自:52VR. ...

  4. 死磕Java多线程(五)---理解CPU高速缓存的工作原理 《JAVA性能优化权威指南》 提到过CPU高速缓存未命中率影响线程切换频率

    https://blog.csdn.net/weixin_44046437/article/details/99101180

  5. Appium 介绍以及工作原理

    目录 1.什么是Appium: 2.Appium各类组件: 3.Appium理念: 4.三张图了解 Appium工作原理: 1.什么是Appium: appium是一个移动端的自动化测试框架,可用于测 ...

  6. MATLAB+Unreal Engine自动驾驶联合仿真

    这里假设已经安装好了Unreal Engine(版本4.25)MATLAB2021b 以及Visual Studio® 2017or higher .MATLAB官方文档给的匹配的UE4版本号是4.2 ...

  7. 《Hadoop MapReduce性能优化》一1.3 Hadoop MapReduce的工作原理

    本节书摘来异步社区<Hadoop MapReduce性能优化>一书中的第1章,第1.3节,作者: [法]Khaled Tannir 译者: 范欢动 责编: 杨海玲,更多章节内容可以访问云栖 ...

  8. Hive架构和工作原理

    一.Hive 架构 下面是Hive的架构图. Hive的体系结构可以分为以下几部分: 1.用户接口主要有三个:CLI,Client 和 WUI.其中最常用的是CLI,Cli启动的时候,会同时启动一个H ...

  9. AI应用开发基础傻瓜书系列1-神经网络的基本工作原理

    Copyright © Microsoft Corporation. All rights reserved. 适用于License版权许可 更多微软人工智能学习资源,请见微软人工智能教育与学习共建社 ...

最新文章

  1. 【跟着子迟品 underscore】Array Functions 相关源码拾遗 小结
  2. tensorflow sigmoid_cross_entropy_with_logits 函数解释
  3. [云炬创业学笔记]第二章决定成为创业者测试7
  4. 利用matlab将二进制小数转换为十进制小数
  5. LocalReport Print with C# C#打印RDLC
  6. 一步步编写操作系统 55 CPL和DPL入门2
  7. java ftp connect_java操作Ftp文件的一些方式(一)
  8. 有一个写代码很厉害的老板是怎样一种体验?
  9. LeetCode Map Sum Pairs
  10. PHP 使用 ZipArchive 将文件打包成 zip
  11. vue项目引入阿里云图标的4种方式
  12. 《Matlab算法》 part1 误差分析
  13. python类直接调用不实例化_python 类不实例化,调用类方法:@staticmethod 和 @classmethod...
  14. Typescript - 安装与配置
  15. 微信注册验证成功之后不跳转_微信为什么会被限制登录?被限制后我们该怎么办?...
  16. 应用与系统稳定性第五篇---Watchdog原理和问题分析
  17. html在浏览器显示图片,html - 在所有Web浏览器中显示TIFF图像
  18. java 单选题,【单选题】Java 语言提供处理不同类型流的类所在的包是( )
  19. 有关 Java Long 型数据的比较
  20. BIM模型文件下载——轻轨站模型

热门文章

  1. android view setleft,android – 在新textview上使用setLeft / setRight方法
  2. python加载模型_解决python 无法加载downsample模型的问题
  3. 事务没提交的数据查的出来吗?_品牛栏山,论分布式事务
  4. c#读取solidworks文件_C#初学者教程系列20:Stream流读写
  5. c语言延时系统,基于VB的单片机C语言延时程序各参数计算系统
  6. ROS中阶笔记(六):机器人感知—机器语音
  7. linux安装phpunit,linux下安装phpunit
  8. antd权限管理_推荐6款超好看实用的管理后台模版
  9. java midlet 是什么_编译JAVA游戏,MIDlet出错,急求大家帮助
  10. 未能加载文件或程序集mysql.web.v20__关于MySQL Server影响ASP.NET网站使用的问题:未能加载文件或程序集MySql.Web.v20...