2013年11月21日 星期四

加菲猫 Just have a little faith. [Cocos2d-x For WP8]矩形碰撞检测

http://www.cnblogs.com/linzheng/p/3279517.html

   在游戏中我们通常会涉及到两个精灵之间的碰撞的计算,那么在Cocos2d-x里面我们通常会用矩形碰撞检测来计算两个精灵在运动的过程中是否碰撞到了。 原理很简单,就是当运动的时候通过精灵的矩形坐标进行遍历来计算精灵之间是否有重合,如果有重合那就证明是碰撞上了。
    下面看一下下面的例子:
Ball精灵会根据帧速率来进行运动的,下面是Ball精灵的实现代码:
复制代码
#ifndef _BALL_H_
#define _BALL_H_

#include "cocos2d.h"
/*
创建一个球的精灵
*/
class Paddle;

using namespace cocos2d;

class Ball : public CCSprite
{
    CCPoint m_velocity;
public:
    Ball(void);
    virtual ~Ball(void);

    float radius();
    //BOOL initWithTexture(CCTexture2D* aTexture);
    //virtual void setTexture(CCTexture2D* newTexture);
    void move(ccTime delta);
    void collideWithPaddle(Paddle* paddle);


public:
    void setVelocity(CCPoint velocity){m_velocity = velocity;}
    CCPoint getVelocity(){return m_velocity;}

public:
    static Ball* ballWithTexture(CCTexture2D* aTexture);
};

#endif


#include "pch.h"
#include "Ball.h"
#include "Paddle.h"

Ball::Ball(void)
{
}

Ball::~Ball(void)
{
}

float Ball::radius()
{
    return getTexture()->getContentSize().width / 2;
}
//使用CCTexture2D创建ball精灵
Ball* Ball::ballWithTexture(CCTexture2D* aTexture)
{
    Ball* pBall = new Ball();
    pBall->initWithTexture(aTexture);
    pBall->autorelease();

    return pBall;
}
//移动ball精灵
void Ball::move(ccTime delta)
{
    //根据m_velocity的数值设置ball精灵的位置
    this->setPosition( ccpAdd(getPosition(), ccpMult(m_velocity, delta)) );
    
    if (getPosition().x > 320 - radius()) 
    {
        setPosition( ccp( 320 - radius(), getPosition().y) );
        m_velocity.x *= -1;
    } 
    else if (getPosition().x < radius()) 
    {
        setPosition( ccp(radius(), getPosition().y) );
        m_velocity.x *= -1;
    }
}
//判断是否碰撞到paddle精灵
void Ball::collideWithPaddle(Paddle* paddle)
{
    //获取paddle精灵的矩形位置
    CCRect paddleRect = paddle->rect();
    //转化成绝对的位置
    paddleRect.origin.x += paddle->getPosition().x;
    paddleRect.origin.y += paddle->getPosition().y;
    //获取paddle精灵的矩形的相关数值
    float lowY = paddleRect.getMinY();  //CCRect::getMidY(paddleRect);
    float midY = paddleRect.getMidY(); //CCRect::CCRectGetMidY(paddleRect);
    float highY =paddleRect.getMaxY();// CCRect::CCRectGetMaxY(paddleRect);
    
    float leftX = paddleRect.getMinX();//CCRect::CCRectGetMinX(paddleRect);
    float rightX =paddleRect.getMaxX(); //CCRect::CCRectGetMaxX(paddleRect);
    
    if (getPosition().x > leftX && getPosition().x < rightX) {
    
        bool hit = false;
        float angleOffset = 0.0f; 
        //判断是否碰撞到paddle精灵
        if (getPosition().y > midY && getPosition().y <= highY + radius()) 
        {
            setPosition( CCPointMake(getPosition().x, highY + radius()) );
            hit = true;
            angleOffset = (float)M_PI / 2;
        }
        else if (getPosition().y < midY && getPosition().y >= lowY - radius()) 
        {
            setPosition( CCPointMake(getPosition().x, lowY - radius()) );
            hit = true;
            angleOffset = -(float)M_PI / 2;
        }
        
        if (hit) 
        {
            //碰撞到则调整方向
            float hitAngle = ccpToAngle(ccpSub(paddle->getPosition(), getPosition())) + angleOffset;
            
            float scalarVelocity = ccpLength(m_velocity) * 1.05f;
            float velocityAngle = -ccpToAngle(m_velocity) + 0.5f * hitAngle;
            
            m_velocity = ccpMult(ccpForAngle(velocityAngle), scalarVelocity);
        }
    }    
} 
复制代码

Paddle精灵相当于是挡板的意思,Paddle精灵是静止的,当Ball精灵碰撞到Paddle精灵的时候,运动的轨迹就会发生变化。
Paddle精灵的代码:
复制代码
#ifndef _PADDLE_H_
#define _PADDLE_H_

#include "cocos2d.h"

using namespace cocos2d;
/*
创建一个挡板精灵
*/
typedef enum tagPaddleState 
{
    kPaddleStateGrabbed,
    kPaddleStateUngrabbed
} PaddleState; 

class Paddle : public CCSprite, public CCTargetedTouchDelegate
{
    PaddleState        m_state;

public:
    Paddle(void);
    virtual ~Paddle(void);

    CCRect rect();
    bool initWithTexture(CCTexture2D* aTexture);
    virtual void onEnter();
    virtual void onExit();
    bool containsTouchLocation(CCTouch* touch);
    virtual bool ccTouchBegan(CCTouch* touch, CCEvent* event);
    virtual void ccTouchMoved(CCTouch* touch, CCEvent* event);
    virtual void ccTouchEnded(CCTouch* touch, CCEvent* event);

    virtual void touchDelegateRetain();
    virtual void touchDelegateRelease();

    static Paddle* paddleWithTexture(CCTexture2D* aTexture);
};

#endif


#include "pch.h"
#include "Paddle.h"

Paddle::Paddle(void)
{
}

Paddle::~Paddle(void)
{
}
//获取paddle精灵的矩形位置
CCRect Paddle::rect()
{
    CCSize s = getTexture()->getContentSize();
    return CCRectMake(-s.width / 2, -s.height / 2, s.width, s.height);
}

Paddle* Paddle::paddleWithTexture(CCTexture2D* aTexture)
{
    Paddle* pPaddle = new Paddle();
    pPaddle->initWithTexture( aTexture );
    pPaddle->autorelease();

    return pPaddle;
}

bool Paddle::initWithTexture(CCTexture2D* aTexture)
{
    if( CCSprite::initWithTexture(aTexture) ) 
    {
        m_state = kPaddleStateUngrabbed;
    }
    
    return true;
}

void Paddle::onEnter()
{
    CCTouchDispatcher* pDispatcher = CCDirector::sharedDirector()->getTouchDispatcher();
    pDispatcher->addTargetedDelegate(this, 0, true);
    CCSprite::onEnter();
}

void Paddle::onExit()
{
    CCTouchDispatcher* pDispatcher = CCDirector::sharedDirector()->getTouchDispatcher();
    pDispatcher->removeDelegate(this);
    CCSprite::onExit();
}    

bool Paddle::containsTouchLocation(CCTouch* touch)
{
    return rect().containsPoint(convertTouchToNodeSpaceAR(touch));;//CCRect::containsPoint(rect(), convertTouchToNodeSpaceAR(touch));
}

bool Paddle::ccTouchBegan(CCTouch* touch, CCEvent* event)
{
    if (m_state != kPaddleStateUngrabbed) return false;
    if ( !containsTouchLocation(touch) ) return false;
    
    m_state = kPaddleStateGrabbed;
    return true;
}
//移动paddle精灵
void Paddle::ccTouchMoved(CCTouch* touch, CCEvent* event)
{
    CCAssert(m_state == kPaddleStateGrabbed, L"Paddle - Unexpected state!");    
    
    CCPoint touchPoint = touch->getLocationInView();
    touchPoint = CCDirector::sharedDirector()->convertToGL( touchPoint );
    
    setPosition( CCPointMake(touchPoint.x, getPosition().y) );
}

void Paddle::ccTouchEnded(CCTouch* touch, CCEvent* event)
{
    CCAssert(m_state == kPaddleStateGrabbed, L"Paddle - Unexpected state!");    
    
    m_state = kPaddleStateUngrabbed;
} 

void Paddle::touchDelegateRetain()
{
    this->retain();
}

void Paddle::touchDelegateRelease()
{
    this->release();
}
复制代码
下面是实现碰撞的Layer:
复制代码
//------------------------------------------------------------------
//
// 初始化[碰撞的Layer
//
//------------------------------------------------------------------
PongLayer::PongLayer()
{
    m_ballStartingVelocity = CCPointMake(20.0f, -100.0f);
    //创建ball精灵
    m_ball = Ball::ballWithTexture( CCTextureCache::sharedTextureCache()->addImage("cat.png") );
    m_ball->setPosition( CCPointMake(160.0f, 240.0f) );
    m_ball->setVelocity( m_ballStartingVelocity );
    addChild( m_ball );
    m_ball->retain();
    
    //创建4个Paddle精灵
    CCTexture2D* paddleTexture = CCTextureCache::sharedTextureCache()->addImage("paddle.png");
    m_paddles = new CCArray(4);
    Paddle* paddle = Paddle::paddleWithTexture(paddleTexture);
    paddle->setPosition( CCPointMake(160, 15) );
    m_paddles->addObject( paddle );
    
    paddle = Paddle::paddleWithTexture( paddleTexture );
    paddle->setPosition( CCPointMake(160, 480 - kStatusBarHeight - 15) );
    m_paddles->addObject( paddle );
    
    paddle = Paddle::paddleWithTexture( paddleTexture );
    paddle->setPosition( CCPointMake(160, 100) );
    m_paddles->addObject( paddle );
    
    paddle = Paddle::paddleWithTexture( paddleTexture );
    paddle->setPosition( CCPointMake(160, 480 - kStatusBarHeight - 100) );
    m_paddles->addObject( paddle );

    CCObject* arrayItem;
    CCARRAY_FOREACH(m_paddles, arrayItem){
       paddle = (Paddle*)(arrayItem);
       if(!paddle)
                break;
            addChild(paddle);
    }
    //每一帧刷新ball精灵的运动
    schedule( schedule_selector(PongLayer::doStep) );
}

PongLayer::~PongLayer()
{
    m_ball->release();
    m_paddles->release();
}

void PongLayer::resetAndScoreBallForPlayer(int player)
{
    m_ballStartingVelocity = ccpMult(m_ballStartingVelocity, -1.1f);
    m_ball->setVelocity( m_ballStartingVelocity );
    m_ball->setPosition( CCPointMake(160.0f, 240.0f) );
}

void PongLayer::doStep(ccTime delta)
{
    //移动ball精灵
    m_ball->move(delta);
    Paddle* paddle;
    CCObject* arrayItem;
    CCARRAY_FOREACH(m_paddles, arrayItem){
       paddle = (Paddle*)(arrayItem);
       if(!paddle)
                break;
            //判断ball精灵是否碰到paddle精灵
            m_ball->collideWithPaddle( paddle );
    }
    //判断是否碰到边界
    if (m_ball->getPosition().y > 480 - kStatusBarHeight + m_ball->radius())
        resetAndScoreBallForPlayer( kLowPlayer );
    else if (m_ball->getPosition().y < -m_ball->radius())
        resetAndScoreBallForPlayer( kHighPlayer );
    m_ball->draw();
} 
复制代码
在helloworld项目中加入该Layer
复制代码
CCScene* HelloWorld::scene()
{
    CCScene * scene = NULL;
    do 
    {   // 'scene'是一个可以自动释放的对象
        scene = CCScene::create();
        //创建失败跳出循环
        CC_BREAK_IF(! scene);
        PongLayer *pongLayer = new PongLayer();
        scene->addChild(pongLayer);
    } while (0);

    // 返回scene
    return scene;
}
复制代码
运行的效果: