网络游戏中的主循环

引言:

网络游戏作为一种复杂度较高的软件,从其设计角度还是有一些共性的,比方说几乎所有的网游都会有一个主循环。由于游戏需要根据输入、游戏内状态的改变来不间断地更新游戏画面,所以游戏的主循环往往看起来像一个“死循环”,那么这个“死循环”是如何工作的?

主循环主要做什么?

1.处理游戏逻辑(输入、AI、事件处理)

2.执行渲染操作(更新游戏画面)

注:对于事件处理,可以归入游戏逻辑一类中,但是在实际中为了处理的方便,往往将事件处理独立出来。(windows环境下)

一个简单的主循环如下:

int WinMain()
{
	bool game_is_running = true;

	while( game_is_running )
	{
		ProcessMessage();	///< 事件处理
		GameMain();			///< 主循环
	}

	return 0;
}

int GameMain()
{
	update_game();   ///< 更新游戏逻辑
	render_game();   ///< 执行渲染操作

	return 0;
}

如果有必要,可以长期在GameMain中循环,而不返回到WinMain中,但是这样做游戏内将不会响应任何输入,因为控制权被GameMain()霸占了,而ProcessMessage()将没有机会执行。正常的情况应当是每次我们执行完一次游戏逻辑和渲染操作后,将程序的控制权交回WinMain,由Windows负责处理输入消息。

游戏速度

上面展示的是最简单的主循环,在真正的设计中,有一个因素不得不考虑,就是游戏的速度。游戏通常以1秒钟画面渲染的帧数(即FPS,Frame per second)来控制游戏速度,一般来说帧数在25帧以上时,肉眼不会察觉游戏帧与帧之间的切换,可以简单的将帧数理解为每秒钟render_game()被调用的次数。

一部分单机游戏会追求极致的显示效果,对于帧数的要求也更高(对显卡的要求也更好-_-!);而网络游戏由于在同一显示范围内(有些游戏会有类似9屏的概念,即将地图划分为若干个方格,以玩家为中心,服务器只向玩家下发周围9个格子内其他玩家或是AI的信息)需要实时更新更多的物体,加上网游对于硬件的低门槛,注定无法一味的追求高帧率。

如果将GameMain()至于循环中放任不管,帧率会居高不下,与此同时机器的CPU使用率高居不下。而如此此时运行其他的大型软件,如虚拟机等,又会发现游戏内的帧率会有很大程度的波动,这样波动的帧率会带来很多潜在的问题。无论开发者还是玩家当然希望游戏速度尽量保持稳定,于是产生了一种固定帧率的做法。

const int FRAMES_PER_SECOND = 50;      ///< FPS:50
const int SKIP_TICKS = 1000 / FRAMES_PER_SECOND;

DWORD next_game_tick = GetTickCount();
int sleep_time = 0;

bool game_is_running = true;

int WinMain()
{
	while( game_is_running )
	{
		ProcessMessage();	///< 处理网络消息
		GameMain();			///< 主循环

		next_game_tick += SKIP_TICKS;
		sleep_time = next_game_tick - GetTickCount();
		if( sleep_time >= 0 )
		{
			Sleep( sleep_time );	///< 出让CPU的控制权
		}
	}

	return 0;
}

int GameMain()
{
	update_game();   ///< 更新游戏逻辑
	render_game();   ///< 执行渲染操作

	return 0;
}

总结

第二种方案比起第一种方案对于CPU的使用率高效了很多,但在这种方案中游戏逻辑与渲染操作是同时进行的,在大多数情况下游戏逻辑更新频率超过一定数值(比方说50帧),玩家已经区分不出如此细微的变化,而对于竞速类、射击类的游戏更高的帧率显然在表现高速移动的物体会带来更好的体验。

于是会有将游戏逻辑与渲染分离的做法,简单的做法是将render_game()移动到循环之外,这样对于CPU更好的机器,便可以跑出更高的帧率。而更为通用的做法是将渲染操作独立为一个线程,这样做最大的好处逻辑线程与渲染线程其中一个线程发生阻塞不会影响到另一个线程的正常运行。与此同时,程序中也需要加入线程间的同步机制,程序实现的复杂度相比单一线程复杂了很多。

参考资料:

http://dewitters.koonsolo.com/gameloop.html

时间: 2024-10-01 04:50:33

网络游戏中的主循环的相关文章

wordpress主循环和全局变量

对于特定的 WordPress Action 和 Filters,你可以很容易知道它们在主循环中哪里执行了.然而有时你在主循环中不想使用 action 或者 filter 而只想调用模板函数(template tag ).这时候,你需要非常了解你想访问的全局变量和可能得到的果. 下面我将讲解 WordPress 主循环,以便你能更好理解哪些全局变量可以被主循环中的模板函数调用. WordPress 主循环->The WordPress Loop WordPress 主循环是用来在一些页面上显示日

WordPress主循环(The Loop)函数have_posts(),the_post()详解

WordPress中调用文章标题是the_title();调用文章内容时用到the_content();调用文章的作者时用到the_author();等等这些函数,都需要在主循环中使用,下面就介绍一下如何用have_posts()和the_post()开始Wordpress文章中循环,并说明如何结束循环. 语法 1 <?php if (have_posts()) :  while (have_posts()) : the_post(); ?> 2 当找到文章时返回此语句 3 <?php 

Cocos2d-x 动手实现游戏主循环

由于Cocos2d-x封装的很好,所以对于很多新手,他们只知道先new一个场景,在场景上添加布景或精灵,然后用Director的runWithScene便可以运行游戏了.如果给一个精灵加个动作,精灵就会动,如果给布景层添加个定时器,游戏会定时执行.你知道为什么会这样吗? 作为一个游戏开发者,我觉得进入游戏这一行业之前,一定要先搞清楚"游戏主循环"这个东东,可惜我到现在才来研究这个东东.或许网上关于Cocos2d-x游戏主循环的讲解一大把,但是这篇文章,我会教你怎么来实现游戏主循环. 一

辛巴学院-Unity-剑英的c#提高篇(一)主循环

这是测试版 辛巴学院:正大光明的不务正业. 最近刚刚离开了我服务了三年多的公司,因为一个无数次碰到的老问题,没钱了. 之前不知道做什么好的时候,机缘巧合之下和哒嗒网络的吴总聊了一下,发现了vr game这扇窗户,这里权当帮哒嗒网络打个广告吧.^_^ 回头看看仓惶的这一段时间,荒废了很多,抽空回来再和大家聊聊c#. 之前做了个入门系列,胡乱说了些东西.感觉入门这样子也就差不多了,该稍微提高一点了. ? 从写一段程序,到写一个app,写一个游戏,到底其中有什么不同呢?一段程序的执行时间很短,一个应用

我的Cocos2d-x学习笔记(九)游戏帧循环(游戏主循环)

游戏运行时候会不断按照游戏逻辑规则重新绘图,反复处理用户输入.处理定时事件.绘图,直到游戏结束. Cocos2d-x引擎也是通过不断绘图来进行游戏的,默认的帧率在AppDelegate.cpp中显示为60帧每秒,也就是每秒执行了60次用户输入.定时时间.绘图等. 抄录Cocos2d-x高级教程中内容如下: 游戏乃至图形界面的本质是不断地绘图,然而绘图并不是随意的,任何游戏都需要遵循一定的规则来呈现出来,这些规则就体现为游戏逻辑. 游戏逻辑会控制游戏内容,使其根据用户输入和时间流逝而改变.因此,游

iOS中NSThread(主线程,子线程)

#import "AppDelegate.h" @interface AppDelegate () { NSInteger _totalTickests; } @property (nonatomic, retain) NSLock *lock; @end @implementation AppDelegate - (void)task1 { NSLog(@"当前线程:%@,是否是主线程:%d",[NSThread currentThread],[[NSThread

游戏主循环知识积累

1. 引言 游戏主循环是每个游戏的心跳,输送着整个游戏需要的养分.不幸的是没有任何一篇好的文章来指导一个菜鸟游戏程序员如何为自己的程序供养.不过不用担心,因为你刚好不小心看到了这篇,也是唯一一篇给予这个话题足够重视的文章. 由于我身为游戏程序员,我见过许许多多的手机小游戏的代码.这些代码给我展示了五彩缤纷的游戏主循环实现方法.你可能要问:"这么简单的一个小玩意还能做到千奇百怪?" 事实就是这样,我就会在此文中讨论一些主流实现的优缺点,并且给你介绍在我看来最好的输送养分的解决方案. 游戏

[libevent]事件主循环

libevent事件处理的中心部分--事件主循环,根据系统提供的事件多路分发机制执行事件循环,对已注册的就绪事件,调用注册事件的回调函数来处理事件. 事件处理主循环 libevent的事件主循环主要是通过event_base_loop ()函数完成的,其主要操作如下面的流程图所示,event_base_loop所作的就是持续执行下面的循环. 上图的简单描述就是: 校正系统当前时间. 将当前时间与存放时间的最小堆中的时间依次进行比较,将所有时间小于当前时间的定时器事件从堆中取出来加入到活动事件队列

1 cocos2dx源码分析-程序启动与主循环

1 启动 在iOS系统中,由main函数启动默认调用了AppController main.m NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; int retVal = UIApplicationMain(argc, argv, nil, @"AppController"); [pool release]; return retVal; 2 AppController  iOS的ViewController都熟悉