口水游戏主循环和组件

游戏主循环和是否是单机游戏并没有多少关系。因为单机游戏只不过是同时拥有服务器和客户端逻辑。那么客户端主循环和服务端主循环的差别也仅仅是设备的差别,客户端需要支持输入设备的读取,屏幕的渲染(Render);服务端需要支持写日志文件,处理来自客户端的远程访问。

对于一个服务端来说,他多的或者说擅长的就是轮询,每秒百亿次的计算可以把所有事情都照顾的妥妥的。那么,在一个无限while循环的帮助下,游戏服务器就出现了。

Game_Init();

while(true)
{
    Parse_Input();
    
    Module.Update();

    Sleep(50); //毫秒
}

Game_Save();

一个游戏,需要不断的读取外部的输入(Input),并用于修正自身的状态(也就是这里的Module.Update)。比较有意思的是,游戏的状态(可以理解为是内存的值,或是变量的值)往往受到两种外力被修改,其一是玩家的操作影响(Parse_Input),其二是系统的反馈或是主动的回应(Module.Update),主动回应包括系统逻辑的更新(每5秒给所有玩家5块钱),ai单位的逻辑。刚好这两部分都包含在这个主循环的功能之中了。

模块和子模块(IGameModule)

一个游戏的处理机制,我习惯于用很多一层一层叠加的模块来表示,每个模块各司其职,一个游戏系统也就呈现出来了。

一个单机游戏和网络游戏最大的区别就是SysPlayer下到底有多少个用户级的实例。系统级的组件(Module)保存着整个游戏世界的状态,而用户级的组件只会保存自己用户的数据(可以理解为存档)。如果是一个单机向的网游(比如卡牌游戏),大部分的服务器逻辑可能都在实现用户级的组件,仅有小部分类似公会管理器,pk管理组件在处理一些玩家之间的交互组件。

组件的粒度

比如背包组件,其中有一些public函数是接口,需要提供给很多组件使用(加一个道具AddItem,加钱AddGold);另一些则是组件的逻辑,只有一个地方需要调到用(整理背包Sort)。如果这两块的函数都杂糅在一起,时间久了往往就无法分辨彼此了。这时候,我们就应该考虑拆分子组件,把系统拆的更细一些。在这里其实是为了区分API方法和逻辑方法,用一个代理对象来专门负责逻辑方法的调用应该就能很好解决。

对象体系

这样下来整个系统对象的关系,基本都是has-a了。在主架构上没有太多的继承关系更容易被理解。这种设计模式下,模块间的关系会变得非常紧耦合,每个模块的API方法被错综复杂的调用,形成网络。我们可以通过类似pub-sub的设计模式来解耦,通过引入消息处理模块,把各个模块直接调用关系转化为孤立的调用者发送消息,被调用者处理消息的模式。

猜你喜欢

转载自blog.csdn.net/narlon/article/details/82981090