碰撞检测
像上面这个,英雄可以穿墙而过,那就没什么意思了。我们要想办法让英雄感受到障碍物的存在。
在第一章中,我们给每个方块都设置了一个“walkable”属性,当某个位置方块的walkable属性是false的时候,英雄就无法穿过它。当值为true的时候,英雄就可以从上面走过(这个东西叫做“逻辑”:)。
为了让这个逻辑起作用,我们将会这样做: 当方向键被按下以后,我们首先检查下一个方块是不是可通行的。 如果是,我们就移动英雄。如果不是,那么就忽略掉按键事件。
这是完美的墙的碰撞:
英雄贴着墙站着,而且下一步他就会进到墙里面。我们不会让它发生的。
但是这个世界总是不够完美,要是英雄只和墙接触一部分呢?
这就要求我们检测英雄的全部四个角是否和墙接触了。只要任意一个角和墙接触(上图中是左下角),移动就是不合理的。
或者,英雄没有贴着墙站,但是下一步就要跑到墙里去了,虽然只是一部分:
我们不得不让他这样贴着墙站着:
“这么难?!”,你也许会喊,“不太可能办到吧?”不用担心,实际上很简单的~
检查四个角
我们不希望英雄的任何一部分能进到墙里面去,只要四个角没有进去就行了,这是假设英雄的大体形状是个长方形(他们确实是的)。
为了实现这个功能,让我们写个函数:getMyCorners function getMyCorners (x, y, ob) { ob.downY = Math.floor((y+ob.height-1)/game.tileH); ob.upY = Math.floor((y-ob.height)/game.tileH); ob.leftX = Math.floor((x-ob.width)/game.tileW); ob.rightX = Math.floor((x+ob.width-1)/game.tileW); //检测他们是否是障碍物 ob.upleft = game["t_"+ob.upY+"_"+ob.leftX].walkable; ob.downleft = game["t_"+ob.downY+"_"+ob.leftX].walkable; ob.upright = game["t_"+ob.upY+"_"+ob.rightX].walkable; ob.downright = game["t_"+ob.downY+"_"+ob.rightX].walkable; }
这个函数接收了3个参数:对象中心的x/y位置(象素值)、对象的名称。
“等一下”,你也许会迷惑,“我们不是已经在英雄对象中保存了他的当前位置了吗?”是的,但是我们当时存的是当前的位置,这里处理的是将要达到位置(先假定英雄可以移动)。
首先,我们根据这个x/y坐标计算出英雄所处的方块。可能英雄的中心在一个方块上面,但是左上角在另外一个方块上面,左下角又在第三个方块中,这是有可能的。 (y+英雄的高度)/方块高度=英雄下面的两个角所在区块的行值。
最后的四行使用了我们计算出的方块的可通行性。例如,左上角使用upY行leftX列的方块的walkable属性。你可以看到,得到的四个结果(upleft、downleft、upright、downright)被保存到ob对象中了,所以我们以后还可以用到它。
我要再一次指出的是,getMyCorners函数不仅可以用在英雄上面,这里的ob也可以是任何可移动的对象。做区块游戏要多考虑函数的通用性,在后面的章节中你会体会到这种思想的正确性。
移动
当我们检查了四个角以后,现在就可以很简单地移动了:
如果4个角都是可以通行的,那么就移动,否则不移动。但是要让最后英雄贴着墙站着,还得多写几个字。修改后的moveChar函数处理4个可能的方向的移动,它看起来可能有些长,实际上仅仅是4段类似的代码。让我们看看:
function moveChar(ob, dirx, diry) { getMyCorners (ob.x, ob.y+ob.speed*diry, ob); if (diry == -1) { if (ob.upleft and ob.upright) { ob.y += ob.speed*diry; } else { ob.y = ob.ytile*game.tileH+ob.height; } } if (diry == 1) { if (ob.downleft and ob.downright) { ob.y += ob.speed*diry; } else { ob.y = (ob.ytile+1)*game.tileH-ob.height; } } getMyCorners (ob.x+ob.speed*dirx, ob.y, ob); if (dirx == -1) { if (ob.downleft and ob.upleft) { ob.x += ob.speed*dirx; } else { ob.x = ob.xtile*game.tileW+ob.width; } } if (dirx == 1) { if (ob.upright and ob.downright) { ob.x += ob.speed*dirx; } else { ob.x = (ob.xtile+1)*game.tileW-ob.width; } } ob.clip._x = ob.x; ob.clip._y = ob.y; ob.clip.gotoAndStop(dirx+diry*2+3); ob.xtile = Math.floor(ob.clip._x/game.tileW); ob.ytile = Math.floor(ob.clip._y/game.tileH); //---------下面两行由qhwa添加-------- ob.height = ob.clip._height/2; ob.width = ob.clip._width/2; //--------------------------------- return (true); }
像以前一样,moveChar函数通过键盘检测函数传递过来的值得到对象和方向。 这一行: getMyCorners (ob.x, ob.y+ob.speed*diry, ob); 计算垂直移动(当diry不等于0时)后的四个角的可行性, 随后,通过四个角walkable的值检查是不是合法的移动: if (diry == -1) { if (ob.upleft and ob.upright) { ob.y += ob.speed*diry; } else { ob.y = ob.ytile*game.tileH+ob.height; } }
这块代码是用来检测向上的移动的。当上箭头键被按下去后,diry的值等于-1。 我们使用了getMyCorners函数得到的ob.upleft和ob.upright值,如果他们都是true,那就意味着上面两个角所在方块都是可通行的,我们就给角色的y坐标加上ob.speed*diry,让角色朝上移动。
但是如果这两个角任何一个碰巧是不可通行的,即ob.upleft或者ob.upright是false, 我们就要把角色放到墙边上。为了让角色贴着它上面的墙,他的中心点必须距离当前方块的上边缘char.height象素,如图:
ob.ytile×game.tileH得到的是当前方块的y坐标,也就是上边缘的y坐标,再加上角色的height值,就是正确的位置了。同样的道理,另外三个方向的部分也可以这样分析出来。
最后一行的把实际的mc放到计算出来的坐标处,让角色显示正确的动画帧,并且更新角色的属性。同以前一样,函数返回true值。
Qhwa注:我在这里加了两行 ob.height = ob.clip._height/2; ob.width = ob.clip._width/2;
这是因为当clip这个影片夹子跳转相应的帧后,原来的_width和_height可能会发生变化,如果还用初始化时的值,可能就会出错。如果英雄的高度和宽度是一样的,就没有必要这么做了。Tony推荐使用确定的而且相同的高度和宽度。
下载源文件
出处:蓝色理想
责任编辑:qhwa
上一页 键盘控制的移动 下一页 芝麻开门-地图切换
◎进入论坛Flash专栏版块参加讨论
|