unity开发排坑指南(关于面向对象)
发布日期:2022-02-26 14:49:40 浏览次数:44 分类:技术文章

本文共 2762 字,大约阅读时间需要 9 分钟。

前言

好久没更了,最近因为疫情的原因在家办公,每天疲(xiao)于(lv)沟(di)通(xia),需要大量的时间来弥补工作,所以最近写博客的时间也不多。希望疫情早点结束,也希望大家身体健康。

今天写这篇的原因呢,也是在最近自己在游戏开发的过程中,发现了开发的一些固有观念和思维定势给我们的开发埋了很大的坑,所以想分享一下,希望大家在开发的过程中避免这样的问题,因为优化实在是太难了,还是找时间重构吧。

说说这个坑

其实面向对象这个词,大家应该听的都很烦了,一个入门编程的人甚至没入门的人应该都知道面向对象是个什么东西,我们今天踩的这个坑其实就是由于我们在开发过程中因为封装一个类而忽略了整体,进而导致的性能以及功能上存在的一些问题。这也提醒我们,在用面向对象的思想去开发的过程中,不能把一个类或者一个对象从他所在的整体里完全抽离,这样虽然开发和维护会比较省事儿,但是性能上的压力,以及在功能上的缺陷是不可避免的。由于这个坑是在开发过程中发现的,所以需要简单介绍下我们工作不同阶段的思路和想法,这样方便大家对比自身看,自己在开发过程中是不是也中招了。

情况简介

我是一个棋牌游戏开发者,棋牌游戏的主要战场肯定是在牌桌,所以牌桌的设计在整个棋牌游戏中应该是很重要的一个环节,而一个游戏好不好玩看设计,能不能玩是看性能的,一个性能上不过关的游戏必定会消亡,这也是我今天赶出这篇博客的原因,希望大家都能避开深坑。

我在加入项目组的时候,设计是已经做完了的,所以这块我是没有参与这块工作的。当时牌桌的设计是这样,我们对每一个玩家的预设进行了封装,由于自己和其他人显示的信息不同,我们分别封装了两个类,但他们都继承同一个父类,这样保证我们在调用公共方法的时候不必考虑是自己还是其他人。具体的前后端交互逻辑和前端修改逻辑我就不细说了,大致就是在每个玩家预设里监听消息,检测id是否是自己,如果是就修改,不是就忽略。这是最早的架构,庆幸的是我们很快就发现了这个架构的不合理性,他在玩家预设里面监听消息,会导致这个脚本增量式拓展,后续可维护性极差。
流程图1
经讨论我们决定对牌桌的逻辑进行重构,也是我当时主要负责的部分(本来想着是优化,但是看了看1000+的玩家脚本,我决定重构),这次重构是比较成功的一次重构,至少在拓展性方面比第一版要好的多。

重构

其实在这一次重构的时候,我们也并没有注意到我们今天说到的这个坑,我们还是延续我们最开始的面向对象的思想,对每个玩家进行封装,唯一不同的是我们在牌桌上加入了一些管理脚本,可以称之为管理器。管理器的作用其实是帮助我们协调整体流程和简化流程的作用。同样是上面的这个流程,我们在新的逻辑里在牌桌内去监听消息,然后去调用相应的管理器,根据数据来选择预设进行处理。

流程图2
这个流程是相对比较简单的了,这也是我们沿用至今的一套架构,稳定性和拓展性上是没有任何问题的。

坑,他来了

至于这个坑是怎么发现的呢,我们在优化时发现牌桌内的drawcall一直降不下去,而且现在来说牌桌的东西越来越多,这个玩家预设也是越来越大,部分低端机用户会出现卡顿的情况,这引起了我们的注意,我们发现每个玩家的预设在绘制的时候都没法进行合批,理论上来说同一个图集里面的资源要尽量合批,但是由于我们对玩家进行了整体预设封装,合批就是不现实的了。我开始反思自己的开发思路,想了很久很久,也没搞清楚到底需要怎么去优化,或者说重构。也是在最近才终于想到了一个开发思路,虽然还不曾实践,但我个人感觉是可以操作的。

怎么解决呢?(仅是设想,未实现)

我最近考虑了一种策略,可以实现对于合批的需要以及模块的拓展性和可维护性,由于需要合批,同一个图集里的资源我们是尽量让他合批的,所以我考虑是否可以把每个玩家预设里的东西拆解出来,比如每个玩家的头像做到一起,比如叫avatars,玩家的金币都放在一起,叫chips。但是这种处理方式是完全打破了我们面向对象的思维方式,因为在面向对象的思路里,玩家就是一个类,其他的头像,金币,姓名等都是他的属性,是不可以拆分的。没错,理论上是这样,但是因为出现了问题,我们试图逆向思维看看到底可不可行。

其实,我们换个角度想想,我们可以把玩家现有的每个属性都看作一个对象,而之前的玩家对象,我们把它看成一个对象的集合,这样思考问题,我们就会发现,其实把头像放到一起,金币放到一起,这些看似荒谬的理论,其实也有自己的合理性。

结构图1

结构图2
流程图
如上面图所示,先看前面两个图的结构,我们现在的问题出了合批的问题,还有就是牌桌脚本拓展性的问题,就在前不久牌桌脚本也成功破千,其实仔细看了下内容,大多是在监听消息和定义变量,冗余的代码并不是很多,这种情况下我们就要考虑是不是架构的问题,一旦一个脚本破千,后续的维护工作势必会很难,所以我们不怕脚本数量多,而是担心脚本的拓展性不好,为了保证项目的可拓展性,我考虑可以引入layer的概念,其实就是一个绝对层级的概念,所谓绝对层级就是layer1和layer2的层级关系是固定的,里面的元素的层级关系也是固定的,因为在牌桌中,很多层级就是固定的,比如牌桌一定是最下层,礼物一定在头像上层等等。而每个layer里面又管理很多对象,如果更细分的话,我们可以把一类对象封装一个管理器,而layer就是管理器的集合,这样逻辑会更清楚。当然在每个layer里面也会有相对层级,这些层级是可以相互调整的。
再看最后的流程图,我们之前是在牌桌里监听协议,然后调用相应模块,这样导致desktop脚本里太多监听以及定义,导致这个脚本没法维护。我们现在的架构是desktop仅仅管理诸多layer,里面只会有layer的加载和初始化方法的调用。具体的监听逻辑放到每个layer里面,同时在layer里对消息进行处理,并找到对应的模块进行调用处理。
这样的处理方式,可以保证我们后续在拓展时更加方便,耦合度不高移植时也更加简单。

总结

其实说来说去,并不是面向对象的问题,而是我们在遇到问题的时候本能的面向对象思维的问题,也可能是我本身能力不够,在一开始开发时,本能地将每个玩家看成一个对象,导致设计出了问题,进而开发和维护难度也增加。

一个良好的设计,比写100个功能更重要。我的同事,也是我工作中的导师,他作为一个主程和资深程序开发,曾经跟我说过,每次接到一个需求,他总会花更多的时间去思考,而不是去盲目的打代码,思路理顺了,代码自然也就出来了。的确,程序员从来都不仅仅是一个敲代码的工作,更多的成长在于代码的设计和架构。一段漂亮的代码,一个拓展简单、维护方便的系统,一个性能优化十分到位的项目,也算是我当前给自己定的小目标了。

最后希望大家能有所收获,有所启发,同时如果大家有更好的思路,也欢迎大家能一起分享讨论。

转载地址:https://blog.csdn.net/qq_31820549/article/details/104390306 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:信管学习笔记(3)
下一篇:Cocos Creator 相关配置

发表评论

最新留言

做的很好,不错不错
[***.243.131.199]2024年03月29日 23时19分25秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章

洛谷 P1048 采药【01背包】 2019-04-28
LeetCode C++ 面试题 02.06. Palindrome Linked List LCCI【Linked List/Recursion】简单 2019-04-28
LeetCode C++ 面试题 04.02. Minimum Height Tree LCCI【Tree/Recursion】简单 2019-04-28
LeetCode C++ 289. Game of Life【Array】中等 2019-04-28
LeetCode C++ 200. Number of Islands【DFS/BFS】中等 2019-04-28
LeetCode C++ 287. Find the Duplicate Number【Array/Two Pointers】中等 2019-04-28
LeetCode C++ 1370. Increasing Decreasing String【String/Hash Table】简单 2019-04-28
LeetCode C++ 面试题 04.03. List of Depth LCCI【Tree/BFS】中等 2019-04-28
LeetCode C++ 剑指 Offer 12. 矩阵中的路径【DFS】中等 2019-04-28
LeetCode C++ 142. Linked List Cycle II【Linked List/Two Pointers】中等 2019-04-28
LeetCode C++ 剑指 Offer 32 - II. 从上到下打印二叉树 II【Tree/BFS】简单 2019-04-28
LeetCode C++ 102. 二叉树的层序遍历【Tree/BFS】中等 2019-04-28
LeetCode C++ 372. Super Pow【Math】中等 2019-04-28
LeetCode C++ 面试题 04.05. Legal Binary Search Tree LCCI【Binary Search Tree/DFS】中等 2019-04-28
LeetCode C++ 面试题 04.06. Successor LCCI【Binary Search Tree/DFS】中等 2019-04-28
LeetCode C++ 48. Rotate Image【Array】中等 2019-04-28
LeetCode C++ 剑指 Offer 64. 求1+2+…+n【Bit Manipulation】中等 2019-04-28
LeetCode C++ 1426. Counting Elements【Array/Hash Table】简单 2019-04-28
LeetCode C++ 1469. Find All The Lonely Nodes【Tree/DFS/BFS】简单 2019-04-28
LeetCode C++ 1688. Count of Matches in Tournament【Math】简单 2019-04-28