学习资料:https://zhuanlan.zhihu.com/p/22813908
新手上路,还需指正
GamePlay架构(一)Actor和Component
万物皆UObject:元数据、反射生成、GC垃圾回收、序列化、编辑器可见,Class Default Object
Actor继承了UObject,有了Replication(网络复制),Tick,Spawn(生成)。其实Actor只是一个概念上的东西,通过添加组件使得其显形在UE4世界中。
正因为如此,Actor并不会自带Transform,浪费内存空间,UE4的Bool都会用位域去尽可能利用空间。UE4将Transform封装进了SceneComponent。大部分的Actor的(GET/SET)ActorLocation是一个封装好的函数,内部实现是先去寻找RootComponent.
Actor通过组装Component武装自己的功能
Actor和Actor之间的父子关系本质是通过SceneComponent实现的(AttachToActor/AttachToComponent)
总结一下:Actor其实就是灵体,通过添加组件使得现形在世界场景中,Actor和Actor之间的父子关系是通过AttachToActor,Actor和组件是直接AttachToComponent,最终都是通过RootComponent去实现父子关系。
GamePlay架构(二)Level和World
一个World支持一个PersistentLevel,对应有多个Level,Level自带一个ALevelScriptActor(关卡蓝图),可以在关卡中通过名字获取Actor。一个Level对于一个AWorldSetting(书记官)。一个Level有多个Actors。
通过关卡流,可以构建庞大的开放世界。但每个Level有唯一的GameMode,通过关卡流加载关卡后,以哪个关卡为主?其实是以当前关卡为主
总结:Level是Actor的容器,同时划分了World
GamePlay架构(三)WorldContext,GameInstance,Engine
WorldContext负责控制World之间的切换,也负责level的切换
有一个平行世界的概念,GAME模式和编辑器模式
思考:为何Level的切换信息不放在World里?
因为UE有一个逻辑,一个World只有一个PersistentLevel(见上篇),而当我们OpenLevel一个PersistentLevel的时候,实际上引擎做的是先释放掉当前的World,然后再创建个新的World。所以如果我们把下一个Level的信息放在当前的World中,就不得不在释放当前World前又拷贝回来一遍了。
而LoadStreamLevel的时候,就只是在当前的World中载入对象了,所以其实就没有这个限制了。
GameInstance是比WorldContext更高一级的类,不管Level如何切换,都不影响里面的数据
Engine比Instance更高一级
StandLone Game:使用UGameEngine创建出唯一的World,因此直接用GameInstance保存
而对于编辑器来说,EditorWorld其实只是用来预览,所以并不拥有OwningGameInstance,而PlayWorld里的OwningGameInstance才是间接保存了GameInstance。因为UE4只允许一个World运行,因此GameInstance也是唯一的
总结:Object→Actor+Component→Level→World→WorldContext→GameInstance→Engine。至此,完成了所有“数据”,接下来讨论具体的游戏逻辑如何布局
GamePlay架构(四)Pawn
Actor是由Component组成的一个个功能,真正的Component应该是可以无痛处的转移到另一个项目,不参杂任何游戏逻辑
因为UE4是FPS起家,一个Pawn是棋子士兵,(和战场上的士兵类似)其能够移动,有物理碰撞,可以响应输入和处理逻辑(Controller),Pawn拥有能力。Pawn就是木偶,Controller是提线人
GamePlay架构(五)Controller
MVC
controller的作用是为游戏编写逻辑,controller和player的关系是1:1;controller调用pawn的方法。如果是一些Pawn本身固有的能力逻辑,如前进后退、播放动画、碰撞检测之类的就完全可以在Pawn内实现;而对于一些可替换的逻辑,或者智能决策的,就应该归Controller管辖。即个性的功能逻辑可以写在Pawn里,通性的可以写在Controller里
controller有两个子类PlayerController(响应玩家输入,处理游戏逻辑)和AIController(用行为树)
controller有场景组件,意义在于ReSpawn的时候可以指定为它的位置,一般是概念上的位置
Info有两个子类是PlayerState(保存玩家在Level的状态,往往Controller里会有这个变量,但AI不会有)和GameState
UE这里其实想说的是,这些更新位置的操作还是让我来为你管理吧,我真的担心你会用错搞出什么乱子来。顺便再说些题外话,对于PlayerController来说,因为玩家需要一个视角来观察世界,所以常常PlayerController常常会扛着个摄像机出现(蓝图里没有,但是会运行时生成PlayerCameraManager和CameraActor),所以就算没有角色可供操作,玩家也依然希望是可以视角漫游观察整个世界的(试试看把默认Level里的PlayerStart删掉后运行看看)。这个时候PlayerController常常会默认创建出一个ASpectatorPawn或者DefaultPawn(根据GameMode里配置),我们虽然看不见Pawn,但依然可以观察世界,靠得就是跟Controller关联的旋转和摄像机。
其实是Actor(AController)对Actor(APawn)的控制
GamePlay架构(六)PlayerController和AIController
一个时刻只有一个PlayerController能够响应
M(PlayerState) V(View,UI) C(Controller)
GamePlay架构(七)GameMode和GameState
GameMode关注于当前世界的游戏规则,负责调度这个世界。
level里的gamemode是强制的,如果为空,就用project setting里的gamemode
GamePlay架构(八)Player
UPlayer有一个PC,UPlayer保存在Instance里
ULocalPlayer继承自UPlayer,关联着输出,多了个ViewPort
玩家是游戏数据的发起者,那么网络远端把数据传过来的也是玩家,所以UNetConnect也继承自UPlayer
GamePlay架构(九)GameInstance
切换关卡,生成Player
SaveGame存档
GamePlay架构(十)总结
https://zhuanlan.zhihu.com/p/24170697/
UE4,万物皆Actor,Actor再通过Component组装功能。Actor又通过UChildActorComponent实现Actor之间的父子嵌套
众多的Actor构成level,一个个level组成了World:
而World之间的切换,UE用了一个WorldContext来保存切换的过程信息。玩家在切换PersistentLevel的时候,实际上就相当于切换了一个World因为UE有一个逻辑,一个World只有一个PersistentLevel(见上篇),而当我们OpenLevel一个PersistentLevel的时候,实际上引擎做的是先释放掉当前的World,然后再创建个新的World。
GamePlay框架使用了MVC架构,Pawn视图,PlayerState数据,PlayerController控制器。
一个游戏由游戏规则(GameMode)和游戏状态组成(GameState);玩家是Pawn,通过PlayerConroller控制,通过PlayerCameraManager观察世界,PlayerState记录玩家数据,HUD显示数据内容;NPC由AIConroller去控制,和玩家进行互动。