我已经设置了一个单独的线程来处理游戏端口上的屏幕刷新。这使我可以专注于使用简单的块图形更新我用于主游戏的内存缓冲区。所有我不得不担心的是改变它的位置,渲染线程将自动更新后台的显示纹理。它工作得很好(在渲染循环之间添加延迟之后。)问题是在看似随机的情况下,SDL_CreateWindow调用永远不会返回。它没有给出错误,只是坐在那里什么都不做。线程的InitVideo部分是:
const HWND desk = GetDesktopWindow();
RECT dsize;
FILE *cfile;
int count1, count2;
printf("Reading font file\r\n");
fopen_s(&cfile,"character.dat","rb");
for(count1 = 0; count1 < 128; count1++)
for(count2 = 0; count2 < 8; count2++)
chROM[count1][count2] = (unsigned char)fgetc(cfile);
fclose(cfile);
printf("Font data read\r\n");
GetWindowRect(desk,&dsize);
if (fullscreen) {
width = dsize.right;
height = dsize.bottom;
} else {
height = (int)(dsize.bottom * .85);
if (((height * 6) / 5) > dsize.right) {
width = (int)(dsize.right * .85);
height = (int)((width * 5) / 6);
} else width = (int)((height * 6) / 5);
}
printf("Initializing SDL\r\n");
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) != 0) {
printf("SDL Initialization failed\r\n");
Sleep(2000);
exit(-1);
}
printf("Creating Window\r\n");
if (fullscreen)
screen = SDL_CreateWindow("Ultima: Escape from Mount Drash!!",SDL_WINDOWPOS_UNDEFINED \
,SDL_WINDOWPOS_UNDEFINED,0,0,SDL_WINDOW_FULLSCREEN_DESKTOP | SDL_WINDOW_OPENGL);
else screen = SDL_CreateWindow("Ultima: Escape from Mount Drash",SDL_WINDOWPOS_CENTERED \
,SDL_WINDOWPOS_CENTERED,width,height,SDL_WINDOW_OPENGL);
if(!screen) {printf("Failed to create screen\r\n");Sleep(2000);exit(-1);}
printf("Screen Created\r\n");
主要介绍点代码是:
int main(int argc, char* argv[])
{
unsigned char lv$[10];
int count;
srand((unsigned int)time(NULL));
_beginthread(Video, 0, NULL);
Sleep(5000);
睡眠命令是给视频时间初始化和打开。它只是一个黑屏,在它启动和运行几秒后,但我可以忍受。无论如何,第一个屏幕只是一个信息屏幕。如果我在屏幕打开并开始渲染之前碰巧进入缓冲区,那应该不是问题。无论如何,它在创建时都填充了零(在标题 - 全局变量中,因此渲染和主线程都可以访问它。)
事情是,大约8次中有10次,窗户永远不会打开。输出在“创建窗口”处停止,从不得到任何其他内容。我什么都没等,最多等了30分钟。我必须强制退出(通过窗口。)我无法弄清楚任何类型的模式。我将加载游戏一次没有问题,然后接下来的五次(使用Visual Studio打开或关闭),它将失败。然后它将在接下来的10次成功,然后失败20次等等...当它工作时,它运行良好,没有任何问题或崩溃(到目前为止。)通过调试模式(Visual C ++ 2010)到达那一点,就坐在那里。没有错误消息,没有任何消息。我点击了F11(踏入),它就在那里。它不会继续,它不会进入。
我试图在SDL论坛上发帖提问,但显然它已被锁定,因此我无法发布新消息。是的,我确实通过电子邮件验证了我的帐户。
答案 0 :(得分:0)
This forum post已经过时了(从SDL2开始),但我认为这部分没有太大变化。我建议你将处理循环移动到另一个线程,并将所有视频和事件保留在主线程中,因为SDL似乎没有为你的用例做好准备,你将达到同样的预期效果。
还要注意,将渲染循环从更新循环中分离可能会导致一些游戏性问题(例如,在玩家倾向于依赖正确的“帧范围”来进行攻击的格斗游戏)。您可能会看到过去的帧而不是当前帧。
答案 1 :(得分:0)
这主要是为了回应所有评论,但也有我原来问题的答案。评论只是没有给我空间来说出这里需要说的一切。
首先,答案,因为它似乎适用于我的问题。似乎我发布的代码实际上工作得很好(至少我的问题至少。)似乎某种程度上Visual C ++已切换到&#34;发布&#34;模式,而我仍然使用调试exe文件进行测试。我不记得改变它(或改变它),从来没有任何理由这样做。我在切换到发布模式时发现了这一点,并发现已经在那里构建了一个exe文件(没有所需的支持文件存在 - 例如SDL2.dll。)返回我的存档(我定期存档整个项目目录,)我跟踪了它切换的两个点(或关闭点),这使我得出以下结论。
1)如果要线程化SDL例程,那么所有SDL调用都必须在该线程中。在该线程之外进行任何SDL调用(即使事件轮询似乎也是如此)将导致显然是随机的(在某些情况下非常不合逻辑)错误。例如,由于某种原因,当我关闭文件流(在这种情况下是我的日志文件)时,在单独的线程中进行SDL调用会导致访问冲突错误.fclose(lfile);虽然调用与SDL没有任何关系,但调用仍会导致错误。
2)启动SDL线程时,请确保在继续主线程之前等待它完成初始化。如果SDL线程正在创建某些东西时主线程中发生了错误的事情,那么由于某种原因它可能会锁定。 SDL_CreateWindow()函数似乎是唯一的罪魁祸首,但我不能肯定地说。在我的最终代码中,我使用全局布尔变量而不是延迟。基本上我创建变量(bool SDLRunning;)并将其设置为false。然后我启动SDL线程(_beginthread(Video,0,NULL);)。接下来我设置了一个只监视变量的循环(while(!SDLRunning)Sleep(10);)。最后,当SDL线程完成初始化所有内容,并且除了函数循环之外什么都没做,它将变量设置为true,让我的主线程知道它可以继续安全。
到目前为止,我现在已经完成了,我没有任何其他类型的问题(除了你的标准错误蔓延到任何程序。)它现在运行顺利。我不得不一次又一次地进去调整一下,但那就是它(这里的时间有点偏离,忘记在那里清除一个变量,等等。)
现在,回答一些评论。视频的渲染被删除了,因为它实际上使编程游戏代码本身更容易。原始代码是围绕一个不需要您编程视频渲染的系统设计的。因此,它使用了打印语句和图形内存地址。我不是必须手动添加渲染调用,而是使用数组来表示&#34; graphics ram&#34;原始系统。然后,当我转换数百行原始代码,用一个值戳图形RAM时,我不会每次都被迫立即调用渲染函数。
此外,原始游戏有几个后台任务,取决于相当准确的时间。它通过使用硬件中断来实现这一点(微软以其无限的智慧取消了对它的所有访问权限。)我最初尝试以各种方式模拟硬件中断,但都没有成功。将它们放在一个单独的线程中(我将视频标记为仅因为它的最重负载是转换和渲染视频),具有可调节的延迟时间是唯一可行的方法。其中一个是每秒7.5次改变显示区域的颜色(每2秒15次。)因为我已经把所有的颜色都放在了视频功能上,所有的东西都是SDL(比如视频功能)必须在同一个线程中,SDL需要与该例程在同一个线程中。
最后,其中一个背景任务是播放各种合成音乐曲调(方波)。我想保留游戏的原始感觉,所以我也希望合成音乐。我不想记录几个波形文件(其中最小的是253K)来替换原始游戏用于它的音乐的3k数据。因此,它需要超出任何其他任务所需的精确计时。我还需要一个可以做到这一点的声音库,但这是一个单独的问题(我设法解决,尽管其他人的侮辱,或我的问题被完全忽略。)
所有这些我原本试图在没有任何中断(或任何仿真)或创建单独的线程的情况下进行。这导致了复杂的混乱,并且浪费了一个月的编程。正是这种原始的代码失败几乎无法遵循(即使是我,我编写了爆炸的东西),这使我开始尝试在中断例程中编程。如果我为DOS编程(并且我对许多人似乎对每个想要为DOS编程的人施放的侮辱感到有些厌倦),我本可以轻松地完成这项工作,但微软的删除中断访问复杂的事情。在DOS中,您只需读取一个以稳定的规律性(每秒x次)触发的中断向量。将该向量重定向到您的代码。然后确保中断代码的最后一个操作是跳转到原始向量。最后,当程序退出时,将中断恢复为原始向量。很好,很简单。我想微软并不相信程序员知道最后两步的必要性,但他们至少可以包含一些方法来插入&#34;函数(或函数)到预先存在的中断例程中。