游戏编程入门学习笔记35——推倒重来篇——《游戏编程入门》示例源码中的两个大bug

来源:互联网 发布:淘宝网店保证金怎么交 编辑:程序博客网 时间:2024/05/16 00:41

(csdn是有很多很傻比的地方的,比如已上传资源无法修改、删除,比如代码框不支持tab,比如草稿箱内容会永久保存在摘要视图中...

原文写于8月12日,今天删除重贴一遍,当然这也意味着这里沉寂两个月之后又要动工了)


接下来的工作重点放在代码重构上,所以取名推倒重来篇

有些基本构想放到后面说,先来讲讲这两天发现原教材中两个bug的过程。

 

一切起源于我对Direct3D绘图过程的进一步理解,当时是想尽量弄清D3D_Init、Clear、BeginScene、EndScene、Present等函数的具体作用,这样在重写“引擎”(虽然简陋,但它确实是个引擎...)的时候才好把握。

这时我记起以前遇到过的动画丢帧以及打印文本不稳定现象,当时判断可能是因为BeginScene不成功导致,是用加绘图标志位及多次循环绘图的办法解决的。

假如BeginScene偶尔会不成功,那是什么原因导致不成功?它的作用到底是什么?

 

带着这个疑问我在群里请教,然后意外的得知两点重要信息:

1,DirectX SDK自带一个Control Panel工具,这里头可以设置程序是否链接使用debug库,及更多debug信息输出选项。同vs里项目配置为debug模式一样。明显开发阶段把各种调试信息都打开更好,这样可以发现隐患。

2,BeginScene()成功则返回D3D_OK,等同于0值。

 

将Control Panel选项全开,编译调试《游戏编程入门》的源码(比如第三章Direct3D初始化),信息窗口就会不断输出BeginScene()failed的信息,因为原代码都是这样处理的:

void GameLoop(HWNDhwnd)

{

        if (!d3ddev) return;

        d3ddev->Clear();

        if( d3ddev->BeginScene())

        {

                //rendering

                d3ddev->EndScene();

                d3ddev->Present(NULL, NULL, NULL,NULL);

        }

}

BeginScene()成功返回0,于是第一次并不会进入rendering,但是GameLoop是不断循环的,马上会第二次调用BeginScene(),这时,因为第一次没有EndScene(),所以失败,而失败恰恰导致进入了rendering,于是程序幸运的执行完绘图任务,如此反复。即每两次循环才成功绘图一次,这样就完美解释了之前遇到过的那些奇怪bug。


假如改成 

while( D3D_OK != d3ddev->BeginScene()) 

        d3ddev->EndScene();

{

        rendering();

}

就不会再提示failed。

当然这只是暂时解决方案,更完美的写法需进一步研究。

 

问题不仅仅于此,还存在内存泄露的bug。即程序在退出,执行heapfree时,会遇到仍在分配中的内存,导致调试中断。

软件调试可借鉴硬件调试的思维,采用分段排除法,最终发现是D3D_Init里分配的backbuffer表面指针接口未Release造成。

 


假如想弄清各函数的具体作用,MSDN仍然是最佳途径,这些就不多说了。

 

另外过程中接触到个叫Leak Detector的工具,可以帮助查出内存泄露的根源,安装用了下,没玩转,这个还需进一步研究。不过这也提醒我,多留心一些好用的IDE插件。

 

发现引领自己一步步入门的教材,实际充满纰漏,这多少有些让人唏嘘。多么希望是大师故意留下这些坑来让学生在发现问题并解决的过程中留下深刻印象啊。
不过也正常,就算是经典的技术书籍也难免存在错误,何况这种不算流行的小众图书?就像小学老师不一定要水平有多高,适合教小朋友就行了。这本《游戏编程入门》,至少语言亲切,不像国内那些东拼西凑的纯骗钱的冷冰冰的”手册“,总体来说功是远大于过吧。

这也应了近期的主题——”推倒重来“。