花费了近三个星期写了一个超级玛丽的游戏,功能完善好了之后,最后突然发现了一个很蛋疼的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,所以在判断马里奥是否能够下降代码中就会判断出来不能下降了,所以就会看到马里奥虽然是在坑里面,但是就是不往下掉落,一开始说的第一关中的所有坑都可以掉落,原因也正是如此,这个不能掉落的坑只是一个巧合罢了,实际上所有的坑因为这个原因都会出现有的坑能够掉落,有的坑不能掉落的情况
原因既然找到了,那么bug就很容易改了嘛,如果判断出来马里奥的脚的下面的一个像素的位置所在的行是大于定义的行的,就判断为马里奥能够下降,这样马里奥就能够掉落到所有的坑里面了,然后在其他地方进行判断如果马里奥全部掉出了地图,那么就证明马里奥是死亡了
判断玛丽能否下落的代码
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;
}