zoukankan      html  css  js  c++  java
  • 易于增加兼容和测试的游戏客户端代码设计方法

    一、前言

      本文讲的设计方法,不涉及算法、优化、接口讲解等技术介绍。

      该设计方法基于MVC设计模式(主要是抽出控制类),而且本文主要面向游戏开发的一些问题。

      该设计方法样例由python编写,但是实际上都是伪代码,有一点代码基础的问基本看得懂。

      该设计方法由师兄教授,在项目使用过之后,感觉确实不错,特地提取一个方法论出来以记录。

    二、MVC简介

      在游戏开发中,目前用到架构主要分为MVC和ESC架构(这部分如有异议欢迎指正,有其他架构也希望能提出,博主也可以学习)。

      在一个功能复杂的模块中,通常会有很多的UI,MVC将控制和View分离可以的看清整个功能的结构,而且在扩展和代码复用方面有很大的益处(同一个控制类中,方法可以复用;以及添加一个界面或功能加文件就行了)

     1 class Model():
     2     def __init__(self):
     3         self.data = {}
     4 
     5 class Ctrl():
     6     def __init__(self):
     7         self.model = Model()
     8         self.view = View()
     9 
    10 class View():
    11     def __init__(self):
    12         LoadUIFile(sUrl)
    View Code

      其他就不做太详细的介绍了,这里起个抛转引玉的作用,想深入了解的可以自行搜索相关内容。

      本文主要用到的是MVC中的控制类

    三、服务类抽取

      这个是本文的重点,目的是将客户端具体实现逻辑和提供服务的引擎接口/通信协议分离。

      1、为什么要将提供非客户端数据的接口/通信协议(主要是获得服务端数据)封装,这点的作用主要表现在下一块,本块不讲

      2、为什么将引擎提供的方法分离(主要是引擎提供的数据和方法),这是本块的重点。

       首先,假设我们做一个pc游戏,我们逻辑正常怎么写?

    class Ctrl():
        def __init__(self):
            self.model = Model()
            self.view = View()
    
        def Working(self):
            DoPCwork()
            DoNextWork()

      如上所示,DoPCwork应该改成Dowork,因为我们如果只是简单制作一个游戏的话,不会考虑跨平台的问题。但是如果你是一个专业的游戏开发者,或者想要把游戏做大的话,就需要考虑这些了。

      这个时候,如果我们需要兼容安卓平台,或者IOS,那应该怎么做?

     def Working(self):
            # 我随便搜到的cocos的接口
            if cc.sys.isMobile:
                DoMobilework()
            else:
                DoPCwork()
            DoNextWork()

      显然,最简单的修改方式很容易想到。这样的修改方式有个问题:控制逻辑和引擎接口耦合了,所以你必须去修改控制逻辑,那怎么确保你现在的控制逻辑是正确的?需要通过测试。当然这个代码只改了一个if,测试起来方便的很,只测一个条件就够了。但是如果其他地方有细微的小改动呢?为了确保质量,必须全部测一遍!

      控制逻辑是代码的核心,必须保证它的正确性。但是我只是做个兼容,本身逻辑没怎么变,居然就要直接对控制逻辑动手脚是种很危险的行为。因此,我们需要把引擎提供的数据和方法抽取出来。

    class ServiceBase():
        @classmethod
        def Dowork(cls):
            pass
    
    class PCService(ServiceBase):
        @classmethod
        def Dowork(cls):
            DoPCwork()
    
    class MobileService(ServiceBase):
        @classmethod
        def Dowork(cls):
            DoMobilework()
    
    def GetService():
        # 这里用到了python的特性
        # 效果等于返回一个实例
        if cc.sys.isMobile:
            return MobileService
        else:
            return PCService
    
    class Ctrl():
        def __init__(self):
            self.model = Model()
            self.view = View()
    
        def Working(self):
            GetService().Dowork()
            DoNextWork()

      这里用到了设计模式的核心思想——面向接口编程。继承实现具体方法,接口选择用哪种去实现。好处其一,就是易扩展,也是设计模式经常考虑的问题之一,我再换个平台(比如Mac端)再写一个方法继承即可。其二就是,无论你怎么扩展,你的核心逻辑不会变,测试成功一次之后,你的这个逻辑就不会错了,错也一定是引擎相关的问题。

      总结:抽出引擎提供的服务,可增加工程的扩展性,以及发生错误时能更快速准确的定位问题

    四、逻辑类测试

      这一块设计的方法和上一块一致,不过把引擎提供的服务改成非客户端提供的数据服务,用人话说就是引擎提供的数据以及服务端提供的数据。

      举个例子

    class Ctrl():
        def __init__(self):
            self.model = Model()
            self.view = View()
    
        def Working(self):
            Socket.GetServerTime()
            DoNextWork()

      很明显,这个逻辑是:先获得服务器时间,再做其他逻辑。看上去没什么问题,现在这个代码交给你,你来测试这个代码,你应该怎么测试,你发现你又只能去改控制逻辑(捂脸笑哭.jpg)。因为你非客户端数据服务和逻辑又耦合了。那么,把它抽出来!

    class ServiceBase():
        # 服务类基础,这里其实并不需要
        @classmethod
        def GetServerTime(cls):
            pass
    
    class Service(ServiceBase):
        # 提供具体服务
        @classmethod
        def GetServerTime(cls):
            return Socket.GetServerTime()
    
    class TestService(Service):
        @classmethod
        def GetServerTime(cls):
            return "2019/11/11 11:11"
    
    test = True
    def GetService():
        # 这里用到了python的特性
        # 效果等于返回一个实例
        global test
        if test:
            return TestService
        else:
            return Service
    
    class Ctrl():
        def __init__(self):
            self.model = Model()
            self.view = View()
    
        def Working(self):
            GetService().GetServerTime()
            DoNextWork()

      和之前抽引擎服务的方式一样,然后去通过继承 and 重写去伪造客户端本身所不能提供的逻辑,可以在不修改控制逻辑的情况下,完成测试。如上面代码样例,测试环境和正式环境只改一个字段就可以了。

    五、总结

      总结:这个设计方法的是将非客户端的数据服务,以及引擎提供的服务,进行提取,然后通过OOP继承and重写的特性去做逻辑测试和兼容。目的是避免测试和兼容过程中,对控制逻辑作修改,保证安全。

      但是这个设计方法有个问题,就是如果不是和数据相关的引擎方法,即使抽取了,测试方法不变,因为他依赖图形界面,造成了代码的冗余。当然你这里可以说“我不提取也可以啊”,这句话没问题,是可以的,但是如果数据服务非数据服务同时存在的同时,只抽取数据服务影响代码的一致性,抽取非数据服务又会造成代码的冗余,这一部分如何去择一,就要看具体需求了。

      设计模式or方法终究是一种思想,是一种对某种特殊情况的巧妙的思想,但是绝不会适用于任何情况。

      这个设计方法经过一段时间的使用之后,我觉得是个非常不错的设计方法。

      方法很简单,一看就会,但是自己就是没有这种自觉,真正用的时候才会发现其巧妙之处,这就是代码设计的魅力。这种“玩法很简单,但是就是能让你眼前一亮”才是小游戏的乐趣。

  • 相关阅读:
    win7用VMware安装CentOs7搭建Linux环境
    安装Vmware并破解
    Webpack打包
    jquery获取动态table列表的值并组装成数组返回
    微信、钉钉、浏览器上H5页面头部标题(title)的修改,不刷新问题
    帮你彻底搞懂JS中的prototype、__proto__与constructor(图解)
    mysql常用语句
    JQuery日期插件
    系统激活教程及文件
    git教程
  • 原文地址:https://www.cnblogs.com/end-emptiness/p/11845478.html
Copyright © 2011-2022 走看看