游戏设计中的实时计时器是什么?爆发出

虽然游戏的类型多种多样,玩法多种多样,平台也在不断拓展,但是从游戏后台程序来看,还是可以发现很多相似之处。从数学模型上看,任何程序都是状态机,确切的说是图灵机,在读写纸带的同时总是机械地改变状态。

驱动游戏服务器这个“机器”不停运转的因素主要有两个,一个是新闻,一个是时间。消息主要体现在播放器在客户端的操作,以及服务器之间的交互和进程的信号控制。在时间上,一种是固定间隔或时间点的特定逻辑触发,一种是事件动态诱发的延迟逻辑,比如玩家进入某种状态,几秒钟后消失。

时间触发逻辑常见的解决方案是定时器,大家应该都很熟悉。无论是系统层还是应用层都有很多优秀的实现。虽然定时器本质上并不是唯一的解决方案,但是定时器概念的引入可以统一时间控制的实现接口,同时相对于传统的轮询检查方式,具有更加灵活简洁的使用方式。

本文将介绍以往项目中计时器系统的设计和实践,主要结合游戏中的实际概念和需求。

一.概念提取

1.滴答,游戏逻辑框架

从游戏后台的真实需求来看,定时事件的触发一般不需要太高的时间精度。为了满足精度要求,清晰地描述游戏时间的概念,这里提取了Tick的概念,也可以称为游戏逻辑框架。根据以往经验,1秒通常分为20个滴答,即每个滴答需要50毫秒。与真实时间相比,Tick还有一个优点,就是具有连续性和单调性。连续时间系统对于定时器系统本身的实现以及应用层的一些特定需求场景来说是一个极其优秀的特性。

2.计时器节点

定时器节点是对定时器本身属性的描述,包括动作间隔、动作次数、触发时的行为以及支持该行为的相关数据。它与常规计时器的定义没有太大的不同。

值得一提的是,本文中的定时器系统支持使用* * *内存,定时器节点采用定长数据结构。为了合理有效地利用内存,在实际应用中,定时器节点通常只存储相关的标识和索引数据。比如Player定时保存定时器,但实际上它的节点中只保存定时器类型和播放器ID,要保存的数据始终在Player上。也说明了时间触发器本身才是定时器的核心功能,而不是维护各种事件的具体数据状态。

关于节点触发的执行行为,常见的设计是设置一个回调函数指针,这也是纯C中常见的方式,用C++实现的时候可以优雅一点,利用多态特性把行为做成虚函数,不同类型的定时器节点可以实现自己的具体行为。如下所示:

但考虑到定时器节点位于SHM中,函数指针和虚函数可能不够完善,基于SHM重启进程时需要重置。目前项目只是简单的直接根据类型进行显式强制转换,没有使用虚函数。示意图如下:

3.TimerID

像所有ID的含义一样,TimerID是定时器节点的唯一标识。定时器设置成功后,会返回一个TimerID,通过它可以执行删除和更改节点的逻辑。

4.计时器所有者

这里提出了业主的概念。在实际应用中,我们主张每个定时器都有它的所有者,也就是它的对象。比如玩家相关定时器属于玩家对象,怪物相关定时器属于怪物对象。所有者对象拥有其所有计时器节点的ID。当主对象被释放时,它负责释放它所占用的计时器资源,以防止计时器泄漏。另一方面,当定时器被触发时,如果找不到主人,也会主动释放自己。本质上,所有权并不是每个定时器的必要属性,比如一些全局唯一的定时器,但是在实际应用中,我们提倡这种使用方式,可以获得定时器的动态特性,避免资源泄露的问题。