《刀塔传奇》主程张振新:客户端技术经验分享

10 月 28 日,在 Cocos 开发者大会(秋季)上,《刀塔传奇》主程序张振新分享了他的客户端技术经验。他们选择了Cocos2d-x,结合小巧高效的Lua,提高了开发效率,帮助手游面临的复杂版本更新问题。

《刀塔传奇》是张振新和他的团队从PC游戏转向手游开发的第一款产品。在演讲中,他还提到了很多初创手游团队可能会遇到的一些“坑”。

比如:《刀塔传奇是win环境下开发的,早期经常遇到的一个问题就是开发环境下的功能还可以,但是在发布版本的时候,手机出现各种错误,而且我不知道错误在哪里。” 他们使用错误消息框来解决这个调试问题。

再比如一个数据,他们每更新一次全包,当天的DAU就会减少10%左右。优化方案是制作低清版本解决包大小问题,或者在游戏中集成下载器,在游戏启动过程中完成完整包的下载和安装。

更多张振新的分享,请看以下内容。

大家好,我叫张振新,来自莉莉丝科技。我从2009年开始负责PC游戏的开发,2013年很荣幸加入莉莉丝科技,参与了《刀塔传奇》的开发。一款手机游戏,很开心。和大家分享一下《刀塔传奇》客户端的技术经验。

首先介绍一下《刀塔传奇》的一个客户端的整体结构。我们在Cocos2d-x下安装了一个数据模块,一个是数据模块,一个是网络模块,最大的一个是业务逻辑模块,分为UI系统、战斗系统和事件系统,游戏大家戏剧是通过这些模块构建的。

除了 Cocos2dx,我们的模块是在 C++ 层面实现的,而 Dota Legends 主要是通过 Lua 实现的。

开发效率

Lua 是一种低成本语言,旨在嵌入应用程序中,为应用程序提供灵活的扩展和定制能力。它具有以下特点。

一是轻量级,正式版只提供精简的核心和最脚本化的库,所以Lua体积小,启动快,非常适合嵌入到其他程序中。

其次是可扩展性,Lua不像其他很多“大而全”的语言,包括很多功能,比如网络通信、图形界面等等。但是 Lua 提供了非常易于使用的扩展接口和机制:这些是由宿主语言(通常是 C 或 C++)提供的。

第三,使用起来非常方便,简单灵活,学习方便,操作灵活,对用户有帮助。

Lua是一种解释型语言,没有编译和链接二进制代码的过程,由lua虚拟机直接解释和执行。开发时,修改代码后,直接运行程序看看效果。因此可以节省大量的开发时间,而且项目越大,效果越明显。

Dota Legend 支持在游戏过程中动态加载 lua 代码,节省重新打开程序的时间。我们在脚本层封装了一个 reload 函数,用于在游戏运行时重新加载 lua 代码。实现原理也很简单:先删除之前需要的模块,然后再重新require所有的模块,相当于重新初始化脚本代码。这为我们节省了关闭和重新打开程序的时间。刀塔传奇的UI场景使用cocos2dx的场景场景,在脚本层维护了一个场景栈。重载时先弹出原场景,再使用重载的代码和资源重新创建新场景,恢复原场景。场景,节省重复操作的时间。

新版本更新

除了开发效率之外,Lua 在 Dota Legend 中给我们带来的好处就是版本更新。版本更新是手游推出后的一个非常重要的环节,直接影响到手游产品的质量。手游版的更新比PC版更差,因为iOS AppStore的审核周期可长可短,而Android则更差,渠道多,版本分发流程复杂。Dota Legend在立项阶段就考虑到了版本更新的问题,整个程序的框架也为版本更新带来了一些便利。

1) 在线热更新

loadstring:函数很简单,效果很明显。之前做PC游戏的时候,每次发现在线紧急情况,我采取的方法都是紧急停止服务器,然后发布更新包,测试,发布。周期长,体验差。无数在线紧急问题就这样被修复了

程序启动时,刀塔传奇会从服务器拉下一段代码,然后执行这段代码。由于这个逻辑发生在加载lua代码之后,版本中的lua代码可以被拉取的代码段覆盖。

注意:只有全局域下可以访问的函数才能被loadstring覆盖。在编程方面,一个模块应该尽可能少地暴露接口,但从在线修补程序的角度来看,它应该可以从全局域访问。为了尽可能多的功能,这样可以提高在线热代码的覆盖率。

2)游戏内更新

这是Dota Legends的常规更新方式,一般用于更新主要功能版本。更新内容包括lua脚本、美术资源、策划资源。由于代码都是lua脚本刀塔2找不到指定模块,更新完成后可以重新初始化整个lua状态,这样更新包在游戏运行时生效。

注意:更新包不能用于解决更新过程之前的bug;当一个版本连续有几个比较大的更新包时,可以考虑给频道非强制更新完整包刀塔2找不到指定模块,这样有利于新玩家的提升。兑换率。

3)完整包更新

这是我们最不愿意采用的版本更新方式,有时也不得不使用。比如《刀塔传奇》刚上线的时候,跨服蓝牙栈功能。

我将在这里与您分享所有数据。每次更新完整包,当天的日活跃量大概会不到10万。它的优化方案是做一个低清版本来解决包大小的问题。另一种是准备继承游戏中一个下载器的功能,即在游戏启动过程中,类似于游戏更新包,在游戏启动过程中完成。下载并安装完整包以优化更新体验。

调试问题

Lua开发者经常会遇到开发效率极快的感觉,一下子搞定,但维护起来特别困难。一个重要的原因是调试困难,bug 难以发现。

除了常规的打印和日志方法,Dota Legend 还使用 lua 的调试库开发了一个 DEBUG 函数,在 IDE 环境中起到断点的作用。该函数会首先输出当前调用堆栈,并进入循环等待用户输入调试语句。在这种状态下,可以通过调试库提供的getinfo等函数查看指定级别栈下的local和upvalues。

错误消息框

我们还做了一个游戏错误框,类似于这个渲染。我们的《刀塔传奇》是在PC下开发的。版本发布的时候,一跑到安卓和iOS就出现了各种问题,不知道哪里出了问题。然后去各个地方查,很麻烦。然后我们在手机版上加了这样一个盒子。当出现错误时,可以以这种形式显示。当我们发布内部测试版时,我们会发送这样一个框,以便轻松定位移动版。他们提交的bug报告可以让我们找到错误的信息,对于bug来说非常方便。

快照:解决 Lua 脚本内存泄漏

使用lua开发也会有内存泄漏的问题。Snapshot 可以对当前 Lua State 进行完整的快照,并记录对象的引用关系。我们可以在不同的手机上拍两张 Lua 的快照,然后对比两次。可以找出新增加的内存所在的位置。

它在第一行做一个快照,分配一个Test1,然后他做了一个S2,这是我们定位的内部斜坡的一个很好的解决方案。特别是对于不容易发现的Lua问题,可以通过这种方式添加来解决。

性能问题

用 C 或 C++ 实现与性能瓶颈相关的代码。

为了解决draglist渲染效率低的问题,增加了可见性裁剪功能,将不在显示区域的item项设置为不可见。第一个版本是通过lua导出接口,在lua层实现切割,确实减少了draw call的调用,但是增加了cpu的负担,主要是产生了大量导出接口的lua调用。第二个版本直接实现了节点级别的自动裁剪,在访问函数中判断是否可见。

预先分配表的大小以减少重新散列。

当我们给表分配一个新的键值时,如果数组和哈希表都满了,就会触发一次rehash。重新散列的成本很高。首先,将在内存中分配一个 rehash。新长度的数组,然后再次对所有记录进行哈希处理,将原始记录转移到新数组中。

Lua的创新不同于其他脚本实现方式。它采用内部实例化的实现方式。所有的字符串在 Lua 中只保存一份,并且都是通过引用保存的。这里可以想一想,Lua保存的方式可以保存在一个表中。

© 版权声明
THE END
喜欢就支持一下吧
点赞0
分享
评论 抢沙发

请登录后发表评论