108. 2024-06-06周总结

  1. 新项目上线
  2. 新项目总结

好久没总结了[唉]

1. 新项目上线

经过几个月的加班加点,我们的小游戏项目终于上线了。[撒花]

2. 新项目总结

因为加班加点赶出来的产品,整个项目到现在存在一些问题,一些由于历史原因比较难改,一些随着进度压力下降正在抽时间慢慢改,总结下这个项目一些问题。

引擎问题

第一次用Cocos Creator开发了整个项目,整个使用体验跟Unity比还是有比较大的差距,包括编辑内运行游戏的体验,ts语言的体验,调试体验,性能优化体验等等。

另外因为输出的是js代码,会导致容易被纂改,我们还花了比较大的工作量在防外挂上。

当然Cocos Creator跟Unity比还是有一定的好处,比如小游戏启动速度会快点(没有编译wasm过程),内存占用会低点,ts热更处理也会比C#热更方便点(但是小程序也热更不了,Unity倒是有方案可以热更),另外自带的auto-atlas这个功能还是挺好用的。

总体来说还是弊大于利吧,后面新项目即使第一平台是微信也会选择Unity的团结引擎了(当然,基于这个项目的换皮项目大概率是不会换引擎了)

怪物动画问题

之前怪物为了省资源和合图集(减少dc),采用了一种小图+Animation的方案,带来的问题就是怪物子节点比较多,几百个怪物同屏幕的时候就会卡顿明显。

后续优化的时候分别测试了Spine/序列帧的方案,发现序列帧性能最高,比之前的方案(600只怪物下)性能可以提升2倍多,spine也能1倍多,但是spine合不来批。

所以以后做这种割草类游戏,怪物优先使用序列帧来做动画,如果怪物种类很多,则可以不考虑合批,直接用spine来减少资源,性能也是会比较好的。

战斗问题

  • 逻辑和表现没有分离

    战斗框架设计的时候没有考虑这点,后面引申出了一些问题:

    1. 可能导致手机卡和手机不卡带来的结果可能不一致。
    2. 想做防外挂不太好做,如果表现和逻辑完全分离,可以通过记录用户操作日志,重现下这场战斗能否打过即可
    3. 如果被子弹引用着的怪物(用来获取怪物位置)死亡,可能导致问题,都要特殊处理。
    4. 一些靠Animation驱动的地方会被打断去播其他动作,导致回调永远不执行

    衍生思考不光这种类型游戏战斗,其他类型的战斗也都应该都这么做,不能通过表现的来驱动战斗,比如说碰撞盒碰撞,Animation回调等。

    另外碰撞盒这边有一个优化,我们子弹,怪物都有一个内存池,之前的内存池做法是设置active来做,导致的问题就是collider的onEnable和onDisaable的addShape和removeShape成为性能大头。优化方案就是进内存池的对象不禁用active,而移到一个很远的地方去做。

  • 没有使用状态机控制怪物

    虽然是割草类游戏,但是怪物状态还是有点多,比如移动,攻击,治疗,各个控制器一起去控制怪物动作导致冲突带来了一些问题。所有不论是角色和怪物都应该有一套状态机来控制,通过配表来控制各个状态之间的冲突关系和优先级关系。

    场中所有对象都应该是个实体,实体各自内部有相应的状态机

  • 错误使用了全属性来做技能

    一开始为了方便所有特性都是靠属性来做的,带来的问题就是属性很多,同时比如一个功能,命中概率增加buff,可能就是要三个属性buff_id, buff_time, buff_pct,如果命中可以增加多个buff那属性就会成倍增加。

    一种改进方案就是用被动触发机制做,命中的行为会触发一系列被动,这个被动其中一种就是加Buff,这样就可以通过一个行为链来做实现命中之后的行为。可以是加多个buff,也可以是再释放一次其他技能等等。

    同时被动行为可以复用,加buff的行为可以复用到杀死某个怪物或者命中某个状态的怪物等其他地方。

  • 技能搜索目标问题

    每个技能的搜索目标方式有不同也有相同的地方,之前为了快速开发同时为了不影响已经开发好的技能,使用的方案是每个技能一种搜索方式。

    所有技能做完之后发现其实可以用一种简单的过滤器模式来做,把每种过滤方式实现成各自的过滤类,每个技能通过组合这些过滤器实现搜索功能。

  • 技能宿主对象没有抽象

    一开始我们技能只能玩家释放,所以整个释放技能过程获取的都是从玩家身上获取属性,后续随着需求增加了宠物也能释放技能,这种技能释放过程中应该从宠物身上获取属性,这就导致改起来很麻烦。

    技能身上应该是一个宿主的抽象(可以统一走实体),方便后续其他人释放技能。

战斗防外挂思考

对于这种纯客户端战斗,并且还是js的游戏还是比较容易出现外挂的,我们游戏上线之后很快就出现了外挂。

目前我们防外挂的手段只能依据简单的数据判定,而且还只能依赖客户端上报这些数据(比较容易篡改),服务端唯一能做的就是检测整个战斗时长是否有异常。所以整个封的过程不是特别准确,而且还是有玩家可以绕过这些检测。

设想的一种比较好的封外挂就是上面提到的重现战斗看是否能打过,这得记录玩家的操作,我们的操作比较少,一个是三选一技能强化项,另外一个就是释放主动技能,为了防止客户端篡改,三选一的选项也可以服务端生成。但因为我们一开始逻辑和表现没有分离,导致这种方案做起来成本太大了。

各种环境及渠道管理

因为数据还行,所以上线除了业务开发以外,还要对接各种渠道,包括抖音小程序,android(转移包、投放包、主播包),ios,快手小程序以及还有对应的马甲包,联运包等等。同时还有各种环境,包括开发环境,外网测试环境,审核环境,正式环境,各种环境的各个渠道可能都需要打包和控制。

为了应对这些需求,我总结下经验,主要有两点吧:

  • 自动打包

    指的是有自动打包脚本以及类似jenkins管理这些打包的后台。这个一定要有,不然那么多环境加渠道出版本要搞死人。

  • 统一从远端拉取配置

    进入游戏第一步就是从远端拉取配置,可以传递一些参数上去,比如平台,系统,app版本,游戏版本,渠道id等等,远端根据这些参数下发对应的配置,比如服务器地址,强更功能,热更地址,还有一些开关等等。

    有了这个功能之后我们就可以很方便的去控制各种渠道的各种环境的表现了,例如可以直接指定某个渠道的正式环境该热更了。另外我们还可以加白名单的功能,发布之前控制某些渠道的正式环境,在某个ip下直接可以热更,或者直接连到外网测试服来测兼容性等等。