基于cocos2d-x对俯视角游戏碰撞检测及碰撞处理的探究
作者:i_dovelemon 引言对于任何游戏来说,碰撞检测和碰撞处理都是非常重要的内容。最近自己在编写一个俯视角的类rouge-like的游戏。游戏基于网格来设计地图,在游戏设计过程中,尤其是在设计游戏的碰撞系统的时候遇到了麻烦。经过多方面的努力,终于解决了问题,现在就此问题,记录下我的心得和体会。 碰撞检测在我的游戏中,大部分的时候都是使用AABB-AABB这种碰撞盒来进行碰撞。对于此种的碰撞体,在前面的文章中也讲述过了,有很多的描述方式。我选用的是Max-Min的描述方式,即使用一个最大点Max和一个最小点Min。这样对于两个AABB盒,我们只要简单的使用下面的代码就能够判断是否发生碰撞了: bool AABB::intersectWithAABB(AABB aabb)
{
if(aabb.vMax.x < m_vMin.x) return false ;
if(aabb.vMax.y < m_vMin.y) return false ;
if(aabb.vMin.x > m_vMax.x) return false ;
if(aabb.vMin.y > m_vMax.y) return false ;
return true ;
}// end for intersectWithAABB
对于AABB碰撞盒来说,这种检测方式最简单,也最实用。 碰撞处理思路由来正如大家看到的一样,这样的游戏碰撞检测是非常容易的,我在编写的时候,也是如上所示那样编写的。但是,我在进行碰撞处理的时候,即发生玩家与障碍物发生碰撞之后,该如何反应上面遇到了问题。我原先的方案在实际运行之后,发现玩家会在地图上乱窜,完全没有碰到障碍物的那种感觉。所以为了更好的研究这个问题的解决方案,我另外开辟了一个应用程序来专门对这个问题进行了研究。 实际测试在明白了上面的那个处理方法能够带来比较好的效果之后,我就想:能不能使用同样的思路,在我的游戏中,当我们检测到碰撞的时候,只要强制的将玩家放在一个刚好触碰的障碍物的位置就可以了。为此,我马上在新开辟的应用程序中编写了如下的代码: #ifndef __HELLOWORLD_SCENE_H__
#define __HELLOWORLD_SCENE_H__
#include "cocos2d.h"
class HelloWorld : public cocos2d::CCLayer
{
public:
// Here's a difference. Method 'init' in cocos2d-x returns bool,instead of returning 'id' in cocos2d-iphone
virtual bool init();
// there's no 'id' in cpp,so we recommand to return the exactly class pointer
static cocos2d::CCScene* scene();
// a selector callback
void menuCloseCallback(CCObject* pSender);
// implement the "static node()" method manually
CREATE_FUNC(HelloWorld);
//
void update(float dt) ;
void collisionTest1();
void collision();
private:
cocos2d::CCSprite* m_pWalls[10] ;
cocos2d::CCSprite* m_pRole ;
};
#endif // __HELLOWORLD_SCENE_H__
#include "HelloWorldScene.h"
using namespace cocos2d;
CCScene* HelloWorld::scene()
{
CCScene * scene = NULL;
do
{
// 'scene' is an autorelease object
scene = CCScene::create();
CC_BREAK_IF(! scene);
// 'layer' is an autorelease object
HelloWorld *layer = HelloWorld::create();
CC_BREAK_IF(! layer);
// add layer as a child to scene
scene->addChild(layer);
} while (0);
// return the scene
return scene;
}
// on "init" you need to initialize your instance
bool HelloWorld::init()
{
bool bRet = false;
do
{
//////////////////////////////////////////////////////////////////////////
// super init first
//////////////////////////////////////////////////////////////////////////
CC_BREAK_IF(! CCLayer::init());
//////////////////////////////////////////////////////////////////////////
// add your codes below...
//////////////////////////////////////////////////////////////////////////
m_pRole = CCSprite::create("Wood.png");
addChild(m_pRole);
m_pRole->setPosition(ccp(100,100));
memset(m_pWalls,0,sizeof(CCSprite*) * 6);
m_pWalls[0] = CCSprite::create("Wall.png");
m_pWalls[0]->setPosition(ccp(160,120));
addChild(m_pWalls[0]);
m_pWalls[1] = CCSprite::create("Wall.png");
m_pWalls[1]->setPosition(ccp(160,120+32));
addChild(m_pWalls[1]);
m_pWalls[2] = CCSprite::create("Wall.png");
m_pWalls[2]->setPosition(ccp(160+32,120+62));
addChild(m_pWalls[2]);
m_pWalls[3] = CCSprite::create("Wall.png");
m_pWalls[3]->setPosition(ccp(160+64,120-32));
addChild(m_pWalls[3]);
scheduleUpdate();
bRet = true;
} while (0);
return bRet;
}
void HelloWorld::menuCloseCallback(CCObject* pSender)
{
// "close" menu item clicked
CCDirector::sharedDirector()->end();
}
void HelloWorld::update(float dt)
{
CCPoint pos = m_pRole->getPosition();
if(GetKeyState(VK_UP) & 0x8000)
{
pos.y += 3 ;
}
else if(GetKeyState(VK_DOWN) & 0x8000)
{
pos.y -= 3 ;
}
if(GetKeyState(VK_LEFT) & 0x8000)
{
pos.x -= 3 ;
}
else if(GetKeyState(VK_RIGHT) & 0x8000)
{
pos.x += 3 ;
}
m_pRole->setPosition(pos);
collision();
}// end for update
void HelloWorld::collision()
{
for(int i = 0 ; i < 10 ; i ++)
{
if(0 == m_pWalls[i])
break ;
//check if they collided
if(m_pRole->boundingBox().intersectsRect(m_pWalls[i]->boundingBox()))
{
CCPoint rolePos = m_pRole->getPosition();
CCPoint wallPos = m_pWalls[i]->getPosition();
CCPoint temp = ::ccpSub(rolePos,wallPos);
if(abs(temp.x) > abs(temp.y))
{
if(rolePos.x >= wallPos.x &&
wallPos.x + 16 + 16 >= rolePos.x)
{
rolePos.x = wallPos.x + 16 + 16 ;
}
else if(rolePos.x <= wallPos.x &&
rolePos.x + 16 + 16 >= wallPos.x)
{
rolePos.x = wallPos.x - 16 - 16 ;
}
}
else
{
if(rolePos.y >= wallPos.y &&
rolePos.y - 16 - 16 <= wallPos.y)
{
rolePos.y = wallPos.y + 16 + 16 ;
}
else if(rolePos.y <= wallPos.y &&
rolePos.y + 16 + 16 >=wallPos.y)
{
rolePos.y = wallPos.y - 16 - 16 ;
}
}
m_pRole->setPosition(rolePos);
}
}//end for
}// end for collision
上面的代码就是经过考虑之后,编写出来的代码,运行检测之后发现的确能够很好的工作,如图所示: 碰撞处理要点在实现这种方案的过程中,我也经过了多次尝试才成功。接下来向大家讲述下这种方案的处理要点。 第一点: CCPoint rolePos = m_pRole->getPosition();
CCPoint wallPos = m_pWalls[i]->getPosition();
CCPoint temp = ::ccpSub(rolePos,wallPos);
这个temp就是玩家位置与障碍物位置之间的向量差。在获取了这个向量差之后,我们只要比较下这个向量差的X和Y轴的分量的绝对值的大小,就能够知道,玩家是在哪个方向上与障碍物碰撞在一起的。比如,当X轴的分量绝对值大于Y轴的值的时候,就表示玩家是与障碍物在X轴向上碰撞的,反之则是在Y轴向上发生了碰撞。如果对这个描述不是很理解,看下下图: 第二点: if(rolePos.x >= wallPos.x &&
wallPos.x + 16 + 16 >= rolePos.x)
{
rolePos.x = wallPos.x + 16 + 16 ;
}
else if(rolePos.x <= wallPos.x &&
rolePos.x + 16 + 16 >= wallPos.x)
{
rolePos.x = wallPos.x - 16 - 16 ;
}
and
if(rolePos.y >= wallPos.y &&
rolePos.y - 16 - 16 <= wallPos.y)
{
rolePos.y = wallPos.y + 16 + 16 ;
}
else if(rolePos.y <= wallPos.y &&
rolePos.y + 16 + 16 >=wallPos.y)
{
rolePos.y = wallPos.y - 16 - 16 ;
}
第三点: 好了,这个问题就讲解这么多。关于如果游戏中使用了其他的诸如Sphere和OBB碰撞体,又该如何解决的问题,等到我以后遇到,并且找到解决方案之后在分享给大家!!! (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |