本文介绍了游戏循环概述、游戏计时机制以及由游戏循环驱动的主要子系统,并结合演示游戏《无限之路》,具体说明了各个子系统以及子系统的开发方法。为了便于理解,《通往无尽之路》作为一款大型游戏架构的方式,本游戏demo可以
《无尽之路》几乎没有美术资源,主要是使用Unity模型构建的,所以它看起来非常简陋。它有一个有很多球的地形,玩家在地面上移动,几个AI跟着玩家,在这个例子中有超过2000个球。游戏的规则也比较简单:玩家通过鼠标操作来移动游戏角色,游戏角色移动到哪里,上面的球就会掉下来,玩家打到哪里就会滴血。
游戏循环概述
节目及其特点
常见的程序,第一类是面向任务的被动处理软件,没有任务可以休眠。一般包括:命令行程序、GUI(图形用户界面)程序、操作系统服务、WEB服务、MIS(管理信息)系统、ERP(企业资源规划)系统和进销存系统以及财务软件等财务系统。这类软件的主要特点是被动处理。只有在请求函数时才需要处理它。如果你不提出请求,你不需要做任何事情,就会自动入睡。
第二类是自动控制软件。在给定设定值后,该软件通过调节器进行自动调节。这种软件的特点是主动处理,不能停止运行。特别是在工业控制中,如果软件停止运行,站点就会失控。
游戏程序与上述两类软件相比,有其独特之处。游戏最典型的特点是,游戏世界是对现实世界的模拟。即使是桌游这样的小游戏,也是对现实世界的模拟。大型游戏,尤其是rpg(角色扮演游戏)这是对真实世界的完全模拟。
更重要的是,游戏世界与现实世界相似,即使玩家什么都不做,游戏世界也能正常运行。正因为如此,游戏需要积极地运行,而游戏世界的运行驱动也会发生在游戏循环中。
游戏循环的角色和层次
在游戏产业中,游戏的循环被称为“心跳”。“游戏循环有三个目的:
①驱动世界。每当游戏“跳动”时,它就会对世界产生推动作用。
②驱动游戏中npc的行为。npc是非玩家角色,通常由AI控制。npc的行为也是由循环驱动的。
游戏世界与玩家之间的互动。
本文中提到的游戏循环主要是指程序循环的前端。它通常分为两个层次:游戏引擎循环和游戏逻辑循环,游戏循环通常是由底层引擎循环驱动的。
游戏引擎循环
实际上,3D引擎的循环模拟了真实世界的相机。在游戏世界中,你有一个带着相机观察世界的摄影师。所以,在3D引擎中,摄像机是主要概念。
此外,3D引擎的概念包括可移动物体、场景和ui。可移动的物体:可以在游戏中移动的物体。场景,通常是游戏中的游戏世界,游戏中所有的动作和事件都发生在这里。UI通常涉及玩家与游戏之间的互动。
在游戏中,引擎通常服务于摄像机、UI系统、场景管理以及游戏对象管理和支持。
主发动机循环由上面所示的五个步骤组成。
首先是游戏逻辑的循环。引擎中有一部分是驱动游戏逻辑循环的。npc、可移动物体位置的改变、属性的改变等等都是游戏循环的一部分。
其次,游戏逻辑控制可移动物体的位置、旋转、缩放等变化,并进行Transform更新。当游戏场景更加复杂时,可以使用空间数据结构管理,涉及二进制或八叉树,以及相应的数据结构更新。
然后,整个场景的渲染。在渲染场景之后,渲染UI,因为UI通常会覆盖在场景的表面。
最后,还有双缓冲开关。通常情况下,当游戏的一帧被显示出来时,另一帧就会被写入后台,这样显示就会更加连贯。
左边是一个构建的场景,带有相机的视角和各种对象的显示,右边是屏幕渲染,实际上是所有3D对象在相机的近裁剪表面上的投影。
游戏逻辑循环是由引擎驱动的。游戏逻辑通常是在引擎循环的每一帧的开始或结束时进行处理,而不是嵌入到中间。因为引擎的整个渲染需要被锁定,所以在中间所做的更改可能会影响到整个引擎管道。
游戏逻辑循环用于驱动游戏的子系统,包括网络、场景、资源、游戏对象、AI、战斗、故事和UI。游戏对象是玩家和npc, AI用于控制npc,战斗包括关于技能和增益的信息。
游戏逻辑循环通常有两种风格:
一种是事件驱动或消息驱动,类似于Windows的消息循环机制,具有更多的设计特性。
另一个是静态循环,它依次调用每个子系统的Tick(或“心跳”),这很简单,也很容易理解。一般来说,游戏逻辑循环更可能是静态循环。因为游戏开发是一个合作的过程,和游戏开发时间较长,一般可能有人员流动,所以保持简单和可以理解的,更重要的是对于开发人员来说,为产品质量和简单,比使用这种技术可能更大。然而,事件或消息驱动方法的编写和调试更为复杂,而且代码不容易理解,因此不经常使用。
游戏循环分类
游戏循环主要有三种类型。第一个是Windows消息主循环。它在Windows上曾经是GUI系统(图形用户界面,图形用户界面),Windows是消息驱动的,所以游戏循环是在Windows消息的主要循环。每当Windows程序处理完消息队列中的消息时,它就会触发游戏。
第二种类型是游戏框架的回调。这是OGRE,旧Windows系统上的开源引擎。它在每帧的开始和结束有两个回调,游戏引擎的循环在渲染当前场景之间。这两个回调函数实际上实现了游戏的逻辑,它们修改了游戏世界的逻辑层中的某些内容,这是由引擎表示的。
第三类是Unity3d。这使得游戏开发变得非常简单,开发者无需再去理解底线。它使用c#脚本,可以解释为事件驱动。但Unity3d实际上是直接在c++的MonoBehaviour中调用Awake、Start、Update和FixedUpdate,而不是使用c#的事件机制。
如上所述,游戏循环驱动子系统。每个子系统基本上都有一个“心跳”,循环是对每个子系统的“心跳”的依次调用。如果循环是静态的,您可以直观地看到各种子系统的调用。如果它是事件驱动的,您将看到一个已注册的列表循环Tick,它的变化取决于子系统是否工作。例如,一些系统可能不需要工作,因此可以从列表中删除它,并且Tick更少,因此性能可能会稍微好一些。但对于一般游戏中的系统,特别是rpg,大多数子系统都是有效的,所以这两种方法之间的差异非常小。
游戏时间
游戏中通常有两种时间类型:绝对时间和相对时间。对于相对的时间,你可以使用单位y的Time scale属性。该属性默认为1,当设置为2、3或4时,游戏进展更快,即相对时间。游戏是由心跳驱动的,这本质上是基于时间的游戏世界的模拟。相对时间是指以一定速度经过的时间,时间经过后会产生相应的影响。因此,如果这个时间的速度加快,整个演算的速度就会加快。
相对时间涉及两个概念,一个是加速重放,也称重放。例如,在《King》中,如果玩家断开了两名玩家之间的连接,当他们重新连接时,他们会看到所有的行动都加速了,这就像重玩游戏一样。
另一种是与重现相对应的反演,也称为时间反演。这种反转最终可以像视频录制一样无缝完成,每一帧都被记录下来,这样你就可以随意跳转到任何一帧。然而,反演的实现是困难的,因为可能不是每一帧都可以被记录下来,而是结合基于最近快照的演算来记录一个周期快照。
如果一个NPC在帧中被杀死,它就会消失。如果你没有记录任何内容,然后根据玩家的行动往回看,在这一帧中你只知道一个NPC消失了,但当你恢复时,你会发现关于这个NPC的许多信息都消失了。这是因为游戏的每一帧都是之前历史信息的积累,除非NPC信息被写下来,否则就很难逆转。因此,能够回到过去的游戏通常都具有先进的技术。
第二,帧时间和实时时间。帧时间是指每一帧的时间,循环中有各种滴答声,通常情况下,你从一帧到下一帧的时间,这样你就知道过去了多少时间。游戏中的逻辑通常以每帧的步长来计算。所以,在游戏中,大量帧时间就足够了。
实际的时间,实际的时间,任何需要花费的时间,都是到这一点的实际花费的时间。在游戏逻辑Tick中有许多移动如果每秒需要30帧,那么每帧最多只需要30ms。但是,如果操作时间为50ms,并且所有的操作都在此帧内完成,则帧率将降低一半。因此,一些逻辑处理需要划分为框架。要将一个操作划分为几个帧,您需要实时。在计算过程中,要注意已经实时计算了多长时间,根据预期的时间片,如果时间到了,操作就会暂停一段时间,到下一帧继续处理。
最后,有高精度的时间,这是依赖于硬件。可实现微秒级的高精度时间,主要用于性能分析。例如,当游戏运行非常慢时,你需要分析原因,你需要测量每个功能的成本,你需要微秒时间。
然后是垂直同步的概念。垂直同步在早期的阴极射线管显示器上使用,但在现代硬件上却不使用。如上图所示,CRT显示器使用电子枪快速逐行扫描屏幕上的点。如果你在电子枪扫描期间更新帧缓存,你会看到不同的颜色。例如,在上图中,如果缓存被更新了,底部的三行是蓝色的,那么最后一帧是紫色的,下一帧是蓝色的,那么这一帧就会被撕裂。为了解决这个问题,它通常会等待每个屏幕被扫描,然后把枪放回屏幕的左上角,这个过程被称为垂直同步。为了让每次更新都是垂直同步,游戏的帧率应该是监视器帧率的倍数,这样屏幕就不会成为问题。
虽然没有这样的概念或要求,但它仍然在引擎中可用。例如,Unity提供30和60帧的标准帧率,而手机游戏目前的标准帧率是30帧/秒。
游戏循环的并行性
我们都假设游戏循环是由渲染循环驱动的,但除了单线程之外,还有一种多线程的方式来运行它。只要你在渲染循环中做一个驱动,比如在启动游戏逻辑后,游戏循环戒指可以自己继续戴下去。
这种方法的典型优点是将逻辑与引擎的线程分离。如果游戏循环与呈现循环分离,那么游戏循环便不会影响玩家所看到的帧率,所以我们便能够更轻松地创造出具有更稳定帧率的游戏。此时,游戏循环和引擎可以在不同的帧率下工作。例如,游戏需要60帧每秒的视觉效果,但游戏逻辑只需要10帧每秒,这可以并行分离。
这种操作方式的缺点是它很复杂。游戏的逻辑实际上是向引擎提供数据。对于并行模式,逻辑和引擎是在两个线程中,这不可避免地会涉及到两个线程之间的数据同步,这将更加麻烦。
异步编程中最复杂的一个方面是锁定。假设一个锁被添加到整个Tick中,它将导致不必要的等待和低性能。如果两个线程的每个Tick都是一个锁,就相当于序列化,并且没有多线程。然而,锁的粒度相对较小,并且只锁那些真正需要同步的线程将会在逻辑上非常复杂,这很容易导致死锁或活动锁。
锁的使用因软件而异。在游戏软件中,由于游戏逻辑的复杂性,锁很少被使用。传统软件倾向于特定于行业,并具有相应的业务模型。这种业务模型趋向于稳定,并且从长期来看,业务中的许多东西,除了直接面向用户的部分,在后期阶段变更较少。所以你可以使用更复杂的技术,因为它不会被应用到所有地方。然而,对于游戏软件来说,所有的游戏设计都是由规划需求驱动的。规划和制作游戏有自己的追求,希望与众不同,这与软件工程的稳定性是完全冲突的。另外,游戏逻辑非常复杂,可以随时改变,所以通常不使用锁定游戏逻辑。本文不涉及游戏循环的知识网络和报文处理、逻辑数据管理、用户输入和操作、作战系统和热点更新。