[{"data":1,"prerenderedAt":526},["ShallowReactive",2],{"\u002F2014-08-09-ue4-official-fps-sample-note":3},{"id":4,"title":5,"body":6,"date":503,"description":12,"extension":504,"meta":505,"navigation":319,"path":519,"seo":520,"stem":521,"tags":522,"__hash__":525},"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":7,"value":8,"toc":493},"minimark",[9,13,23,26,30,38,44,57,60,64,67,74,89,92,102,105,112,115,118,129,132,146,149,156,159,162,165,168,171,174,177,465,468,471,474,477,480,486,489],[10,11,12],"p",{},"看完Digital Tutors的UE4教程之后，参照官方wiki的教程文章把FPS的范例实现了一遍。于是趁热打铁，用文字的形式记录和总结一下。",[10,14,15,16,22],{},"教程原文地址：",[17,18,19],"a",{"href":19,"rel":20},"https:\u002F\u002Fwiki.unrealengine.com\u002FFirst_Person_Shooter_C%2B%2B_Tutorial",[21],"nofollow","。",[10,24,25],{},"使用的UE版本是4.4版，按照教程中的顺序来：",[27,28,29],"h2",{"id":29},"新建项目",[10,31,32,33,37],{},"直接新建一个空白项目，不要默认的资源文件。项目名",[34,35,36],"strong",{},"FPSProject","，为了方便后面代码的复制粘贴。在这里不做变更。",[10,39,40,41,22],{},"新建后点保存，将地图保存为",[34,42,43],{},"FPSMap",[10,45,46,47,50,51,54,55,22],{},"然后有一个步骤是在",[34,48,49],{},"Game>Project Settings",">",[34,52,53],{},"Maps & Modes","中将编辑器默认地图设置为刚刚保存的",[34,56,43],{},[10,58,59],{},"这一步在操作的时候不知为何自动被添加了一块地板，总之不影响操作，直接无视。",[27,61,63],{"id":62},"创建gamemode","创建GameMode",[10,65,66],{},"粗略的浏览了下文档之后，发现GameMode大概相当于游戏规则的核心管理类。大体上的地位相当于cocos2d中的Scene。如下图：",[10,68,69],{},[70,71],"img",{"alt":72,"src":73,"title":72},"clip_image001","\u002Fwp-content\u002Fuploads\u002F2014\u002F08\u002Fclip_image001_thumb.jpg",[10,75,76,77,80,81,84,85,88],{},"在菜单",[34,78,79],{},"File","下",[34,82,83],{},"Add Code to Project","中新建一个类，基类选择为",[34,86,87],{},"GameMode","。选择编辑会自动打开VS2013。这个时候intellisense会报错，按照官方的建议，直接无视即可。原因似乎是有的文件要到编译的时候才会生成，这里的情况问题出在\"FPSGameMode.generated.h\"上。",[10,90,91],{},"在生成的类中覆盖掉BeginPlay函数，并在屏幕上输出HelloWolrd。关闭UEEditor，在VS中进行代码编译。要注意如果不关闭编辑器的话，VS会无法写入文件导致失败。",[10,93,94,95,98,99,22],{},"生成完毕之后重新打开UE，在配置中的",[34,96,97],{},"Game>Project Settings>Maps & Modes","将Default Mode设置为",[34,100,101],{},"FPSGameMode",[27,103,104],{"id":104},"创建角色",[10,106,107,108,111],{},"和上面一样，添加代码命名",[34,109,110],{},"FPSCharacter","，继承Charater。和上面的步骤几乎没什么不同，继承BeginPlay并输出调试文字。",[10,113,114],{},"完成操作之后在FPSGameMode的构造函数中添加DefaultPawnClass = AFPSCharacter::StaticClass()。",[27,116,117],{"id":117},"玩家操作",[10,119,120,121,124,125,128],{},"UE的操作接口分为",[34,122,123],{},"Axis Mappings","和",[34,126,127],{},"Action Mapings","，分别对应连续输入的方向控制操作和离散输入的按键操作。设定在项目配置中的Input项内进行。",[10,130,131],{},"接口的作用是将硬件输入绑定到特定的名称上，然后由Charater类来注册。主要提供一个映射的操作来隔离游戏逻辑和硬件操作，很常见的引擎输入控制模式，基本上按照文档描述没有遇到什么问题。",[10,133,134,135,138,139,138,142,145],{},"途中有介绍到",[34,136,137],{},"UFUNCTION","、",[34,140,141],{},"UCLASS",[34,143,144],{},"UPROPERTY","这几个宏，它们的作用是将特定的函数、类、属性暴露给引擎，代码中出现这几个宏的代码变动时必须要在VS中重新编译。",[27,147,148],{"id":148},"为角色添加网格",[10,150,151,152,155],{},"新建一个BlusPrint继承刚刚建立的",[34,153,154],{},"FPSCharater","。通过BluePrint的编辑器将Mesh设置为下载并导入的人物模型。没什么特别的问题。",[10,157,158],{},"需要在VC中将玩家的Chara修改为刚刚新建的BluePrint,看起来有点繁琐，不过也就是复制粘贴一下。",[10,160,161],{},"添加完后会发现视角有点低，通过添加一个UCameraComponent来解决。根据文档描述，个类就是作为摄像机的配置而存在的。",[27,163,164],{"id":164},"为角色添加第一人称网格",[10,166,167],{},"在这个游戏模式的定义中，玩家是看不见自己的身体网格的，只能看见自己的手。似乎和通常遇到的FPS有点不一样，不过Demo就是这么定义的。",[10,169,170],{},"和上一步没啥不同，关键性的部分是调整可见性的语句。",[27,172,173],{"id":173},"子弹和射击",[10,175,176],{},"新建一个继承自Actor的类，创建碰撞形态和移动逻辑的Component，并且设定生命周期为3秒。",[178,179,184],"pre",{"className":180,"code":181,"language":182,"meta":183,"style":183},"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","",[185,186,187,196,241,262,278,303,314,321,327,359,369,385,399,412,424,439,444,450],"code",{"__ignoreMap":183},[188,189,192],"span",{"class":190,"line":191},"line",1,[188,193,195],{"class":194},"sZdD5","\u002F\u002F Use a sphere as a simple collision representation\n",[188,197,199,203,207,210,213,216,218,221,225,228,232,234,238],{"class":190,"line":198},2,[188,200,202],{"class":201},"s0idv","CollisionComp ",[188,204,206],{"class":205},"sLXdl","=",[188,208,209],{"class":201}," PCIP.CreateDefaultSubobject",[188,211,212],{"class":205},"\u003C",[188,214,215],{"class":201},"USphereComponent",[188,217,50],{"class":205},[188,219,220],{"class":201},"(",[188,222,224],{"class":223},"s4Wjf","this",[188,226,227],{"class":201},", ",[188,229,231],{"class":230},"scAT4","TEXT",[188,233,220],{"class":201},[188,235,237],{"class":236},"sZnmU","\"SphereComp\"",[188,239,240],{"class":201},"));\n",[188,242,244,247,250,252,256,259],{"class":190,"line":243},3,[188,245,246],{"class":201},"CollisionComp->",[188,248,249],{"class":230},"InitSphereRadius",[188,251,220],{"class":201},[188,253,255],{"class":254},"stP0Q","15.0",[188,257,258],{"class":205},"f",[188,260,261],{"class":201},");\n",[188,263,265,268,271,273,276],{"class":190,"line":264},4,[188,266,267],{"class":201},"CollisionComp->BodyInstance.",[188,269,270],{"class":230},"SetCollisionProfileName",[188,272,220],{"class":201},[188,274,275],{"class":236},"\"Projectile\"",[188,277,261],{"class":201},[188,279,281,284,287,289,291,293,296,300],{"class":190,"line":280},5,[188,282,283],{"class":201},"CollisionComp->OnComponentHit.",[188,285,286],{"class":230},"AddDynamic",[188,288,220],{"class":201},[188,290,224],{"class":223},[188,292,227],{"class":201},[188,294,295],{"class":205},"&",[188,297,299],{"class":298},"sfUV7","AFPSProjectile",[188,301,302],{"class":201},"::OnHit);\n",[188,304,306,309,311],{"class":190,"line":305},6,[188,307,308],{"class":201},"RootComponent ",[188,310,206],{"class":205},[188,312,313],{"class":201}," CollisionComp;\n",[188,315,317],{"class":190,"line":316},7,[188,318,320],{"emptyLinePlaceholder":319},true,"\n",[188,322,324],{"class":190,"line":323},8,[188,325,326],{"class":194},"\u002F\u002F Use a ProjectileMovementComponent to govern this projectile's movement\n",[188,328,330,333,335,337,339,342,344,346,348,350,352,354,357],{"class":190,"line":329},9,[188,331,332],{"class":201},"ProjectileMovement ",[188,334,206],{"class":205},[188,336,209],{"class":201},[188,338,212],{"class":205},[188,340,341],{"class":201},"UProjectileMovementComponent",[188,343,50],{"class":205},[188,345,220],{"class":201},[188,347,224],{"class":223},[188,349,227],{"class":201},[188,351,231],{"class":230},[188,353,220],{"class":201},[188,355,356],{"class":236},"\"ProjectileComp\"",[188,358,240],{"class":201},[188,360,362,365,367],{"class":190,"line":361},10,[188,363,364],{"class":201},"ProjectileMovement->UpdatedComponent ",[188,366,206],{"class":205},[188,368,313],{"class":201},[188,370,372,375,377,380,382],{"class":190,"line":371},11,[188,373,374],{"class":201},"ProjectileMovement->InitialSpeed ",[188,376,206],{"class":205},[188,378,379],{"class":254}," 3000.",[188,381,258],{"class":205},[188,383,384],{"class":201},";\n",[188,386,388,391,393,395,397],{"class":190,"line":387},12,[188,389,390],{"class":201},"ProjectileMovement->MaxSpeed ",[188,392,206],{"class":205},[188,394,379],{"class":254},[188,396,258],{"class":205},[188,398,384],{"class":201},[188,400,402,405,407,410],{"class":190,"line":401},13,[188,403,404],{"class":201},"ProjectileMovement->bRotationFollowsVelocity ",[188,406,206],{"class":205},[188,408,409],{"class":254}," true",[188,411,384],{"class":201},[188,413,415,418,420,422],{"class":190,"line":414},14,[188,416,417],{"class":201},"ProjectileMovement->bShouldBounce ",[188,419,206],{"class":205},[188,421,409],{"class":254},[188,423,384],{"class":201},[188,425,427,430,432,435,437],{"class":190,"line":426},15,[188,428,429],{"class":201},"ProjectileMovement->Bounciness ",[188,431,206],{"class":205},[188,433,434],{"class":254}," 0.3",[188,436,258],{"class":205},[188,438,384],{"class":201},[188,440,442],{"class":190,"line":441},16,[188,443,320],{"emptyLinePlaceholder":319},[188,445,447],{"class":190,"line":446},17,[188,448,449],{"class":194},"\u002F\u002F Die after 3 seconds by default\n",[188,451,453,456,458,461,463],{"class":190,"line":452},18,[188,454,455],{"class":201},"InitialLifeSpan ",[188,457,206],{"class":205},[188,459,460],{"class":254}," 3.0",[188,462,258],{"class":205},[188,464,384],{"class":201},[10,466,467],{},"通过BluePrint继承这个类，并且在BluePrint编辑器中添加Component并附上网格，同时要关闭其碰撞。因为基类中已经有了碰撞逻辑的实现。",[10,469,470],{},"关于碰撞方面，不知为何碰撞组的管理是在ini文件中进行配置的，这个留到以后再研究。",[10,472,473],{},"子弹的发射还有一个添加瞄准准星的HUD的过程，大体过程就是绘制，没别的了。是否HUD类能够处理鼠标事件目前不明。",[27,475,476],{"id":476},"角色动画",[10,478,479],{},"角色动画通过状态机来管理，工作在BluePrint中进行。没有用到C++代码，可视化的调整状态机的逻辑，非常的方便。",[10,481,482],{},[70,483],{"alt":484,"src":485,"title":484},"image","\u002Fwp-content\u002Fuploads\u002F2014\u002F08\u002Fimage_thumb.png",[10,487,488],{},"总体而言算是大致的了解了UE4用C++的方式来搭建游戏的过程，不过详细的API的熟悉还是需要一定的时间过程。作为开头，刚好到此为止。",[490,491,492],"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":183,"searchDepth":198,"depth":243,"links":494},[495,496,497,498,499,500,501,502],{"id":29,"depth":198,"text":29},{"id":62,"depth":198,"text":63},{"id":104,"depth":198,"text":104},{"id":117,"depth":198,"text":117},{"id":148,"depth":198,"text":148},{"id":164,"depth":198,"text":164},{"id":173,"depth":198,"text":173},{"id":476,"depth":198,"text":476},"2014-08-09","md",{"layout":506,"status":507,"published":319,"author":508,"author_login":509,"author_email":510,"author_url":511,"wordpress_id":512,"wordpress_url":513,"date_gmt":514,"excerpt":515},"post","publish",{"display_name":509,"login":509,"email":510,"url":511},"chaoshikari","chaoshikari@gmail.com","\u002F",950,"\u002F\u002F?p=950","2014-08-09 12:59:00 +0000",{"type":7,"value":516},[517],[10,518,12],{},"\u002F2014-08-09-ue4-official-fps-sample-note",{"title":5,"description":12},"_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",[523,524],"c++","UE4","nAEOG3fwndTuulhA-Y-0puqE5TaHgKzX6coCPlzh4yQ",1788763181130]