现在的位置: 首页 > 综合 > 正文

关于游戏中的调度器

2013年05月27日 ⁄ 综合 ⁄ 共 2137字 ⁄ 字号 评论关闭

1.1 调度游戏中的事件

  一个调度其可以有效帮助以下游戏技术的实现,他们包括物理仿真,人物运动,碰撞检测,游戏中的人工智能,渲染。在所有这些技术中有一个关键问题就是时间。在不同的时间里,当数百个不同的物体和过程都需要更新时,这些仿真技术的很多种东西变得非常复杂。

  调度器的重要能力在于它能够动态地增加和删除物体,这可以使新物体很平滑地加入到游戏里面去,和其他游戏里面的物体一起参加仿真,然后在不需要的时候从调度里面把它删除。

  1.1.1 调度器的组成

  调度器的基本组件包括任务管理器,事件管理器和时钟。通过这些组件调度器就能生成基于时间或者基于帧的事件,然后调用相应的事件处理器。

  任务管理器处理任务的注册和组织。每个任务都有一个包含了一个管理器可以调用的回调函数的接口。任务管理器维护了一个任务列表,其中包含了每一个任务的调度信息---例如开始时间,执行频率,持续时间,优先级和其他的属性。他也可能包含一个用户数据的指针或者性能统计信息。

  事件管理器是调度器的核心部分。任务管理器里面的每一个任务都定义了一个或多个其需要处理的事件。一个事件指的是一个任务需要执行的时间。事件管理器的责任就是要产生必须的事件以执行相应的任务。

  真实时间与虚拟时间:一个真实时间的调度在概念上是很简单的—时间管理器不停地进行循环,察看一个真实的时间时钟,每当目标到达的时候它就会触发一个事件。一个虚拟事件的调度器会把时间分成帧。任务在帧之间以批处理的方式进行,在虚拟时间里运行,然后在每帧渲染出来的时候与真实的时间进行同步。

  时钟组件是用来跟踪真实时间,当前的仿真时间和帧数的。时间管理器负责事件的排序和产生。在某些情况下,多个任务可能会设置在同一个时间运行。有较高优先级的先执行。如果优先级相等或者系统没有优先级就轮流执行。我们经常需要动态地更改一个已经注册的任务属性,这可能会牵涉到更改他的优先级,周期,持续时间或者要求在它找到还没有结束的时候就将它删除。为了能更新任务的属性,我们必须使用一个外部的方法来找到他,可以使用一个唯一的注册ID来标志一个任务。

  1.1.2 一个简单的调度器

  调度器的设计主要集中在两个组件上面-----调度器引擎本身和ITask插件接口。要使调度器运行起来,必须要有一个调用它的程序。在一个非图形里面的程序里面,这要求把它放在一个循环里面然后执行顺序里面然后执行就可以。While (running) scheduler.ExecuteFrame();有两种方法把调度器集成在一个消息驱动的图形界面上。第一种方法是修改消息循环来处理消息和调用调度器。这是一个最容易想到的方法,但是有个缺点,就是当窗口大小来来改变的时候调度器会停止工作。第二种方法是创建一个Windows时钟,利用时钟消息来调用调度器。由于时钟消息并不会被窗口的拖动打断,调度器就可以在后台就接续运行了。

  仿真:调度器可以用来驱动仿真系统。为了实现动画和碰撞检测功能,大多数仿真引擎都将时间分成独立的小片。

  我的理解:

  调度器相当于一个总的控制器或者叫协调器,我称之为manager。游戏中的大部分事情可以看为一个任务,例如渲染, 碰撞检测,都可以看做一个任务,任务的执行是靠事件来驱动的,而事件在这里3种,即:基于时间的事件,基于帧的事件,以及渲染事件。

  基于时间的事件:该事件的发生依靠时间来判断,即当什么时间到了后,某个事件就会发生。

  基于帧的事件:该事件的发生依靠帧来判断,即当游戏运行到指定帧后,某个事件就被触发。

  渲染事件:该事件很特殊,它在每一帧里都会发生,因为每一帧游戏都会渲染。

  在游戏里,我们把一切任务注册到调度器里,然后任务的执行就完全交给了调度器。调度器会负责什么时候执行哪个任务,什么时候删除哪个任务,等等之类。

  而为了实现让调度器能够处理一切注册进来的任务,调度器就加进了一个任务管理器。事实上,在这里也就利用了语言的一个特性---定义了一个抽象类:

  class ITask

  {

  public:

  virtual void Execute(int id, int time, void * arg) = 0;

  };

  要使用调度器,就需要从这个类继承下来处理所有任务。然后调度器通过ITask指针来操作所有任务。

  看一下调度器的组成:

  调度器为了知道什么时候基于时间的事件发生了,什么时候基于帧的时间发生了,于是就加进了一个时钟组件(Clock)。因为事件有三类,当然与之对应的任务也有三类,于是调度器就维护了三类任务,分别为渲染任务(其对应的事件只有一个),基于时间的任务,基于帧的任务。因此基于时间和帧的任务不止一个,因此在这里又维护了两个链表。

  游戏每一帧都会调用调度器的ExecuteFrame,该函数首先更新时间,然后执行所有会在本帧里执行的基于时间的任务和基于帧的任务,以及渲染任务。

  注意所有存储在链表中的任务实际上都是按照其执行的先后来排列的,这样程序每次都只需要从表头元素进行处理。当程序执行完一次任务后,程序会检查该任务是否还需要进行执行,是的话就把它重新插入链表,否则就标记为删除。

  关于使用虚拟时间的好处,还不是很明白。

抱歉!评论已关闭.