给虚幻4添加内存跟踪功能

最后更新于:2022-04-01 11:43:23

本文章由cartzhang编写,转载请注明出处。 所有权利保留。 文章链接:[http://blog.csdn.net/cartzhang/article/details/50524317](http://blog.csdn.net/cartzhang/article/details/50524317) 作者:cartzhang 本篇译文同发与蛮牛译馆, 地址:[http://www.manew.com/thread-46327-1-1.html?_dsign=ae91354a](http://www.manew.com/thread-46327-1-1.html?_dsign=ae91354a) 从我上次谈论内存申请和跟踪已经有一段时间了。我得抽出时间来在虚幻4上实现跟踪,并且已经完成了。我假设你已经阅读过来之前的博客:“虚幻引擎4中内存跟踪功能的局限性”和“内存申请和跟踪”。 ## 虚幻引擎4内存管理的API ### 基本的内存分配方法。 虚幻引擎4中,有三种基本的内存分配和释放方法: 1.使用GMalloc指针。这种方法是获得全局的分配器,分配器的使用依赖于GCreateMalloc()函数。 2.FMemory函数。有静态函数比如:Malloc(),Realloc(),Free()。他们也是使用GMalloc来申请内存,但是在此之前,它会在每次allocation, reallocation或free之前检查GMalloc 是否定义。若GMalloc 是空,就调用GCreateMalloc() 。 3.全局的的New和delete操作。缺省情况下,只在模块的ModuleBoilerplate.h 的文件中定义,也就是说,很多调用new和delete的操作不在虚幻4的内存系统中管理。重载操作实际上调用的是FMemory函数。 这些情况就会出现使用这些机制就可能出现内存不会释放和清空的问题。为了扑捉这些问题,我提交了一个申请,已经被整合并发布在版本4.9上,通过C运行时库调用_CrtSetAllocHook()来获取这些分配。其中一个例子,是引擎中Zlib集成并没有使用引擎工具来分配,它使用了_CrtSetAllocHook() ,我提交了一个修复版本在4.9版本发布。 直接调用GMalloc 和FMemory 函数这两个基础的API,如下: ~~~ virtual void* Malloc( SIZE_T Count, uint32 Alignment = DEFAULT_ALIGNMENT ) = 0; virtual void* Realloc( void* Original, SIZE_T Count, uint32 Alignment = DEFAULT_ALIGNMENT ) = 0; virtual void Free( void* Original ) = 0; These are the places that would need to be modified if we want to add any new piece of data per allocation. ~~~ ### 引擎的整合 为了写法类似,我重载了分配器,我从FMalloc继承了一个新类,叫做FMallocTracker,这样就可以勾到虚幻的内存分配系统上。因为一个有效的分配器必须在创建FMallocTracker 实例时所有实际的分配已经由其分配器完成。FMallocTracker 只是保存了跟踪信息。但是这是不够的,实际上需要知道分配器所有方法来跟踪数据。因此,第一步就是当我们使用内存跟踪功能时,修改分配器函数。 ~~~ #if USE_MALLOC_TRACKER virtual void* Malloc( SIZE_T Count, uint32 Alignment, const uint32 GroupId, const char * const Name ) = 0; virtual void* Realloc( void* Original, SIZE_T Count, uint32 Alignment, const uint32 GroupId, const char * const Name ) = 0; #else virtual void* Malloc( SIZE_T Count, uint32 Alignment = DEFAULT_ALIGNMENT ) = 0; virtual void* Realloc( void* Original, SIZE_T Count, uint32 Alignment = DEFAULT_ALIGNMENT ) = 0; #endif // USE_MALLOC_TRACKER ~~~ 新参数: 名称:分配名称。这个名称可以任何名称,但是建议写易懂便于搜索。在本文后面,将会展示更多相关内容。 分组:组ID是针对于当前所分配的。有些分组我已经定义过来,但是有些你需要根据你的需要来定义。 这个改变就意味着,所有的分配器在引擎中都是透明的,一旦完成,你可以标记分配器,而不用担心底层的实现。标记分配的目的不仅仅是为了跟踪,也涉及到代码调试。一旦这些便签在很大的代码库中实现后,当内存飙升,处理不同组的交互,修复相关的内存崩溃时,就会有很大的好处了。 下一步是集成new和delete操作。我之前提到过,在引擎的ModuleBoilerplate.h文件中已经定义好,为了更好的的覆盖,我把它移动到MemoryBase.h中。下一步是定义新的重载操作,并传入名称和分组。 ~~~ OPERATOR_NEW_MSVC_PRAGMA FORCEINLINE void* operator new (size_t Size, const uint32 Alignment, const uint32 GroupId, const char * const Name) OPERATOR_NEW_NOTHROW_SPEC{ return FMemory::Malloc(Size, Alignment, GroupId, Name); } OPERATOR_NEW_MSVC_PRAGMA FORCEINLINE void* operator new[](size_t Size, const uint32 Alignment, const uint32 GroupId, const char * const Name) OPERATOR_NEW_NOTHROW_SPEC{ return FMemory::Malloc(Size, Alignment, GroupId, Name); } OPERATOR_NEW_MSVC_PRAGMA FORCEINLINE void* operator new (size_t Size, const uint32 Alignment, const uint32 GroupId, const char * const Name, const std::nothrow_t&) OPERATOR_NEW_NOTHROW_SPEC { return FMemory::Malloc(Size, Alignment, GroupId, Name); } OPERATOR_NEW_MSVC_PRAGMA FORCEINLINE void* operator new[](size_t Size, const uint32 Alignment, const uint32 GroupId, const char * const Name, const std::nothrow_t&) OPERATOR_NEW_NOTHROW_SPEC { return FMemory::Malloc(Size, Alignment, GroupId, Name); } ~~~ 为避免使用USE_MALLOC_TRACKER来进行检测,提供这些定义来创建这些申请,在使用USE_MALLOC_TRACKER设置,但当不设置时并不增加不必要的开销。其目的是不增加任何不必要的开销。下面是基本定义: ~~~ #if USE_MALLOC_TRACKER #define PZ_NEW(GroupId, Name) new(DEFAULT_ALIGNMENT, (Name), (GroupId)) #define PZ_NEW_ALIGNED(Alignment, GroupId, Name) new((Alignment), (Name), (GroupId)) #define PZ_NEW_ARRAY(GroupId, Name, Type, Num) reinterpret_cast<##Type*>(FMemory::Malloc((Num) * sizeof(##Type), DEFAULT_ALIGNMENT, (Name), (GroupId))) #define PZ_NEW_ARRAY_ALIGNED(Alignment, GroupId, Name, Type, Num) reinterpret_cast<##Type*>(FMemory::Malloc((Num) * sizeof(##Type), (Alignment), (Name), (GroupId))) #else #define PZ_NEW(GroupId, Name) new(DEFAULT_ALIGNMENT) #define PZ_NEW_ALIGNED(Alignment, GroupId, Name) new((Alignment)) #define PZ_NEW_ARRAY(GroupId, Name, Type, Num) reinterpret_cast<##Type*>(FMemory::Malloc((Num) * sizeof(##Type), DEFAULT_ALIGNMENT)) #define PZ_NEW_ARRAY_ALIGNED(Alignment, GroupId, Name, Type, Num) reinterpret_cast<##Type*>(FMemory::Malloc((Num) * sizeof(##Type), (Alignment))) #endif // USE_MALLOC_TRACKER ~~~ 下面是两个例子,说明在代码中,带标签和不带标签内存分配的比较: 分配器的跟踪由容器来完成 在命名分配空间时出现了一个问题,用一个简单方法来识别,我们使用容器来处理。引擎中所有对象几乎不可能只有一个单例,在做游戏时,可放置的例子,几个玩家,所以引擎中使用很多容器。在使用带容器的分配器时,使用一个通用的名字是没有什么用处的。我们来看看这个FMeshParticleVertexFactory::DataType例子: ![这里写图片描述](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b008680e26.jpg "") ![这里写图片描述](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b0086a0936.jpg "") ~~~ /** The streams to read the texture coordinates from. */ TArray<FVertexStreamComponent,TFixedAllocator<MAX_TEXCOORDS> > TextureCoordinates; ~~~ 在容器中,一个普通的名字分配器类似于“TFixedAllocator::ResizeAllocation”。没有太多意义。反之,对于容器来说,一个好的名字像这样“FMeshParticleVertexFactory::DataType::TextureCoordinates”。因此,我们需要给容器标记名称和分组,如此以来,当一个分配器在容器中后,通过容器的名称和分组获得所有的内存分配。因此,我们需要改变容器,让分配器额可以使用这些容器。这将涉及到当使用USE_MALLOC_TRACKER时,需要为每个容器添加指针和一个32位的无符号整数,并修改必要的构造函数来添加选项信息。TArray的一个构造函数如下: ~~~ TArray(const uint32 GroupId = GROUP_UNKNOWN, const char * const Name = "UnnamedTArray") : ArrayNum(0) , ArrayMax(0) #if USE_MALLOC_TRACKER , ArrayName(Name) , ArrayGroupId(GroupId) #endif // USE_MALLOC_TRACKER {} ~~~ 这样以来,我们可以把必要的信息传递给分配器来标记这些分配。接下来是要保证把这些改变信息传递到底层内存分配器中被使用。这些容器分配器通常使用FMemory来分配内存,FContainerAllocatorInterface定义ResizeAllocation 函数来做实际的内存申请。与之前的修改一样,我们需要为内存分配添加名称和分组。 ~~~ #if USE_MALLOC_TRACKER void ResizeAllocation(int32 PreviousNumElements, int32 NumElements, SIZE_T NumBytesPerElement, const uint32 GroupId, const char * const Name); #else void ResizeAllocation(int32 PreviousNumElements, int32 NumElements, SIZE_T NumBytesPerElement); #endif // USE_MALLOC_TRACKER ~~~ 同样,因为我们不想使用那个ifdefs来填充引擎的代码,我们再次使用定义来简化: ~~~ #if USE_MALLOC_TRACKER #define PZ_CONTAINER_RESIZE_ALLOCATION(ContainerPtr, PreviousNumElements, NumElements, NumBytesPerElement, GroupId, Name) (ContainerPtr)->ResizeAllocation((PreviousNumElements), (NumElements), (NumBytesPerElement), (GroupId), (Name)) #else #define PZ_CONTAINER_RESIZE_ALLOCATION(ContainerPtr, PreviousNumElements, NumElements, NumBytesPerElement, GroupId, Name) (ContainerPtr)->ResizeAllocation((PreviousNumElements), (NumElements), (NumBytesPerElement)) #endif // USE_MALLOC_TRACKER ~~~ 这样,我们可以把ArrayName和ArrayGroup传递给容器分配器。 在构造之后,还有一个需要修改容器的名称或分组,因为给容器的容器命名分配器是非常有必要的。其中的一个例子就是,在任一TMap容器中FindOrAdd后,我们需要设置名称或分组。 ~~~ /** Map of object to their outers, used to avoid an object iterator to find such things. **/ TMap<UObjectBase*, TSet<UObjectBase*> > ObjectOuterMap; TMap<UClass*, TSet<UObjectBase*> > ClassToObjectListMap; TMap<UClass*, TSet<UClass*> > ClassToChildListMap; ~~~ 这样以来,所有的容器内存分配器有了标签属性。现在,我们需要的是给容器设置名称。以FMeshParticleVertexFactory::DataType::TextureCoordinates为例,我们可以设置它的名称和分组: ~~~ DataType() : TextureCoordinates(GROUP_RENDERING, "FMeshParticleVertexFactory::DataType::TextureCoordinates") , bInitialized(false) { } ~~~ ### 定义作用域 作为“内存申请和跟踪”博客中一部分,为提供上下文链接,我提及到内存分配定义作用域的必要性。这个作用域与调用栈(它已经由MallocProfiler提供)不一样。很多分配在同一栈中,但是涉及到完全不同的对象。在使用蓝图过程中更是普遍。正是由于这个,作用域在跟踪或甚至带蓝图的内存使用都是非常有用的。 为利用引擎中已有的代码,我采用了重用FScopeCycleCounterUObject 结构体的方法,这个结构体用来在状态系统中定义作用域的相关对象。引擎已经给他们配置了必要的作用域,并且你也可以使用FMallocTrackerScope 类来放置我们的内存跟踪特性的作用域。也在每个FScopeCycleCounterUObject上自动创建的两个域的可见性上做了改进,一个是对象类名的域,一个是对象名的域。这样当我们最终创建一个可视数据工具时,对每个类名进行折叠时就会更简单。让我们从精灵Demo来看一看单独作用域,它是一个感觉还不错的复杂东西。 ![这里写图片描述](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b0086b7092.jpg "") 我们分析作用域下的内存分配,结果如下: ~~~ Address Thread Name Group Bytes Name 0x0000000023156420 Main Thread UObject 96 InterpGroupInst 0x00000000231cf000 Main Thread Unknown 64 UnnamedTSet 0x0000000023168480 Main Thread UObject 80 InterpTrackInstMove 0x0000000028ee8480 Main Thread Unknown 64 UnnamedTSet 0x0000000022bc2420 Main Thread Unknown 32 UnnamedTArray 0x00000000231563c0 Main Thread UObject 96 InterpGroupInst 0x00000000231cefc0 Main Thread Unknown 64 UnnamedTSet 0x0000000023168430 Main Thread UObject 80 InterpTrackInstMove 0x00000000231cef80 Main Thread Unknown 64 UnnamedTSet 0x0000000022bc2400 Main Thread Unknown 32 UnnamedTArray 0x0000000023156360 Main Thread UObject 96 InterpGroupInst 0x00000000231cef40 Main Thread Unknown 64 UnnamedTSet 0x00000000231683e0 Main Thread UObject 80 InterpTrackInstMove 0x0000000028ee8380 Main Thread Unknown 64 UnnamedTSet 0x0000000022bc23e0 Main Thread Unknown 32 UnnamedTArray 0x00000000231cef00 Main Thread UObject 64 InterpTrackInstAnimControl 0x00000000231ceec0 Main Thread UObject 64 InterpTrackInstVisibility ~~~ 在蓝图的运行函数中只有17个内存分配。当我扑捉精灵Demo中实际的内存分配数为584454。唯一名称的作用域数量高达4175。还有我们在捕捉时的内存分配为607M,而内存峰值为603M。这说明了对于这些需要内存跟踪的必要性。 ### MallocTracker的实现 正如之前所说,MallocTracker 的使用方法与之前内存分配很相似。MallocTracker 是轻量级的,并且依据在“内存分配和跟踪”文章中所说的性能需求。 使用方法在缺省状态下是足够快的,不会有太多的性能影响,并在内存方面有相当地的开销。例如元素Demo显示跟踪开销~30M,在Debug模式下CPU时间低于2ms,更不用说在优化发布版本下了。与平时一样,在内存消耗和性能之前有个取舍,这些数字取决于我所选取的方法。还有其他的方法,可以优化性能或内存开销,但是我想还是保持合理的平衡。 为分析应用,我们来看一个具体的例子。下面是当 FMemory::Malloc()调用时所发生的: ![这里写图片描述](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b0086d78c2.jpg "") 1. FMemory::Malloc() 被调用,名字和分组需要一定的字节分配。 2. FMemory::Malloc()调用带同样参数的FMallocTracker::Malloc(),假设GMalloc指针指向的是FMallocTracker 的实例。 3. FMallocTracker::Malloc()在FMallocTracker创建期间,使用传进来的分配器分配实际的内存,本例中是FMallocBinned类。 4. FMallocTracker::Malloc() 自动修改一些全局内存分配状态,例如内存分配峰值内存大小,内存峰值数等等。 5. FMallocTracker::Malloc()关联到当前线程PerThreadData 实例。 6. FMallocTracker::Malloc() 调用PerThreadData::AddAllocation来保存在此线程容器中的的内存分配数据。 7. FMallocTracker::Malloc() 返回指针给步骤三中的底层内存分配器。 ### 全局静态 几乎不包含全局状态。只是给你一个快速的概览而已。全局状态包括: 分配的字节。数据入栈时分配字节数。 分配次数。数据入栈时分配次数。数越大就会有更多的内存碎片。 分配字节的峰值。从MallocTracker 可用以后的最大字节分配数。 内存分配峰值次数。从MallocTracker 可用后,实时分配的最大数。 消耗字节。MallocTracker 的内部开销字节数。 从所有线程分配内存开始,所有这些状态自动更新。 ### 各线程的数据 为提高性能和避免多线程下内存分配和释放的资源竞争,大部分工作在每个线程基础中完成。所有内存分配和栈的作用域范围被存储在每个线程中。所有分配有一个相对的栈域定义,最大域范围为GlobalScope。同样的域名常出现在多域栈中。由此,为最小化内存开销,某个线程的所有域名被存储为独一无二的,并关联到域的栈上。因为域栈可以在多个内存分配中显示,所有我们可以独一无二的保存在域栈上。我们来看具体的例子,域名为蓝色,分配器为桔黄色: ![这里写图片描述](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b0086ea9e8.jpg "") 为保存数据,我们有三个不同的数组,它们在不同线程中不共享: 唯一域名。保存本线程中唯一域名。至少GlobalScope 要在这里。它将会保存它们加入到栈中的新的域名。 唯一的域栈。它用一个固定长度的动态数组保存唯一栈,用索引指向相关的域名。 分配器。每个分配器的数据。它包含分配器地址,字节大小,分组和名称,还有唯一域栈的索引。 若我们参考之前的图,我们可以看到五个分配器。下面为5个分配器的数据存储: ![这里写图片描述](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b00870c8dd.jpg "") ### 重新分配和释放内存 重新分配内存和释放内存有点复杂,因为实际上在虚幻引擎中在一个线程中分配多个实例,然后被重新分配或在其他线程释放或是很普遍的。也就是说,我们不能假设我们找到每个调用线程上的每个线程数据。因为那样的话,也就意味着我们需要加一些锁。为减少竞争,使用全局锁而不是每个线程数据类有自己的锁。在重新分配和释放当前调用线程的每个线程数据时,对当前存在分配器进行检测。若没有找到,当前分配器在其他线程数据中在处理中被锁定了。这用来确保减少竞争,并让它们尽可能的忙碌。 ### 命名的处理 为使MallocTracker足够快和内存消耗可被接受,我不得不制定严格的命名规范,无论是分配内存或域名命名。这种限制是,这些名字与内存的生命周期内,必须一致或比实际申请或域的存在时间要长。原因是,任何数据拷贝影响性能和内存开销,所以只保存指针。虽然这貌似是一个复杂的规则,我觉得这个是完全正常的,因为你应该知道你分配器的生命周期。若你不知道你分配器的生命周期,为要知道这些名字要存在多久,那么你有了大麻烦处理了。 另外一个关于分配器和域名的特别应用是需要ANSI和宽字节。为使这些更透明,所有指针假定为ANSI,除非指针中的第63位字节被设置,它会假定指针指向一个宽字节。FMallocTracker提供了一个获取为宽字节设置位的指针操作方法,并对宽字节或ANSI的FNames在必要情况下可设置。在输出到文件时,名字是正确的并转存到文件中。 ### 结论 只有当制作了一个可视的工具来展示数据时,我才会说这个系统真的很有用啊!你可以继续我的工作,它真的很有用。查找碎片问题和处理内存使用使用这个数据会更加简单。这个真的比引擎中已经提供的性能工具,内存消耗和数据质量要好。 下一步将真正全面转换引擎使用标签内存分配,但是仍有事情必须完成。若大部分内存没有冲突,真的没有必要花费太多时间来只是为了标记内存分配。它不只是更好标记大的内存分配,而是获取更多的洞察细节的问题。尽管你会觉得标记分配器非常无聊,但是你仍会获得有用的数据。下面是精灵Demo中截取的最大分配器的数据: ### 样例数据和源代码 为说明问题,我提供了样例数据。样例数据来自测试版本的精灵Demo的修改版本的运行。你可以在这里下载,在支持大文件的文本编辑器中打开。 若Epic Game 接受了我的代码更新请求,你可以通过下载我上传请求到Epic Games的代码来查看。上传请求的有效地址:[虚幻4上传请求地址](https://github.com/EpicGames/UnrealEngine/pull/1500)。 Video overview. 视频概述 [译者注:28分钟的视频,有需要的可以下!] 原文地址:[https://pzurita.wordpress.com/2015/08/26/adding-memory-tracking-features-to-unreal-engine-4/](https://pzurita.wordpress.com/2015/08/26/adding-memory-tracking-features-to-unreal-engine-4/) ### -THE—END– 若有问题,请随时联系!!! 非常感谢!!!
';

OCulus Rift 游戏开发六原则

最后更新于:2022-04-01 11:43:21

本文章由cartzhang编写,转载请注明出处。 所有权利保留。 文章链接:[http://blog.csdn.net/cartzhang/article/details/46685477](http://blog.csdn.net/cartzhang/article/details/46685477) 作者:cartzhang ### OCulus Rift 游戏开发六原则 你是一个游戏开发者?在你的生活中,你涉及游戏开发的那些点呢?你是否听说过Oculus Rift VR系统呢?你是否赞同业内的一些声音,说Oculus Rift将会是游戏的未来? 当我们游戏开发插件或一些工程时候,这有一些点,需要注意或弄清楚的。 虚幻4引擎已经集成了Oculus Rift开发,你只需从网上下载Oculus的SDK,插入Oculus设备,虚幻4引擎可以自动设备。要是你认为就这么简单的话,那你完全错了。这就是生活啊! 为保证你开发Oculus游戏的可玩性,有一些注意事项你需要了解: 1. 玩家的移动 2. 游戏帧率FPS 3. 适合HMD(头戴设备,本文中指的是Oculus Rift)的关卡设计 4. 针对Oculus 5. 玩家的舒适度 6. Rift分辨率 ## 一、 玩家的移动 Ok,这个很基本。在头戴设备的射击游戏中,有三种方法可以使玩家移动: ——玩家朝着他看的方向移动,包含Oculus的方向 ——玩家移动由鼠标方向确定,但是Oculus Rift可以实现相机偏移,这样玩家玩家可以往边上看,但是移动方向不变。就像是鼠标控制移动方向,头却被Oculus Rift 控制。想象一声机甲战士。 ——玩家移动仅由玩家看的方向控制,但是当你使用头盔看的角度大于60度时,你可以旋转角色。 前两个可以设置玩家相机,是否跟随HMD的方向旋转。第三个选项你可以跟随HMD方向,并添加蓝图代码来控制相机(当HMD旋转角度大于±60度时,旋转角色角度)。 如下图所示: ![这里写图片描述](image/56a4e1484ffd8.png "") ## 二、 帧率 Oculus Rift DK1官方建议帧率为60fps,这就意味着当帧率低于60时,在玩游戏的过程中,玩家体验会一团糟。 另一方面,Oculus Rift DK2官方建议帧率为75fps。这就意味游戏有足够的宽容度来满足在不同的电脑上运行,并保证至少高于值(70,85FPs)10FPS,也意味着更多的硬件开销。 你可以使用以下手段来提高你游戏的宽容度:垂直同步(V-Sync),HMD分辨率(HMD resolution),纹理质量(texture quality), shadow quality,特效质量(effects qualit)和 post-process 质量。 控制台命令:“hmd sp 值”用来设置显示屏百分比-HMD的分辨率,在没有HMD插入时候,此命令不起作用,所以要确保包含此选项来方便玩家使用。hmd sp 100意思就是玩家设置为100%,由于一些原因,它没有缺省值。 **译者说明:DK2缺省为135%。** ## 三、 关卡设计 现在,除了这些技术选项外,你还需要考虑到的就是关卡的设计。东西不要显的太小。另外,在当前的Oculus Rift阶段,不要让玩家读太多东西,因为分辨率真是太低了,太多的阅读,玩家很难集中注意力在小的物体或文字上面。这也适用于GUI。经验表明3维的GUI要玩家很容易的瞄准按键是最好的选择,等等。 ## 四、 针对Oculus 针对Oculus这个建议可能有点搞笑。因为你将会计算HMD相对于玩家位置的偏移。Oculus本身内置相机有它自己的跟踪空间(相对位置和旋转),而相机本身与渲染点有偏移。 这就意味着你需要从相机位置发个射线,设置相机的开始位置加上HMD的偏移位置。对于旋转来说,最好的方法是从相机对象或相机控制者取得一个向前的向量。 设置HMD位置: ![这里写图片描述](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b0083240f3.jpg "") ~~~ HMD射线跟踪: ~~~ ![HMD](image/56a4e14bee186.png "") ## 五、 玩家的舒适度 玩家的舒适度意味着在没有玩家输入的时候,不要过多的移动玩家。这就意味着不要有“走路颠簸”或走路时摆动。不要让玩家在莫名的外力影响下移动等等。若你做这些事情,一些 玩家可能会觉得眩晕或恶心。相信我,我们已经做过这些。 ## 六、 Rift的分辨率 目前Rift的分辨率很难让人注意到游戏中小的细节或文字。所以一些小的重要的对象,一定要放在HMD位置的前方,这样玩家就可以容易看到他们。同理对于三维的文字或带文字的对象。将来分辨率或DPI会提高,暂时呢只能这样。 我希望这些小小的技巧可以帮助到向我们一样遇到问题的人。Oculus游戏的开发也许当然很混乱和令人沮丧,但是沉下心来,会觉得很有意思。实话实说,为最新的电子游戏技术开发电子游戏感觉特别棒。 原文地址:[http://pluggedthegame.com/community/devblog/six-ways-to-make-that-awesome-game-youre-developing-work-on-oculus-rift/](http://pluggedthegame.com/community/devblog/six-ways-to-make-that-awesome-game-youre-developing-work-on-oculus-rift/) 若有问题,请随时联系! 非常感谢!
';

美轮美奂宇宙星空制作神器Spacescape

最后更新于:2022-04-01 11:43:19

本文章由cartzhang编写,转载请注明出处。 所有权利保留。 文章链接:[http://blog.csdn.net/cartzhang/article/details/46444569](http://blog.csdn.net/cartzhang/article/details/46444569) 作者:cartzhang ## 一、 简介 Spacescape 宇宙星空工具就是创建宇宙天空盒的工具,天空盒有星星和星云组成。 工具的渲染有Ogre来实现。 先来请您欣赏美轮美奂的星空片段: ![如梦似幻](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b00805f553.jpg "") ![魔幻](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b0080cf7e1.jpg "") ![冰与火](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b00813e56a.jpg "") ![冰火2](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b0081aa390.jpg "") ![淡蓝的梦](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b00821c232.jpg "") ## 二、 功能说明和使用方法 功能:可导出无缝的带有星星和星云的太空盒。 可导出HDR的天空盒 可导出符合虚幻4或Unity的天空盒。 导出文件格式是XML;包含了几个示例文件和我自己下载或自己做的XML文件,可以直接导出为PNG格式、DDS等格式。 星空材质的导出可以是PNG、JPG、Tga、dds四种格式。 ![导出图片格式](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b008247cb3.jpg "") 导出的还有选择,如下图: ![引擎选择](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b00826a7fd.jpg "") ## 三、 关于版权 这个工具**没有版权和你可以随便使用,也可以商业应用,不需要许可和版税要求**。 再次感谢ALEX PETERSON 给我们提供这么好的工具! ## 四、 这么好的东西,哪里有呢? 不给地址就是刷流氓,不免分下载就是不诚意: 里面附送2个自己制作的星空,水平一般,诚意十足! http://download.csdn.net/detail/cartzhang/8791763 还有网盘下载: 链接:[http://pan.baidu.com/s/1gdGRUTH](http://pan.baidu.com/s/1gdGRUTH) 密码:ynmx 若有问题,请随时联系! 当然,好东西要共享!如果你还满意,请移动鼠标,点个赞,like一下,就是对我莫大的鼓舞。 再次感谢各位。
';

UE4制作插件的插件神器pluginCreator

最后更新于:2022-04-01 11:43:16

本文章由cartzhang编写,转载请注明出处。 所有权利保留。  文章链接:http://blog.csdn.net/cartzhang/article/details/45644007 作者:cartzhang ## UE4制作插件的插件神器 **本博文同步在:[Galaxix动漫大陆UE4板块](http://www.galaxix.com/bbs/read.php?tid=5370&ds=1) ** 看近来,很多使用UE4插件的同学,都苦恼于UE4插件,官方给了好多资料,但是还是无从下手啊。 下面这个是官方插件说明: [https://docs.unrealengine.com/latest/INT/Programming/Plugins/index.html](https://docs.unrealengine.com/latest/INT/Programming/Plugins/index.html) 有人看完还是一头雾水,从哪里开始呢。 Wiki有人写了个说明,照着可以做,但是各种说明: 官方地址如下: [https://wiki.unrealengine.com/An_Introduction_to_UE4_Plugins](https://wiki.unrealengine.com/An_Introduction_to_UE4_Plugins)   重点来了,如果你还是觉得不知道怎么办,那当然是怪我了! 下面,就是给你介绍这个插件神器的时候。   **不给源码的不诚意,那就是耍流氓啊!** **不耍流氓地址:[https://github.com/cartzhang/PluginCreator](https://github.com/cartzhang/PluginCreator)** 声明这个玩意不是我弄,只是发现比较好玩,能简化虚幻的插件入门水平,给大家介绍使用。 之前这个版本支持的比较老旧,我当初用的第一次版本也没有成功,但是看主家更新了,就是那么的好用。     插件神器来了!!!!前方高能,请无关人躲避啊! ### 一、安装插件 话不多说,开始了,首先下载后插件你需要重新命名一下,名称修改后要用里面的*.uplugin的名称一样,我的是这样的:  ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b007e4e15c.jpg)   然后就回到Engine的目录下,找到GenerateProjectFiles.bat,双击,等待自己搞定。然后打开ue4.sln,然后编译一下工程(不需要重新编译)。  ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b007e72501.jpg) 为什么需要编译呢,是因为每个版本的下图红框内大臣dll不一样,你要是下载4.6的,然后当然不放在4.7的版本下运行了啊!所有,需要每个人自己编译一下。  ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b007e8d081.jpg)   至此插件安装完毕。来看打开效果:  ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b007ea8211.jpg)   ### 二、使用方法 这个很简单啊。 打开PluginCreator,然后出现界面如下:  ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b007ecd95c.jpg) 看到了有三个可选择的,填写以下你的想要的信息,然后点击生成,就会弹出信息,是否开始创建的插件地址,当然你随意了。 插头一样的图标你可以自己选你的风格:  ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b007ef2ce7.jpg)      ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b007f16a32.jpg)   当然里面的文件里面可以打开你的uplugin,可以看到你填写的信息!  ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b007f2f753.jpg)   至此大功告成了没有? 没有。 你发现你的文件夹内少点东西啊。是啊,少Binaries,缺省本plugin的dll啊。 所以,继续,重新找到虚幻工程里面的这个玩意GenerateProjectFiles.bat.  ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b007f4885e.jpg) 然后点击运行,然后在虚幻的插件里面出现:  ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b007f6fb38.jpg) 你只需要编译一下就可以了!记得不需要重新编译啊,也不需要清理啊!   然后就是见证奇迹的时候: 生成了插件的DLL有某有:  ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b007f927f3.jpg)   插件source自动添加的基础代码有某有:  ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b007fb1190.jpg) 若你选择的是生成Basic或Advanced,你也可以看你的需要的代码,并且可以自己设置点击事件:  ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b007fd10b6.jpg)   若此这般,这般若此,你还不会自己建立自己的插件嘛!    说明:现在版本为虚幻的4.7版本。  ------------------------------------------------ 若有问题,请随时联系!  当然,好东西要共享!如果你还满意,请移动鼠标,点个赞,like一下,就是对我莫大的鼓舞。   再次感谢各位。
';

Kinect舒适区范围–UE4 的Blueprint测试范例

最后更新于:2022-04-01 11:43:14

本文章由cartzhang编写,转载请注明出处。 所有权利保留。  文章链接: http://blog.csdn.net/cartzhang/article/details/44748475 作者:cartzhang 说明:kinect使用的是一代版本,SDK为1.8版本 ### 一、概述 根据上篇博客,可大致知道Kinect舒适区范围。下面就借助Ue4平台来表现一下。   建立工程,加载Kinect库。当然,我并没有直接使用Kinect库,而是自己封装了一个自己的库。 使用方法与添加UE4的各种库类似,具体可以UE4的wiki上自行搜索,当然自己也有尚未解决的问题。 ### 二、BP代码部分 在我的Character的头文件上加入调用Kinect玩家位置的函数 ~~~ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Gameplay) bool BGetPostionSucess; UFUNCTION(BlueprintCallable, Category = KINECTUE) FVector4 GetPlayerPositon(); ~~~ CPP中代码如下: ~~~ FVector4 AU4UE4TestCharacter::GetPlayerPositon() { FVector4 VPosition = FVector::ZeroVector; BGetPostionSucess = false; UDKinect::UDKVector pPosition; int iFlag = KinectGetUserPosition(USER_INDEX, pPosition); if (iFlag == 0) { BGetPostionSucess = false; //GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, "can get player position for the moment"); } else{ VPosition = FVector(pPosition.x, pPosition.y, pPosition.z); BGetPostionSucess = true; } return VPosition; } ~~~   这样就基本搞定了代码部分。   ### 三、Blueprint的函数实现   首先是初始化舒服区的位置,在BP中写一个函数即可。 根据之前博客范围,我想把左右范围控制在角度为60度,这样做有就跟为90-60= 30度,这样根据正切函数就可以得到舒适距离与左右范围的为dist/tan60;因此在下图中有个变量叫做根号三,就是约等于1.732。 我本有意画个图,奈何画的比较烂,不贴了。 初始化图: > ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b007c6ea0a.jpg)   说明:MinZ为舒适区的最近距离,MaxZ为舒适区设置的最大距离。这个根据Kinect摄像头的高度和角度来调节。 接下来就是画线了:画了红绿两种线段来表示区域。 > ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b007ca79a7.jpg) 红色交汇处为左上角点,绿色交汇点为右下角点。 Basic position是kinect的位置,也可以叫做相对位置点。 然后就是玩家位置了,BP如下: >  ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b007ce2f4e.jpg) ### 四、主BP调用 这些函数搞定以后,就是在关卡的BP中进行调用了。这个就简单多了。 初始化调用: >  ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b007d2d3b9.jpg) 位置实时判断表示: >  ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b007d5b50d.jpg)   ### 五、测试结果       若都写完了,编译也没有错误,就Play来运行检测一下。    看你的Kinect是否可以找到玩家位置,我这里用来一个小木块来代替了玩家,做了个小gif. ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b007d878bd.jpg) THE  END! --------------------------- 若有问题,请随时联系! 非常感谢!
';

热烈庆祝UE4完全免费Free—GitHub上源码的关联方法

最后更新于:2022-04-01 11:43:12

声明:所有权利保留。  转载请说明出处:http://blog.csdn.net/cartzhang/article/details/44040317 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b007b7a4f4.jpg) ## IF YOU LOVE SOMETHING, SET IT FREE 我真是喜欢这句啊! Unreal Engine 4 is now available to everyone for free, and all future updates will be free! 今天是全体游戏人的欢乐激动啊,群里面各种讨论。因为完全免费开源了。 真心是赞啊! **UE4免费开源后的,这里给出关于github关联方式:** 一、当然是Epic和github注册了。 二、注册好Epic后登录,然后在https://www.unrealengine.com/settings 中填写东西,要填上你的GitHub的名字,这样随后Epic就可以发对你的Github邀请。  ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b007bab54d.jpg) 三、去Github上,https://github.com/EpicGames 看到加入的按钮。 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b007bd9379.jpg) 然后会弹出就可以看到EpicGame对你的邀请,很明显接受邀请啊 那是网站会自动发送一个邀请,去你的注册邮箱里面看看有邀请链接,点它。 四、然后哈哈哈。如果你在网页https://github.com/EpicGames看到如下东西,恭喜你了。  ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b007bf37f9.jpg) --------------------------- 若有问题,请随时联系! 非常感谢!
';

UE4的JSON读写方式&lt;二&gt;

最后更新于:2022-04-01 11:43:10

声明:所有权利保留。 转载必须说明出处:http://blog.csdn.net/cartzhang/article/details/43794409 Json的Writer博客地址: http://blog.csdn.net/cartzhang/article/details/41009343  [UE4的Json读写方式<一>](http://blog.csdn.net/cartzhang/article/details/41009343) ## UE4 的json读写方式 ### 一、UE4的Json解析代码 有网友说UE4的解析有问题,解析出错。个人觉得应该是Json格式有问题。或解析的对象不正确,对象或数组用来对应的解析方式来解析的。 解析方式有两种:类名为:FJsonSerializer,头文件名称为JsonSerializer.h ~~~ template <class CharType> static bool Deserialize( const TSharedRef< TJsonReader<CharType> >& Reader, TArray< TSharedPtr<FJsonValue> >& OutArray ) { StackState State; if ( !Deserialize( Reader, /*OUT*/State ) ) { return false; } if ( State.Object.IsValid() ) { return false; } OutArray = State.Array; return true; } template <class CharType> static bool Deserialize( const TSharedRef< TJsonReader<CharType> >& Reader, TSharedPtr<FJsonObject>& OutObject ) { StackState State; if ( !Deserialize( Reader, /*OUT*/State ) ) { return false; } if ( !State.Object.IsValid() ) { return false; } OutObject = State.Object; return true; } ~~~ 可以看出,第一个是针对数组的,第二个是针对Json对象的。请注意分别。 ### 二、样例代码 CPP中包含头文件#include "Json.h"。 ~~~ void ATestPhysics1Character::JsonDeserialize() { FString JsonValue = "[{\"author\":\"4446545646544\"},{\"name\":\"jack\"}]"; FString JsonStr; TArray<TSharedPtr<FJsonValue>> JsonParsed; TSharedRef< TJsonReader<TCHAR> > JsonReader = TJsonReaderFactory<TCHAR>::Create(JsonValue); bool BFlag = FJsonSerializer::Deserialize(JsonReader, JsonParsed); if (BFlag) { int ArrayNumber = JsonParsed.Num(); if (ArrayNumber == 2) { FString FStringAuthor = JsonParsed[0]->AsObject()->GetStringField("author"); if (GEngine) { FString TmpString = "author :" + FStringAuthor; GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TmpString); } FString FStringName = JsonParsed[1]->AsObject()->GetStringField("name"); if (GEngine) { FString TmpString = "name :" + FStringName; GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TmpString); } } } } ~~~ 代码不严谨,但是是个样子。 ### 三、结果截图 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b007b1d07f.jpg) ================ 若有问题,请随时联系! 感谢浏览!
';

UE4.5.0的Kinect插件(Plugin)—插件使用说明&lt;二&gt;

最后更新于:2022-04-01 11:43:07

声明:所有权利保留。 转载必须说明出处:http://blog.csdn.net/cartzhang/article/details/43563959 ### 一、起因: 写了个UE4的Kinect的插件,结果,有网友说需要给个例子。例子做了也想上传,发现Ue4最简单的例子压缩后也太大,不能上传,故此,截图来说明用法。若还不明白,在上传样例。 关于Ue4的Kinect18插件,很明显Kinect版本为1.8而不是2.0的. 而UE4版本为为4.5.0,当然目前4.6.0也是可以用的。 ### 二、步骤: 1. 当然是下载插件了,地址多次给出:[Kinect18Plugin](https://github.com/cartzhang/Kinect4UE4Plugin) https://github.com/cartzhang/Kinect4UE4Plugin 现在后插件的文件名陈为Kinect4Ue4Plugin,把这个文件拷贝到Ue4的Plugins的文件夹中,并修改名字为Kinect18Plugin。 文件的结构如下图: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b0079dfc26.jpg) ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b007a13f4d.jpg) 2. 重新生成UE4,并编译Ue4 在Unreal engine引擎的文件夹中有个批处理文件,名字为GenerateProjectFiles.bat,双击生成。然后重新 打开UE4,重新编译。 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b007a4e970.jpg) 3.利用UE editor来创建一个第一人称的项目工程,可以是C++的也可以是蓝图的,随意。 4. 修改editor中的游戏模式(gamemode),在WorldSetting中的game mode,如下图: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b007a660e3.jpg) 5. 最后,就是测试了。 左右侧身,就可以控制,游戏中第一人称人物的左右移动了。 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b007a8afda.jpg) 欢迎来到Kinect和UE4的世界。 ---------------End--------------------- 若有问题,请随时联系。 非常感谢!
';

UE4.5.0的Kinect插件(Plugin)&lt;一&gt;

最后更新于:2022-04-01 11:43:05

声明:所有权利保留。 转载必须说明出处:http://blog.csdn.net/cartzhang/article/details/43193431  UE4 Plugin,在UE4的官网,放出了有个BlankPlugin和一个UObject两个Plugin,提供给大家学习和使用。 https://docs.unrealengine.com/latest/INT/Programming/Plugins/index.html  https://wiki.unrealengine.com/An_Introduction_to_UE4_Plugins  但是呢,还是不好弄啊。 我也研究了很久,下来各种Plugin来研究和学习,最终才搞成这样子的。 Kinect,这个微软的之前游戏里的黑科技的玩意,现在一代居然不买了。估计他们有又很牛逼的的东西了,这就是全息啊---微软全息眼镜HoloLens。 但是国内还有很多玩Kinect的,而已不是二代,因为二代贵啊,还得Win8系统。大多的人,还是Kinect一代的,自己也是用一代的,一个字就是没有钱啊。 现在自己做了个基于UE4.5.0版本的,Kinect18的插件。 做到不精,好多东西还得开发着自己写,要想要现成的,你想要的,这个就自己知道想着要啥,所以在给出的插件的基础上继续,填写代码。 工程已经放在了github上。 说了太多,没有地址,那很明显就是流氓行为啊。所以地址:[Kinect4UE4Plugin](https://github.com/cartzhang/Kinect4UE4Plugin) https://github.com/cartzhang/Kinect4UE4Plugin 欢迎各路高人,完善并使用此工程。 顺道说:ue4.5.1和4.6.0版本的针对Kinect2.0二代的插件,也是有的。 地址:[Kinect版本为2.0,Ue4版本为4.5.1.](http://download.csdn.net/detail/cartzhang/8400387)  http://download.csdn.net/detail/cartzhang/8400387 [Kinect版本为2.0,UE4版本为4.6.0](http://download.csdn.net/detail/cartzhang/8400397)     http://download.csdn.net/detail/cartzhang/8400397  ========== 2015-02-06 插件更新了一点内容。 若使用插件,必须更改下插件文件名字与插件名字相同。然后GenerateProjectFile.bat文件,重新编译Unreal engine. ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-05_572b00798edc5.jpg) 图片为网友提供的。非常感谢! ================ 若有问题,请随时联系! 共勉!thanks!
';

UE4的JSON读写方式&lt;一&gt;

最后更新于:2022-04-01 11:43:03

声明:所有权利保留。 转载必须说明出处:http://blog.csdn.net/cartzhang/article/details/41009343 UE4的Json的解析博客地址: http://blog.csdn.net/cartzhang/article/details/43794409 [UE4的Json读写方式<二>](http://blog.csdn.net/cartzhang/article/details/43794409) **UE4 的json读写方式** JSON 的解析有很多开源库。UE4的JSON使用在代码的Public->Serialization中。 头文件包含:Json.h Json的读写创建是static形成的单例模式,有JsonWrite或JsonReader的Factory来创建(Create)出来。 以读方式为例子: 读的代码: ~~~ float _score = MyPlayerState->GetScore(); FString _player_name = MyPlayerState->GetName(); int _kills = MyPlayerState->GetKills(); int _deaths = MyPlayerState->GetDeaths(); FString JsonStr; TSharedRef< TJsonWriter<TCHAR, TCondensedJsonPrintPolicy<TCHAR> > > JsonWriter = TJsonWriterFactory<TCHAR, TCondensedJsonPrintPolicy<TCHAR> >::Create(&JsonStr); JsonWriter->WriteObjectStart(); JsonWriter->WriteValue(TEXT("name"), _player_name); JsonWriter->WriteValue(TEXT("score"), _score); JsonWriter->WriteValue(TEXT("deaths"), _deaths); JsonWriter->WriteValue(TEXT("kills"), _kills); <span style="white-space:pre"> </span>JsonWriter->WriteObjectEnd(); // Close the writer and finalize the output such that JsonStr has what we want JsonWriter->Close(); return JsonStr; ~~~ 写的对象可以为Objcet或Array数组,value值。 一个读写过程中,Start和End要对应,因为Json格式的要求,要不我怎么知道哪里是一个对象或数组的结尾呢。这样就会造成混乱的。 所有的读写都在close之后才真正的完成处理。 看网上例子很少,自己贴个,自己也方便! ---------------------------- 若有问题,请随时联系! 非常感谢!
';

UE4在VS2013中各个编译配置代表意义

最后更新于:2022-04-01 11:43:01

UE4中有个各式各样的编译配置,都怎么个意思呢? 对原文的理解和翻译。 https://docs.unrealengine.com/latest/INT/Programming/Development/CompilingProjects/index.html UE4通过UnrealBuildTool 来解决各个复杂的工程和对引擎的链接。 UnralBuildTool使用*.build.cs和*.Target,cs来构建有效工程。当创建C++模板或Blueprint使用C++引导来添加代码到工程中的时候,他们可以自动产生。   UE4和UnrealBuildTool(UE4构建工具)使用不同的配置,来决定你引擎的编译方式。不同使用目的对应于不同方式编译方式。   每项编译配置包含两个关键字。第一个是表征引擎的状态和你工程的状态,例如:你编译了一个debug版本,你可以调试你的代码了。第二个关键字表明编译的目标。若你想在Unreal中打开一个工程,你需要编译为Editor(编辑器)关键字;然而你编译你游戏的可执行版本,那就不使用目标关键字(第二个关键字不要就可以了)   <table><tbody><tr><td valign="top" style="background:rgb(128,0,128)"><p>状态配置</p></td><td valign="top" style="background:rgb(128,0,128)"><p>描述</p></td></tr><tr><td valign="top" style="background:rgb(128,0,128)"><p>Debug</p></td><td valign="top" style="background:rgb(128,0,128)"><p>调试。使用<span style="font-family:Times New Roman">-debug</span><span style="font-family:宋体">标示,你可以随时看到代码变化对你工程的影响。(我理解的这个是项目工程与</span><span style="font-family:Times New Roman">Editor </span><span style="font-family:宋体">可以同时打开的)</span></p></td></tr><tr><td valign="top" style="background:rgb(128,0,128)"><p>Development</p></td><td valign="top" style="background:rgb(128,0,128)"><p>就是<span style="font-family:Times New Roman">Release</span><span style="font-family:宋体">版本。使用</span><span style="font-family:Times New Roman">Development</span><span style="font-family:宋体">配置,可在</span><span style="font-family:Times New Roman">Editor</span><span style="font-family:宋体">打开工程后,可以工程的代码变化的作用。(我理解的这个是项目工程与</span><span style="font-family:Times New Roman">Editor</span><span style="font-family:宋体">不同时打开的)</span></p></td></tr><tr><td valign="top" style="background:rgb(128,0,128)"><p>Shipping</p></td><td valign="top" style="background:rgb(128,0,128)"><p>此配置不包括命令行,状态和分析工具</p></td></tr><tr><td valign="top" style="background:rgb(128,0,128)"><p> </p></td><td valign="top" style="background:rgb(128,0,128)"><p> </p></td></tr></tbody></table>     <table><tbody><tr><td valign="top" style="background:rgb(0,204,255)"><p>目标配置</p></td><td valign="top" style="background:rgb(0,204,255)"><p>描述</p></td></tr><tr><td valign="top" style="background:rgb(0,204,255)"><p><span style="color:rgb(22,22,23); background:rgb(255,255,255)">[empty]</span></p></td><td valign="top" style="background:rgb(0,204,255)"><p>单机可执行版本,对于某些平台需要<span style="font-family:Times New Roman">cooked</span><span style="font-family:宋体">内容。</span></p></td></tr><tr><td valign="top" style="background:rgb(0,204,255)"><p><span style="color:rgb(22,22,23)">Editor</span></p></td><td valign="top" style="background:rgb(0,204,255)"><p>可以用<span style="font-family:Times New Roman">Editor</span><span style="font-family:宋体">打开,并且可以看到所有代码的变化影响。</span></p></td></tr><tr><td valign="top" style="background:rgb(0,204,255)"><p><span style="color:rgb(22,22,23)">Uncooked</span></p></td><td valign="top" style="background:rgb(0,204,255)"><p>工程使用没有<span style="font-family:Times New Roman">cooked</span><span style="font-family:宋体">类型,标志位</span>”-game”,<span style="font-family:宋体">在运行是在新窗口中使用没有</span><span style="font-family:Times New Roman">cooked</span><span style="font-family:宋体">内容,相当于在编辑器</span><span style="font-family:Times New Roman">editor</span><span style="font-family:宋体">中</span><span style="color:rgb(22,22,23); background:rgb(255,255,255)"> </span><span style="color:rgb(22,22,23)">Play in &gt; </span><span style="color:rgb(22,22,23)">新窗口作为缺省的启动</span><span style="color:rgb(22,22,23); background:rgb(255,255,255)"> </span></p></td></tr><tr><td valign="top" style="background:rgb(0,204,255)"><p> </p></td><td valign="top" style="background:rgb(0,204,255)"><p/></td></tr></tbody></table>   若有问题,请随时联系!非常感谢! ====================
';

前言

最后更新于:2022-04-01 11:42:58

> 原文出处:[虚幻4基础学习](http://blog.csdn.net/column/details/ue4cartzhang.html) 作者:[cartzhang](http://blog.csdn.net/cartzhang) **本系列文章经作者授权在看云整理发布,未经作者允许,请勿转载!** # 虚幻4基础学习 > 关于新手对于虚幻4的一些问题,包括但不限于VR开发,源码下载,编辑器快捷使用,新建自己的插件,关于Kinect的虚幻应用
';