欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  互联网

漫谈手游商业游戏前端框架

程序员文章站 2022-03-16 19:46:52
今天我想聊一聊手游商业游戏前端框架.技术选型这其中有两个关键字,一是"手游", 二是"商业" .说到"手游", 基本上引擎就定位在了 Unity. 在手游开发行业, 除了个别技术实力非常雄厚的公司,和个别另辟蹊径的公司, 绝大部分用的引擎都是 Unity ; 还有一些公司因为旧项目依旧能盈利,拖着商业包袱,坚守着 Cocos2DX ; UE4现在用的越来越多, 但总的来说还是偏少,我不熟悉,因此在这不多加评论.就绝大部分情况来说,Unity还是绝对的主流.说到"商业" , 在需求上"热更新" 几乎是...

今天想聊一聊手游商业游戏前端框架. 既然是前端框架,就先从选哪个引擎开始聊起吧.

技术选型

这其中有两个关键字,一是"手游", 二是"商业" .

说到"手游", 基本上引擎就定位在了 Unity. 在手游开发行业, 除了个别技术实力非常雄厚的公司,和个别另辟蹊径的公司, 绝大部分用的引擎都是 Unity ; 还有一些公司因为旧项目依旧能盈利,拖着商业包袱,坚守着 Cocos2DX ; UE4现在用的越来越多, 但总的来说还是偏少,我不熟悉,因此在这不多加评论.就绝大部分情况来说,Unity还是绝对的主流.

说到"商业" , 在需求上"热更新" 几乎是必须要做的.不仅要做,而且要做到几乎所有游戏内容都可以热更新.这就限制了开发时的技术策略. 资源能热更新肯定是必须的, 代码能热更新几乎就限制了主要逻辑的开发语言必须是 Lua . 当然也可以用其他脚本语言, 我这里就只聊主流. 目前 Lua 绝对是手游开发里面的霸主, 去招聘 App 上搜一搜工作机会一目了然.

引擎定了, 开发语言定了,就要构思一下代码框架了. 咱们这里以 Unity 引擎 + XLua 举例.

在游戏项目中, C# 和 Lua 谁是主力,谁是辅助,必须得在开发最开始就定清楚.有的游戏是 C# 开发业务逻辑, Lua 做一些辅助. 我早年参与过的项目 “雷神2 暗黑世界” 就是这样. 底层提供了足够多的接口, 因为涉及的逻辑比较少, Lua 很多时候甚至是给策划写的. 程序员写一些 逻辑经常变化的 lua 脚本, 而这部分 Lua 脚本也仅仅是处理一些 C# / C++ 关键的回调. 这种开发模式统称为 C#为主, Lua 为辅.

另一种情况目前更流行. 开局一个 DoString() , 引擎的原生语言 C# 直接创建好 Lua 虚拟机, DoString() 一个 Lua 文件, 然后就大撒把了. 这样之后, 所有的游戏主框架, 都需要在 Lua 里做. 原生语言 C# 只提供少量的 桥接调用,把引擎的 Update() 之类的关键函数 调用到 Lua 里. 其他时候, Lua 只有在需要图形声音的地方 , 调用一下 引擎 C# 导出到 Lua 的 API . 这种模式更加灵活, 可以达到 “整个游戏内容全部热更新” 的效果. 我们称之为 Lua 为主, C# 为辅.

第二种情况在目前的商业游戏里明显更流行,也更加灵活. 因此,商业手游前端最稳健的技术选型就定下来了: 引擎选Unity , 开发语言选 Lua, Lua库选择目前最流行的 XLua, 并且要明确 Lua 为主, C# 为辅.

流程框架

接下来说一下写这样一个商业手游, 客户端代码都要经历哪些流程.

launcher

进游戏, 需要一个最小化的 “launcher” .这个 launcher 一定是很少的 C# 代码和 Lua 代码完成的.并且也只拥有最少量的美术资源. 比如只在游戏开头有一个非常简单的公司 LOGO 之类的图片做占位. 这样做是因为这部分将会是游戏唯一一块无法被热更新的部分. 这部分的 美术资源 要少, 因为你将来没有办法替换这一部分的内容. 这部分即使是 Lua 文件 逻辑也不要依赖于游戏逻辑框架, 因为将来游戏逻辑框架是要被热更新的, 可以理解为游戏流程跑到这里, 其他的功能代码还不存在. 因此这里越简单越好.

对应到 Unity 引擎里, 这部分的 Lua 代码和美术资源可以放在 Resources 目录.

热更新

从 launcher 出来,紧接着就要进入到热更新阶段.可能有些游戏会先登录,再进入热更新. 这样做的劣势很明显: 登录界面是无法被热更新的. 将来更换主题, 添加版号等内容时,这样做都是麻烦. 因此, 从 launcher 出来,第一步一定要进入热更新阶段.

这部分的 Lua 代码就可以放到常规开发路径了,因为这部分代码将来是有可能发生改变的.热更新的流程也有一套模板化规律可循:

  1. 检查版本号. 已经最新则进入下一阶段.否则准备热更新
  2. 请求即将更新到的版本的资源列表. 比较待更新资源和本地资源的 md5 值, 确定一个更新 list
  3. 把需要更新的文件下载到本地. 这个目录在 Unity 里通常可以是 Application.persistentPath.因为这个目录是可读写的目录.
  4. 在更新下来的目录里,写入新的版本号.

至此,完成了一套热更新的流程.

对应到 Unity 引擎里面. Resources 目录存放 launcher 步骤相关的最小化的游戏内容. 这一步之前已经提到过. Application.persistentPath 用于存储所有的游戏资源. 同时,这里还要负责维护当前更新到的资源的版本号,以及各个资源文件的md5值.

可以更新的资源, 通常只包含两类: AssetBundle 和 Lua . 在游戏开发阶段,采用 AssetDatabase 的方法来读取本地资源,在编辑器下跑游戏. 在非编辑器模式下, 需要把游戏资源目录下的所有资源打成 AssetBundle 到 Application.persistentPath, 并把 Lua 文件拷贝到该目录, 用加载 AssetBundle 的形式测试正常的游戏读取资源的流程.

这两种资源读取方式的区分 , 对上层逻辑必须是透明的,并且 保证行为是一致的.否则如果需要上层逻辑根据编辑器 和 实际环境不同需要写不同的代码, 就大乱套了.

游戏登录 & SDK

完成了热更新之后,可以走正常的登录流程和SDK 登录流程了. SDK的接入有一点我想说的是, 尽量避免修改过多的 iOS 和 Android 的原生代码, 流程控制能让 Lua 控制的地方,一定要托管给 Lua . 因为无论是 iOS 的 ObjC ,Swift 还是 Android 的 Java,Kotlin 都是无法热更新的. 而SDK 流程如果能做到全部交给 Lua 处理, 就可以享受到 在热更新之后再进入登录流程这样做的好处了.

进入游戏主页

登录成功之后,就可以准备进入游戏了.

游戏逻辑组成部分

手机游戏通常可以分为以下几个组成部分

  • 面向对象
  • 事件机制
  • MVC 处理 界面业务逻辑
  • 核心战斗 ECS
  • timer 机制
  • 网络通信
  • 教学
  • 支付

这些部分可以统一用一个全局的 game 对象来做管理.下面我逐一来说.

面向对象

之所以把面向对象放到第一个来说, 是因为 Lua 并没有很多语言天然支持面向对象. 必须自己把 metatable 进行简单的封装,才能支持面向对象编程. 而确认大家风格统一, 使用同样的面向对象机制,编写一个正确的面向对象逻辑就显得格外重要.

否则大家使用 Lua 时全局变量乱飞, 各处都是简单粗暴的 table 将会使得项目无法维护.

事件机制 & MVC

当今的手游随随便便就可以数出来若干个子系统: 主线关卡, 抽卡,英雄养成, 装备养成, 活动, 签到, 工会联盟, 聊天, 各式玩法副本,教学, 红点等等.每个功能少说也要涉及 五六个界面, 多了10个20个都很正常. 每个子系统还都要存储数据, 数据结构也五花八门. 如果是 3D 游戏那还涉及很多场景. MVC 这种数据表现分离的代码模式几乎是唯一的解决方案.

事件机制是实现观察者模式的基础.如果一个游戏没有事件机制无法想象逻辑要写的有多么混乱.MVC 更无从谈起.

把这些子系统要想有条不紊的安排好, 只能采用 MVC 的思想: Model 用于存储 服务器数据的拷贝, 每次网络通信都要和服务器完全同步; View 负责处理各个界面; 模块和模块之间采用 观察者模式, 大家互相抛事件的方式来处理业务逻辑 .

拥有一套完善的 MVC 框架 和 事件机制 , 才能保证各种各样的子系统能安稳的在游戏里面运行 . Lua 里没有 C# 现成的 delegate , event 的机制, 因此 事件机制需要自己手动实现 .

核心战斗 ECS

介绍 ECS 的文章非常多, 我强烈推荐大家都去了解 ECS 的思想和写法,并且在实际项目中尝试运用因为 ECS 真的能把逻辑写的非常清晰有条理. 我经手的项目在使用 ECS 之前逻辑一团乱码, 使用之后逻辑清晰明了, 易于扩展和维护,我对 ECS 的优势体会颇深.
ECS 的 “System 只负责逻辑没有状态, Component 只有状态没有逻辑” 是 ECS 的精髓.并且当一件事发生需要处理时,在 Component 里做标记,等待某个 System 集中处理 ,这种延时处理的方式, 也是 ECS 重要的一大特点 .
并且用 ECS 写逻辑还可以附带这样的好处: 可以把游戏逻辑写成一个 单独的 Lua 工程, 这个 Lua 工程靠死循环 来 驱动逻辑帧步步向前, 做逻辑处理. 与表现相关的都记录在 Component 数据里.但是这个 Lua逻辑工程不写表现相关的 System ,相当于表现相关的数据不做任何处理.
在 Unity 游戏环境下 ,依然调用这段逻辑代码, 唯一不同的是 驱动逻辑帧步步向前的 是 Unity的 Update() 函数.再给 游戏搭配上 grafic system , sfx system ,这样就使得 战斗逻辑有了表现和声音.
将来无论是做战斗录像, 还是做服务器战斗演算, 还是模拟战斗, 只需要在 裸 Lua 工程里 做一些改动,就可以实现了.
由于逻辑帧是脱离于表现单独可以跑的, 因此在将来加入联网和同步机制时, 逻辑也是现成的.
ECS 在实现中需要注意的是, 经常要根据各种各样的 component 条件,来做 entity 的筛选. 这样的筛选非常频繁,必须充分考虑到效率问题.

总结

剩余的 timer 机制, 网络通信, 教学, 支付等等话题我今天先不提了.我还设想要再聊一聊版本管理, 打包等话题 , 但是那又将是非常大的两个话题. 这些内容都留给以后吧.

本文地址:https://blog.csdn.net/korekara88730/article/details/109268215

相关标签: 工作日志 折腾