搬运工的项目总结-1 吐槽篇

前言:此项目是一个基于quick-cocos2dx 2.2.5版本的卡牌游戏,设定的目标为《放开那三国》,目前项目已经进入了收尾阶段,这也是我参与第一个lua项目。

本系列文章主要是概括项目的进展,总共分以下几个部分。

第一部分是对这个项目遇到的坑进行总(tu)结(cao)经验教训(大部分是自己挖的坑)

 

第二部分是在项目中一些小的工具类的分享。

 

第三部分是分享回合制卡牌战斗部分(我负责的部分)的经验,主要是如何使用解释器模式对战斗技能部分进行解耦,策划可以自己通过配置文件,实现丰富多样的技能效果(这部分我个人认为是比较炫的部分)。

 

最后一部分是本人的一个设想,能够让策划独立配置界面,并且通过解释器模式实现界面间最基本的跳转功能。

 

1.      编码风格:没看《代码大全》同学赶紧去看看吧,至少有感情朗读看得懂的章节,然后背诵一遍看不懂的章节。这样至少不至于同一个类文件里写俩名字相同的闭包函数啊!我负责的部分也没写多好,但至少没写成书里的反例啊。

代码的首要任务是供人阅读,要是顺便能运行,那就更好了。

 

2.MVC!MVC! MVC!

之前公司的老大是个中科院的博士,写的那代码跟从教科书上抄的似的,所以使用这种基本的MVC框架是很自然的要求。所以这次的项目没有使用MVC模式刚开始有一种很新奇的感觉,觉得终于不用写model类了,好开心啊!

然后就掉到一个个黑洞洞的大坑里了。

 

以我负责的“角色列表”为例来阐述第一个缺陷。

 

小坑1:数据更新

这个功能是一个tableview,用来显示玩家所拥有的全部英雄角色。同时玩家可以通过点击cell上的按钮来打开一个“强化界面“和”进阶界面“,并且在对应的界面进行对应的操作,返回后可以更新列表。

 

如果是以往的MVC框架将如何实现呢?首先,游戏会在登陆时从服务器获取玩家所有的角色数据,保存在单例model里。当玩家打开“角色列表”时,tableview通知各个cell当前的id,然后每个cell通过这个id独立去model类里取出相应的角色数据额即可。打开“强化界面”和“进阶界面”的操作也很类似,只需要cell把当前的index传入即可。

具体结构如下图。


箭头部分代表的是index,这两个界面只要独立从model类里面取得数据即可。而不使用MVC的则是这个样子的。



这里面的线代表的是数据

但强化界面和进阶界面不仅需要读取数据,同时有可能改变数据,所以实际上每个箭头都应该是双向的。

这时候MVC第一个小优点就显示出来了,改变数据之后,上层的cell无需接受从强化界面和进阶界面回传的数据,只需要重新取一下update一下数据即可。

这个界面非常简单,使用MVC优势没那么明显,如果是更多层级,更加复杂的界面的话,MVC的优势会更加明显。

当然了,有些同学采用了一些更粗暴的手段来更新数据,每次关闭界面时,都会重新请求一次列表数据……然后服务器那边的同学就疯了。

 

大坑2:海量的数据请求

目前项目的数据请求非常华丽,服务器同学叫苦不迭。因为前端不缓存数据,所以每次打开角色列表都会请求一次数据。如果玩家拥有100个角色,每次的请求流量将会是KB级别的大,而且封测数据显示,还有一些付费玩家使用的是2G网络,这尼玛能不能花点钱投资个3G网啊。

更有意思的是主界面的子界面打开即需要发送两条请求!也就是说,全体玩家只需要同时快速反复切换界面就可以轻松压垮服务器,达到DDOS的效果。很多玩家在删档测试时表示游戏很卡也是这个原因,这是我头一次见到由于前端的设计缺陷导致的网络拥堵问题。

 

如果使用MVC做前端数据缓存的话,第一切换界面时是没有数据请求的,只需要前端自取一下model中的数据即可。像强化/进阶等需要改变数据的操作,只要发送一条几B数据的请求,然后前后台同步数据即可。

 

在这之后,我把我负责的“角色列表”界面改用了MVC模式,分析了一下,还是不能在前端缓存!因为有另外几个同学做的界面同样会对“角色列表”中的数据进行修改….然后,我就只能每次进入界面的时候每次都用model进行请求数据..这里引出了第三个大坑,数据内聚

 

大坑3:数据内聚

角色列表里有一个需求,就是要对所有的角色按照策划设定的规则进行排序,例如主角第一个,蓝色品质第二个,绿色再排在之后等等。所以我在角色列表中排序之后,由于其他人并不使用这个model类,所以他们如果需要实现类似的功能的话,需要在他们请求的位置上再写一次排序算法,当然model类的这个排序算法设计为静态的,他们可以去调用,可是,真的需要每次都调用一次么?抽象封装的也太差了。

 

数据不内聚的第二个问题就数据的概况十分的不方便。例如最近策划有个新需求,希望如果玩家的装备碎片能够合成,则在主界面装备图标上增加提示符号。但是目前的结构实现起来是不可能的。因为前端在打开装备界面之前,根本不知道装备信息。而也不可能因为需要显示一个小红点,就得在主界面请求一次装备列表吧?

而且这类的需求非常之多,不能每次进主界面都玩命对服务器请求数据吧?

 

总结起来,这个项目没有规范大家去使用MVC是非常不明智的选择,目前已经严重到了影响用户体验的地步,而且更糟糕的是,由于前端的写法各式各样,再加上后端根本没有提供点数据更新的接口(主要是没要求提供),短期内不太可能替换为登陆界面加载的MVC结构,所以2G网络的用户,你们就先卡着吧。(反正是服务器的大胡子同学背黑锅)

 

二.界面框架设计

我们目前游戏的主界面类似于下图



基本的结构是:最上方有一个通知栏,最下方有个状态栏

每一个图标都会切换到对应的scene上去,但就是这个scene让人非常疑惑。

 

首先的第一个问题,scene的内容是不能共用的,也就是说不同界面的scene的实现者需要分别去实现上面的通知栏和下面的状态栏,而且下方的状态栏基本功能都一样,这就造成了很多重复的工作量。为了解决这个问题,开发了一个basescene的基本类,然后各个界面去继承它,这个basescene提供一个通知栏,信息栏和状态栏。通过继承可以解决这个问题。

 

但是问题2又出现了,如上面的右图,这个界面,是没有主角的信息的,而且还有更多的界面有不同的需求。然后解决方案是----basesceneExt…一个不带上下栏且可以自定义的类……

 

我长跪不起。Orz

当时我有一种活在HeadFirst反例中的感觉。

 

第二个比较严重的问题是数据共享。上面提到了,此游戏并没用严格使用MVC的解构,其结果就是每个界面在切换的时候,都会重复请求数据(后台同学怒吼得来问我,为什么每次界面切换都有两条数据请求),这简直让人蛋疼菊花紧,上下状态栏重复创建/销毁的问题反而成了小问题。

 

第三个其实是诡异用法的问题。项目中部分界面采用了不安全的popscene和pushscene,造成了一个偶发性的bug就是某些界面会因为逻辑问题将所有scene push出去造成黑屏….Orz,处理这种不应该存在的bug实在不是什么好玩的事.

 

那应该怎么做呢?也许这么做是有道理的(只是我实在没看出来),在我看来,使用scene来实现不同功能界面的切换实在是毫无必要,这种层级的问题node/layer应该就足够可以满足了。


粗略分三层

背景层 (蓝框部分)

功能层(红框部分)

前景层(绿框部分)

 

背景层按照当前功能的需要替换成相应的背景

界面按钮被激活后,功能层移除之前的功能layer,然后将新创建的功能layer加到功能层上

前景层在zorder的最上层,按照不同的需要显示不同的组建,对应的界面激活时,创建即可。

相对于之前scene界面,这套方案有以下几点好处:

1.      前景层中的组件没有创建/销毁的开销,不需要使用的层设置隐藏即可

2.      同一scene下的界面显示数据只需要调用同一个update函数即可,内聚性好。即便在MVC结构下,也是一个很好的实践。

3.      不会出现稀奇古怪的popscene的bug….

 

可能是我才疏学浅,在我看来,普通的UI界面中,使用scene这种重型武器实在是十分之无必要。规划良好的layer/node 完全可以满足一般的日常需要。除非像战斗这种需要大规模变化的场景,其他小功能使用scene实在是毫无必要(但实际上我的战斗部分依然是封装成了一个战斗layer,这样能方便其他程序同学在不同的战斗场景内调用)

 

带入行的老大总是提醒我要看设计良好的代码,但我觉得,偶尔多掉到几个坑里,会更长记性



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值