[{"data":1,"prerenderedAt":815},["ShallowReactive",2],{"blog-page-21":3,"blog-count":814},[4,155,308,597,727],{"id":5,"title":6,"body":7,"date":131,"description":13,"extension":132,"meta":133,"navigation":136,"path":148,"seo":149,"stem":150,"tags":151,"__hash__":154},"blogs\u002F_legacy\u002F2015\u002F2015-08-18-hello-slate.md","Hello Slate",{"type":8,"value":9,"toc":123},"minimark",[10,14,17,20,31,35,38,49,52,56,59,68,71,77,80,86,89,93,96,99,102,108,111,117,120],[11,12,13],"p",{},"UE4中通常的游戏内逻辑使用UMG就可以了，当需要一些独特的功能时就会需要用到Slate。",[11,15,16],{},"当前UE4版本4.8.3。",[11,18,19],{},"Slate是UE4的用户界面系统，UE4编辑器的大部分界面都是由Slate构建的。同时，在编辑器中使用的UMG也是在Slate的基础上封装的。",[11,21,22,23,30],{},"本文参照官方社区文档",[24,25,29],"a",{"href":26,"rel":27},"https:\u002F\u002Fwiki.unrealengine.com\u002FSlate,_Hello",[28],"nofollow","Slate, Hello","完成。",[32,33,34],"h2",{"id":34},"准备工作",[11,36,37],{},"要使用Slate，第一步是将其API开放到项目。在项目对应的Build.cs中将下面的代码的注释去掉即可",[39,40,45],"pre",{"className":41,"code":43,"language":44},[42],"language-text","\u002F\u002F Uncomment if you are using Slate UI\n\nPrivateDependencyModuleNames.AddRange(new string[] { \"Slate\", \"SlateCore\" });\n","text",[46,47,43],"code",{"__ignoreMap":48},"",[11,50,51],{},"根据UE4版本的不同如果没有这行的直接加上就好了。",[32,53,55],{"id":54},"slatewidget","SlateWidget",[11,57,58],{},"创建用于显示文字的Slate控件。",[11,60,61,62,67],{},"Slate控件的一些特殊的宏和界面定义方式的详情可以参考官方的",[24,63,66],{"href":64,"rel":65},"https:\u002F\u002Fdocs.unrealengine.com\u002Flatest\u002FCHN\u002FProgramming\u002FSlate\u002FOverview\u002Findex.html",[28],"Slate概述","。",[11,69,70],{},"StandardSlateWidget.h",[39,72,75],{"className":73,"code":74,"language":44},[42],"#pragma once\n#include \"Test_mp.h\" \u002F* \u003C项目头文件 *\u002F\n\nclass SStandardSlateWidget: public SCompoundWidget\n{\nSLATE_BEGIN_ARGS(SStandardSlateWidget){}\n\n\u002F*\u003C参照下面的OwnerHUD的声明 *\u002F\nSLATE_ARGUMENT(TWeakObjectPtr\u003Cclass AHUD>,OwnerHUD)\n\n\nSLATE_END_ARGS()\n\npublic:\n\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\n\u002F\u002F\u002F\u002F\u002F\u003C每一个控件都必须要有这个函数\n\u002F\u002F\u002F\u002F\u002F\u003C构建控件及其子控件\nvoid Construct(const FArguments& InArgs);\nprivate:\n\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\u002F\n\u002F\u002F\u002F\u002F\u002F\u003C指向控件的持有者Hud\n\u002F\u002F\u002F\u002F\u002F\u003C使用弱引用持有HUD的指针，因为HUD是使用强引用来持有Widget的。\n\u002F\u002F\u002F\u002F\u002F\u003C如果双方都为强引用的话将会导致解构时形成循环引用并引发内存泄露\nTWeakObjectPtr\u003Cclass AHUD> OwnerHUD;\n\n};\n",[46,76,74],{"__ignoreMap":48},[11,78,79],{},"StandardSlateWidget.cpp",[39,81,84],{"className":82,"code":83,"language":44},[42],"#include \"Test_mp.h\"\n#include \"Plugin\u002FStandardSlateWidget.h\"\n \nvoid SStandardSlateWidget::Construct(const FArguments& InArgs)\n{\n    OwnerHUD = InArgs._OwnerHUD;\n \n    ChildSlot\n        .VAlign(VAlign_Fill)\n        .HAlign(HAlign_Fill)\n        [\n            SNew(SOverlay)\n            + SOverlay::Slot()\n            .VAlign(VAlign_Top)\n            .HAlign(HAlign_Center)\n             [\n                 SNew(STextBlock)\n                 .ShadowColorAndOpacity(FLinearColor::Black)\n                 .ColorAndOpacity(FLinearColor::Red)\n                 .ShadowOffset(FIntPoint(-5, 5))\n                 .Font(FSlateFontInfo(\"Veranda\", 24))\n                 .Text(FText::FromString(\"Hello, Slate!\"))\n             ]\n        ];\n \n}\n",[46,85,83],{"__ignoreMap":48},[11,87,88],{},"在这里使用OwnerHUD并不是必须的，主要的作用是作为参数传递的示范。",[32,90,92],{"id":91},"hud","HUD",[11,94,95],{},"通过自定义一个HUD用于Slate控件的展示。添加HUD代码没有什么特殊的地方。",[11,97,98],{},"添加好之后直接在BeginPlay中将Widget输出到屏幕即可。",[11,100,101],{},"StandardHud.h",[39,103,106],{"className":104,"code":105,"language":44},[42],"#pragma once\n#include \"GameFramework\u002FHUD.h\"\n#include \"StandardHUD.generated.h\"\n \nclass SStandardSlateWidget;\n \nUCLASS()\nclass AStandardHUD : public AHUD\n{\n    GENERATED_BODY()\n \npublic:\n    AStandardHUD();\n    TSharedPtr\u003CSStandardSlateWidget> myUIWidget;\n \n    void BeginPlay();\n};\n\n",[46,107,105],{"__ignoreMap":48},[11,109,110],{},"StandardHud.cpp",[39,112,115],{"className":113,"code":114,"language":44},[42],"#pragma once\n \u002F\u002F Fill out your copyright notice in the Description page of Project Settings.   \n #include \"Test_mp.h\" \n #include \"Plugin\u002FStandardSlateWidget.h\" \n #include \"Plugin\u002FStandardHUD.h\"   \n \nAStandardHUD::AStandardHUD() \n{   \n\n}\n\nvoid AStandardHUD::BeginPlay() \n{     \n    SAssignNew(myUIWidget, SStandardSlateWidget).OwnerHUD(this);       \n    if (GEngine->IsValidLowLevel())     \n    {         \n        GEngine->GameViewport->AddViewportWidgetContent(SNew(SWeakWidget).PossiblyNullContent(myUIWidget.ToSharedRef()));     \n    }       \n    if (myUIWidget.IsValid())     \n    {         \n        myUIWidget->SetVisibility(EVisibility::Visible);     \n    } \n}\n\n",[46,116,114],{"__ignoreMap":48},[32,118,119],{"id":119},"最终结果",[11,121,122],{},"原始的教程中还有自定义GameMode的部分，在这里就不执行了。直接在编辑器中将HUD指定为刚刚定义的StandardHud即可。\n指定完成后，点击运行即可在屏幕上方看到“Hello, Slate!”的文字输出了。",{"title":48,"searchDepth":124,"depth":125,"links":126},2,3,[127,128,129,130],{"id":34,"depth":124,"text":34},{"id":54,"depth":124,"text":55},{"id":91,"depth":124,"text":92},{"id":119,"depth":124,"text":119},"2015-08-18","md",{"layout":134,"status":135,"published":136,"author":137,"author_login":138,"author_email":139,"author_url":140,"wordpress_id":141,"wordpress_url":142,"date_gmt":143,"excerpt":144},"post","publish",true,{"display_name":138,"login":138,"email":139,"url":140},"chaoshikari","chaoshikari@gmail.com","\u002F",1472,"\u002F\u002F?p=1472","2015-08-18 11:56:34 +0000",{"type":8,"value":145},[146],[11,147,13],{},"\u002F2015-08-18-hello-slate",{"title":6,"description":13},"_legacy\u002F2015\u002F2015-08-18-hello-slate",[152,153],"UE4","Slate","nMQL2MFSDju9LkcCXygpyjn248xCbtlgOGdlwP5Xquk",{"id":156,"title":157,"body":158,"date":131,"description":162,"extension":132,"meta":293,"navigation":136,"path":302,"seo":303,"stem":304,"tags":305,"__hash__":307},"blogs\u002F_legacy\u002F2015\u002F2015-08-18-slate-icons.md","Slate中图标的使用",{"type":8,"value":159,"toc":288},[160,163,165,168,171,174,180,183,189,192,198,201,204,210,214,217,220,223,226,232,235,241,244,253,256,259,262,265,268,271,279,282,285],[11,161,162],{},"在Slate控件中，图标的使用除了直接引用图片外，还是有一些其他便利的方法的。",[11,164,16],{},[11,166,167],{},"目前官方的版本已经到4.9.0了，但是由于FMod并没有更新对应版本的插件，项目无法迁移，故而依然使用4.8.3版本。",[32,169,170],{"id":170},"自带图标",[11,172,173],{},"UE4自带的图标在对SWidget进行封装时比较容易遇到。UWidget在编辑器中的显示图标使用的就是自带的图标系统。通常为对如下的函数进行覆盖实现：",[39,175,178],{"className":176,"code":177,"language":44},[42],"const FSlateBrush* UCListViewWidget::GetEditorIcon() \n{     \n  return FUMGStyle::Get().GetBrush(\"Widget.TextBlock\"); \n}\n",[46,179,177],{"__ignoreMap":48},[11,181,182],{},"这些图标都是在引擎内部预定义的，通过搜索就能看到这些图标的定义。",[39,184,187],{"className":185,"code":186,"language":44},[42],"\u002F\u002F UMGStyle.cpp  \n\nStyle->Set(\"Widget.NativeWidgetHost\", new IMAGE_BRUSH(TEXT(\"NativeWidgetHost\"), Icon16x16));\n",[46,188,186],{"__ignoreMap":48},[11,190,191],{},"其中IMAGE_BRUSH是同一个文件中定义的宏",[39,193,196],{"className":194,"code":195,"language":44},[42],"#define IMAGE_BRUSH( RelativePath, ... ) FSlateImageBrush( Style->RootToContentDir( RelativePath, TEXT(\".png\") ), __VA_ARGS__ )\n",[46,197,195],{"__ignoreMap":48},[11,199,200],{},"ICON16x16也同样是宏定义，再次就不赘述了。",[11,202,203],{},"自带图标也可以直接在控件中进行使用",[39,205,208],{"className":206,"code":207,"language":44},[42],"const FSlateBrush *m_Icon = FUMGStyle::Get().GetBrush(\"Palette.Icon\");\n \nreturn SNew(STableRow\u003C FFileListItemPtr >, OwnerTable)\n[\nSNew(SHorizontalBox)\n    + SHorizontalBox::Slot()\n    .HAlign(HAlign_Left)\n    .AutoWidth()\n    [\n        SNew(SImage)\n        .Image(m_Icon)\n    ]\n    + SHorizontalBox::Slot()\n    .HAlign(HAlign_Left)\n    [\n        SNew(STextBlock)\n        .Text(FText::FromString(Item->GetDisplayName()))\n        .Font(FSlateFontInfo(FPaths::EngineContentDir() \u002F TEXT(\"Slate\u002FFonts\u002FRoboto-Bold.ttf\"), 12))\n    ]\n];\n",[46,209,207],{"__ignoreMap":48},[32,211,213],{"id":212},"icon-font","Icon Font",[11,215,216],{},"Icon Font添加图标确实非常的方便，UE4编辑器中也有使用到Font Awesome作为图标显示。",[11,218,219],{},"由于Font Awesome比较有名，项目中使用的也是它。",[11,221,222],{},"C++中的使用",[11,224,225],{},"要在C++中使用Font Awesome，首先要对UFont对象进行生成。",[39,227,230],{"className":228,"code":229,"language":44},[42],"FontAwesome = Cast\u003CUFont>(StaticLoadObject(UFont::StaticClass(), NULL, TEXT(\"Font'\u002FGame\u002FResource\u002FFont\u002Ffontawesome-webfont.fontawesome-webfont'\")));\n",[46,231,229],{"__ignoreMap":48},[11,233,234],{},"其中Font部分为直接对导入的字体进行引用复制所得。接下来只要使用STextBlock来使用相应的图标就可以了",[39,236,239],{"className":237,"code":238,"language":44},[42],"return SNew(STableRow\u003C FFileListItemPtr >, OwnerTable)\n    [\n        SNew(SHorizontalBox)\n        + SHorizontalBox::Slot()\n        .HAlign(HAlign_Left)\n        .AutoWidth()\n        .Padding(FMargin(0.0f, 3.0f, 1.0f, 0.0f))\n        [\n            SNew(STextBlock)\n            .Font(FSlateFontInfo(FontAwesome, 12))\n            .Text(FText::FromString(FString(Item->IsDirectory ? TEXT(\"\\xf07b\" \u002F*fa-folder*\u002F) : TEXT(\"\\xf15b\" \u002F*fa-file*\u002F))))\n        ]\n        + SHorizontalBox::Slot()\n        .HAlign(HAlign_Left)\n        .Padding(FMargin(3.0f, 3.0f, 0.0f, 0.0f))\n        [\n            SNew(STextBlock)\n            .Text(FText::FromString(Item->GetDisplayName()))\n            .Font(FSlateFontInfo(FPaths::EngineContentDir() \u002F TEXT(\"Slate\u002FFonts\u002FRoboto-Bold.ttf\"), 12))\n        ]\n \n    ];\n",[46,240,238],{"__ignoreMap":48},[11,242,243],{},"如果要在蓝图中进行使用的话，需要首先将字体的使用制定为对应的Icon Font。",[11,245,246,247,252],{},"通常Icon Font在提供的时候都会有每一个图标对应的Unicode编码，Font Awesome的编码集在[",[24,248,251],{"href":249,"rel":250},"https:\u002F\u002Ffortawesome.github.io\u002FFont-Awesome\u002Fcheatsheet\u002F",[28],"这里","]。",[11,254,255],{},"Font Awesome的编码集页面是可以直接复制对应的字符的，但是也有只提供了编码集的情况出现。",[11,257,258],{},"只要有编码集就可以对Icon进行使用了，要输入对应的Unicode码有很多种方式，这里提供两种。",[11,260,261],{},"Win+R输入并运行charmap，在其中选择对应的图标并复制其Unicode码。",[11,263,264],{},"在Word中输入对应的Unicode码，选中这些值按Alt+X就可以实现转换。",[11,266,267],{},"由于UE4编辑器中使用的字体并非对应的Icon Font，所以会以乱码显示，不过并不影响实际使用。",[32,269,270],{"id":270},"排版",[11,272,273,274,67],{},"使用到图标的时候有时需要对布局进行控制，布局的具体操作可以参看官方的",[24,275,278],{"href":276,"rel":277},"https:\u002F\u002Fdocs.unrealengine.com\u002Flatest\u002FCHN\u002FProgramming\u002FSlate\u002FWidgets\u002Findex.html",[28],"Slate控件文档",[11,280,281],{},"其中需要注意的是，Padding布局使用的是数据结构FMargin。作用参看FMargin本身的构造函数定义即可，只是Padding参数本身对FMargin进行了封装，光看官方的例子会有一定的迷惑性。",[11,283,284],{},"对于FMargin，当参数为4个时分别对应Left, Top, Right, Bottom；为2个时对应Horizontal和Vertical；为1个时则对应所有方向。",[11,286,287],{},"另外要注意的是在列表中AutoWidth必须手动指定，并不像文档中所描述的一样是默认启动的。",{"title":48,"searchDepth":124,"depth":125,"links":289},[290,291,292],{"id":170,"depth":124,"text":170},{"id":212,"depth":124,"text":213},{"id":270,"depth":124,"text":270},{"layout":134,"status":135,"published":136,"author":294,"author_login":138,"author_email":139,"author_url":140,"wordpress_id":295,"wordpress_url":296,"date_gmt":297,"excerpt":298},{"display_name":138,"login":138,"email":139,"url":140},1524,"\u002F\u002F?p=1524","2015-08-18 04:53:15 +0000",{"type":8,"value":299},[300],[11,301,162],{},"\u002F2015-08-18-slate-icons",{"title":157,"description":162},"_legacy\u002F2015\u002F2015-08-18-slate-icons",[152,153,306],"Icon","A8mfQicx3BCg5ItNQMw-s9Z2yH3w34f1SwIYUghvJeE",{"id":309,"title":310,"body":311,"date":578,"description":315,"extension":132,"meta":579,"navigation":136,"path":591,"seo":592,"stem":593,"tags":594,"__hash__":596},"blogs\u002F_legacy\u002F2015\u002F2015-08-16-ue4-event-dispatcher-and-delegate.md","事件调度器及C++中的使用",{"type":8,"value":312,"toc":573},[313,316,318,321,324,327,330,333,340,343,348,351,355,358,361,364,367,370,373,463,466,502,505,537,540,546,549,555,558,563,566,569],[11,314,315],{},"事件调度器非常的适合在各个蓝图之间实现通信功能。",[11,317,16],{},[11,319,320],{},"在蓝图中，事件调度器的作用就像是事件的派发器。通过将事件预先的绑定在事件调度器上，可以让系统可以在需要时将事件派发给所有已经绑定的事件。",[32,322,323],{"id":323},"事件调度器的使用",[11,325,326],{},"一个比较常见的使用事件调度器的地方，就是关卡蓝图。",[11,328,329],{},"关卡蓝图中的很多物体的Actor及其逻辑，如果要脱离关卡蓝图的话会比较难于使用。因此，可以通过将关卡蓝图中实现好的功能绑定到某个类，例如GameMode的事件调度器上来方便调度。",[11,331,332],{},"如下图，在关卡蓝图的BeginPlay中将功能绑定到GameMode上：",[11,334,335],{},[336,337],"img",{"alt":338,"src":339},"image","\u002Fwp-content\u002Fuploads\u002F2015\u002F08\u002Fimage_thumb1.png",[11,341,342],{},"然后可以在GameMode的蓝图中，设定定时的调度关卡蓝图上的功能",[11,344,345],{},[336,346],{"alt":338,"src":347},"\u002Fwp-content\u002Fuploads\u002F2015\u002F08\u002Fimage_thumb2.png",[11,349,350],{},"事件调度器同时支持解绑和解绑全部，以方便对事件调度的逻辑进行进一步的控制。",[32,352,354],{"id":353},"delegate","Delegate",[11,356,357],{},"在C++和蓝图混合使用时，使用Blueprint Function Library可以方便的实现蓝图到C++的调用。如果要实现C++对蓝图的逻辑的话，事件调度机制就非常的方便。",[11,359,360],{},"在C++中，与事件调度器对应的是Delegate机制。事实上，Delegate机制的涵盖范围比事件调度器要大一些。",[11,362,363],{},"Delegate机制是一种常见的设计模式，相信做程序的童鞋都比较熟悉。这里不做多余的说明了。",[11,365,366],{},"UE4官方对Delegate的翻译名称唯代理。按照类型，分为单播代理和多播代理。其中，多播代理与事件调度器的功能类似，而单播代理更接近单纯的代理，也就是只能绑定一个执行函数。",[11,368,369],{},"要让多播代理像是事件调度器一样使用的话，需要将其定义为Dynamic。对于单播代理也是一样的。",[11,371,372],{},"在C++中使用UE4的代理功能，需要使用官方提供的宏进行定义。",[374,375,376,387],"table",{},[377,378,379],"thead",{},[380,381,382,385],"tr",{},[383,384],"th",{},[383,386],{},[388,389,390,399,407,415,423,431,439,447,455],"tbody",{},[380,391,392,396],{},[393,394,395],"td",{},"函数签名",[393,397,398],{},"声明宏",[380,400,401,404],{},[393,402,403],{},"void Function()",[393,405,406],{},"DECLARE_DELEGATE( DelegateName )",[380,408,409,412],{},[393,410,411],{},"void Function( Param1 )",[393,413,414],{},"DECLARE_DELEGATE_OneParam( DelegateName, Param1Type )",[380,416,417,420],{},[393,418,419],{},"void Function( Param1, Param2 )",[393,421,422],{},"DECLARE_DELEGATE_TwoParams( DelegateName, Param1Type, Param2Type )",[380,424,425,428],{},[393,426,427],{},"void Function( Param1, Param2, ... )",[393,429,430],{},"DECLARE_DELEGATE_NumParams( DelegateName, Param1Type, Param2Type, ... )",[380,432,433,436],{},[393,434,435],{},"RetVal Function()",[393,437,438],{},"DECLARE_DELEGATE_RetVal( RetValType, DelegateName )",[380,440,441,444],{},[393,442,443],{},"RetVal Function( Param1 )",[393,445,446],{},"DECLARE_DELEGATE_RetVal_OneParam( RetValType, DelegateName, Param1Type )",[380,448,449,452],{},[393,450,451],{},"RetVal Function( Param1, Param2 )",[393,453,454],{},"DECLARE_DELEGATE_RetVal_TwoParams( RetValType, DelegateName, Param1Type, Param2Type )",[380,456,457,460],{},[393,458,459],{},"RetVal Function( Param1, Param2, ... )",[393,461,462],{},"DECLARE_DELEGATE_RetVal_NumParams( RetValType, DelegateName, Param1Type, Param2Type, ... )",[11,464,465],{},"针对不同的代理类型，使用不同的宏前缀即可：",[374,467,468,476],{},[377,469,470],{},[380,471,472,474],{},[383,473],{},[383,475],{},[388,477,478,486,494],{},[380,479,480,483],{},[393,481,482],{},"多播代理",[393,484,485],{},"DECLARE_MULTICAST_DELEGATE...",[380,487,488,491],{},[393,489,490],{},"动态单播代理",[393,492,493],{},"DECLARE_DYNAMIC_DELEGATE...",[380,495,496,499],{},[393,497,498],{},"动态多播代理",[393,500,501],{},"DECLARE_DYNAMIC_MULTICAST_DELEGATE...",[11,503,504],{},"实际使用时，首先要对代理类型进行定义",[39,506,510],{"className":507,"code":508,"language":509,"meta":48,"style":48},"language-cpp shiki shiki-themes github-light-high-contrast github-dark monokai","DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FDspLRValDelegate, float, LVals, float, RVals);\n","cpp",[46,511,512],{"__ignoreMap":48},[513,514,517,521,525,529,532,534],"span",{"class":515,"line":516},"line",1,[513,518,520],{"class":519},"scAT4","DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams",[513,522,524],{"class":523},"s0idv","(FDspLRValDelegate, ",[513,526,528],{"class":527},"sGZGq","float",[513,530,531],{"class":523},", LVals, ",[513,533,528],{"class":527},[513,535,536],{"class":523},", RVals);\n",[11,538,539],{},"然后在具体的类，例如GameState中进行具体的声明",[39,541,544],{"className":542,"code":543,"language":44},[42],"UPROPERTY(BlueprintAssignable, Category = \"SoundFuncs\")\n    FDspLRValDelegate  DspLRVals;\n",[46,545,543],{"__ignoreMap":48},[11,547,548],{},"这样一来，就可以在需要调用这个代理的地方进行调用了，例如在某个数据生成的地方。",[39,550,553],{"className":551,"code":552,"language":44},[42],"AMyGameState* tags = Cast\u003CAMyGameState>(SL_Contex.LC_cGameState);\nif (tags)\n    tags->DspLRVals.Broadcast(tfLval, tfRval);\n",[46,554,552],{"__ignoreMap":48},[11,556,557],{},"之后，就可以在蓝图中向这个多播代理绑定事件来获得生成的数据了",[11,559,560],{},[336,561],{"alt":338,"src":562},"\u002Fwp-content\u002Fuploads\u002F2015\u002F08\u002Fimage_thumb3.png",[32,564,565],{"id":565},"总结",[11,567,568],{},"事件调度器的机制在逻辑实现中非常的方便，尤其是在关卡设计中，可能会比较经常用到。使用中也没有遇到什么特别需要注意的地方，逻辑很单纯。",[570,571,572],"style",{},"html pre.shiki code .scAT4, html code.shiki .scAT4{--shiki-default:#622CBC;--shiki-dark:#B392F0;--shiki-sepia:#A6E22E}html pre.shiki code .s0idv, html code.shiki .s0idv{--shiki-default:#0E1116;--shiki-dark:#E1E4E8;--shiki-sepia:#F8F8F2}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 .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":48,"searchDepth":124,"depth":125,"links":574},[575,576,577],{"id":323,"depth":124,"text":323},{"id":353,"depth":124,"text":354},{"id":565,"depth":124,"text":565},"2015-08-16",{"layout":134,"status":135,"published":136,"author":580,"author_login":582,"author_email":583,"wordpress_id":584,"wordpress_url":585,"date_gmt":586,"excerpt":587},{"display_name":581,"login":582,"email":583,"url":48},"风铃","flinkor","flinkor@foxmail.com",1450,"\u002F\u002F?p=1450","2015-08-16 04:00:50 +0000",{"type":8,"value":588},[589],[11,590,315],{},"\u002F2015-08-16-ue4-event-dispatcher-and-delegate",{"title":310,"description":315},"_legacy\u002F2015\u002F2015-08-16-ue4-event-dispatcher-and-delegate",[152,595,354],"事件调度器","erOr00Vt93ckLDdi2Mv8NeMJyYp0YYoVyMZW1VTURTs",{"id":598,"title":599,"body":600,"date":711,"description":604,"extension":132,"meta":712,"navigation":136,"path":721,"seo":722,"stem":723,"tags":724,"__hash__":726},"blogs\u002F_legacy\u002F2015\u002F2015-08-11-ue4-level-streaming.md","UE4流关卡",{"type":8,"value":601,"toc":705},[602,605,608,611,614,617,626,629,638,641,644,647,650,653,656,659,662,665,669,672,675,678,681,684,687,690,693,696,699,702],[11,603,604],{},"流关卡可以使得关卡内容只在玩家“需要”的时候才加载，在很多游戏中都有使用这个技术。",[11,606,607],{},"当前UE4版本4.11.0 P6。",[11,609,610],{},"官方提供的流关卡功能目前有两种应用方式。",[32,612,613],{"id":613},"世界构成器",[11,615,616],{},"有点类似于Tile地图制作的方法，可以将子关卡拼成大地图。方便关卡设计人员的并行开发以及内容的复用。",[11,618,619,620,625],{},"这部分官方有提供详细的",[24,621,624],{"href":622,"rel":623},"https:\u002F\u002Fdocs.unrealengine.com\u002Flatest\u002FCHN\u002FEngine\u002FLevelStreaming\u002FWorldBrowser\u002Findex.html",[28],"文档","可供参考。这里只作简短的记录。",[11,627,628],{},"开启世界构成器的部分，属性的路径稍微有些变更。",[11,630,631,632,637],{},"关于Simplygon，虽然官方说明上是说引擎自带的，但是要使用的话需要Simplygon官方的许可证。有许可证之后可以参照Simplygon官方的",[24,633,636],{"href":634,"rel":635},"https:\u002F\u002Fwww.simplygon.com\u002Fmedia\u002F1636\u002Fsimplygonue4integration.pdf",[28],"说明","在UE4中开启相关功能。",[32,639,640],{"id":640},"流关卡",[11,642,643],{},"如果关闭World Composition开关的话，就是“正常”的使用流关卡功能了。",[11,645,646],{},"使用流关卡功能时，相关界面会比世界构成器时有一些功能上的变更。",[11,648,649],{},"在此功能下用于计算每个关卡大小的Level Bounds不会再被创建。",[11,651,652],{},"在关卡列表中可以对每个子关卡的动态加载方式进行选择",[11,654,655],{},"关卡左边有蓝色图标的为使用蓝图进行动态加载的子关卡。",[11,657,658],{},"“总是加载”的子关卡在游戏开始后默认是可见的。",[11,660,661],{},"“蓝图”控制动态加载的子关卡还可以在关卡详细属性中进行单独的配置",[11,663,664],{},"关卡的载入以及可视性可以通过很多方法进行控制。",[32,666,668],{"id":667},"level-streaming-volume","Level Streaming Volume",[11,670,671],{},"当玩家进入体积中时，被绑定的子关卡就会进行相应的变更。默认的情况下，子关卡将会显示出来，而玩家离开时就会将关卡移除。",[11,673,674],{},"具体的功能对应，可以在属性中进行调整：",[11,676,677],{},"作用都比较直观。",[11,679,680],{},"子关卡和体积的绑定是在关卡详细设定中进行的。",[11,682,683],{},"Level Streaming Volume必须放到永久关卡中才会起作用。",[11,685,686],{},"对于简单的逻辑使用关卡流体积非常的方便。",[32,688,689],{"id":689},"蓝图",[11,691,692],{},"对于一些复制的逻辑，可以使用蓝图对关卡的载入进行控制。",[11,694,695],{},"例如，使用触发器对关卡的载入进行控制。",[11,697,698],{},"或者用按钮进行调试。",[11,700,701],{},"蓝图和Level Streaming Volume联合使用时可能会出现冲突，使用时需要注意。",[11,703,704],{},"关卡的载入和移除都是异步的，可以在操作完成之后再触发其他事件，例如打开通往该关卡的门。",{"title":48,"searchDepth":124,"depth":125,"links":706},[707,708,709,710],{"id":613,"depth":124,"text":613},{"id":640,"depth":124,"text":640},{"id":667,"depth":124,"text":668},{"id":689,"depth":124,"text":689},"2015-08-11",{"layout":134,"status":135,"published":136,"author":713,"author_login":138,"author_email":139,"author_url":140,"wordpress_id":714,"wordpress_url":715,"date_gmt":716,"excerpt":717},{"display_name":138,"login":138,"email":139,"url":140},1467,"\u002F\u002F?p=1467","2015-08-11 11:34:05 +0000",{"type":8,"value":718},[719],[11,720,604],{},"\u002F2015-08-11-ue4-level-streaming",{"title":599,"description":604},"_legacy\u002F2015\u002F2015-08-11-ue4-level-streaming",[152,725],"level-streaming","-i3VivbL-5U3ehcqzG7IacDir1BQ9_6sNI3XrjU5u2Q",{"id":728,"title":729,"body":730,"date":798,"description":734,"extension":132,"meta":799,"navigation":136,"path":808,"seo":809,"stem":810,"tags":811,"__hash__":813},"blogs\u002F_legacy\u002F2015\u002F2015-08-08-ue4-windows10-update-log.md","UE4的Windows10升级记录",{"type":8,"value":731,"toc":796},[732,735,738,741,744,747,762,765,768,777,790,793],[11,733,734],{},"一时冲动下把系统升级到windows10了，在这样的早期自然是遇到了很多问题。虽然Windows10早就发布了技术预览版，但是很多软件都因为技术上或是其他的什么问题而没有办法迅速的对操作系统的升级进行跟进。比如说支付宝到目前为止依然没有提供windows10的数字证书解决方案……",[11,736,737],{},"按照微软之前的宣传来看，win10似乎进一步加强了应用程序的权限控制。在默认运行的情况下，各个进程之间无法相互进行干涉。直接导致迅雷快鸟无法正常的进行加速，解决方案是把迅雷快鸟和迅雷都使用管理员权限运行。不过这种问题应该过一段时间就能解决了，一般“整合”的系统都会把这些功能给去掉。就像安装ghost版的win7的情况下基本不会遇到烦人的UAC一样。",[11,739,740],{},"不过这些都不是什么太重要的问题，升级了win10之后遇到的最大的麻烦来自UE4。",[11,742,743],{},"首先的一个问题是，UE4并不支持vs2015。赶时髦的安装了vs2015之后的结果是，最后又安装了一个vs2013……主要原因是在UE的编辑器中无法识别vs2015的c++编译器，导致无法对项目进行实时编译。",[11,745,746],{},"另有一个问题就是在使用vs打开项目时可能会遇到这样的报错：",[748,749,750,753,756,759],"blockquote",{},[11,751,752],{},"1>------ 已启动生成: 项目: Test_mp, 配置: Development_Editor x64 ------",[11,754,755],{},"1> 系统找不到指定的路径。",[11,757,758],{},"1>C:\\Program Files (x86)\\MSBuild\\Microsoft.Cpp\\v4.0\\V140\\Microsoft.MakeFile.Targets(37,5): error MSB3073: 命令“\"D:\\Software\\Epic Games\\4.8\\Engine\\Build\\BatchFiles\\Build.bat\" Test_mpEditor Win64 Development \"F:\\Unreal\\Test_mp\\Test_mp.uproject\" -rocket”已退出，代码为 3。",[11,760,761],{},"========== 生成: 成功 0 个，失败 1 个，最新 0 个，跳过 0 个 ==========",[11,763,764],{},"这个错误之前也遇到过，其原因是ue4的安装目录改变了，导致编译时无法找到Build.bat。解决方案也很简单，只要打开UE4编辑器，在“文件”菜单下找到“刷新visual studio项目”即可。不过，如果之前关闭的时候是编译失败的话，这个时候是无法打开UE4编辑器的。要手动去修改项目的路径配置将会是一个很浩大的工程。不过，其实只要安装UE4的时候使用同样的路径就可以了……只不过重装系统的话，多少都会有些重新整理的冲动。另外的，在进行引擎版本升级的时候也会有类似的问题。故而在进行大版本迁移的时候最好保留一段时间的旧版本引擎。",[11,766,767],{},"当前UE4的正式版本为4.8.3。而4.9的预览版已经更新到P2了。",[11,769,770,771,776],{},"关于win10和vs2015，官方在",[24,772,775],{"href":773,"rel":774},"https:\u002F\u002Fforums.unrealengine.com\u002Fshowthread.php?78220-Unreal-Engine-4-9-Preview&p=342676#post342676",[28],"4.9预览版的更新说明","里是这样解释的：",[778,779,780,784,787],"ul",{},[781,782,783],"li",{},"UE4.9预览版还不支持vs2015，当前Github上的vs2015支持功能处于试验状态，预计4.9.0版本释出",[781,785,786],{},"UE4.9可以运行在win10上，但是还没有彻底的检验过",[781,788,789],{},"DX12在UE4.9中处于试验状态，但是在Win10上可以通过 -D3D12参数开启",[11,791,792],{},"到目前为止，UE4.8.3在Win10上运行一切正常，没有遇到什么太大的问题。",[11,794,795],{},"Win10总体试用感觉还是不错的，如果短期内没有什么重大的问题的话，大概就会一直使用了~",{"title":48,"searchDepth":124,"depth":125,"links":797},[],"2015-08-08",{"layout":134,"status":135,"published":136,"author":800,"author_login":582,"author_email":583,"wordpress_id":801,"wordpress_url":802,"date_gmt":803,"excerpt":804},{"display_name":581,"login":582,"email":583,"url":48},1415,"\u002F\u002F?p=1415","2015-08-08 04:47:37 +0000",{"type":8,"value":805},[806],[11,807,734],{},"\u002F2015-08-08-ue4-windows10-update-log",{"title":729,"description":734},"_legacy\u002F2015\u002F2015-08-08-ue4-windows10-update-log",[152,812],"win10","AzHDZItPQVH87tBRAzmOWlOc6ZC7YaJ3-ysmH1Km2is",222,1788763184108]