[{"data":1,"prerenderedAt":1855},["ShallowReactive",2],{"blog-page-19":3,"blog-count":1854},[4,227,579,1374,1614],{"id":5,"title":6,"body":7,"date":203,"description":13,"extension":204,"meta":205,"navigation":208,"path":220,"seo":221,"stem":222,"tags":223,"__hash__":226},"blogs\u002F_legacy\u002F2017\u002F2017-04-23-ue4-render-target-preview.md","UE4渲染目标使用",{"type":8,"value":9,"toc":184},"minimark",[10,14,17,20,31,35,38,45,50,53,57,60,65,68,71,74,77,80,83,86,89,92,95,100,103,106,109,114,117,120,124,127,130,134,137,141,144,147,150,155,158,161,164,169,172,175],[11,12,13],"p",{},"UE4的Render Target提供了渲染缓存功能，可以制作很多有趣的特效。",[11,15,16],{},"当前UE4版本4.15.1。",[11,18,19],{},"Render Target作为一种动态生成内容的方法，为材质系统提供了更加精细的控制。引擎本身提供的Scene Capture功能也是可输出到Render Target并连接到材质上作为摄像展示功能的。通过Render Target也能实现类似神秘海域里面的擦拭物体表面灰层的功能。",[11,21,22,23,30],{},"本文参照[",[24,25,29],"a",{"href":26,"rel":27},"https:\u002F\u002Fdocs.unrealengine.com\u002Flatest\u002FCHN\u002FEngine\u002FRendering\u002FRenderTargets\u002FBlueprintRenderTargets\u002FOverview\u002Findex.html",[28],"nofollow","官方文档","]进行完成，由于官方文档的介绍并不详细，所以在这里记录下自己的经验和理解。",[32,33,34],"h2",{"id":34},"高度场绘制器",[11,36,37],{},"高度场绘制器是通过将玩家操作反映到Render Target，然后传入材质来提升World Position来实现的，是一个非常简单的示例，基本按照官方的指引操作即可。",[11,39,40],{},[41,42],"img",{"alt":43,"src":44},"image","\u002Fwp-content\u002Fuploads\u002F2017\u002F04\u002Fimage_thumb-5.png",[46,47,49],"h3",{"id":48},"mat_heighfieldpainter","MAT_HeighfieldPainter",[11,51,52],{},"这个材质是最终应用到物体上的材质,Heightfield参数是Render Target的传入路径。可以看到，传入的高度场被拼接为(0,0,Z)的Vector输出到了World Position上，这就会导致被应用了材质的物体的对应位置向上偏移，同时传入的Render Target也到达了自发光颜色，使得升起的部分有了白色的效果。",[46,54,56],{"id":55},"mat_forcesplat","MAT_ForceSplat",[11,58,59],{},"这个材质是用来绘制Render Target的，直观的讲，就是在指定的点生成一个由内向外的Gradient一样的效果，作用到高度场上就是上图中那样的锥形效果。材质在实现上，首先根据传入的Force Position，mask之后，让其与UV相减，之后再求取向量长度的话，那么自然就会成为一个由那个点向外扩散的圆了：",[11,61,62],{},[41,63],{"alt":43,"src":64},"\u002Fwp-content\u002Fuploads\u002F2017\u002F04\u002Fimage_thumb-6.png",[11,66,67],{},"这里要注意的是，UV在材质中是以左上角为(0,0)的，在材质蓝图中分别展示为R和G两个通道，但是负数全是黑色的。求出向量长度之后的操作就比较单纯了，通过减法和取整就可以得到目标的“圆点”了。",[46,69,70],{"id":70},"蓝图",[11,72,73],{},"蓝图的设置上比较方便，因为官方有提供在网页上复制蓝图脚步的功能，只要拷贝过去就可以了。基本上的功能也非常的直观。",[11,75,76],{},"在ConstructionScript中构建并指定材质，由于这里的功能比较简单，RenderTarget时在这里直接构建的；",[11,78,79],{},"在BeginPlay时打开Input交互，同时将RenderTarget设置给动态材质；",[11,81,82],{},"鼠标按下时标记MouseDown，此时Tick事件就会开始进行Trace操作；",[11,84,85],{},"整个蓝图的核心就是通过这个Trace给物体施加Damage事件，并在Damage处理中对进行RenderTarget的绘制。",[46,87,88],{"id":88},"绘制点的获取",[11,90,91],{},"需要注意的是，官方在获取绘制点的时候使用了一个简便的方法，这点官方也有给出注释说明。如果想要在所有的Mesh上都实现类似的效果，而不只是在例子中才能使用的话，需要使用Trace时获得的UV来进行计算。",[11,93,94],{},"而这个功能是4.13才有的新功能，要使用的话必须在项目设置>引擎>Physics中打开：",[11,96,97],{},[41,98],{"alt":43,"src":99},"\u002Fwp-content\u002Fuploads\u002F2017\u002F04\u002Fimage_thumb-7.png",[11,101,102],{},"使用时要注意勾选Trace Complex才行。",[32,104,105],{"id":105},"流体表面",[11,107,108],{},"其实就是一个水波纹的效果，会在用户点击和玩家碰撞的地方产生扩散衰减的水波纹效果。是比高度图稍微复杂一些的效果，也是非常实用的效果。",[11,110,111],{},[41,112],{"alt":43,"src":113},"\u002Fwp-content\u002Fuploads\u002F2017\u002F04\u002Fimage_thumb-10.png",[11,115,116],{},"由于需要的效果比较复杂，所以用到了三个缓存用Render Target以及一个绘制法线贴图的Render Target。",[11,118,119],{},"而相对的，绘制用材质ForceSplat可以采用高度场里面用到的材质，是一样的。",[46,121,123],{"id":122},"watermaterial","WaterMaterial",[11,125,126],{},"这个材质是应用于“水面”的材质，其本身的材质很简单，和通常的水面材质不同的是，将输入的Texture应用到了世界位置便宜与法线上。",[11,128,129],{},"其中Heightfield的应用方式与高度场的材质应用是相同的。",[46,131,133],{"id":132},"heightsim","HeightSim",[11,135,136],{},"这个是计算水面波纹的核心材质，原理上，首先将PreviousHeight1分别朝四个方向进行TexelSize单位的移动，然后相互叠加后与原有的图形进行相减，就能得到一个向外扩散的外沿了。之后再减去可以算作完全消退的PreviousHeight2作进一步的修正，就是当前的Height了。",[46,138,140],{"id":139},"computenormal","ComputeNormal",[11,142,143],{},"根据当前的绘制水面的高度图计算出法线贴图，使用的数学原理与HeightSim类似。",[46,145,70],{"id":146},"蓝图-1",[11,148,149],{},"蓝图实现上的整体逻辑和高度场示例中差不多，复杂之处在于使用tick函数来计算和更新了水波。其中最大的核心在这里：",[11,151,152],{},[41,153],{"alt":43,"src":154},"\u002Fwp-content\u002Fuploads\u002F2017\u002F04\u002Fimage_thumb-8.png",[11,156,157],{},"每次计算的时候通过交换3个Render Target，分别作为Current，Prev1， Prev2来进行水波扩散的更新模拟计算，而法线的运算反而简单，只是在“绘制”执行的时候对“当前”水波进行法线计算而已。",[32,159,160],{"id":160},"小心有坑",[11,162,163],{},"官方亲切的提供了在网页中复杂蓝图节点的功能，但是要注意的是，当前版本中有一个BUG，材质蓝图中的所有参数名被多次使用时复制后会被自动重命名，并且这个过程是没有提示的，最后材质中就会变成这样：",[11,165,166],{},[41,167],{"alt":43,"src":168},"\u002Fwp-content\u002Fuploads\u002F2017\u002F04\u002Fimage_thumb-9.png",[11,170,171],{},"此时材质参数无法正常传递，就会出现奇怪的特效。",[32,173,174],{"id":174},"源码",[11,176,177,178,183],{},"由于官方当前在文档中并没有提供一些必要的Mesh等资源，需要从Content Example中复制，为了方便大家研究，已经将完成的版本上传到了[",[24,179,182],{"href":180,"rel":181},"https:\u002F\u002Fgithub.com\u002Fsteinkrausls\u002FRtg_Test",[28],"GitHub","]上，欢迎取用。",{"title":185,"searchDepth":186,"depth":187,"links":188},"",2,3,[189,195,201,202],{"id":34,"depth":186,"text":34,"children":190},[191,192,193,194],{"id":48,"depth":187,"text":49},{"id":55,"depth":187,"text":56},{"id":70,"depth":187,"text":70},{"id":88,"depth":187,"text":88},{"id":105,"depth":186,"text":105,"children":196},[197,198,199,200],{"id":122,"depth":187,"text":123},{"id":132,"depth":187,"text":133},{"id":139,"depth":187,"text":140},{"id":146,"depth":187,"text":70},{"id":160,"depth":186,"text":160},{"id":174,"depth":186,"text":174},"2017-04-23","md",{"layout":206,"status":207,"published":208,"author":209,"author_login":211,"author_email":212,"wordpress_id":213,"wordpress_url":214,"date_gmt":215,"excerpt":216},"post","publish",true,{"display_name":210,"login":211,"email":212,"url":185},"风铃","flinkor","flinkor@foxmail.com",1615,"\u002F\u002F?p=1615","2017-04-23 14:26:42 +0000",{"type":8,"value":217},[218],[11,219,13],{},"\u002F2017-04-23-ue4-render-target-preview",{"title":6,"description":13},"_legacy\u002F2017\u002F2017-04-23-ue4-render-target-preview",[224,225],"UE4","RenderTarget","bAxg5KEnuL8U7tmCW8-5niZcxunUKbD5aFWgOYaDb3o",{"id":228,"title":229,"body":230,"date":562,"description":234,"extension":204,"meta":563,"navigation":208,"path":572,"seo":573,"stem":574,"tags":575,"__hash__":578},"blogs\u002F_legacy\u002F2017\u002F2017-04-20-ue4-multiplayer-and-network-overview.md","UE4网络连接与多人游戏",{"type":8,"value":231,"toc":549},[232,235,238,241,244,253,256,266,269,272,275,278,281,284,287,290,293,298,301,307,310,315,318,323,326,332,335,341,344,350,353,356,359,364,367,370,376,379,385,393,396,399,402,405,408,411,414,417,420,423,426,431,434,446,449,455,458,464,467,470,473,480,502,506,509,512,515,518,521,524,531,535,542],[11,233,234],{},"UE4的Replication系统在设计上非常的高效和迅速，要制作联网游戏相关功能的话使用起来也很方便。",[11,236,237],{},"当前UE4版本为4.15.0。",[32,239,240],{"id":240},"概述",[11,242,243],{},"蓝图中的GameMode是自带支持Replication系统的，GameMode也是整个Replication系统在逻辑设计上的中心。如果在设计游戏时，不需要联网功能，目前引擎已经提供了一个新的基类GameModeBase来使用。",[11,245,246,247,252],{},"网络模式相关的官方文档在[",[24,248,251],{"href":249,"rel":250},"https:\u002F\u002Fdocs.unrealengine.com\u002Flatest\u002FCHN\u002FGameplay\u002FNetworking\u002Findex.html",[28],"这里","]。",[11,254,255],{},"网络模式的调试可以在PIE中进行，点击调试旁边的下拉三角就可以进行设置。Server的模式分为DedicatedServer和ListenServer，如果在多客户端模式下进行屏幕调试输出，会发现输出会自动表明输出者的身份，在源代码中可以找到它的实现方式：",[257,258,263],"pre",{"className":259,"code":261,"language":262},[260],"language-text","UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, false);\nFString Prefix;\nif (World)\n{\n  if (World->WorldType == EWorldType::PIE)\n  {\n    switch(World->GetNetMode())\n    {\n      case NM_Client:\n        Prefix = FString::Printf(TEXT(\"Client %d: \"), GPlayInEditorID - 1);\n      break;\n      case NM_DedicatedServer:\n      case NM_ListenServer:\n        Prefix = FString::Printf(TEXT(\"Server: \"));\n      break;\n      case NM_Standalone:\n      break;\n    }\n  }\n}\n","text",[264,265,261],"code",{"__ignoreMap":185},[11,267,268],{},"这个宏也可以自己拿来做一些判断用，例如区分独立服务器与监听服务器。",[11,270,271],{},"另外，如果要使用官方模板进行功能测试的话需要注意：",[11,273,274],{},"TopDown模板的角色移动是基于NavMesh的，而这个机制本身是不支持直接联网的，因此角色不会移动。",[11,276,277],{},"而默认的Blank模板的Pawn是没有CharacterMoveMentComponent的，当然也就不会同步角色移动。",[32,279,280],{"id":280},"属性复制",[11,282,283],{},"Replication系统的核心是复制，“真正”的UWorld更新是在Server上运行的，而Client上的UWorld则接受来自Server的复制更新，同时Client也会进行一定的“预测”，但是一切状态值以Server上的为准。这样的设计下，只要采用合理的逻辑，就可以杜绝作弊。",[11,285,286],{},"UE4中Replication在设置完成后是自动运行的，在使用时只要留意，凡是需要所有客户端都获得的状态更新，包括Spawn、Replicate的值，都必须在服务器上进行。在客户端上进行修改只会让他们在下一次同步时被覆盖掉。",[46,288,289],{"id":289},"开启复制",[11,291,292],{},"属性复制可以在Actor的属性面板中开启",[11,294,295],{},[41,296],{"alt":43,"src":297},"\u002Fwp-content\u002Fuploads\u002F2017\u002F04\u002Fimage_thumb.png",[11,299,300],{},"C++中则需要在构造函数中指定",[257,302,305],{"className":303,"code":304,"language":262},[260],"bReplicates = true;\n",[264,306,304],{"__ignoreMap":185},[11,308,309],{},"开启复制之后，对于具体的属性，只要指定为可复制即可：",[11,311,312],{},[41,313],{"alt":43,"src":314},"\u002Fwp-content\u002Fuploads\u002F2017\u002F04\u002Fimage_thumb-1.png",[11,316,317],{},"其中Replicated就是复制模式，而RepNotify会在属性被变更时触发一个通知事件，非常的有用。在蓝图下，这个事件会自动生成：",[11,319,320],{},[41,321],{"alt":43,"src":322},"\u002Fwp-content\u002Fuploads\u002F2017\u002F04\u002Fimage_thumb-2.png",[11,324,325],{},"而在C++模式下，就要做一些额外的设定操作，除了指定属性可复制之外",[257,327,330],{"className":328,"code":329,"language":262},[260],"UPROPERTY(Replicated)\nuint32 bFlag:1;\n",[264,331,329],{"__ignoreMap":185},[11,333,334],{},"还要设置同步属性表",[257,336,339],{"className":337,"code":338,"language":262},[260],"void AReplicatedActor::GetLifetimeReplicatedProps(TArray\u003C FLifetimeProperty > & OutLifetimeProps) const\n{\nDOREPLIFETIME(AReplicatedActor, bFlag);\nDOREPLIFETIME(AReplicatedActor, IntegerArray);\n}\n",[264,340,338],{"__ignoreMap":185},[11,342,343],{},"如果要使用RepNotify的话，就必须在UPROPERTY宏中进行指定",[257,345,348],{"className":346,"code":347,"language":262},[260],"UPROPERTY(ReplicatedUsing=OnRep_Flag)\nuint32 bFlag:1;\n",[264,349,347],{"__ignoreMap":185},[11,351,352],{},"然后实现这个函数方可。",[46,354,355],{"id":355},"条件复制",[11,357,358],{},"Replication有一个重要的设置是条件复制，在蓝图中可以看到选项",[11,360,361],{},[41,362],{"alt":43,"src":363},"\u002Fwp-content\u002Fuploads\u002F2017\u002F04\u002Fimage_thumb-3.png",[11,365,366],{},"意思都比较明显，关于Owner以及Simulated、Autonomous的意思在下面会有说明，而Replay指代的是官方的回放系统，如果没有使用到的话就可以不用管它。",[11,368,369],{},"C++中要调整这个属性则需要用到属性表：",[257,371,374],{"className":372,"code":373,"language":262},[260],"void AActor::GetLifetimeReplicatedProps( TArray\u003C FLifetimeProperty > & OutLifetimeProps ) const\n{\n  DOREPLIFETIME_CONDITION( AActor, ReplicatedMovement, COND_SimulatedOnly );\n}\n",[264,375,373],{"__ignoreMap":185},[11,377,378],{},"在C++中还可以使用额外的自定义标志位来进行更新控制：",[257,380,383],{"className":381,"code":382,"language":262},[260],"void AActor::PreReplication( IRepChangedPropertyTracker & ChangedPropertyTracker )\n{\n  DOREPLIFETIME_ACTIVE_OVERRIDE( AActor, ReplicatedMovement, bReplicateMovement );\n}\n",[264,384,382],{"__ignoreMap":185},[11,386,387,388,392],{},"但是这样可能会降低效率，更详细的说明可以参照[",[24,389,29],{"href":390,"rel":391},"https:\u002F\u002Fdocs.unrealengine.com\u002Flatest\u002FCHN\u002FGameplay\u002FNetworking\u002FActors\u002FProperties\u002FConditions\u002Findex.html",[28],"]。另外，在文档中有说明，属性表是无法在同步确立之后进行修改的。",[46,394,395],{"id":395},"参数设置",[11,397,398],{},"Replication中还有一些可选参数，其中比较重要的是Relevant机制，这个机制通过判定Actor当前是否与Player相关来有效的削减同步的数据量",[11,400,401],{},"Always Relevant会使得Actor始终被同步到各个客户端",[11,403,404],{},"Net Use Owner Relevancy则会让Actor的同步相关性与持有者相同",[11,406,407],{},"Only Relevant To Owner使得Actor只与自己的持有者保持相关",[11,409,410],{},"这里Owner的定义指代持有其的PlayerController或者Pawn，而是否相关的判定机制是可以重载的，在类中对IsNetRelevantFor函数进行重载即可。",[11,412,413],{},"还有一个特殊的标志位是bTearOff，一旦Server设置了这个值，Replication机制会将控制权交给所有的Client，实质上Actor就变成了不同步的物体，由各个Client进行控制。",[11,415,416],{},"而ReplicateMovement将会使得物体的移动被同步，也就是速度、位置这些信息。",[11,418,419],{},"bNetLoadOnClient会使得物体在Server上加载\u002FSpawn后在Client上被自动加载。",[32,421,422],{"id":422},"远程调用",[11,424,425],{},"远程函数调用提供Clinet与Server之间的函数调用桥梁，调用模式分为三种：",[11,427,428],{},[41,429],{"alt":43,"src":430},"\u002Fwp-content\u002Fuploads\u002F2017\u002F04\u002Fimage_thumb-4.png",[11,432,433],{},"选中蓝图中的函数就能看到这个设置，它们之间的区别如下：",[435,436,437,440,443],"blockquote",{},[11,438,439],{},"NetMulticast: 多路传送，由服务器调用所有的客户端，比如用于播放特效",[11,441,442],{},"Server: 在服务器上运行，只有角色标志为ROLE_Authority或ROLE_AutonomousProxy的Actor才能调用",[11,444,445],{},"Client: 在其所属的客户端上运行",[11,447,448],{},"其中，Server函数还可以提供额外的校验函数以方便对传输到服务器的值进行检测，这个设置只能在C++中使用，蓝图中并未看到相关选项：",[257,450,453],{"className":451,"code":452,"language":262},[260],"UFUNCTION(Server, Reliable, WithValidation)\nvoid Server_ReliableFunctionCallThatRunsOnServer();   UFUNCTION(Client, Reliable)\nvoid Client_ReliableFunctionCallThatRunsOnOwningClientOnly();   UFUNCTION(NetMulticast, Unreliable)\nvoid Client_UnreliableFunctionCallThatRunsOnAllClients();\n",[264,454,452],{"__ignoreMap":185},[11,456,457],{},"对函数进行实现",[257,459,462],{"className":460,"code":461,"language":262},[260],"void AReplicatedActor::Server_ReliableFunctionCallThatRunsOnServer_Implementation()\n{\n}\nbool AReplicatedActor::Server_ReliableFunctionCallThatRunsOnServer_Validate()\n{\n   \u002F\u002F 返回FALSE的话该Server函数就不会被执行\n   return true;\n}\nvoid AReplicatedActor::Client_ReliableFunctionCallThatRunsOnOwningClientOnly()\n{\n}   \nvoid AReplicatedActor::Client_UnreliableFunctionCallThatRunsOnAllClients_Implementation()\n{\n\n}\n",[264,463,461],{"__ignoreMap":185},[11,465,466],{},"函数还可以选择是否为可靠函数，UnReliable的设定将会使得引擎在网络带宽压力大的情况下丢弃这个函数的执行。",[11,468,469],{},"应当尽量避免频繁的远程调用以及在远程调用中直接传输并设置Server参数的行为，而只将输入传递到Server。",[32,471,472],{"id":472},"游戏架构",[11,474,475,476,252],{},"官方的指引文档中就有介绍过游戏架构,但实际上在做单机游戏时,将逻辑放在很多类中其实是等效的,这点会让人很迷惑。其实架构中的许多类是在多人模式下才会体现出他们的不同的，详细的内容可以看[",[24,477,29],{"href":478,"rel":479},"https:\u002F\u002Fdocs.unrealengine.com\u002Flatest\u002FCHN\u002FGameplay\u002FNetworking\u002FBlueprints\u002Findex.html",[28],[481,482,483,487,490,493,496,499],"ul",{},[484,485,486],"li",{},"GameInstance: 不参与网络复制，作为游戏实例而存在，在关卡切换载入时依然存在，可以用于存储用户本身的数据",[484,488,489],{},"GameMode: 只存在于Server上，核心逻辑存在于这里",[484,491,492],{},"GameState: 在Server和所有Client之间复制，用于存储当前游戏相关的数据",[484,494,495],{},"PlayerController: 每个Client只持有自己的PlayerController，并与Server进行同步",[484,497,498],{},"PlayerState: 存储每个Player的信息，会同步到所有的Client。",[484,500,501],{},"Pawns: 与所有的Client同步，但是与PlayerController以及PlayerState不同的是，Pawn死亡之后会被销毁，直到Respawn为止。",[46,503,505],{"id":504},"role与同步","Role与同步",[11,507,508],{},"Server和Client都可以使用Role系统来判断持有的Actor状态，Role被分为四种",[11,510,511],{},"ROLE_Authority：对Actor拥有控制权",[11,513,514],{},"ROLE_None：不参与网络复制",[11,516,517],{},"ROLE_SimulatedProxy：接受来自服务器更新的模拟控制器，使用已知信息进行预判",[11,519,520],{},"ROLE_AutonomousProxy：接受来自玩家的直接控制，配合玩家输入进行预判",[11,522,523],{},"客户端的模拟预判可以有效防止网络延迟引起的Lag，但是在处理角色动作，尤其是Blink之类的操作时，依然会导致奇怪的现象。",[11,525,526,527,252],{},"为了防止这些现象，官方对CharacterMovmentComponent作了增强，这个类会维护一个动作列表，在同步时，Client会被要求进行动作列表的重放，而不是直接更新位置。详细的可以参考[",[24,528,29],{"href":529,"rel":530},"https:\u002F\u002Fdocs.unrealengine.com\u002Flatest\u002FCHN\u002FGameplay\u002FNetworking\u002FCharacterMovementComponent\u002Findex.html",[28],[32,532,534],{"id":533},"misc","Misc",[11,536,537,538,252],{},"UE4提供了服务器进行无缝和非无缝关卡切换的函数，其具体的限制和功能可参看[",[24,539,29],{"href":540,"rel":541},"https:\u002F\u002Fdocs.unrealengine.com\u002Flatest\u002FCHN\u002FGameplay\u002FNetworking\u002FTravelling\u002Findex.html",[28],[11,543,544,545,252],{},"同时，为了提供轻便的服务器状况查询，而不是被强制加载关卡，UE4有提供Online Beacons的功能，由于不会用到就没有继续研究了，详情见[",[24,546,29],{"href":547,"rel":548},"https:\u002F\u002Fdocs.unrealengine.com\u002Flatest\u002FCHN\u002FGameplay\u002FNetworking\u002FOnlineBeacons\u002Findex.html",[28],{"title":185,"searchDepth":186,"depth":187,"links":550},[551,552,557,558,561],{"id":240,"depth":186,"text":240},{"id":280,"depth":186,"text":280,"children":553},[554,555,556],{"id":289,"depth":187,"text":289},{"id":355,"depth":187,"text":355},{"id":395,"depth":187,"text":395},{"id":422,"depth":186,"text":422},{"id":472,"depth":186,"text":472,"children":559},[560],{"id":504,"depth":187,"text":505},{"id":533,"depth":186,"text":534},"2017-04-20",{"layout":206,"status":207,"published":208,"author":564,"author_login":211,"author_email":212,"wordpress_id":565,"wordpress_url":566,"date_gmt":567,"excerpt":568},{"display_name":210,"login":211,"email":212,"url":185},1599,"\u002F\u002F?p=1599","2017-04-20 15:03:17 +0000",{"type":8,"value":569},[570],[11,571,234],{},"\u002F2017-04-20-ue4-multiplayer-and-network-overview",{"title":229,"description":234},"_legacy\u002F2017\u002F2017-04-20-ue4-multiplayer-and-network-overview",[224,576,577],"MultiPlayer","Replication","PzRHWaoekIzNbx0GPfE_pJ_Ew2JUROcQg2OmymCgFC8",{"id":580,"title":581,"body":582,"date":1358,"description":586,"extension":204,"meta":1359,"navigation":208,"path":1368,"seo":1369,"stem":1370,"tags":1371,"__hash__":1373},"blogs\u002F_legacy\u002F2017\u002F2017-03-25-ue4-time-mange.md","UE4时间相关函数",{"type":8,"value":583,"toc":1346},[584,587,590,593,597,600,604,607,634,637,640,643,754,757,760,763,769,772,800,803,845,849,852,1115,1118,1123,1126,1130,1133,1139,1145,1148,1152,1155,1161,1168,1176,1180,1183,1189,1196,1199,1238,1246,1268,1271,1293,1297,1300,1306,1309,1312,1315,1318,1324,1330,1333,1339,1342],[11,585,586],{},"在进行游戏构建时，我们通常会用到和时间相关的逻辑，在此稍微做下小结。",[11,588,589],{},"当前UE4版本为4.15。",[11,591,592],{},"基本上游戏引擎都是以Tick为中心运转的，表现在玩家面前就是帧率了，UE4也是如此。",[32,594,596],{"id":595},"tick","Tick",[11,598,599],{},"tick是引擎的心跳，引擎中很多逻辑就是借助的tick机制自动注册的，例如UMG的动画播放功能就是tick驱动的。",[46,601,603],{"id":602},"c中使用","C++中使用",[11,605,606],{},"在蓝图中要使用tick的话只要从灰色的tick上拖出调用就可以了，但是在C++中使用的时候需要注意的是，在默认的情况下，tick是关闭的，必须自己开启才行。",[257,608,612],{"className":609,"code":610,"language":611,"meta":185,"style":185},"language-cpp shiki shiki-themes github-light-high-contrast github-dark monokai","PrimaryActorTick.bCanEverTick = true; \n","cpp",[264,613,614],{"__ignoreMap":185},[615,616,619,623,627,631],"span",{"class":617,"line":618},"line",1,[615,620,622],{"class":621},"s0idv","PrimaryActorTick.bCanEverTick ",[615,624,626],{"class":625},"sLXdl","=",[615,628,630],{"class":629},"stP0Q"," true",[615,632,633],{"class":621},";\n",[11,635,636],{},"另外由于tick本身是在每帧上调用的，出于效率考虑，要尽量避免往上添加复杂的逻辑，例如寻找1000以内的素数之类的。",[11,638,639],{},"还有一个比较少动到的地方是ShouldTickIfViewportsOnly，这个和编辑器的Tick策略关系比较大，但是在实际游戏中也可以使用。",[11,641,642],{},"主要的差分来自于LevelTick的类型",[257,644,646],{"className":609,"code":645,"language":611,"meta":185,"style":185},"\u002F** Type of tick we wish to perform on the level *\u002F\nenum ELevelTick\n{\n\u002F** Update the level time only. *\u002F\nLEVELTICK_TimeOnly = 0,\n\u002F** Update time and viewports. *\u002F\nLEVELTICK_ViewportsOnly = 1,\n\u002F** Update all. *\u002F\nLEVELTICK_All = 2,\n\u002F** Delta time is zero, we are paused. Components don't tick. *\u002F\nLEVELTICK_PauseTick = 3,\n};\n",[264,647,648,654,664,669,675,691,697,710,716,729,735,748],{"__ignoreMap":185},[615,649,650],{"class":617,"line":618},[615,651,653],{"class":652},"sZdD5","\u002F** Type of tick we wish to perform on the level *\u002F\n",[615,655,656,660],{"class":617,"line":186},[615,657,659],{"class":658},"sGZGq","enum",[615,661,663],{"class":662},"sfUV7"," ELevelTick\n",[615,665,666],{"class":617,"line":187},[615,667,668],{"class":621},"{\n",[615,670,672],{"class":617,"line":671},4,[615,673,674],{"class":652},"\u002F** Update the level time only. *\u002F\n",[615,676,678,682,685,688],{"class":617,"line":677},5,[615,679,681],{"class":680},"s7EbQ","LEVELTICK_TimeOnly",[615,683,684],{"class":625}," =",[615,686,687],{"class":629}," 0",[615,689,690],{"class":621},",\n",[615,692,694],{"class":617,"line":693},6,[615,695,696],{"class":652},"\u002F** Update time and viewports. *\u002F\n",[615,698,700,703,705,708],{"class":617,"line":699},7,[615,701,702],{"class":680},"LEVELTICK_ViewportsOnly",[615,704,684],{"class":625},[615,706,707],{"class":629}," 1",[615,709,690],{"class":621},[615,711,713],{"class":617,"line":712},8,[615,714,715],{"class":652},"\u002F** Update all. *\u002F\n",[615,717,719,722,724,727],{"class":617,"line":718},9,[615,720,721],{"class":680},"LEVELTICK_All",[615,723,684],{"class":625},[615,725,726],{"class":629}," 2",[615,728,690],{"class":621},[615,730,732],{"class":617,"line":731},10,[615,733,734],{"class":652},"\u002F** Delta time is zero, we are paused. Components don't tick. *\u002F\n",[615,736,738,741,743,746],{"class":617,"line":737},11,[615,739,740],{"class":680},"LEVELTICK_PauseTick",[615,742,684],{"class":625},[615,744,745],{"class":629}," 3",[615,747,690],{"class":621},[615,749,751],{"class":617,"line":750},12,[615,752,753],{"class":621},"};\n",[11,755,756],{},"其中，如果编辑器不是在RealTime模式（Ctrl+R打开）的话，就会是LEVELTICK_TimeOnly ，如果是的话就是LEVELTICK_ViewportsOnly 。一般不是实时模式的话，一些依赖于Tick表现的效果就不会更新，例如旧的粒子特效。",[11,758,759],{},"当LEVELTICK_ViewportsOnly 时，只有与角色有相关性的东西才会Tick。而对于在游戏中需要设定达到这个效果的话，可以设置UWorld的bPlayersOnly，这样的话就会强制将Tick类型变为LEVELTICK_ViewportsOnly 。",[11,761,762],{},"原始的过滤发生在如下的定义：",[257,764,767],{"className":765,"code":766,"language":262},[260],"void AActor::TickActor( float DeltaSeconds, ELevelTick TickType, FActorTickFunction& ThisTickFunction )\n{\n  \u002F\u002Froot of tick hierarchy\n\n  \u002F\u002F Non-player update.\n  const bool bShouldTick = ((TickType!=LEVELTICK_ViewportsOnly) || ShouldTickIfViewportsOnly());\n  if(bShouldTick)\n  {\n    \u002F\u002F If an Actor has been Destroyed or its level has been unloaded don't execute any queued ticks\n    if (!IsPendingKill() && GetWorld())\n    {\n      Tick(DeltaSeconds);    \u002F\u002F perform any tick functions unique to an actor subclass\n    }\n  }\n}\n",[264,768,766],{"__ignoreMap":185},[11,770,771],{},"在C++中使用时，如果遇到Tick函数在编辑模式下不会运行的情况，这时可以尝试对以下函数进行重载：",[257,773,775],{"className":609,"code":774,"language":611,"meta":185,"style":185},"virtual bool ShouldTickIfViewportsOnly() const override;\n",[264,776,777],{"__ignoreMap":185},[615,778,779,782,785,789,792,795,798],{"class":617,"line":618},[615,780,781],{"class":625},"virtual",[615,783,784],{"class":658}," bool",[615,786,788],{"class":787},"scAT4"," ShouldTickIfViewportsOnly",[615,790,791],{"class":621},"() ",[615,793,794],{"class":625},"const",[615,796,797],{"class":625}," override",[615,799,633],{"class":621},[11,801,802],{},"然后直接返回True。",[257,804,806],{"className":609,"code":805,"language":611,"meta":185,"style":185},"bool AMyActor::ShouldTickIfViewportsOnly() const\n{\n return true;\n}\n",[264,807,808,827,831,840],{"__ignoreMap":185},[615,809,810,813,816,819,822,824],{"class":617,"line":618},[615,811,812],{"class":658},"bool",[615,814,815],{"class":662}," AMyActor",[615,817,818],{"class":621},"::",[615,820,821],{"class":787},"ShouldTickIfViewportsOnly",[615,823,791],{"class":621},[615,825,826],{"class":625},"const\n",[615,828,829],{"class":617,"line":186},[615,830,668],{"class":621},[615,832,833,836,838],{"class":617,"line":187},[615,834,835],{"class":625}," return",[615,837,630],{"class":629},[615,839,633],{"class":621},[615,841,842],{"class":617,"line":671},[615,843,844],{"class":621},"}\n",[46,846,848],{"id":847},"tickgroup","TickGroup",[11,850,851],{},"UE4内部对Tick进行了分组管理，TickGroup之间按照先后顺序进行调用，其在引擎内部的定义为：",[257,853,855],{"className":609,"code":854,"language":611,"meta":185,"style":185},"UENUM(BlueprintType)\nenum ETickingGroup\n{\n\u002F** 在物理模拟开始之前的TickGroup *\u002F\nTG_PrePhysics UMETA(DisplayName=\"Pre Physics\"),\n\n\u002F** 用于开始物理模拟的特殊的TickGroup *\u002F\nTG_StartPhysics UMETA(Hidden, DisplayName=\"Start Physics\"),\n\n\u002F** 与物理模拟并行的TickGroup *\u002F\nTG_DuringPhysics UMETA(DisplayName=\"During Physics\"),\n\n\u002F** 用于接受物理模拟的特殊TickGroup *\u002F\nTG_EndPhysics UMETA(Hidden, DisplayName=\"End Physics\"),\n\n\u002F**在物理模拟（包括衣料材质演算）后运行的TickGroup *\u002F\nTG_PostPhysics UMETA(DisplayName=\"Post Physics\"),\n\n\u002F** 所有的更新操作完成后的TickGroup *\u002F\nTG_PostUpdateWork UMETA(DisplayName=\"Post Update Work\"),\n\n\u002F** 包含所有被延迟到最后的TickGroup *\u002F\nTG_LastDemotable UMETA(Hidden, DisplayName = \"Last Demotable\"),\n\n\u002F** 并非TickGroup的伪TickGroup，用于处理新生成的物体 *\u002F\nTG_NewlySpawned UMETA(Hidden, DisplayName=\"Newly Spawned\"),\n\nTG_MAX,\n\n};\n",[264,856,857,865,872,876,881,898,903,908,928,932,937,951,955,961,980,985,991,1006,1011,1017,1032,1037,1043,1062,1067,1073,1092,1097,1105,1110],{"__ignoreMap":185},[615,858,859,862],{"class":617,"line":618},[615,860,861],{"class":787},"UENUM",[615,863,864],{"class":621},"(BlueprintType)\n",[615,866,867,869],{"class":617,"line":186},[615,868,659],{"class":658},[615,870,871],{"class":662}," ETickingGroup\n",[615,873,874],{"class":617,"line":187},[615,875,668],{"class":621},[615,877,878],{"class":617,"line":671},[615,879,880],{"class":652},"\u002F** 在物理模拟开始之前的TickGroup *\u002F\n",[615,882,883,886,889,891,895],{"class":617,"line":677},[615,884,885],{"class":621},"TG_PrePhysics UMETA(",[615,887,888],{"class":680},"DisplayName",[615,890,626],{"class":625},[615,892,894],{"class":893},"sZnmU","\"Pre Physics\"",[615,896,897],{"class":621},"),\n",[615,899,900],{"class":617,"line":693},[615,901,902],{"emptyLinePlaceholder":208},"\n",[615,904,905],{"class":617,"line":699},[615,906,907],{"class":652},"\u002F** 用于开始物理模拟的特殊的TickGroup *\u002F\n",[615,909,910,913,916,919,921,923,926],{"class":617,"line":712},[615,911,912],{"class":621},"TG_StartPhysics UMETA(",[615,914,915],{"class":680},"Hidden",[615,917,918],{"class":621},", ",[615,920,888],{"class":680},[615,922,626],{"class":625},[615,924,925],{"class":893},"\"Start Physics\"",[615,927,897],{"class":621},[615,929,930],{"class":617,"line":718},[615,931,902],{"emptyLinePlaceholder":208},[615,933,934],{"class":617,"line":731},[615,935,936],{"class":652},"\u002F** 与物理模拟并行的TickGroup *\u002F\n",[615,938,939,942,944,946,949],{"class":617,"line":737},[615,940,941],{"class":621},"TG_DuringPhysics UMETA(",[615,943,888],{"class":680},[615,945,626],{"class":625},[615,947,948],{"class":893},"\"During Physics\"",[615,950,897],{"class":621},[615,952,953],{"class":617,"line":750},[615,954,902],{"emptyLinePlaceholder":208},[615,956,958],{"class":617,"line":957},13,[615,959,960],{"class":652},"\u002F** 用于接受物理模拟的特殊TickGroup *\u002F\n",[615,962,964,967,969,971,973,975,978],{"class":617,"line":963},14,[615,965,966],{"class":621},"TG_EndPhysics UMETA(",[615,968,915],{"class":680},[615,970,918],{"class":621},[615,972,888],{"class":680},[615,974,626],{"class":625},[615,976,977],{"class":893},"\"End Physics\"",[615,979,897],{"class":621},[615,981,983],{"class":617,"line":982},15,[615,984,902],{"emptyLinePlaceholder":208},[615,986,988],{"class":617,"line":987},16,[615,989,990],{"class":652},"\u002F**在物理模拟（包括衣料材质演算）后运行的TickGroup *\u002F\n",[615,992,994,997,999,1001,1004],{"class":617,"line":993},17,[615,995,996],{"class":621},"TG_PostPhysics UMETA(",[615,998,888],{"class":680},[615,1000,626],{"class":625},[615,1002,1003],{"class":893},"\"Post Physics\"",[615,1005,897],{"class":621},[615,1007,1009],{"class":617,"line":1008},18,[615,1010,902],{"emptyLinePlaceholder":208},[615,1012,1014],{"class":617,"line":1013},19,[615,1015,1016],{"class":652},"\u002F** 所有的更新操作完成后的TickGroup *\u002F\n",[615,1018,1020,1023,1025,1027,1030],{"class":617,"line":1019},20,[615,1021,1022],{"class":621},"TG_PostUpdateWork UMETA(",[615,1024,888],{"class":680},[615,1026,626],{"class":625},[615,1028,1029],{"class":893},"\"Post Update Work\"",[615,1031,897],{"class":621},[615,1033,1035],{"class":617,"line":1034},21,[615,1036,902],{"emptyLinePlaceholder":208},[615,1038,1040],{"class":617,"line":1039},22,[615,1041,1042],{"class":652},"\u002F** 包含所有被延迟到最后的TickGroup *\u002F\n",[615,1044,1046,1049,1051,1053,1055,1057,1060],{"class":617,"line":1045},23,[615,1047,1048],{"class":621},"TG_LastDemotable UMETA(",[615,1050,915],{"class":680},[615,1052,918],{"class":621},[615,1054,888],{"class":680},[615,1056,684],{"class":625},[615,1058,1059],{"class":893}," \"Last Demotable\"",[615,1061,897],{"class":621},[615,1063,1065],{"class":617,"line":1064},24,[615,1066,902],{"emptyLinePlaceholder":208},[615,1068,1070],{"class":617,"line":1069},25,[615,1071,1072],{"class":652},"\u002F** 并非TickGroup的伪TickGroup，用于处理新生成的物体 *\u002F\n",[615,1074,1076,1079,1081,1083,1085,1087,1090],{"class":617,"line":1075},26,[615,1077,1078],{"class":621},"TG_NewlySpawned UMETA(",[615,1080,915],{"class":680},[615,1082,918],{"class":621},[615,1084,888],{"class":680},[615,1086,626],{"class":625},[615,1088,1089],{"class":893},"\"Newly Spawned\"",[615,1091,897],{"class":621},[615,1093,1095],{"class":617,"line":1094},27,[615,1096,902],{"emptyLinePlaceholder":208},[615,1098,1100,1103],{"class":617,"line":1099},28,[615,1101,1102],{"class":680},"TG_MAX",[615,1104,690],{"class":621},[615,1106,1108],{"class":617,"line":1107},29,[615,1109,902],{"emptyLinePlaceholder":208},[615,1111,1113],{"class":617,"line":1112},30,[615,1114,753],{"class":621},[11,1116,1117],{},"在蓝图中有提供修改的接口，默认情况下的TickGroup是PrePhysics。",[11,1119,1120],{},[41,1121],{"alt":43,"src":1122},"\u002Fwp-content\u002Fuploads\u002F2017\u002F03\u002Fimage_thumb.png",[11,1124,1125],{},"在C++中可以额外的添加Tick函数，使得对象可以获得额外的其他TickGroup的Tick，但是在设计上意义并不是很大。",[46,1127,1129],{"id":1128},"prerequisite","Prerequisite",[11,1131,1132],{},"在同一个TickGroup中的Tick似乎是根据优先调度进行的，其顺序近似随机。在特殊的需求下，UE4还提供了前置Tick Actor的设置，它可以保证两个Actor之间的tick按照顺序被调用。",[11,1134,1135],{},[41,1136],{"alt":1137,"src":1138},"H13GGJPBRDQ5V0","\u002Fwp-content\u002Fuploads\u002F2017\u002F03\u002FH13GGJPBRDQ5V0DB0O4XN.png",[11,1140,1141],{},[41,1142],{"alt":1143,"src":1144},"QSKL3GNA7SKY","\u002Fwp-content\u002Fuploads\u002F2017\u002F03\u002FQSKL3GNA7SKYM_JGEC_thumb.png",[11,1146,1147],{},"这个函数只影响当前Actor的tick的执行次序，其子组件的tick并不受影响，如有需要需另行指定。",[32,1149,1151],{"id":1150},"timeline","TimeLine",[11,1153,1154],{},"时间轴是相当有效的在蓝图中对数值进行时间演变的方式，可以在很多地方被应用。例如打开房门或者是渐变材质的颜色等。",[11,1156,1157],{},[41,1158],{"alt":1159,"src":1160},"91ZMJYQTWY","\u002Fwp-content\u002Fuploads\u002F2017\u002F03\u002F91PCXG9493HZMJYQTWY_thumb.png",[11,1162,1163,1164,252],{},"功能一目了然，不明了的地方可以参照[",[24,1165,29],{"href":1166,"rel":1167},"https:\u002F\u002Fdocs.unrealengine.com\u002Flatest\u002FCHN\u002FEngine\u002FBlueprints\u002FUserGuide\u002FTimelines\u002Findex.html",[28],[11,1169,1170,1171,252],{},"TimeLine也可以在C++中使用，但是实际应用的场景似乎不大，如有遇到可以参考这篇[",[24,1172,1175],{"href":1173,"rel":1174},"https:\u002F\u002Fwiki.unrealengine.com\u002FTimeline_in_c%2B%2B",[28],"社区文档",[32,1177,1179],{"id":1178},"timer","Timer",[11,1181,1182],{},"UE4提供了定时运行的Timer供使用，在游戏逻辑上还是有经常被用到的。",[11,1184,1185],{},[41,1186],{"alt":1187,"src":1188},"D8TY","\u002Fwp-content\u002Fuploads\u002F2017\u002F03\u002FD8TYNNII5VXAS_F28XHO_thumb.png",[11,1190,1191,1192,252],{},"使用起来需要连接事件或者提供函数名，详细的可以参考[",[24,1193,29],{"href":1194,"rel":1195},"https:\u002F\u002Fdocs.unrealengine.com\u002Flatest\u002FCHN\u002FGameplay\u002FHowTo\u002FUseTimers\u002Findex.html",[28],[11,1197,1198],{},"在C++中使用Timer也相对简单，使用TimerManager提供的接口函数就可以了。",[257,1200,1202],{"className":609,"code":1201,"language":611,"meta":185,"style":185},"FTimerHandle DummyHandle;\n\nGetWorld()->GetTimerManager()->SetTimer( DummyHandle, StartPulseSurveyIconDelegate, SurveyPulseTimeInterval, false );\n",[264,1203,1204,1209,1213],{"__ignoreMap":185},[615,1205,1206],{"class":617,"line":618},[615,1207,1208],{"class":621},"FTimerHandle DummyHandle;\n",[615,1210,1211],{"class":617,"line":186},[615,1212,902],{"emptyLinePlaceholder":208},[615,1214,1215,1218,1221,1224,1226,1229,1232,1235],{"class":617,"line":187},[615,1216,1217],{"class":787},"GetWorld",[615,1219,1220],{"class":621},"()->",[615,1222,1223],{"class":787},"GetTimerManager",[615,1225,1220],{"class":621},[615,1227,1228],{"class":787},"SetTimer",[615,1230,1231],{"class":621},"( DummyHandle, StartPulseSurveyIconDelegate, SurveyPulseTimeInterval, ",[615,1233,1234],{"class":629},"false",[615,1236,1237],{"class":621}," );\n",[11,1239,1240,1241,1245],{},"第二个参数是一个",[24,1242,1244],{"href":1243},"\u002F2015-08-16-ue4-event-dispatcher-and-delegate\u002F","Delegate","，定义如下：",[257,1247,1249],{"className":609,"code":1248,"language":611,"meta":185,"style":185},"DECLARE_DELEGATE(FTimerDelegate);\n\nFTimerDelegate StartPulseSurveyIconDelegate;\n",[264,1250,1251,1259,1263],{"__ignoreMap":185},[615,1252,1253,1256],{"class":617,"line":618},[615,1254,1255],{"class":787},"DECLARE_DELEGATE",[615,1257,1258],{"class":621},"(FTimerDelegate);\n",[615,1260,1261],{"class":617,"line":186},[615,1262,902],{"emptyLinePlaceholder":208},[615,1264,1265],{"class":617,"line":187},[615,1266,1267],{"class":621},"FTimerDelegate StartPulseSurveyIconDelegate;\n",[11,1269,1270],{},"使用到的第一个参数为FTimerHandle，是用来标识并事后交付给TimerManager用于操作指定Timer的，如果没有这个需求，可以直接定义一个Dummy。",[257,1272,1274],{"className":609,"code":1273,"language":611,"meta":185,"style":185},"GetWorld()->GetTimerManager().UnPauseTimer(SampleTimerHandle);\n",[264,1275,1276],{"__ignoreMap":185},[615,1277,1278,1280,1282,1284,1287,1290],{"class":617,"line":618},[615,1279,1217],{"class":787},[615,1281,1220],{"class":621},[615,1283,1223],{"class":787},[615,1285,1286],{"class":621},"().",[615,1288,1289],{"class":787},"UnPauseTimer",[615,1291,1292],{"class":621},"(SampleTimerHandle);\n",[46,1294,1296],{"id":1295},"delay","Delay",[11,1298,1299],{},"延迟执行节点，在蓝图中有时会用到的逻辑。Delay相当于一个不重复执行的Timer，因此在C++中要使用这种功能的话，就是用Timer来了。",[11,1301,1302],{},[41,1303],{"alt":1304,"src":1305},"RPNTM","\u002Fwp-content\u002Fuploads\u002F2017\u002F03\u002FRPNT_K6CC@5L6HMONU7M_thumb.png",[11,1307,1308],{},"其中上方的那个Delay节点提供的功能是：当还在倒数时被调用到，就会重新开始倒数。",[32,1310,1311],{"id":1311},"速度调节",[11,1313,1314],{},"UE4有提供调节步进的接口函数，这个功能通常名为slow mode，功能上类似于FPS游戏中经常出现的子弹时间效果。",[11,1316,1317],{},"这个系列的函数提供两种类型的控制，一种是全局的，一种是针对某个特定物体的。",[11,1319,1320],{},[41,1321],{"alt":1322,"src":1323},"ZB08UAW","\u002Fwp-content\u002Fuploads\u002F2017\u002F03\u002FZB08UAWFXEIJ5F@GM_thumb.png",[11,1325,1326],{},[41,1327],{"alt":1328,"src":1329},"DP71R","\u002Fwp-content\u002Fuploads\u002F2017\u002F03\u002FDP7MMNR3LR14QVF81R_thumb.png",[11,1331,1332],{},"全局的时间调节也可以使用控制台指令打开：",[11,1334,1335],{},[41,1336],{"alt":1337,"src":1338},"P2YPAQQ7","\u002Fwp-content\u002Fuploads\u002F2017\u002F03\u002FP39OU9IC2Y52GHPAQQ7_thumb.png",[11,1340,1341],{},"这个功能相对简单，时间设定的细节需要到具体使用的时候调节为上。",[1343,1344,1345],"style",{},"html pre.shiki code .s0idv, html code.shiki .s0idv{--shiki-default:#0E1116;--shiki-dark:#E1E4E8;--shiki-sepia:#F8F8F2}html pre.shiki code .sLXdl, html code.shiki .sLXdl{--shiki-default:#A0111F;--shiki-dark:#F97583;--shiki-sepia:#F92672}html pre.shiki code .stP0Q, html code.shiki .stP0Q{--shiki-default:#023B95;--shiki-dark:#79B8FF;--shiki-sepia:#AE81FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .sZdD5, html code.shiki .sZdD5{--shiki-default:#66707B;--shiki-dark:#6A737D;--shiki-sepia:#88846F}html pre.shiki code .sGZGq, html code.shiki .sGZGq{--shiki-default:#A0111F;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit;--shiki-sepia:#66D9EF;--shiki-sepia-font-style:italic}html pre.shiki code .sfUV7, html code.shiki .sfUV7{--shiki-default:#702C00;--shiki-default-text-decoration:inherit;--shiki-dark:#B392F0;--shiki-dark-text-decoration:inherit;--shiki-sepia:#A6E22E;--shiki-sepia-text-decoration:underline}html pre.shiki code .s7EbQ, html code.shiki .s7EbQ{--shiki-default:#023B95;--shiki-dark:#79B8FF;--shiki-sepia:#F8F8F2}html pre.shiki code .scAT4, html code.shiki .scAT4{--shiki-default:#622CBC;--shiki-dark:#B392F0;--shiki-sepia:#A6E22E}html pre.shiki code .sZnmU, html code.shiki .sZnmU{--shiki-default:#032563;--shiki-dark:#9ECBFF;--shiki-sepia:#E6DB74}",{"title":185,"searchDepth":186,"depth":187,"links":1347},[1348,1353,1354,1357],{"id":595,"depth":186,"text":596,"children":1349},[1350,1351,1352],{"id":602,"depth":187,"text":603},{"id":847,"depth":187,"text":848},{"id":1128,"depth":187,"text":1129},{"id":1150,"depth":186,"text":1151},{"id":1178,"depth":186,"text":1179,"children":1355},[1356],{"id":1295,"depth":187,"text":1296},{"id":1311,"depth":186,"text":1311},"2017-03-25",{"layout":206,"status":207,"published":208,"author":1360,"author_login":211,"author_email":212,"wordpress_id":1361,"wordpress_url":1362,"date_gmt":1363,"excerpt":1364},{"display_name":210,"login":211,"email":212,"url":185},1571,"\u002F\u002F?p=1571","2017-03-25 03:26:22 +0000",{"type":8,"value":1365},[1366],[11,1367,586],{},"\u002F2017-03-25-ue4-time-mange",{"title":581,"description":586},"_legacy\u002F2017\u002F2017-03-25-ue4-time-mange",[224,1372],"Time","8vy4gJatwGajxVpI01uxbY-PZbVt-kezVlysdvuWkgE",{"id":1375,"title":1376,"body":1377,"date":1595,"description":1381,"extension":204,"meta":1596,"navigation":208,"path":1608,"seo":1609,"stem":1610,"tags":1611,"__hash__":1613},"blogs\u002F_legacy\u002F2015\u002F2015-09-17-ue4-paper-2d-overview.md","UE4中Paper2D初探",{"type":8,"value":1378,"toc":1586},[1379,1382,1385,1393,1397,1400,1403,1406,1409,1415,1418,1423,1426,1431,1434,1439,1442,1447,1450,1455,1458,1463,1466,1471,1474,1477,1483,1486,1489,1492,1495,1502,1505,1508,1511,1514,1519,1523,1526,1529,1532,1535,1538,1541,1544,1549,1552,1556,1559,1562,1565,1568,1571,1574,1577],[11,1380,1381],{},"Paper 2D是UE4提供的基于精灵的2D系统，可以用于构建2D和2D\u002F3D混合游戏。",[11,1383,1384],{},"当前UE4版本4.10.2；",[11,1386,1387,1388,1392],{},"部分内容参考的[",[24,1389,29],{"href":1390,"rel":1391},"https:\u002F\u002Fwww.google.com.hk\u002Furl?sa=t&rct=j&q=&esrc=s&source=web&cd=1&cad=rja&uact=8&ved=0ahUKEwj7yprKvc7SAhVFlJQKHfgCA1AQFggaMAA&url=%68%74%74%70%73%3a%2f%2f%64%6f%63%73%2e%75%6e%72%65%61%6c%65%6e%67%69%6e%65%2e%63%6f%6d%2f%6c%61%74%65%73%74%2f%43%48%4e%2f%45%6e%67%69%6e%65%2f%50%61%70%65%72%32%44%2f&usg=AFQjCNG1WlLQvG_q8TSg44GLR4WEJ0ATdA",[28],"]版本标识为UE4.9。目前Paper 2D的动画系统是基于帧动画的，社区讨论中虽然有加入2D骨骼动画系统的讨论，但是目前的正式引擎版本中并没有这样的功能。",[32,1394,1396],{"id":1395},"sprite","Sprite",[11,1398,1399],{},"Sprite是帧动画的基本单位，它来源于对Texture的裁剪，形成动画的一个帧并最终用于FlipBook中。",[11,1401,1402],{},"Sprite可以从Texture创建也可以创建之后指定Texture，同时UE4支持导入从Texture Packer和Adobe Flash CS6生成的帧动画。",[46,1404,1405],{"id":1405},"属性",[11,1407,1408],{},"Sprite的属性相对比较简单：",[11,1410,1411],{},[1412,1413,1414],"strong",{},"Source Texture",[11,1416,1417],{},"当前精灵所使用的2D图像",[11,1419,1420],{},[1412,1421,1422],{},"Source UV",[11,1424,1425],{},"裁剪的起始点在原图形上的偏离，像素单位",[11,1427,1428],{},[1412,1429,1430],{},"Source Dimension",[11,1432,1433],{},"裁剪的大小，像素单位",[11,1435,1436],{},[1412,1437,1438],{},"Default Material",[11,1440,1441],{},"精灵的默认材质，系统提供的为Unlit Masked的材质，这个材质可以在Flipbook中进行覆盖设置。",[11,1443,1444],{},[1412,1445,1446],{},"Pixels per unit",[11,1448,1449],{},"设置像素与引擎内部单位之间的缩放比例，用于调整精灵在UE世界中的大小。在Paper2D的插件设置中有全局设定，也可以对每个精灵进行指定。",[11,1451,1452],{},[1412,1453,1454],{},"Pivot Mode",[11,1456,1457],{},"中心点位置。设置为Custom的话可以进行精细的设置。",[11,1459,1460],{},[1412,1461,1462],{},"Sockets",[11,1464,1465],{},"和3D动画中的Sockets功能时类似的，提供一个接口。",[11,1467,1468],{},[1412,1469,1470],{},"Sprite Collision Domain",[11,1472,1473],{},"精灵碰撞模式，可以选择无碰撞、2D或者3D。",[11,1475,1476],{},"属性中有些详细的可以在使用的时候进行调整测试。",[11,1478,1479,1480,1482],{},"有一个属性为Additional Textures，从引擎的描述上看是用于为精灵提供额外的贴图的，当前文档中没有给出描述。要使用的话似乎需要对默认材质进行修改，有使用需求的可以参考",[615,1481,251],{},"的讨论。",[11,1484,1485],{},"精灵的材质可以进行很多的调整和自定义，从Flipbook过来的Sprite Color属性在材质中是Vertext Color的输入项。",[46,1487,1488],{"id":1488},"碰撞检测",[11,1490,1491],{},"整个Paper2D中可以选择的碰撞方式为2D和3D两种，3D碰撞使用的引擎内通用的PhysX，而2D碰撞使用的是Box2D。当前Box2D仍然处于实验阶段，有一些功能是不完整的，文档支持也不够完整。Box2D的支持也只能到Win32和Win64两个平台，要使用额外的功能还要在项目设置中打开bEnable2DPhysics开关。总体而言，目前不是很适合使用。",[11,1493,1494],{},"同时，在Sprite的编辑器中可以选择对碰撞形态进行调整，除了引擎提供的几种模式之外，可以自行指定。",[11,1496,1497,1498,1501],{},"各个碰撞模式的不同可以看官方的",[615,1499,1500],{},"图例","，相当的直观。",[11,1503,1504],{},"使用上的建议是使用默认的简单图形进行碰撞，如果需要使用精细的碰撞的话可以使用Sockets来外挂碰撞体来进行碰撞检测，逐帧进行精细的碰撞检测是相当消耗系统运算力的。",[46,1506,1507],{"id":1507},"区域调整",[11,1509,1510],{},"在Sprite编辑器中可以对裁剪的区域进行调整，也有提供一些快捷的功能。不过一般情况下应该在TexturePacker之类的软件中对这些功能进行调整会更方便一些。",[11,1512,1513],{},"区域调整功能更重要的是对渲染区域进行调整，将有的精灵中多余的空白部分排除到渲染之外的话，可以很好的降低渲染压力。",[11,1515,1516],{},[41,1517],{"alt":185,"src":1518},"\u002Fwp-content\u002Fuploads\u002F2017\u002F03\u002FphpykKLq1.1453384829.png",[32,1520,1522],{"id":1521},"flipbook","FlipBook",[11,1524,1525],{},"FlipBook是帧动画的管理类，将Sprite组织成动画进行播放。FlipBook中的属性并不多：",[11,1527,1528],{},"Frames Per Seconds为每秒帧率；",[11,1530,1531],{},"Default Material为所有动画帧使用的材质；",[11,1533,1534],{},"Key Frames中存储的是每一个用到的关键帧的精灵的属性，可以调整的目前只有Frame Run，为帧的持续帧数；",[11,1536,1537],{},"Collision Source为碰撞模式选择，可以选择关闭碰撞、逐帧碰撞或者使用第一帧进行碰撞。",[11,1539,1540],{},"实际运用中更加重要的是FlipBook Components，这个蓝图类提供了很多方便的对Flipbook进行操作的接口。",[11,1542,1543],{},"在使用中如果开启物理出现了翻转出平面之类的不合理现象的话，需要对坐标进行锁定:",[11,1545,1546],{},[41,1547],{"alt":185,"src":1548},"\u002Fwp-content\u002Fuploads\u002F2017\u002F03\u002Fphp7XeP65.1453384926.png",[11,1550,1551],{},"其他的在使用上并没有什么太多的复杂的地方，唯一的麻烦死目前并没有Paper2D的动画状态机，需要在BP中使用事件自行进行动画的更新控制。",[32,1553,1555],{"id":1554},"tileset-tilemap","TileSet& TileMap",[11,1557,1558],{},"瓦片地图功能，当前在UE4中为实验功能。如果不使用的话可以不用太详细研究的感觉。",[11,1560,1561],{},"UE4支持从Tiled进行瓦片地图的导入。",[11,1563,1564],{},"瓦片地图的原理本身比较简单，TileSet与TileMap中的属性设置也都非常的直观。",[11,1566,1567],{},"Tile本身的碰撞是在TileSet中进行设置和调整的，在TileMap中也有开关进行碰撞类型的选择，Sprite Collision",[11,1569,1570],{},"Domain的含义与Sprite中是一样的。TileSet编辑器中也可以对每一个Tile进行一些独立的调整。",[11,1572,1573],{},"TileMap本身支持多层地图，功能上还是比较完整的。",[11,1575,1576],{},"有一个属性需要注意，Sepration Per Layer，这个属性是用于调整每一层地图之间的距离的，在多层地图中，相当于通常的Z-Order设置的位置，可以为一些地图特效提供帮助。",[11,1578,1579,1580,1585],{},"最后，在C++中使用Paper2D的功能当前并未完全开发，官方的建议是自己参考相关文档，例如[",[24,1581,1584],{"href":1582,"rel":1583},"https:\u002F\u002Fwww.google.com.hk\u002Furl?sa=t&rct=j&q=&esrc=s&source=web&cd=1&cad=rja&uact=8&ved=0ahUKEwi8rtmcwM7SAhWIm5QKHTJCAacQFggaMAA&url=%68%74%74%70%73%3a%2f%2f%64%6f%63%73%2e%75%6e%72%65%61%6c%65%6e%67%69%6e%65%2e%63%6f%6d%2f%6c%61%74%65%73%74%2f%49%4e%54%2f%41%50%49%2f%50%6c%75%67%69%6e%73%2f%50%61%70%65%72%32%44%2f%55%50%61%70%65%72%46%6c%69%70%62%6f%6f%6b%43%6f%6d%70%6f%6e%65%6e%74%2f%69%6e%64%65%78%2e%68%74%6d%6c&usg=AFQjCNHCRxbIjTBa0auVoc68-WMegXvSIg",[28],"UPaperFlipbookComponent的文档","]进行相关操作。",{"title":185,"searchDepth":186,"depth":187,"links":1587},[1588,1593,1594],{"id":1395,"depth":186,"text":1396,"children":1589},[1590,1591,1592],{"id":1405,"depth":187,"text":1405},{"id":1488,"depth":187,"text":1488},{"id":1507,"depth":187,"text":1507},{"id":1521,"depth":186,"text":1522},{"id":1554,"depth":186,"text":1555},"2015-09-17",{"layout":206,"status":207,"published":208,"author":1597,"author_login":1598,"author_email":1599,"author_url":1600,"wordpress_id":1601,"wordpress_url":1602,"date_gmt":1603,"excerpt":1604},{"display_name":1598,"login":1598,"email":1599,"url":1600},"chaoshikari","chaoshikari@gmail.com","\u002F",1486,"\u002F\u002F?p=1486","2015-09-17 12:42:51 +0000",{"type":8,"value":1605},[1606],[11,1607,1381],{},"\u002F2015-09-17-ue4-paper-2d-overview",{"title":1376,"description":1381},"_legacy\u002F2015\u002F2015-09-17-ue4-paper-2d-overview",[224,1612],"Paper2d","RyVgAPhmb0BSN6mth_bfpap0N4j-E_ekUqpbEXBflh0",{"id":1615,"title":1616,"body":1617,"date":1838,"description":1621,"extension":204,"meta":1839,"navigation":208,"path":1848,"seo":1849,"stem":1850,"tags":1851,"__hash__":1853},"blogs\u002F_legacy\u002F2015\u002F2015-09-12-ue4-ai-and-behavior-trees.md","UE4中AI与行为树",{"type":8,"value":1618,"toc":1821},[1619,1622,1625,1628,1631,1634,1637,1646,1649,1653,1656,1659,1663,1666,1674,1678,1681,1686,1689,1692,1697,1700,1703,1708,1711,1714,1718,1721,1728,1732,1735,1738,1741,1744,1751,1755,1758,1761,1765,1768,1771,1775,1778,1781,1789,1793,1796,1799,1802,1806,1809,1812,1815,1818],[11,1620,1621],{},"UE4的AI系统使用行为树作为中心进行，同时搭配场景查询系统帮助AI快速获取周围环境状况。",[11,1623,1624],{},"在UE4中，Pawn是可以由玩家或者AI控制的基础类。通常使用的Character类就是对Pawn类的一个具体实现。",[11,1626,1627],{},"就像玩家通过PlayerController控制Character一样，AI则通过AIController来控制Character。",[11,1629,1630],{},"AIController为AI对Pawn进行操作的核心类，但是AI的主要逻辑是在行为树中进行的。",[32,1632,1633],{"id":1633},"行为树",[11,1635,1636],{},"UE4的行为树实现是事件驱动的，相比逐帧的有限状态机之类的实现，能够更好的控制AI运行的成本。",[11,1638,1639,1640,1645],{},"官方有提供行为树的入门指南，按照",[24,1641,1644],{"href":1642,"rel":1643},"https:\u002F\u002Fdocs.unrealengine.com\u002Flatest\u002FCHN\u002FEngine\u002FAI\u002FBehaviorTrees\u002FQuickStart\u002Findex.html",[28],"官方指南","一步一步操作的话，就能初步熟悉行为树的工作原理了。",[11,1647,1648],{},"行为树的节点分为Composite、Task、Decorator、Service这四种。还有一个比较特殊的节点为Root，是行为树执行的起点。其本身是没有属性的，选中Root节点展示的是行为树本身的属性。",[46,1650,1652],{"id":1651},"blackboard","Blackboard",[11,1654,1655],{},"根据官方描述，Blackboard是AI的“记忆”。其中以键值的形式存储各种数值、对象以供行为树使用。",[11,1657,1658],{},"对于行为树而言，Blackboard类是非常重要的类，它是用于行为树本身与“外界”进行交互的媒介之一，同时也是各个节点之间进行数据交互的中介存储器。",[46,1660,1662],{"id":1661},"task","Task",[11,1664,1665],{},"实际任务执行节点，可以通过蓝图或代码进行自定义，官方的入门指南中有对Task进行定义的例子。",[11,1667,1668,1669,1673],{},"引擎自带了一些基本的Task节点可供使用，功能包括移动AI、延时等待以及进行场景查询等。详细的自带节点可以参考",[24,1670,29],{"href":1671,"rel":1672},"https:\u002F\u002Fdocs.unrealengine.com\u002Flatest\u002FCHN\u002FEngine\u002FAI\u002FBehaviorTrees\u002FNodeReference\u002FTasks\u002Findex.html",[28],"。",[46,1675,1677],{"id":1676},"composite","Composite",[11,1679,1680],{},"对任务进行组合的节点，目前只有三种：",[11,1682,1683],{},[1412,1684,1685],{},"Selector",[11,1687,1688],{},"从左到右执行子节点，直到遇到一个子节点返回成功为止，此时返回成功，其他情况都返回失败。",[11,1690,1691],{},"直观的看，就是选择器，从节点中由左向右找到第一个能够成功执行的子节点，如果没有的话就返回失败。",[11,1693,1694],{},[1412,1695,1696],{},"Sequence",[11,1698,1699],{},"从左到右执行子节点，直到某一个子节点返回失败，此时返回失败，其他情况则返回成功，例如全部执行完成则返回成功。",[11,1701,1702],{},"总体而言，就是按序对子节点进行执行，但是如果有一个子节点失败的话，就放弃并返回失败。",[11,1704,1705],{},[1412,1706,1707],{},"Simple Parallel",[11,1709,1710],{},"在节点的子任务执行的同时，在后台运行另一个子任务。",[11,1712,1713],{},"可以选择属性来控制是否要等待后台的任务完成，还是在主任务完成时直接结束后台任务。",[46,1715,1717],{"id":1716},"decorator","Decorator",[11,1719,1720],{},"装饰器，在Task或者Composite中使用，为节点的执行添加条件。",[11,1722,1723,1724,1673],{},"可以自己进行定义，官方也提供了较多的基础装饰器。包括冷却、限时、Blackboard值检测、强制成功等许多常用功能。详细的可以参照",[24,1725,29],{"href":1726,"rel":1727},"https:\u002F\u002Fdocs.unrealengine.com\u002Flatest\u002FCHN\u002FEngine\u002FAI\u002FBehaviorTrees\u002FNodeReference\u002FDecorators\u002Findex.html",[28],[46,1729,1731],{"id":1730},"service","Service",[11,1733,1734],{},"服务，只能被附加到Composite节点上。当该Composite被执行时，会按照设定的频率不断的执行。",[11,1736,1737],{},"通常用于更新Blackboard的值，就像官方的指南中，用来更新玩家的位置和引用。",[32,1739,1740],{"id":1740},"场景查询系统",[11,1742,1743],{},"在行为树中，AI可以通过Run EQS Query的Task来对周围的环境进行查询以形成判定。",[11,1745,1746,1747,1673],{},"官方也为场景查询系统的入门提供了指南，可以参考",[24,1748,251],{"href":1749,"rel":1750},"https:\u002F\u002Fdocs.unrealengine.com\u002Flatest\u002FCHN\u002FEngine\u002FAI\u002FEnvironmentQuerySystem\u002FQuickStart\u002Findex.html",[28],[46,1752,1754],{"id":1753},"nav-mesh","Nav Mesh",[11,1756,1757],{},"Nav Mesh是场景查询系统运行的基础，但是即便不使用Run EQS Query节点，Nav Mesh也是必须的。因为Move to之类的需要寻路的行为树节点也是需要Nav Mesh的生成数据的。",[11,1759,1760],{},"通过按P键可以切换导航网格的生成结果预览，绿色的地方就是生成了路径的地方。",[46,1762,1764],{"id":1763},"eqs-testing-pawn","EQS Testing Pawn",[11,1766,1767],{},"用于对EQS进行调试的节点，将其拖放到Nav Mesh生成区域的相应的位置，就会在该处执行属性中设置的场景查询。",[11,1769,1770],{},"结果的展示通过彩色的球体来表现，蓝色的球体表示失败的或是返回false的布尔查询，由绿到红的颜色区间则对应返回数值的区间变化。",[46,1772,1774],{"id":1773},"generators","Generators",[11,1776,1777],{},"EQS的基础节点，用于查询的基础的生成器。",[11,1779,1780],{},"生成器的结果被称为Items，包括返回的实际的Actor以及生成的位置。",[11,1782,1783,1784,1788],{},"当前的几种自带的生成器可以参考",[24,1785,29],{"href":1786,"rel":1787},"https:\u002F\u002Fdocs.unrealengine.com\u002Flatest\u002FCHN\u002FEngine\u002FAI\u002FEnvironmentQuerySystem\u002FNodeReference\u002Findex.html#generators",[28],"。生成器是可以通过蓝图或者C++自行定义的。",[46,1790,1792],{"id":1791},"tests","Tests",[11,1794,1795],{},"在Generators中使用的节点，对Generators生成的Items进行进一步的测试。以便筛选出想要的结果。",[11,1797,1798],{},"Tests只能通过C++进行定义，当前并没有蓝图实现的方法。",[11,1800,1801],{},"在上面的文档的地址中就能看到当前系统自带的测试节点。",[46,1803,1805],{"id":1804},"contexts","Contexts",[11,1807,1808],{},"在Generators和Tests中可能会被用到的上下文。在EQS的指南中，就有为Distance测试提供的PlayerContext，方便EQS通过与玩家的距离进行评分。",[11,1810,1811],{},"可以通过蓝图或者C++进行自定义。",[32,1813,1814],{"id":1814},"总结",[11,1816,1817],{},"总体而言，UE4提供的AI系统的逻辑性还是比较清晰的。理解了其逻辑之后，要在游戏中添加AI就比较简单了。",[11,1819,1820],{},"不过要在行为树的基础上实现复杂的AI逻辑还是需要经验和想象力的。",{"title":185,"searchDepth":186,"depth":187,"links":1822},[1823,1830,1837],{"id":1633,"depth":186,"text":1633,"children":1824},[1825,1826,1827,1828,1829],{"id":1651,"depth":187,"text":1652},{"id":1661,"depth":187,"text":1662},{"id":1676,"depth":187,"text":1677},{"id":1716,"depth":187,"text":1717},{"id":1730,"depth":187,"text":1731},{"id":1740,"depth":186,"text":1740,"children":1831},[1832,1833,1834,1835,1836],{"id":1753,"depth":187,"text":1754},{"id":1763,"depth":187,"text":1764},{"id":1773,"depth":187,"text":1774},{"id":1791,"depth":187,"text":1792},{"id":1804,"depth":187,"text":1805},{"id":1814,"depth":186,"text":1814},"2015-09-12",{"layout":206,"status":207,"published":208,"author":1840,"author_login":1598,"author_email":1599,"author_url":1600,"wordpress_id":1841,"wordpress_url":1842,"date_gmt":1843,"excerpt":1844},{"display_name":1598,"login":1598,"email":1599,"url":1600},1519,"\u002F\u002F?p=1519","2015-09-12 02:38:57 +0000",{"type":8,"value":1845},[1846],[11,1847,1621],{},"\u002F2015-09-12-ue4-ai-and-behavior-trees",{"title":1616,"description":1621},"_legacy\u002F2015\u002F2015-09-12-ue4-ai-and-behavior-trees",[224,1852],"AI","5eQFa3YTr9UdzBaVHIG9Y9lyglACrrJq_KH9tiZFmOU",222,1788763183753]