基于cocos2d-x对俯视角游戏碰撞检测及碰撞处理的探究

作者:i_dovelemon

来源:CSDN

日期:2015/2/10

主题:俯视角游戏,碰撞检测,碰撞处理

引言

对于任何游戏来说,碰撞检测和碰撞处理都是非常重要的内容。最近自己在编写一个俯视角的类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碰撞盒来说,这种检测方式最简单,也最实用。

碰撞处理

思路由来

正如大家看到的一样,这样的游戏碰撞检测是非常容易的,我在编写的时候,也是如上所示那样编写的。但是,我在进行碰撞处理的时候,即发生玩家与障碍物发生碰撞之后,该如何反应上面遇到了问题。我原先的方案在实际运行之后,发现玩家会在地图上乱窜,完全没有碰到障碍物的那种感觉。所以为了更好的研究这个问题的解决方案,我另外开辟了一个应用程序来专门对这个问题进行了研究。

那么,该使用什么样的处理方法才能够很合适的碰撞了?在考虑这个问题的时候,我发现在我以前写的游戏Demo中,我总会给游戏加上一个世界的边界,大多时候是一个矩形来完成的。我控制一个矩形在这个大的矩形世界里面到处移动。当移动到边缘的时候,我会检测它是否超出了大的矩形世界的边缘,然后强制的将玩家的位置设置到刚好和矩形世界边缘触碰的位置处。而在使用这样的方法之后,发现我们控制的小矩形能够很好的与矩形世界进行碰撞反应。

实际测试

在明白了上面的那个处理方法能够带来比较好的效果之后,我就想:能不能使用同样的思路,在我的游戏中,当我们检测到碰撞的时候,只要强制的将玩家放在一个刚好触碰的障碍物的位置就可以了。为此,我马上在新开辟的应用程序中编写了如下的代码:

#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碰撞体,又该如何解决的问题,等到我以后遇到,并且找到解决方案之后在分享给大家!!!

时间: 2024-11-05 14:40:28

基于cocos2d-x对俯视角游戏碰撞检测及碰撞处理的探究的相关文章

基于cocos2d开发的android小游戏——採花仙

/*cocos 2d 已经成为了如今移动端游戏开发的强有力的工具,眼下主流游戏中多採用cocos 2d游戏引擎. 我也尝试了一下该引擎.我是用的是cocos2d-android,以后要移植到Cocos2d-x上.废话不多说,代码例如以下.*/ watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaGFwcHlub29t/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/South

转:高层游戏引擎——基于OGRE所实现的高层游戏引擎框架

高层游戏引擎——基于OGRE所实现的高层游戏引擎框架 这是意念自己的毕业论文,在一个具体的实践之中,意念主要负责的是物件和GUI之外的其他游戏系统.意念才学疏陋,望众位前辈不吝赐教.由于代码质量不高.环境很难于配置.资源包过大等问题,意念暂先不提供代码和程序,未来有时间的时候组织一下这些曾经的代码,再向外发布. 文过三月,也有些新的想法,以后会慢慢跟大家聊的,欢迎拍砖哦^_^. 关键字与术语: 游戏. 游戏引擎 .高层引擎.规则 .场景.物件.Terrain(地形).解释器 .Applicati

课程设计小组报告——基于ARM实验箱的捕鱼游戏的设计与实现

课程设计小组报告--基于ARM实验箱的捕鱼游戏的设计与实现 一.任务简介 1.1 任务内容 捕鱼游戏这个项目是一个娱乐性的游戏开发,该游戏可以给人们带来娱乐的同时还可以给人感官上的享受,所以很受人们的欢迎.本次游戏的程序设计包含,java swing组件的合理运用,还有图像的变动达到一个动态的动画效果,线程的运用,游戏的异常处理,等方面的知识.培养学生运用所学知识的基础理论.基本知识和基本技能,分析解决实际问题能力的一个重要环节.它与课堂教学环节彼此配合,相辅相成,在某种程度上是课堂学习的继续.

基于FPGA的飞机的小游戏

基于FPGA的飞机的小游戏 实验原理 ????该实验主要分为4个模块,采用至上而下的设计方法进行设计.由50M的晶振电路提供时钟源,VGA显示控制模块.图形显示控制模块.移动模块的时钟为25M,由时钟分频电路产生获得.时钟分频模块采用PLL进行设计,由50M时钟进行2分频获得25M时钟. ????移动模块,控制我方飞机和敌方飞机.子弹的移动,移动的速度可以通过时钟的频率进行控制,操作我方飞机的移动和子弹的发射由外部按键进行控制,控制的方式有发射子弹.左移.右移. ????图像显示控制模块,用于控

基于HTML5实现的中国象棋游戏

棋类游戏在桌面游戏中已经非常成熟,中国象棋的版本也非常多.今天这款基于HTML5技术的中国象棋游戏非常有特色,我们不仅可以选择中国象棋的游戏难度,而且可以切换棋盘的样式.程序写累了,喝上一杯咖啡,和电脑对弈几把吧,相信这HTML5中国象棋游戏的实现算法你比较清楚,可以打开源码来研究一下这款HTML5中国象棋游戏. 在线预览   源码下载 实现的代码. html代码: <div class="box" id="box"> <div class=&qu

基于HTML5实现五彩连珠小游戏

今天给大家分享一款基于HTML5实现五彩连珠小游戏.这款游戏的规则:点击彩球移动到期望的位置,每移动一次,画面将随机出现3个新的彩球:当同一颜色的彩球连成5个一行或一列或一斜线时,这5个彩球同时消失,游戏得分10分.当画面上每个方格都被彩球占满时,游戏结束. 在线预览   源码下载 实现的代码. html代码: <canvas id="canvas" height="400" width="600" style="backgrou

基于html5实现的愤怒的小鸟网页游戏

之前给大家分享一款基于html5 canvas和js实现的水果忍者网页版,今天给大家分享一款基于html5实现的愤怒的小鸟网页游戏.这款游戏适用浏览器:360.FireFox.Chrome.Safari.Opera.傲游.搜狗.世界之窗. 不支持IE8及以下浏览器.效果图如下: 在线预览   源码下载 实现的代码. html代码: <div id="chinaz"> <div id="mylegend"> loading……</div&

课程设计个人报告——基于ARM实验箱的捕鱼游戏的设计与实现

课程设计个人报告--基于ARM实验箱的捕鱼游戏的设计与实现 个人贡献 实验环境的搭建 代码调试 在电脑上成功运行 在ARM实验箱上成功实现 给程序增加功能(没成功) 研究程序代码撰写小组报告 一.实验环境 Eclipse软件开发环境: ARM实验箱(HonyaS5PC100): windows操作系统. 二.实践内容 Windows环境下ARM集成开发环境的搭建与使用: 安装软件到模拟器: 连接ARM实验箱与PC机: 将工程代码在ARM实验箱上实现: 给程序增加新的功能. 三.实践步骤 3.1

飞行游戏中的碰撞算法-边界框碰撞检测

参考源地址http://xxxxxfsadf.iteye.com/blog/540669 在飞行射击游戏中,我们的飞机大多都是三角形的,我们可以用三角形作近似的边界框.现在我们假设飞机是一个正三角形(或等要三角形,我想如果谁把飞机设计成左右不对称的怪物,那他的审美观一定有问题),我的飞机是正着的.向上飞的三角形,敌人的飞机是倒着的.向下飞的三角形,且飞机不会旋转(大部分游戏中都是这样的).我们可以这样定义飞机:中心点O(Xo,Yo),三个顶点P0(X0,Y0).P1(X1,Y1).P2(X2,Y