花费了近三个星期写了一个超级玛丽的游戏,功能完善好了之后,最后突然发现了一个很蛋疼的bug,就是这里看着明明是一个坑,为什么马里奥就是掉不下去呢,并且更奇葩的是在第一关中马里奥就可以很正常的掉到坑里面,但是为什么在第二关就是掉不下去了呢,检查地图数组数据的初始化时没有任何问题的,第一关和第二关只是用的贴图不同罢了,代码逻辑都是同一个代码,这究竟是为什么呢?这个问题真是有些奇葩了
地图数组定义
struct Unit { short row; //行 short col; //列 short id; //id号 short addInfo; //附加信息(表示是宽度还是高度) short page; //页数 POINT marioPos; //玛丽进入管道或者出来管道应在的位置 //short marioPosX; //short marioPosY; }; typedef Unit UNIT[13][16]; //地图中的每页的二维数组 UNIT* mapArr; //将地图信息全部保存到MapArr三维数组中去
经过调试发现为什么判断玛丽是否可以下降的代码中,竟然玛丽奥的下面位置的id号不为0,而是26(26就是表示是青砖的位图id号),并且我定义的地图的行一共才13行,所以下标应该是只能到12,下标到了13行了,为什么没有越界呢,后来经过将问题进行简单化做了一个例子,才发现虽然我定义的地图每页是13行16列的二维数组,但是前面还有页数呢,所以即使是行也是不会越界的
例如下面的程序:
#include "stdafx.h" typedef int INT[3]; int main() { INT* pInt = new INT[3]; memset(pInt, 0, sizeof(INT) * 3); pInt[2][1] = 10; //2行1列为10 实际上也可以写成0行 7列或者1行4列都是一样的(我猜想的,下面进行验证) printf("pInt[2][1]=%d\n", pInt[2][1]); printf("pInt[0][7]=%d\n", pInt[0][7]); printf("pInt[1][4]=%d\n", pInt[1][4]); /* 验证结果得出三个答案都是10 原因就是因为在内存中数据没有一维二维之分,只是一位一位的内存序列号 */ printf("%d\n", pInt[9][10]); //这个就是正真的越界了 while (1) {} return 0; }
所以在我程序中虽然mapArr[5][13][0] 表面上看13行这一行是越界了,但是实际上并没有,实际上这个位置的数据等于mapArr[6][0][0]的数据是一样的,又因为在初始化的时候mapArr[5][0][0]这个在数组内存中的值为26,不为0,所以在判断马里奥是否能够下降代码中就会判断出来不能下降了,所以就会看到马里奥虽然是在坑里面,但是就是不往下掉落,一开始说的第一关中的所有坑都可以掉落,原因也正是如此,这个不能掉落的坑只是一个巧合罢了,实际上所有的坑因为这个原因都会出现有的坑能够掉落,有的坑不能掉落的情况
判断玛丽能否下落的代码
bool Mario::IsCanDown(Map &map, vector<Enemy> &enemyVect,vector<Ladder*> &ladderVect) { CheckEnemyColider(enemyVect); if (CheckLadderColider(ladderVect,false)) { return false; } Unit tempLeft, tempRight; //tempLeft.page = posX / (unitW*16); tempLeft.page = (posX + 2) / (unitW * 16); //上面的写法是不正确的,既然列进行了posX偏移,那么求page的PosX也应该进行偏移(如果posX=4479,那么用posX求的page=7但是用posX+2求的page就是8,就会出现得到的列数不是同一页的列) tempLeft.row = (posY + height*coliderSize - 1 + 1) / unitH; //之所以要+2,这样做即使人物的左边角是在障碍物右边两个像素的位置上方,这样人物依然可以下降的 //不然如果一个空格两边都是平的砖块,那么人物必须要正正好好站在空格地方之上,一个像素也不能差,这个位置太难找了 //会出现看着人物下面是有一个空格子,但是人物下不去的情况,为了解决这个问题,才进行了+2以及-2操作,允许有两个像素位置的偏差 // tempLeft.col = (posX % (unitW*16)+2) / unitW; tempLeft.col = (posX + 2) % (unitW * 16) / unitW; //应该是这样写的,上面的写法是不正确的 tempRight.page = (posX + width - 1-2) / (unitW*16); tempRight.row = tempLeft.row; // tempRight.col = ((posX + width - 1) % (unitW*16)-2) / unitW; tempRight.col = (posX - 2 + width - 1) % (unitW * 16) / unitW; //应该这样写,先偏移在求余 UNIT* mapHead = map.GetMapArrHead(); if (tempLeft.row >12) //玛丽的脚如果是大于定义的地图的高度12行之后不管下面是什么都直接返回true,证明能够继续掉落 { return true; } if (mapHead[tempLeft.page][tempLeft.row][tempLeft.col].id != 0 || mapHead[tempRight.page][tempRight.row][tempRight.col].id != 0) { return false; //表示下面为障碍物,不能继续降落了 } return true; }