fps-bp-notes
FPS Blueprint notes
Subtitle: Youtube 上的 FPS UE 项目笔记
Created: 2023-11-14T21:55+08:00
Published: 2023-11-24T16:54+08:00
Categories: UnrealEngine
Creating A First Person Shooter Game (FPS) With Unreal Engine 4 - YouTube
这个项目要实现的效果:
FPS 游戏,玩家操控角色攻击 NPC,可以快速了解一个 FPS 游戏大概是怎么样的。
- 切换枪支
- 可以扔手雷
- 开镜
- 角色移动,Sprint、下蹲、角色看天空和看地面的时候脊椎有变化
- 简单的 AI 系统
- 小地图 Minimap
- 简单的地形编辑
不支持:
- 换枪动画、扔手雷动画
- AIController
- Level Streaming
这种教程可以优化的点是,看一开始就规划好整个项目类的成员变量等、函数、事件,不然临时改来改去。
-
Intro
-
True First Person Camera
- Camera 不要用 rope 了,直接 attach 到 SM 上,并且设置 parent 为 head,并修改 transform
- 不论是第一人称还是第三人称,都需要确定好相机和人物 rotation 如何被控制,对于第一人称
- 相机用 Controller 的 Rotation:相机可以「指哪打哪」
- Character 用 Controller 的 Yaw:人物只能左右
-
Player Health & Armor
- Character 需要有 Health 和 Armor,我提取成一个 ActorComp 来处理,任何有这个 comp 的 actor 就认为有血量
- 用 Widget 中的 Progress Bar 来显示 Health 和 Armor,注意是浮点数,bar 长度用 percent 控制,CreateBinding > GetPlayerCharacter > ...
- 在 Character 的 EventBeginPlay 中 create widget 并 AddToViewport
-
Regenerating Armor & Damage Function:
- 实现 Armor 自增长:Armor 小于 1 的时候要随时间增加
- TakeDamage: 先减少 Armor,若 Armor 减少后为负数,说明该负数要作用于 Health 上,最后因为显示的原因,set armor 为 0
我封装到 Comp 里了,而且 Damage 是浮点函数输入
-
Blood Splash Effect:做一个 Widget,就一张图片,用动画设置透明度,每次受伤就添加到 viewport
- 为 UI 添加一个 Fade Animation:牢记动画是属性的变化
- 在 UI Graph 中,每次创建就播放动画,不要在 Comp 或者 Actor 创建再播放,UI 动画的播放由 UI 自己管理
- TakeDamage 结尾创建 UI 并 AddToViewport,注意给一个 controller
note. 但是这样会浪费内存吧
-
Setting Up Character Animations:
- Ironsights 的 BlendSpace: Jump
- ABP 的状态:Locomotion:Jump
-
Finishing The Animation Blueprint
- 和 UE 自定义的 ABP 的区别在于,Walk/Run 里面直接用 BS_Aiming
- 使用 Direction 和 Speed 作为 BS_Aiming 的输入
-
Adding The AK-47 Weapon
- 导入材质:包括以 SK 导入枪支,还有 Texture、Specular 和 Normal Map,Tip 是先在 Content Drawer 中选中 Texture2D,
然后在新建的 Mat 编辑器中按住T
再鼠标右键,就可以直接导入,最后在 SK 编辑器里为其设置 Material。 - 在 Skeletal 编辑器中为角色添加 socket 用来放置枪支,可以开启 preview asset on socket 以及 preview with animation
- 在 Character 中 Spawn and Attach
- 导入材质:包括以 SK 导入枪支,还有 Texture、Specular 和 Normal Map,Tip 是先在 Content Drawer 中选中 Texture2D,
-
Firing Our AK-47 Weapon
- Make Bullet: BP_BulletBase, 使用 Mesh 和 ProjectileMovement Comp,设置 InitialSpeed MaxSpeed GravityScale,我自定义了 CollisionComp 作为 root
- Weapon Can Fire: SK Weapon 的 Muzzle Socket 和 Fire event,商店里那个 Package 已经做好了 Muzzle,同理需要 preview
note. 一个非常小的细节:如果 Weapon SM 的朝向是 Y,那么后续所有的都是 Y,因为 socket 上 preview 的是 Static Mesh 的坐标系
就不能修改 SK 的朝向,然后子弹的朝向也要和 Y 一致 - 设置 Input Map,通过 Spawn Bullet 开火
-
Crouching With Animations: 通过新的状态来实现下蹲,记得要在 CharacterMovementComponent 里开启下蹲 和使用 Crouch 和 UnCrouch 函数
- 新建 BlendSpace
- InputMapping 蹲下,注意设置 MaxSpeed
- 状态机,让我惊讶的是,Crouch 进入两个状态的 Condition 是一样的
-
Sprinting With Animations: 类似上面,就是按下 Shift 可以进入 Sprint 状态
-
Using Control Rotation: 角色看天空和看地面的时候脊椎有变化
- 三个 Modify Bones,并且取消 ExposeAsPin Translation、Scale\Alpha,RotationMode 为 AddToExisting,bone 分别为 spine, spine01, spine02,
- 用一个 AimRotation 变量,此处有点儿像黑魔法,没有看懂,只能理解只需要修改 Pitch
-
Fully Automatic Rifle:按着左键后就开枪,松起就不开枪,同时枪支有弹药数量限制,显示子弹数量
- note. 其实就是一个「一直按着」的状态,按下左键后设置 IsFiring 为 True,表示正在按着左键,然后调用 Fire 后,delay 看是否还按着,如果是的话就接着 Fire
- 子弹数量限制就 Weapon 新增变量,并且每次 Fire 前检查是否有子弹
- 显示子弹数量用 Text 以及 CreateBinding
-
Aiming Down Sights: 瞄准视角切换
- 加上 SK Weapon,切记不要 attach 到 CharacterMesh 上,否则,Character 会 idle 枪支也会跟着抖动
Mesh HiddenInGame,attach 一个 Camera 给它,muzzle 上,调整位置,设置 AutoActivate False - 按下鼠标右键就 aim,就是设置 activate 和 deactivate,
note. 遇到一个 idle 枪支抖动的问题,我的方法是把摄像头往前,不要看到准星 - 算了,直接 copy 一个 Camera,搞不定抖动的问题
- 加上 SK Weapon,切记不要 attach 到 CharacterMesh 上,否则,Character 会 idle 枪支也会跟着抖动
-
Spawn Muzzle Flash: 下载一个 Effect 特效在 MarketPlace,在 Weapon SK 中新建并调整 Muzzle preview 这个 effect,
生成的时候 spawn emitter 就好 -
Fix:Crouch 和 SPrint 冲突了,需要按下 Sprint 按键的时候,检查是否处于 Crouch,if so,break out of this state,按下 Crouch 同理
-
Setting Up Ammo & Reloading:
首先知道什么是 Clip,区别于 Magazine:Why use clips when you can use magazines? - YouTube- 枪支的逻辑:
CarriedAmmo:士兵携带的总弹药
CurAmmo:枪支弹匣(Magazine)内的弹药
Reload 前,弹匣里子弹满,Reload 后,不管 CurAmmo 中是否有剩余,就都丢掉,使用 CarriedAmmon 中 Clip 的数量补充为 CurAmmo - 我自己是制作了一个 Inventory Comp 来完成这个功能
- 枪支的逻辑:
-
Conservative Ammo Reloading: 和 17P 的逻辑不同,尽量让 CurAmmo 为一个 Clip 的数量,
if CurAmmo >= Clip: print("Full and Don't need Reload") return AmmoDiff = Clip - CurAmmo if CarriedAmmo >= AmmoDiff: CarriedAmmo -= AmmoDiff CurAmmo += AmmoDiff else: CarriedAmmo = 0 CurAmmo += CarriedAmmo
-
Weapon Reload Animation: 也是为 BP_Character 定义状态,一旦 Reload,ABP 中的状态就是一个 Reload Sequence,
何时从 Reload 转移?在 BP_Character Blueprint 中过 0.2s 后改变状态变量
note. 这是一种原始的做法,我的方法是 Play Montage,Rewrite UpperBody,在 Sequence 中也可以增加 Notify,并且放到 Montage 后还是会生效
PlayMontage 结束后再 setReloading?
这个状态变量 -
Firing System Fix: Fire 前检查不能是 Sprint 或者 Reload
-
Aiming With A Crosshair: 在 UI 里加 居中的 image,同时对 image 的 visibility 属性 binding 到 Character 的是否 ADS 状态上
-
Dynamic Spread Crosshair: 用了一个别人做的 asset,可以配置 crosshair 的长度、厚度,Jump
-
Picking Up Ammo:角色可以通过 Overlap 到弹匣补充身上的子弹
-
Setting Up AI & Bullet Damage:
- new Character BP,named SimpleAI,add SK and ABP for it
- AI Overlap 后转 BulletBase,对自身的 Health 造成损害,可以通过 Comp 实现,Health <= 0 后 destroy self
-
Enemy AI Following/Chasing:
- NavMeshBounds 拖进去,按
P
显示 - Add PawnSensingComp For AI, OnSeePawnEvent, PrintString 检查下,有个 AIMoveTo 的 api,像一个 Util,把 Pawn 移动到对应的 actor
- NavMeshBounds 拖进去,按
-
Aiming Fixes:
- ADS event 内,若处于 Reload,就啥都不做
- ADS Event 内,若处于 Sprint,立刻停止 Sprint 再 ADS
- Sprint 时候 Deactivate ADS
Tip: 可以 Collapse 成 Function - Aim 的时候不要显示那个可以伸缩的 Crosshair,就是复制粘贴四份 Binding 函数
-
SmarterAI:
- AI 被子弹击中,播放中弹粒子效果,
- 被子弹击中,MoveTo 开枪对象,这里我用 Spawn Bullet 时候的 Instigator 实现
-
Random AI Roaming Setup:
- 当 AI 看不见角色的时候需要随机移动,所以设置变量表示是否看到了角色,在 SeePawn 里
- EventTick 时候检查这个变量,用 GetRandomReachablePointInRadius 随机选点的 api
-
Smooth Gun Movement: 角色左右移动的时候,枪支会抖动,带动准星的视角一起抖动,解决方法是在移动的状态里 LayeredBlendPerBone
-
Simple Objective System: UI 上显示目标文字,当 Character 和某个 Collision 重合的时候,就改变这个目标文字
-
Setting Up Our HUD:血条、血量、当前弹匣子弹,所有子弹
- 修改 ProgressBar,三个属性,BackgroundImage、FillImage 以及 Appearance,每个 Image 都是可以设置 Tint 属性的
- Image 组件要让大小和原来的图片大小一致,这样 Place 好 Health 和 Armor 的 Icon
-
Enemy Killfeed: 一个 Kill Who 的 Popup,然后下移并且 Fade,下移的目的是为了不挡住后面 Popup 的
- 专门做一个 Widget,Layout
- 动画以 background 为主,两个 Text 的 keyframe 参数直接复制粘贴
- bug: 如果在 delay 前面 CreateWidget,Widget 就会有延时,如果在 Delay 后就不会
-
Creating a Minimap: 在用户头上放一个摄像机,附带一个 拍摄的画面渲染到一个 texture 上
- Camera 下面有一个 RenderComp2D,新建一个 Texture 作为 RenderTarget
- 新建 Material,Domain 是 UserInterface,选 RenderTarget,将此 Material 作为 UI 中 Image 的 brush
-
Match Timer: 屏幕左上角有倒计时
- 摆 UI
- GameMode 中新建变量表示游戏开始了多久,用 delay 1s 做更新的逻辑
- 有个 api 叫做 GetGameMode
- 要 PauseGame 可以直接用这个名称的 API
-
Hit Marker With Animation:击中敌人,屏幕中心出现 Marker,并 Fade,Jump
-
Starting Weapon Switching (HUD): UI 显示 按下 1 切换到武器 1,按下 2 切换到武器 2
- InputAction 改变 Character 内一个 选中 Weapon 的 Num
- 绑定到 UI 上文字和 两张图片的 visibility
文字没法直接 MakeText
-
M4A1 Weapon Pickup
- 拖入 FBX 文件和 Diffuse 图片,用 DiffuseTexture 做一个 Material,并且设置 Metallic, Roughness 和 EmissiveColor
- 新建 Actor,M4A1Pickup, ControlledCharacter 内设置变量 是否 Pickup 了 M4A1,if so,才可以进行切换
我使用一个 Pickup base Class 做这件事,包括 Collision 和 SkeletonMesh - 设置 Collision,碰撞后 set character 的 bPickup 并且 destroy
-
Weapon Pickup Message:当和 M4A1 发生碰撞后,就 Popup 一个 Message,显示 pickup 了,用动画结合 Opacity 实现
-
M4A1 Weapon Setup:添加 M4A1 Actor,
- 作为 WeaponBase 的子类,可以新加一个 SK 放置 AK47,然后对其二者的大小和扳机位置
- 对 SK add socket,并且 Preview Flash 和 Bullet,调整 Transform
-
Changing Weapon Visibility:通过在 Character 内保存两个 Weapon Actor 的引用,通过切换武器的时候 SetVisibility
- EventBeginPlay Spawn 并且保存 SecondaryWeapon,SetVisibility
- InputAction 时候 SetVisibility
-
Displaying Secondary Ammo:在 UI 中显示当前枪支内还剩下多少子弹以及有多少用于填充
我觉得要用数组来管理 -
Consuming & Reloading M4A1 Ammo: 所有直接使用 Weapon01 的地方,都要修改,我的方法是做一个 GetEquippedWeapon 的地方。
-
Weapon Line Tracing:修改 Fire 函数,使用摄像机的 Location 和 Rotation 的 ForwardVector 做 LineTrace
我的方法是用 ConvertScreenLocationToWorldSpace -
Ray Tracing While Aiming Down Sight: 通过 Aiming 变量判断应该使用哪一个相机
-
Damaging With Line Trace: Jump, 这蓝图连的也太糟了。
-
Crouch Height Adjustment: 在 CharacterMovementComponent 中有一个 CanCrouch,并且蓝图本身也可以调用 Crouch 和 UnCrouch 函数
-
Zombie body setup: 导入 Zombie 资源,写 ABP,使用 Zombie SK
-
Zombie Ragdoll Death Physics: 对死亡后的 NPC 的 Mesh SetSimulatePhysics
-
Zombie Attack Animation: 为 ZombieCharacter 设置状态切换,攻击是通过 在 Mesh 上加 Socket 并 attach 一个 Collision,重叠就掉血
-
Grenade Spawning:按下
G
,在 Character 前面一点的位置生成一个 Grenade,并且在 BeginPlay 的时候给它一个 Impulse,3s 后 SpawnEmitter -
Grenade Damage:添加 Collision,可以 GetOverlappingActors,Destroy 之
-
Starting The Game Level:使用 Landscape 编辑器让平地周围隆起小丘,中间一个坑,使用 Paint 画出一条小路,最后效果如下:
- 下载 OpenWorldDemoCollection
- 新建 Level,删除地板,打开 Landscape,新建一个 Landscape,可以探索 Sculpt Tool,有 Sculpt、Erase 和 Erosion 等
在一个地点 Sculpt 会导致出现山峰,Erase 可以削减山峰,Erosion 可以使地面凹陷
- 新建 Material,并且用 Grass 和 Mud Blend,将此 Material 作为 landscape 的 Mat
在 Landscape 的 DetailsPanel 中选择它作为 LandscapeMaterial - 在 Paint Tool 中,Create 刷材质。
-
Decorating our level:
- FoliageMode,把要的 Tree 的 Mesh 拖进去,然后可以调节 Density 等参数,Paint
- 新建 Plane,水面只是一块有 水的 Material 的平面,
- 使用 PostprocessingVolume,调整 Scale 使之 Cover 整个 Level,
- 把 Zombie 放进去,记得要 NavMesh 再 Build Path
-
Smoke Grenades:在 Spawn Grenade 的时候换一种 Emitter,并且记得 Destroy
-
Grenade Count:可以捡起 Grenade,并计数,Jump