MTK平台游戏移植时的数据溢出检查

      这篇文章是我在公司写的第一篇经验共享,那是四五个月前的事了。那时候真是惬意啊,学习阶段主要就移植这款坦克大战的游戏!看着游戏框架一天比一天完善,心里真是甜蜜。只可惜现在的时间都被沉重的工作所占据,业余的时间也被无尽的加班淹没,神啊!!好啦下面切入正题。

接触MTK平台以来来,第一个练手的项目就是移植一款MFC写的游戏“坦克大战”。在移植游戏的时候,数据的溢出问题出现好些次。这些溢出问题一般都很隐蔽,需要认真排查才能确定问题的所在。数据溢出有上溢出和下溢出两种情况。下面我就结合我在游戏中所遇到的bug,讲解我我排查这些溢出问题的思路。

1.   上溢出

上溢出,就是变量所要表示的值,超过了变量类型所能表示的最大值。比如S8类型的变量所能表示的范围是-127~128,当一个S8类型的变量所要表示的值超过了128的时候,数据便上溢出了。

绘制游戏地图时,曾在模拟器上出现过图1这种情况。我们可以看到游戏的上半部分绘制地很好,而下半部分却出了问题。

 

 

 

                     1

游戏地图的生成,是根据一个一维数组的值,确定地图二维数组的值。代码如下:

    S8 k=0;

       pCurrMapPtr = g_pMapPtr[n_currLevel];//当前地图指针,指向当前等级对应的数组

       for( i = 0; i < PLANE_H; i ++ ) // PLANE_H等于13,表示地图高度上地块数

       {

              for( j = 0; j < PLANE_W; j ++ ) // PLANE_W等于13,表示地图宽度上地块数

              {

                     plane.m_map[i][j].obj = *(pCurrMapPtr+k);

                     ……

                     k++;

              }

}

这里可以看到,双重for循环后,k的值至少是13*13。但是前面定义k的类型为S8,数据长度显然不够。所以当k大于128的时候,k的值就上溢出变为负数了。这样语句*(pCurrMapPtr+k);实际上是数组越界访问。这也就解释了为什么地图上半部分能绘制完好(因为k值未超过128),而下半部分出问题了。将k的类型改为S16,问题解决。

2.   下溢出

下溢出,就是变量所要表示的值,超过了变量类型所能表示的最小值。比如无符号数所能表示的最小值为0,当一个无符号数表示负数的时候,该数便下溢出了。

游戏制作的一个重要元素便是碰撞检测。比如我的坦克只能在一个固定的矩形框里面活动,那么就需要对坦克当前坐标和矩形框进行碰撞检测。检测示例代码如下:

if(tank->m_x<= 0 ) //坦克水平方向上的坐标小于零的时候,置值为1

        tank->m_x = 1;

else if( tank->m_x + tank->m_uWidth > MAX_X ) //坦克水平方向坐标不能超过边框的

        tank->m_x = MAX_X - tank->m_uWidth -1;

但是最初测试的时候发现,坦克运动到最左边的时候,不是停住,而是直接出现在最右端。图2是坦克和边界碰撞前,图3是坦克穿越边界到最右边。

            

 

                 2                                      3

这个bug,查了好一段时间,开始以为是碰撞检测代码的问题,后来才发现原来我设定的坦克坐标类型为U16。当坐标x的值为“负数”时,机器内却是很大的正数。代码执行是走的else分支,而不是我们认为的if分支。将坦克坐标类型改为S16,问题解决。

3.   模拟器和真机的差异性“溢出”

这里说的差异性“溢出”是指,真机arm处理器的ads的编译器,char类型变量是默认为unsigned的。所以我们用S8类型数据时,会发此变量值为负数时模拟器上表现正常,但是在真机上就发现这个数据“溢出”了。

游戏在模拟器上调试好后,在真机中却不能正常表现。图4是模拟器上的截图,图5是真机上的拍截图。真机上面,我方敌方坦克不能移动,无障碍物的地方显示不正常,但是坦克能响应开火按键。

   

                       4                                                                                  5

最开始以为是方向按键在真机上不能被响应,但是通过catch工具,能捕获到方向键的消息传递;同时若方向键不能被响应的话,开火键也应该不能被响应。所以问题应该出现在游戏逻辑上。注意到敌方坦克也不能移动,所以问题应该出现在坦克移动的函数上。游戏中坦克移动的逻辑判断是先判断坦克前面的障碍物类型是什么,当前面没有障碍物,或者障碍物是树的时候,坦克能前进。障碍物的枚举如下:

enum{

OBJ_NULL = -1,          //空白

OBJ_BRICK = 0,          //泥块

OBJ_CONCRETE = 1,  //混凝土

……

};

打印坦克前面的障碍物类型,发现模拟器上值为-1时,真机上的值为255。可见默认情况下,真机是将S8类型的变量作为U8来处理的。不过真机只是将char类型变量默认处理为unsigned char,而short 类型不变。因为在第2点里面,坦克坐标类型改为S16的时候,程序表现正常。

4.   总结

溢出问题确实很难检测出来。最好的情况是移植游戏时不要改变原来游戏代码的数据类型和数据结构。当移植需要改变数据结构或数据类型时,要选择合理的数据类型和合适的数据精度,这样对防数据溢出大有裨益。当程序出现溢出问题时,要细心排查问题所在位置。我们可通过程序的异常表现,判定溢出问题所在的大致模块,如上文第三类溢出检测便是一个很好例子。

 

最后想要源码玩玩的朋友可以留下邮箱神马的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值