最近在做一个类似切水果的小游戏,需要检测手在屏幕上是否划到了园形的精灵。
对于圆形的碰撞检测还是很容易的:当触点到圆心的距离小于等于圆的半径时,结果为真。
void MGame::ccTouchMoved(CCTouch *pTouch, CCEvent *pEvent) { CCPoint p1 = spr_ball_->convertTouchToNodeSpaceAR(pTouch);//将pTouch转换为以spr_ball为原点的坐标 if (ccpDistance(p1, ccp(0,0)) <= spr_ball_->getContentSize().width*0.5f) { } //当触点到圆心的距离小于等于圆的半径时,结果为真 }
在测试的时候发现,如果滑动速度很快时,会出现手明明划到了物体但检测结果为false的情况。实际上,上面的方法只考虑了静态情况下的碰撞检测。对于运动的物体,如果还按照静态的方法去检测,就会出错。这是因为快速移动的物体(这里是手的触点)在帧与帧之间又移动了一段距离,这段移动是不会进行碰撞检测的。
细小的物体快速移动时,其路径可能会在碰撞世界的连续快照中留下空隙,从而错过碰撞。《游戏引擎架构》在12.3.5.7介绍了运动物体的碰撞检测,称这种现象为”tunneling“或"bullet through paper"。
所以,改进的方法是判断球心到前一次触点和本次触摸点形成的线段的距离。
void MGame::ccTouchMoved(CCTouch *pTouch, CCEvent *pEvent) { CCPoint previous_p=spr_ball_->convertToNodeSpaceAR(pTouch->getPreviousLocation()); //得到上一个触摸位置 CCPoint p1 = spr_ball_big_->convertTouchToNodeSpaceAR(pTouch);//spr_ball_big_为原点 CCPoint v_ab = ccpSub(p1, previous_p); CCPoint v_cd = ccpPerp(ccpNormalize(v_ab));//旋转90d得到垂直于Vab的单位向量 v_cd = ccpMult(v_cd, spr_ball_->getContentSize().width*0.5f); bool intersect = ccpSegmentIntersect(ccp(0, 0), v_cd, p1, previous_p); if (!intersect)//有可能是Vab的旋转方向导致线段没交上 { v_cd = ccpNeg(v_cd); intersect = ccpSegmentIntersect(ccp(0, 0), v_cd, p1, previous_p); } }