C++经验
经验1.1 NULL安全
服务器是一个非常关注数据安全的东西,尤其对于C/C++
程序来说,空指针和野指针是一个非常重要且一定要避免的问题,与之同级的还有数组越界的问题。
经验1.2 善用unsigned long long int
当年OI的经验告诉我们u64
可以存很大的数,于是这个类型就用来解决了算法结束后答案过大导致int32溢出的问题,但没想到,这个东西用来当“身份证”有奇效!
我们的游戏中涉及大量的元素,包括但不限于大量的NPC,大量的同时在线的玩家,大量的建筑,大量的怪物……各种各样不同的元素。游戏将所有的实体都使用一个u64
作为唯一标记,通过哈希函数和哈希表实现rid查万物。
经验1.3 善用Enum
枚举类型Enum在C++中经常当做一个常数来使用,在一些只给程序员使用的配置项,比如数组长度,请求超时,资源类型,使用这个可以避开污染Define、全局变量的问题。
经验1.4 善用do while()
do while()
在学习过程中作为一种很冷门的循环语法其实一直不被重视,但是实际工作中,do while()在优化代码可读性方面有奇效。
借助它一定运行一次的特性我们可以用do while(0)
使代码只运行一次,在其中使用break来中断循环,以此起到一种平替inline function
的用法。
借助它一定运行一次的特性我们可以用do while(1)
使代码反复运行,在其中使用break来中断循环,以此起到一种“输入数据”不合规则重新输入的功能。
工程经验
经验2.1 性能和易读性
在开发过程当中,我们经常会遇到代码性能和代码可读性的矛盾,在某些时候我们只能选择其一,所以在代码性能和代码可读性的平衡中,我们有以下原则:
“当一件事情没有达到性能瓶颈时,就不要去考虑性能问题,易读性优先。”
经验2.2 代码生成
在很多情况下,我们的需求其实都是简单且重复的,比如对某一些变量的增删改查,那么对于这种类型的需求,其实我们可以采用代码生成的方式来解决,通过简单的配置,直接生成对应的代码,能够极大地提高开发效率。
代码生成的原理很简单,通过一段配置,结合相应模板就能生成一份代码,但是代码生成的细节很值得深入研究,例如怎么设计模板代码,怎么保证系统的可扩展性等。
经验2.3 表达式系统
所谓表达式系统,其实是一种脚本语言,它由关键字-函数-公式
构成。在游戏开发中,策划决定表达式内容,程序决定表达式运行时机,以及表达式中支持的函数和公式。之所以不使用Lua转而构建一个自己的系统,是因为Lua本身的运行速度较慢,而表达式则是基于C++实现的一种方便调用C++函数的脚本,因此运行速度要比Lua快的多,常用在PVP战斗系统当中。
经验2.4 版本管理
其实版本管理不论对于什么体量的团队来说都是一个难题,我们没法用程序去强行限制人的行为,所以团队内部需要形成一些常见的规矩,所有人都“原则上”遵守这个规矩办事,就能避免很多问题了。
对于一个多人协作的项目而言,开发阶段一定会涉及到多端的协同问题,客户端有客户端的需求,策划有策划的想法,服务器又有服务器的实现方式,因此我们需要形成一些内部统一的工作流,并准备好备用方案,每个部门各自维护自己的版本,版本分为若干类,并交由版本管理工具来管理。
在我实习的单位中,服务器组使用的是下面这样的版本管理方案:
服务器组的代码使用SVN进行维护,没有像Git那么多的分支,因为服务器组人数不多,所以大家都在同一个分支上进行开发,因为很多代码都基于代码生成工具生成,因此svn上只保存了生成代码用到的配置文件,而生成的代码并不由svn管理,合并冲突也会少很多。
我们把svn的版本称为开发版,不对客户端和策划公开;但是对于客户端和策划来说他们又需要私服进行开发调试,那么服务器组就需要每过一段时间发布一个新版本,这个更新频率非常快,有时版本构建机都需要排队使用,依次构建,构建成功并上传至版本库之后,面向客户端和策划开放,他们可以将自己的私服更新到这个版本,用来开发调试。
相应的客户端也是一样,组内的版本管理工具保证开发者的代码处于最新状态,构建开发版并公开给其他组保证其他组能第一时间进行开发调试。
岗位分工
经验3.1 程序?策划!
在之前做的一大堆小项目里,临近DDL的时候基本只有我一个程序在忙活,印象最深的得是《雪域守梦人》那个扫雷塔防,怪物不多,防御塔不多,但是每种怪物和防御塔全都要我一个程序来做,对于有不同逻辑的防御塔则需要各种重构,最后IDE里面全是各种各样的C#文件,各种面向对象的操作,修好这个防御塔,另一个防御塔就出问题。
往往在你最崩溃的时候就能看到一个连外卖都不能帮忙拿的策划在群里晒明日方舟抽卡和肉鸽战绩。你心一横,有必要把他拉过来产出内容了。你想了个办法,得让他来干活了,于是有一个很好用的东西出现了,那就是Lua,在项目中集成了Lua解释器,这样大量的逻辑都可以通过编写Lua代码,将逻辑交给Lua解释器来实现。一方面Lua作为一种解释性语言并不需要提前编译,这使得它能作为一种资产文件实现热更新,也就意味着我们可以使用Lua代码实现游戏逻辑的热更新;另一方面,Lua代码的语法非常简单,很适合策划等有一些逻辑能力,又没怎么接触过编程的用户使用。
举个例子,游戏中角色的技能逻辑其实很多是由策划操刀开发的,因为策划最懂自己的需求,而我们只需要为策划准备一个施展身手的舞台,即合格的Lua解释器。
当然Lua也会有性能问题,为了解决这个问题我们做了表达式系统(见 经验2.3 )使其实现C++性能。