[{"data":1,"prerenderedAt":1078},["ShallowReactive",2],{"page-UE4-17":3,"page-count-UE4":1077},[4,132,250,324,575],{"id":5,"title":6,"body":7,"date":109,"description":13,"extension":110,"meta":111,"navigation":114,"path":126,"seo":127,"stem":128,"tags":129,"__hash__":131},"blogs\u002F_legacy\u002F2014\u002F2014-10-05-ue4%e5%bc%82%e6%ad%a5%e8%bd%bd%e5%85%a5%e8%b5%84%e6%ba%90.md","UE4异步载入资源",{"type":8,"value":9,"toc":105},"minimark",[10,14,17,27,30,33,36,47,50,57,60,65,68,74,77,83,86,89,95,102],[11,12,13],"p",{},"所有的“硬”指针指向的资源都会被UE4在启动时进行载入，为了防止某些情况下引发的巨大延迟，必要的时候我们需要使用异步资源载入系统。",[11,15,16],{},"当前UE4版本为4.7。",[11,18,19,20,26],{},"本文参考：",[21,22,23],"a",{"href":23,"rel":24,"title":23},"https:\u002F\u002Fdocs.unrealengine.com\u002Flatest\u002FINT\u002FProgramming\u002FAssets\u002FAsyncLoading\u002Findex.html",[25],"nofollow","进行整理。同时也是研究引擎的记录。",[11,28,29],{},"对于异步载入有两个类很重要:FStringAssetReferences和TAssetPtr。",[11,31,32],{},"FStringAssetReferences是对资源(Asset)的“软”引用，这个结构在BP中使用起来就像是UObject指针一样。而TAssetPtr是对FStringAssetReferences的一个弱引用封装，同时规范所指向的类型，可以在需要的时候调用Get()来解析到具体的资源。",[11,34,35],{},"为了避免理解上的偏差，来实际测试一次是最快的。在代码中添加如下属性：",[37,38,43],"pre",{"className":39,"code":41,"language":42},[40],"language-text","UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = Test) \n    FStringAssetReference tf;\n\nUPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = Test) \n    TAssetPtr\u003CUTexture2D> ttp;\n","text",[44,45,41],"code",{"__ignoreMap":46},"",[11,48,49],{},"FstringAssetReference在可视化编辑器中显示如下，确实是可以当作UObject指针来使用：",[11,51,52],{},[53,54],"img",{"alt":55,"src":56},"image","\u002Fwp-content\u002Fuploads\u002F2014\u002F10\u002Fimage_thumb.png",[11,58,59],{},"TAssetPtr则显示如下：",[11,61,62],{},[53,63],{"alt":55,"src":64},"\u002Fwp-content\u002Fuploads\u002F2014\u002F10\u002Fimage_thumb1.png",[11,66,67],{},"大多数时候，异步载入一个单独的资源基本没什么意义，除非他真的很大。如果要批量异步载入资源的话，就需要用到Object Libraries。这个是Content brower用来进行资源筛选和显示的类，在游戏逻辑中使用也是可以的。",[37,69,72],{"className":70,"code":71,"language":42},[40],"if (!ObjectLibrary)\n{\n       ObjectLibrary = UObjectLibrary::CreateLibrary(BaseClass, false, GIsEditor);\n       ObjectLibrary->AddToRoot();\n}\nObjectLibrary->LoadAssetDataFromPath(TEXT(\"\u002FGame\u002FPathWithAllObjectsOfSameType\");\nif (bFullyLoad)\n{\n       ObjectLibrary->LoadAssetsFromAssetData();\n}\n",[44,73,71],{"__ignoreMap":46},[11,75,76],{},"上面的代码对指定的目录创建了一个Library，并且资源进行了载入操作。第二个步骤并不是必须的：",[37,78,81],{"className":79,"code":80,"language":42},[40],"TArray\u003CFAssetData> AssetDatas;\nObjectLibrary->GetAssetDataList(AssetDatas);\n\nfor (int32 i = 0; i \u003C AssetDatas.Num(); ++i)\n{\n       FAssetData& AssetData = AssetDatas[i];\n\n       const FString* FoundTypeNameString = AssetData.TagsAndValues.Find(GET_MEMBER_NAME_CHECKED(UAssetObject,TypeName));\n\n       if (FoundTypeNameString && FoundTypeNameString->Contains(TEXT(\"FooType\")))\n       {\n              return AssetData;\n       }\n}\n",[44,82,80],{"__ignoreMap":46},[11,84,85],{},"上面的代码在ObjectLibrary中进行筛选并将找到的第一个资源返回。",[11,87,88],{},"得到AssetData之后可以将其转换为FStringAssetReference，然后使用StreamableManager进行异步载入。",[37,90,93],{"className":91,"code":92,"language":42},[40],"void UGameCheatManager::GrantItems()\n{\n       TArray\u003CFStringAssetReference> ItemsToStream;\n       FStreamableManager& Streamable = UGameGlobals::Get().StreamableManager;\n       for(int32 i = 0; i \u003C ItemList.Num(); ++i)\n       {\n              ItemsToStream.AddUnique(ItemList[i].ToStringReference());\n       }\n       Streamable.RequestAsyncLoad(ItemsToStream, FStreamableDelegate::CreateUObject(this, &UGameCheatManager::GrantItemsDeferred));\n}\n\nvoid UGameCheatManager::GrantItemsDeferred()\n{\n       for(int32 i = 0; i \u003C ItemList.Num(); ++i)\n       {\n              UGameItemData* ItemData = ItemList[i].Get();\n              if(ItemData)\n              {\n                     MyPC->GrantItem(ItemData);\n              }\n       }\n}\n",[44,94,92],{"__ignoreMap":46},[11,96,97,98,101],{},"StreamableManager可以对传递给他的StringReference所引用的资源全部进行载入操作。上面的例子中ItemList的定义为",[44,99,100],{},"TArray\u003C TAssetPtr\u003CUGameItem> >","。也就说针对一个弱引用的列表，获得所有的StringReference之后传递给StreamableManager进行一次性的异步载入。载入之后会调用回调函数以便及时进行处理。在回调函数之前，所有的引用都会被StreamableManager保留，防止其被垃圾回收。",[11,103,104],{},"如上，将StreamableManager和Object Libaries进行组合使用，即可实现异步的资源载入了。",{"title":46,"searchDepth":106,"depth":107,"links":108},2,3,[],"2014-10-05","md",{"layout":112,"status":113,"published":114,"author":115,"author_login":116,"author_email":117,"author_url":118,"wordpress_id":119,"wordpress_url":120,"date_gmt":121,"excerpt":122},"post","publish",true,{"display_name":116,"login":116,"email":117,"url":118},"chaoshikari","chaoshikari@gmail.com","\u002F",1052,"\u002F\u002F?p=1052","2014-10-05 09:27:08 +0000",{"type":8,"value":123},[124],[11,125,13],{},"\u002F2014-10-05-ue4异步载入资源",{"title":6,"description":13},"_legacy\u002F2014\u002F2014-10-05-ue4%e5%bc%82%e6%ad%a5%e8%bd%bd%e5%85%a5%e8%b5%84%e6%ba%90",[130],"UE4","bqp6nM7vv0ExVK_MBRUW0P-TNF0F9dbp-0DmCdQN_QE",{"id":133,"title":134,"body":135,"date":234,"description":139,"extension":110,"meta":235,"navigation":114,"path":244,"seo":245,"stem":246,"tags":247,"__hash__":249},"blogs\u002F_legacy\u002F2014\u002F2014-09-28-ue4%e5%ae%98%e6%96%b9ui%e5%ba%93umg%e7%ae%80%e5%8d%95%e8%8f%9c%e5%8d%95%e6%90%ad%e5%bb%ba.md","UE4官方UI库UMG简单菜单搭建",{"type":8,"value":136,"toc":232},[137,140,143,148,151,156,159,162,167,170,175,178,183,186,191,194,199,202,207,210,215,218,221,226,229],[11,138,139],{},"UMG在当前UE4版本中仍然是实验功能，不过已经能基本满足日常UI的制作需求了。于是参考官方教程对UMG的UI的制作进行了熟悉。",[11,141,142],{},"首先要做的是在引擎中打开UMG功能：",[11,144,145],{},[53,146],{"alt":55,"src":147},"\u002Fwp-content\u002Fuploads\u002F2014\u002F09\u002Fimage_thumb2.png",[11,149,150],{},"打开之后就能在新建菜单中看到UMG相关的项目了，如果看不到的话重启一下引擎就可以了",[11,152,153],{},[53,154],{"alt":55,"src":155},"\u002Fwp-content\u002Fuploads\u002F2014\u002F09\u002Fimage_thumb3.png",[11,157,158],{},"选择Wdiget  BluePrint就可以新建一个UMG的UI了。",[11,160,161],{},"在这里我们先新建一个Slate Blush以作背景用",[11,163,164],{},[53,165],{"alt":55,"src":166},"\u002Fwp-content\u002Fuploads\u002F2014\u002F09\u002Fimage_thumb4.png",[11,168,169],{},"Slate Brush内部很单纯，选择一张已经导入的图片即可。",[11,171,172],{},[53,173],{"alt":55,"src":174},"\u002Fwp-content\u002Fuploads\u002F2014\u002F09\u002Fimage_thumb5.png",[11,176,177],{},"创建完Brush之后到刚刚新建的Wdiget里面，删掉默认添加的元件。按照下面的元件顺序添加即可",[11,179,180],{},[53,181],{"alt":55,"src":182},"\u002Fwp-content\u002Fuploads\u002F2014\u002F09\u002Fimage_thumb6.png",[11,184,185],{},"背景图片的配置在Border的属性中",[11,187,188],{},[53,189],{"alt":55,"src":190},"\u002Fwp-content\u002Fuploads\u002F2014\u002F09\u002Fimage_thumb7.png",[11,192,193],{},"基本上使用起来和普通的UI编辑器没什么大的区别，也就是拖拖调调就可以了。事件的绑定在右边的属性栏里面都能够找到。",[11,195,196],{},[53,197],{"alt":55,"src":198},"\u002Fwp-content\u002Fuploads\u002F2014\u002F09\u002Fimage_thumb8.png",[11,200,201],{},"新建的事件处理函数名称是可以修改的。给Quit按钮绑定退出功能：",[11,203,204],{},[53,205],{"alt":55,"src":206},"\u002Fwp-content\u002Fuploads\u002F2014\u002F09\u002Fimage_thumb9.png",[11,208,209],{},"这样的话UMG菜单部分就完成了。接下来只要找一个合适的时机将菜单弹出即可，可以选择在level blueprint的beginplay弹出。这里选择在Hud里面弹出：",[11,211,212],{},[53,213],{"alt":55,"src":214},"\u002Fwp-content\u002Fuploads\u002F2014\u002F09\u002Fimage_thumb10.png",[11,216,217],{},"这里面有两个需要注意的地方。选中modal可以防止菜单后面的3d界面得到控制权，就是普通ui中的模态。show cursor可以让UI界面中显示鼠标指针，而不用到controller里面去关闭全局的鼠标隐藏。",[11,219,220],{},"完成之后的界面如下，为了直观将背景图片去掉了：",[11,222,223],{},[53,224],{"alt":55,"src":225},"\u002Fwp-content\u002Fuploads\u002F2014\u002F09\u002Fimage_thumb11.png",[11,227,228],{},"以上就是全部的步骤了，最后吐槽下UE的暂停游戏功能。在当前版本下，不使用C++进行处理的情况下，暂停游戏的那行文字提示是去不掉的。不过使用模态来处理的话，基本的应用情境中是不需要暂停游戏的。但是暂停菜单的话……",[11,230,231],{},"总体而言UMG的功能很容易上手，没有想象中的那么复杂。",{"title":46,"searchDepth":106,"depth":107,"links":233},[],"2014-09-28",{"layout":112,"status":113,"published":114,"author":236,"author_login":116,"author_email":117,"author_url":118,"wordpress_id":237,"wordpress_url":238,"date_gmt":239,"excerpt":240},{"display_name":116,"login":116,"email":117,"url":118},1043,"\u002F\u002F?p=1043","2014-09-28 12:55:58 +0000",{"type":8,"value":241},[242],[11,243,139],{},"\u002F2014-09-28-ue4官方ui库umg简单菜单搭建",{"title":134,"description":139},"_legacy\u002F2014\u002F2014-09-28-ue4%e5%ae%98%e6%96%b9ui%e5%ba%93umg%e7%ae%80%e5%8d%95%e8%8f%9c%e5%8d%95%e6%90%ad%e5%bb%ba",[130,248],"UMG","BQd5f503SbSHEZMn43WMjG44sJksfuBw9wt09EXA-ow",{"id":251,"title":252,"body":253,"date":234,"description":257,"extension":110,"meta":309,"navigation":114,"path":318,"seo":319,"stem":320,"tags":321,"__hash__":323},"blogs\u002F_legacy\u002F2014\u002F2014-09-28-ue4%e5%af%bc%e5%85%a5mmd%e7%9a%84%e5%8f%a6%e4%b8%80%e6%9d%a1%e8%b7%af%e5%be%84.md","UE4导入MMD的另一条路径",{"type":8,"value":254,"toc":307},[255,258,266,269,277,282,285,288,293,296],[11,256,257],{},"虽然上次最后搞定了MMD导入到UE4的问题，但是中途要使用到的工具实在是太多了。最后经过寻找，终于找到了一条更便捷的导入路径。",[11,259,260,261,265],{},"要使用到的工具只有一个：MMD4Mecanim。工具的官方网站在这里：",[21,262,263],{"href":263,"rel":264,"title":263},"http:\u002F\u002Fstereoarts.jp\u002F",[25],"。",[11,267,268],{},"这个工具包本身是给Unity使用的，但是它在打包过程中的fbx文件可以正常的导入到UE4。",[11,270,271,272,276],{},"这篇文章中的部分内容已经过期，详情请参考",[21,273,275],{"href":274},"\u002F?p=1250","这篇文章","。\n下载工具并解压之后，其内部的pmx2fbx.exe可以拿来单独使用。",[11,278,279],{},[53,280],{"alt":55,"src":281},"\u002Fwp-content\u002Fuploads\u002F2014\u002F09\u002Fimage_thumb.png",[11,283,284],{},"使用过程相当简单，按照上面的参数说明。第一个参数是pmx文件路径，后面紧跟vmd动作的路径。转换成功后的fbx直接导入到UE4中即可。",[11,286,287],{},"导入之后可以直接使用，唯一的问题是贴图和材质会出现问题。可以选择在UE中对材质进行重新编辑或者在3dsmax之类的软件中进行修复之后再导入。",[11,289,290],{},[53,291],{"alt":55,"src":292},"\u002Fwp-content\u002Fuploads\u002F2014\u002F09\u002Fimage_thumb1.png",[11,294,295],{},"错误大体上就像上面显示的一样，只是单纯的材质问题。骨骼和动画的使用一切正常。",[297,298,299],"blockquote",{},[11,300,301,302,306],{},"工具的网盘备份：链接: ",[21,303,304],{"href":304,"rel":305},"http:\u002F\u002Fpan.baidu.com\u002Fs\u002F1sjsdkPf",[25]," 密码: vbd1",{"title":46,"searchDepth":106,"depth":107,"links":308},[],{"layout":112,"status":113,"published":114,"author":310,"author_login":116,"author_email":117,"author_url":118,"wordpress_id":311,"wordpress_url":312,"date_gmt":313,"excerpt":314},{"display_name":116,"login":116,"email":117,"url":118},1018,"\u002F\u002F?p=1018","2014-09-28 12:50:12 +0000",{"type":8,"value":315},[316],[11,317,257],{},"\u002F2014-09-28-ue4导入mmd的另一条路径",{"title":252,"description":257},"_legacy\u002F2014\u002F2014-09-28-ue4%e5%af%bc%e5%85%a5mmd%e7%9a%84%e5%8f%a6%e4%b8%80%e6%9d%a1%e8%b7%af%e5%be%84",[130,322],"MMD","mhmDKxLuvXIdq4Bn_cPnhmnineFpPwq_QJ5Yt3qs-io",{"id":325,"title":326,"body":327,"date":560,"description":331,"extension":110,"meta":561,"navigation":114,"path":570,"seo":571,"stem":572,"tags":573,"__hash__":574},"blogs\u002F_legacy\u002F2014\u002F2014-08-22-ue4%e5%af%bc%e5%85%a5mmd%e6%a8%a1%e5%9e%8b.md","UE4导入MMD模型",{"type":8,"value":328,"toc":558},[329,332,338,341,353,358,361,367,375,378,383,386,391,394,397,400,403,408,411,416,419,424,427,430,435,438,441,446,449,454,457,462,465,470,473,478,491,494,497,500,503,508,511,514,519,522,527,530,533,536,541,544,547,552,555],[11,330,331],{},"折腾了好几天，终于把MMD模型导入UE4给搞定了。于是总结下经验，顺便把从贴吧淘到的夏娜模型导入到UE4。",[11,333,271,334,337],{},[21,335,336],{"href":274},"[这篇文章]","。\n事先声明，本人对3D建模这块一窍不通。如果术语方面有出入，请见谅。下面正式开始：",[11,339,340],{},"首先在Blender这边需要做一点准备工作。",[11,342,343,344,348,349,352],{},"到Blender的插件目录\u003C",[345,346,347],"span",{},"Blender","\\2.71\\scripts\\addons>中找到FBX导出插件",[345,350,351],{},"io_scene_fbx","，用编辑器打开export_fbx.py文件。到第2425行，对两行代码进行注释，如果使用的编辑器没有跳转的功能话可以用搜索快速定位。",[11,354,355],{},[53,356],{"alt":55,"src":357},"\u002Fwp-content\u002Fuploads\u002F2014\u002F08\u002Fimage_thumb1.png",[11,359,360],{},"修改后的文本如下：",[37,362,365],{"className":363,"code":364,"language":42},[40],"#for bonename, bone, obname, me, armob in ob_bones:\nfor my_bone in ob_bones:\n# Always parent to armature now\nif my_bone.parent:\nfw('\\n\\tConnect: \"OO\", \"Model::%s\", \"Model::%s\"' % (my_bone.fbxName, my_bone.parent.fbxName))\n#else:\n# the armature object is written as an empty and all root level bones connect to it\n#fw('\\n\\tConnect: \"OO\", \"Model::%s\", \"Model::%s\"' % (my_bone.fbxName, my_bone.fbxArm.fbxName))\n",[44,366,364],{"__ignoreMap":46},[11,368,369,370,374],{},"然后需要做的事情是到这个地址去下载Blender用的MMD插件：",[21,371,372],{"href":372,"rel":373,"title":372},"https:\u002F\u002Fgithub.com\u002Fsugiany\u002Fblender_mmd_tools",[25],"。下载完成后将mmd_tools文件夹解压到Blender的插件目录。",[11,376,377],{},"打开Blender，进入用户配置。启用刚刚下载的插件。用MMD这个关键词可以快速的过滤到插件，在选择框上打勾即可。如果启用成功的话，在左边的功能区域就能看到MMD的功能了。如果启用了看不见的话可以试着重启一下Blender。",[11,379,380],{},[53,381],{"alt":55,"src":382},"\u002Fwp-content\u002Fuploads\u002F2014\u002F08\u002Fimage_thumb2.png",[11,384,385],{},"做好了上面的工作之后就可以进行导入的工作了。点击MMD面板上的Model按钮，在弹出的选择窗口左边，将缩放比例调整为8倍。选择想要导入的模型即可，这里选择前几天在贴吧淘到的夏娜模型。",[11,387,388],{},[53,389],{"alt":55,"src":390},"\u002Fwp-content\u002Fuploads\u002F2014\u002F08\u002Fimage_thumb3.png",[11,392,393],{},"之所以在这里做缩放操作，是因为这个插件不支持导入之后做任何缩放。有读刚刚GitHub页面作者说明的童鞋应该有注意到。由于这个限定的关系，为了保证最终导入UE4的单位不会出错，通过计算得出这里的缩放倍数是8。",[11,395,396],{},"需要注意的是，这里根据模型的不同，导入之后在视图上看到的模型材质可能是灰色的。",[11,398,399],{},"模型导入之后先不忙着下一步动作，要对模型的骨骼结构进行一下检查。由于UE4是不支持多个骨骼作为根骨骼的，所以对于不符合的模型我们要稍作修改。",[11,401,402],{},"在右上角的大纲视图中找到骨架，并查看其结构。如果是类似这样的结构就不需要更改了：",[11,404,405],{},[53,406],{"alt":55,"src":407},"\u002Fwp-content\u002Fuploads\u002F2014\u002F08\u002Fimage_thumb4.png",[11,409,410],{},"这样的就是可以使用的结构，所有的骨骼都在\u003C全ての親>这个节点之下。如果导入之后发现没有这个节点，而是这样的结构：",[11,412,413],{},[53,414],{"alt":55,"src":415},"\u002Fwp-content\u002Fuploads\u002F2014\u002F08\u002Fimage_thumb5.png",[11,417,418],{},"那么就需要对骨骼结构进行修正。推荐使用PmxEditor进行修正，打开PE，导入刚刚的模型。执行下面的操作即可：",[11,420,421],{},[53,422],{"alt":55,"src":423},"\u002Fwp-content\u002Fuploads\u002F2014\u002F08\u002Fimage_thumb6.png",[11,425,426],{},"保存之后重新导入Blender，再次检查一下就能进行下一步工作了。",[11,428,429],{},"模型导入完成之后，接下来就导入动作。这里用的是忘记是哪里下载过来的行走的vmd。导入动作之前注意，保证所有的Object，包括骨骼都处于被选中的状态。Blender全选的快捷键是A。导入时同样调整缩放为8。",[11,431,432],{},[53,433],{"alt":55,"src":434},"\u002Fwp-content\u002Fuploads\u002F2014\u002F08\u002Fimage_thumb7.png",[11,436,437],{},"完成以上两个操作之后，使用下面的动画预览功能看看模型和骨骼的绑定是否正常。通常缩放设定不一致、导入动画时没有选中骨骼是常见的出错原因。",[11,439,440],{},"成功导入之后看到的差不多就是这样的模型了：",[11,442,443],{},[53,444],{"alt":55,"src":445},"\u002Fwp-content\u002Fuploads\u002F2014\u002F08\u002Fimage_thumb8.png",[11,447,448],{},"然后我们把Blender的场景单位调节为与UE4一致，在右边的面板上可以找到场景标签。调整为下面的样子就可以了：",[11,450,451],{},[53,452],{"alt":55,"src":453},"\u002Fwp-content\u002Fuploads\u002F2014\u002F08\u002Fimage_thumb9.png",[11,455,456],{},"接下来我们可以打开环境光，再加上些光源，拖一个摄像机到视图上。",[11,458,459],{},[53,460],{"alt":55,"src":461},"\u002Fwp-content\u002Fuploads\u002F2014\u002F08\u002Fimage_thumb10.png",[11,463,464],{},"然后选择最左边的那个面板进行渲染预览，就可以看到并检查是否所有的导入都正常了。",[11,466,467],{},[53,468],{"alt":55,"src":469},"\u002Fwp-content\u002Fuploads\u002F2014\u002F08\u002Fimage_thumb11.png",[11,471,472],{},"预览渲染如果正常话，就可以开始进行导出操作了。配置上保持默认就可以了，为了防止版本不同引起的问题，配置截图如下：",[11,474,475],{},[53,476],{"alt":55,"src":477},"\u002Fwp-content\u002Fuploads\u002F2014\u002F08\u002Fimage_thumb12.png",[11,479,480,481,486,487,490],{},"导出之后的FBX如果直接导入到UE4的话会出错，因为UE4导入FBX时不识别其中的非英文字符，会出现命名冲突错误。由于MMD模型中的日文实在是太多了，想要手动修正显然不现实。于是参照FBX的官方文档，通过FBX SDK写了一个转义工具来解决这个问题。直接从\u003C",[21,482,485],{"href":483,"rel":484},"http:\u002F\u002Fpan.baidu.com\u002Fs\u002F1xW9ye",[25],"这里","|共享密码：5xrd|>下载，具体的说明请看\u003C",[21,488,485],{"href":489},"\u002Fpage\u002Ffbx-utf8-convert",">。",[11,492,493],{},"打开工具，点上面的按钮打开fbx，然后点击下面的转换按钮即可。转换成功的话会在同级目录下生成文件名追加了_ue4的fbx文件。",[11,495,496],{},"这样导出的文件就能成功的导入UE4了，只是由于UE4的导入器目前功能还不是很完善。其导入的结果和3dsmax导入的结果有一定差别。最容易出现的问题就是斗鸡眼了，虽然可以在UE4中进行调整，但这里我们还是将模型导入3dsmax再重新导出一次，对模型格式进行一下规范化。",[11,498,499],{},"FBX这个文件格式是autodesk定义的，其他软件对fbx的支持或多或少都会有些问题。多次的导入导出很容易出现奇怪的问题。所以出现问题时，用autodesk官方的软件进行一下规范化比较好。",[11,501,502],{},"导入时保持默认配置不动，会有一些警告信息，不会有什么影响。导入之后是这个效果：",[11,504,505],{},[53,506],{"alt":55,"src":507},"\u002Fwp-content\u002Fuploads\u002F2014\u002F08\u002Fimage_thumb13.png",[11,509,510],{},"对比下会发现下方丝袜的材质效果有些不明显，材质相关的建议在UE4中进行相应的修正。在这里进行修正需要会使用3dsmax，而且也无法保证UE4对材质的处理和3dsmax完全相同。",[11,512,513],{},"同时我们会发现动画的帧数变成了100，由于始终没有搞明白3dsmax要怎么用。这里我选择将其发送到MotionBuilder。",[11,515,516],{},[53,517],{"alt":55,"src":518},"\u002Fwp-content\u002Fuploads\u002F2014\u002F08\u002Fimage_thumb14.png",[11,520,521],{},"发送成功之后，我们会发现夏娜的丝袜又回来了……",[11,523,524],{},[53,525],{"alt":55,"src":526},"\u002Fwp-content\u002Fuploads\u002F2014\u002F08\u002Fimage_thumb15.png",[11,528,529],{},"调整好动画的帧数之后，直接导出成FBX。",[11,531,532],{},"至此，UE4外部的模型转换工作就完成了。",[11,534,535],{},"打开UE4，新建一个ThirdPerson BluePrint的项目。项目打开之后，到Content Browser，在自己喜欢的位置导入FBX模型。",[11,537,538],{},[53,539],{"alt":55,"src":540},"\u002Fwp-content\u002Fuploads\u002F2014\u002F08\u002Fimage_thumb16.png",[11,542,543],{},"根据模型文件的不同可能会出现一些警告信息，例如POT、平滑组之类的。一般情况下无视就好了。",[11,545,546],{},"导入之后效果如下：",[11,548,549],{},[53,550],{"alt":55,"src":551},"\u002Fwp-content\u002Fuploads\u002F2014\u002F08\u002Fimage_thumb17.png",[11,553,554],{},"唯一的遗憾是表情动画从3dsmax导出之后再导入就消失了，尝试导入到别的3d软件也无法读取到。不是很明白其中的机理，好在从游戏制作的方面来看，表情动画并不是必须的。需要的情况下也可以直接在UE4的编辑器里面拖动骨骼，故而这个问题只能暂时搁置了。",[11,556,557],{},"到这里模型的导入工作就完成了。材质和光照全部保存默认的情况下，效果不会很好。这些都是之后需要研究的地方，模型导入成功的话就什么都好说了。",{"title":46,"searchDepth":106,"depth":107,"links":559},[],"2014-08-22",{"layout":112,"status":113,"published":114,"author":562,"author_login":116,"author_email":117,"author_url":118,"wordpress_id":563,"wordpress_url":564,"date_gmt":565,"excerpt":566},{"display_name":116,"login":116,"email":117,"url":118},1002,"\u002F\u002F?p=1002","2014-08-22 02:12:24 +0000",{"type":8,"value":567},[568],[11,569,331],{},"\u002F2014-08-22-ue4导入mmd模型",{"title":326,"description":331},"_legacy\u002F2014\u002F2014-08-22-ue4%e5%af%bc%e5%85%a5mmd%e6%a8%a1%e5%9e%8b",[130,322],"0EpYkU2jkiiy9se72Lmd6y6ntUUM_PAZaylrB-jAjWg",{"id":576,"title":577,"body":578,"date":1061,"description":582,"extension":110,"meta":1062,"navigation":114,"path":1071,"seo":1072,"stem":1073,"tags":1074,"__hash__":1076},"blogs\u002F_legacy\u002F2014\u002F2014-08-09-ue4%e5%ae%98%e6%96%b9fps%ef%bc%88c%ef%bc%89%e8%8c%83%e4%be%8b%e5%ae%9e%e7%8e%b0%e8%ae%b0%e5%bd%95.md","UE4官方FPS（C++）范例实现记录",{"type":8,"value":579,"toc":1051},[580,583,590,593,597,605,611,624,627,631,634,640,655,658,668,671,678,681,684,695,698,712,715,722,725,728,731,734,737,740,743,1024,1027,1030,1033,1036,1039,1044,1047],[11,581,582],{},"看完Digital Tutors的UE4教程之后，参照官方wiki的教程文章把FPS的范例实现了一遍。于是趁热打铁，用文字的形式记录和总结一下。",[11,584,585,586,265],{},"教程原文地址：",[21,587,588],{"href":588,"rel":589},"https:\u002F\u002Fwiki.unrealengine.com\u002FFirst_Person_Shooter_C%2B%2B_Tutorial",[25],[11,591,592],{},"使用的UE版本是4.4版，按照教程中的顺序来：",[594,595,596],"h2",{"id":596},"新建项目",[11,598,599,600,604],{},"直接新建一个空白项目，不要默认的资源文件。项目名",[601,602,603],"strong",{},"FPSProject","，为了方便后面代码的复制粘贴。在这里不做变更。",[11,606,607,608,265],{},"新建后点保存，将地图保存为",[601,609,610],{},"FPSMap",[11,612,613,614,617,618,621,622,265],{},"然后有一个步骤是在",[601,615,616],{},"Game>Project Settings",">",[601,619,620],{},"Maps & Modes","中将编辑器默认地图设置为刚刚保存的",[601,623,610],{},[11,625,626],{},"这一步在操作的时候不知为何自动被添加了一块地板，总之不影响操作，直接无视。",[594,628,630],{"id":629},"创建gamemode","创建GameMode",[11,632,633],{},"粗略的浏览了下文档之后，发现GameMode大概相当于游戏规则的核心管理类。大体上的地位相当于cocos2d中的Scene。如下图：",[11,635,636],{},[53,637],{"alt":638,"src":639,"title":638},"clip_image001","\u002Fwp-content\u002Fuploads\u002F2014\u002F08\u002Fclip_image001_thumb.jpg",[11,641,642,643,646,647,650,651,654],{},"在菜单",[601,644,645],{},"File","下",[601,648,649],{},"Add Code to Project","中新建一个类，基类选择为",[601,652,653],{},"GameMode","。选择编辑会自动打开VS2013。这个时候intellisense会报错，按照官方的建议，直接无视即可。原因似乎是有的文件要到编译的时候才会生成，这里的情况问题出在\"FPSGameMode.generated.h\"上。",[11,656,657],{},"在生成的类中覆盖掉BeginPlay函数，并在屏幕上输出HelloWolrd。关闭UEEditor，在VS中进行代码编译。要注意如果不关闭编辑器的话，VS会无法写入文件导致失败。",[11,659,660,661,664,665,265],{},"生成完毕之后重新打开UE，在配置中的",[601,662,663],{},"Game>Project Settings>Maps & Modes","将Default Mode设置为",[601,666,667],{},"FPSGameMode",[594,669,670],{"id":670},"创建角色",[11,672,673,674,677],{},"和上面一样，添加代码命名",[601,675,676],{},"FPSCharacter","，继承Charater。和上面的步骤几乎没什么不同，继承BeginPlay并输出调试文字。",[11,679,680],{},"完成操作之后在FPSGameMode的构造函数中添加DefaultPawnClass = AFPSCharacter::StaticClass()。",[594,682,683],{"id":683},"玩家操作",[11,685,686,687,690,691,694],{},"UE的操作接口分为",[601,688,689],{},"Axis Mappings","和",[601,692,693],{},"Action Mapings","，分别对应连续输入的方向控制操作和离散输入的按键操作。设定在项目配置中的Input项内进行。",[11,696,697],{},"接口的作用是将硬件输入绑定到特定的名称上，然后由Charater类来注册。主要提供一个映射的操作来隔离游戏逻辑和硬件操作，很常见的引擎输入控制模式，基本上按照文档描述没有遇到什么问题。",[11,699,700,701,704,705,704,708,711],{},"途中有介绍到",[601,702,703],{},"UFUNCTION","、",[601,706,707],{},"UCLASS",[601,709,710],{},"UPROPERTY","这几个宏，它们的作用是将特定的函数、类、属性暴露给引擎，代码中出现这几个宏的代码变动时必须要在VS中重新编译。",[594,713,714],{"id":714},"为角色添加网格",[11,716,717,718,721],{},"新建一个BlusPrint继承刚刚建立的",[601,719,720],{},"FPSCharater","。通过BluePrint的编辑器将Mesh设置为下载并导入的人物模型。没什么特别的问题。",[11,723,724],{},"需要在VC中将玩家的Chara修改为刚刚新建的BluePrint,看起来有点繁琐，不过也就是复制粘贴一下。",[11,726,727],{},"添加完后会发现视角有点低，通过添加一个UCameraComponent来解决。根据文档描述，个类就是作为摄像机的配置而存在的。",[594,729,730],{"id":730},"为角色添加第一人称网格",[11,732,733],{},"在这个游戏模式的定义中，玩家是看不见自己的身体网格的，只能看见自己的手。似乎和通常遇到的FPS有点不一样，不过Demo就是这么定义的。",[11,735,736],{},"和上一步没啥不同，关键性的部分是调整可见性的语句。",[594,738,739],{"id":739},"子弹和射击",[11,741,742],{},"新建一个继承自Actor的类，创建碰撞形态和移动逻辑的Component，并且设定生命周期为3秒。",[37,744,748],{"className":745,"code":746,"language":747,"meta":46,"style":46},"language-cpp shiki shiki-themes github-light-high-contrast github-dark monokai","\u002F\u002F Use a sphere as a simple collision representation\nCollisionComp = PCIP.CreateDefaultSubobject\u003CUSphereComponent>(this, TEXT(\"SphereComp\"));\nCollisionComp->InitSphereRadius(15.0f);\nCollisionComp->BodyInstance.SetCollisionProfileName(\"Projectile\");\nCollisionComp->OnComponentHit.AddDynamic(this, &AFPSProjectile::OnHit);\nRootComponent = CollisionComp;\n\n\u002F\u002F Use a ProjectileMovementComponent to govern this projectile's movement\nProjectileMovement = PCIP.CreateDefaultSubobject\u003CUProjectileMovementComponent>(this, TEXT(\"ProjectileComp\"));\nProjectileMovement->UpdatedComponent = CollisionComp;\nProjectileMovement->InitialSpeed = 3000.f;\nProjectileMovement->MaxSpeed = 3000.f;\nProjectileMovement->bRotationFollowsVelocity = true;\nProjectileMovement->bShouldBounce = true;\nProjectileMovement->Bounciness = 0.3f;\n\n\u002F\u002F Die after 3 seconds by default\nInitialLifeSpan = 3.0f;\n","cpp",[44,749,750,758,802,822,838,863,874,880,886,918,928,944,958,971,983,998,1003,1009],{"__ignoreMap":46},[345,751,754],{"class":752,"line":753},"line",1,[345,755,757],{"class":756},"sZdD5","\u002F\u002F Use a sphere as a simple collision representation\n",[345,759,760,764,768,771,774,777,779,782,786,789,793,795,799],{"class":752,"line":106},[345,761,763],{"class":762},"s0idv","CollisionComp ",[345,765,767],{"class":766},"sLXdl","=",[345,769,770],{"class":762}," PCIP.CreateDefaultSubobject",[345,772,773],{"class":766},"\u003C",[345,775,776],{"class":762},"USphereComponent",[345,778,617],{"class":766},[345,780,781],{"class":762},"(",[345,783,785],{"class":784},"s4Wjf","this",[345,787,788],{"class":762},", ",[345,790,792],{"class":791},"scAT4","TEXT",[345,794,781],{"class":762},[345,796,798],{"class":797},"sZnmU","\"SphereComp\"",[345,800,801],{"class":762},"));\n",[345,803,804,807,810,812,816,819],{"class":752,"line":107},[345,805,806],{"class":762},"CollisionComp->",[345,808,809],{"class":791},"InitSphereRadius",[345,811,781],{"class":762},[345,813,815],{"class":814},"stP0Q","15.0",[345,817,818],{"class":766},"f",[345,820,821],{"class":762},");\n",[345,823,825,828,831,833,836],{"class":752,"line":824},4,[345,826,827],{"class":762},"CollisionComp->BodyInstance.",[345,829,830],{"class":791},"SetCollisionProfileName",[345,832,781],{"class":762},[345,834,835],{"class":797},"\"Projectile\"",[345,837,821],{"class":762},[345,839,841,844,847,849,851,853,856,860],{"class":752,"line":840},5,[345,842,843],{"class":762},"CollisionComp->OnComponentHit.",[345,845,846],{"class":791},"AddDynamic",[345,848,781],{"class":762},[345,850,785],{"class":784},[345,852,788],{"class":762},[345,854,855],{"class":766},"&",[345,857,859],{"class":858},"sfUV7","AFPSProjectile",[345,861,862],{"class":762},"::OnHit);\n",[345,864,866,869,871],{"class":752,"line":865},6,[345,867,868],{"class":762},"RootComponent ",[345,870,767],{"class":766},[345,872,873],{"class":762}," CollisionComp;\n",[345,875,877],{"class":752,"line":876},7,[345,878,879],{"emptyLinePlaceholder":114},"\n",[345,881,883],{"class":752,"line":882},8,[345,884,885],{"class":756},"\u002F\u002F Use a ProjectileMovementComponent to govern this projectile's movement\n",[345,887,889,892,894,896,898,901,903,905,907,909,911,913,916],{"class":752,"line":888},9,[345,890,891],{"class":762},"ProjectileMovement ",[345,893,767],{"class":766},[345,895,770],{"class":762},[345,897,773],{"class":766},[345,899,900],{"class":762},"UProjectileMovementComponent",[345,902,617],{"class":766},[345,904,781],{"class":762},[345,906,785],{"class":784},[345,908,788],{"class":762},[345,910,792],{"class":791},[345,912,781],{"class":762},[345,914,915],{"class":797},"\"ProjectileComp\"",[345,917,801],{"class":762},[345,919,921,924,926],{"class":752,"line":920},10,[345,922,923],{"class":762},"ProjectileMovement->UpdatedComponent ",[345,925,767],{"class":766},[345,927,873],{"class":762},[345,929,931,934,936,939,941],{"class":752,"line":930},11,[345,932,933],{"class":762},"ProjectileMovement->InitialSpeed ",[345,935,767],{"class":766},[345,937,938],{"class":814}," 3000.",[345,940,818],{"class":766},[345,942,943],{"class":762},";\n",[345,945,947,950,952,954,956],{"class":752,"line":946},12,[345,948,949],{"class":762},"ProjectileMovement->MaxSpeed ",[345,951,767],{"class":766},[345,953,938],{"class":814},[345,955,818],{"class":766},[345,957,943],{"class":762},[345,959,961,964,966,969],{"class":752,"line":960},13,[345,962,963],{"class":762},"ProjectileMovement->bRotationFollowsVelocity ",[345,965,767],{"class":766},[345,967,968],{"class":814}," true",[345,970,943],{"class":762},[345,972,974,977,979,981],{"class":752,"line":973},14,[345,975,976],{"class":762},"ProjectileMovement->bShouldBounce ",[345,978,767],{"class":766},[345,980,968],{"class":814},[345,982,943],{"class":762},[345,984,986,989,991,994,996],{"class":752,"line":985},15,[345,987,988],{"class":762},"ProjectileMovement->Bounciness ",[345,990,767],{"class":766},[345,992,993],{"class":814}," 0.3",[345,995,818],{"class":766},[345,997,943],{"class":762},[345,999,1001],{"class":752,"line":1000},16,[345,1002,879],{"emptyLinePlaceholder":114},[345,1004,1006],{"class":752,"line":1005},17,[345,1007,1008],{"class":756},"\u002F\u002F Die after 3 seconds by default\n",[345,1010,1012,1015,1017,1020,1022],{"class":752,"line":1011},18,[345,1013,1014],{"class":762},"InitialLifeSpan ",[345,1016,767],{"class":766},[345,1018,1019],{"class":814}," 3.0",[345,1021,818],{"class":766},[345,1023,943],{"class":762},[11,1025,1026],{},"通过BluePrint继承这个类，并且在BluePrint编辑器中添加Component并附上网格，同时要关闭其碰撞。因为基类中已经有了碰撞逻辑的实现。",[11,1028,1029],{},"关于碰撞方面，不知为何碰撞组的管理是在ini文件中进行配置的，这个留到以后再研究。",[11,1031,1032],{},"子弹的发射还有一个添加瞄准准星的HUD的过程，大体过程就是绘制，没别的了。是否HUD类能够处理鼠标事件目前不明。",[594,1034,1035],{"id":1035},"角色动画",[11,1037,1038],{},"角色动画通过状态机来管理，工作在BluePrint中进行。没有用到C++代码，可视化的调整状态机的逻辑，非常的方便。",[11,1040,1041],{},[53,1042],{"alt":55,"src":1043,"title":55},"\u002Fwp-content\u002Fuploads\u002F2014\u002F08\u002Fimage_thumb.png",[11,1045,1046],{},"总体而言算是大致的了解了UE4用C++的方式来搭建游戏的过程，不过详细的API的熟悉还是需要一定的时间过程。作为开头，刚好到此为止。",[1048,1049,1050],"style",{},"html pre.shiki code .sZdD5, html code.shiki .sZdD5{--shiki-default:#66707B;--shiki-dark:#6A737D;--shiki-sepia:#88846F}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 .s4Wjf, html code.shiki .s4Wjf{--shiki-default:#023B95;--shiki-dark:#79B8FF;--shiki-sepia:#FD971F}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}html pre.shiki code .stP0Q, html code.shiki .stP0Q{--shiki-default:#023B95;--shiki-dark:#79B8FF;--shiki-sepia:#AE81FF}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 .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);}",{"title":46,"searchDepth":106,"depth":107,"links":1052},[1053,1054,1055,1056,1057,1058,1059,1060],{"id":596,"depth":106,"text":596},{"id":629,"depth":106,"text":630},{"id":670,"depth":106,"text":670},{"id":683,"depth":106,"text":683},{"id":714,"depth":106,"text":714},{"id":730,"depth":106,"text":730},{"id":739,"depth":106,"text":739},{"id":1035,"depth":106,"text":1035},"2014-08-09",{"layout":112,"status":113,"published":114,"author":1063,"author_login":116,"author_email":117,"author_url":118,"wordpress_id":1064,"wordpress_url":1065,"date_gmt":1066,"excerpt":1067},{"display_name":116,"login":116,"email":117,"url":118},950,"\u002F\u002F?p=950","2014-08-09 12:59:00 +0000",{"type":8,"value":1068},[1069],[11,1070,582],{},"\u002F2014-08-09-ue4-official-fps-sample-note",{"title":577,"description":582},"_legacy\u002F2014\u002F2014-08-09-ue4%e5%ae%98%e6%96%b9fps%ef%bc%88c%ef%bc%89%e8%8c%83%e4%be%8b%e5%ae%9e%e7%8e%b0%e8%ae%b0%e5%bd%95",[1075,130],"c++","nAEOG3fwndTuulhA-Y-0puqE5TaHgKzX6coCPlzh4yQ",85,1788763178462]