欢迎光临Software MyZone,有问题可留言或到站点论坛发帖,争取第一时间帮忙解决 || 站点论坛:火龙论坛 || 淘宝小店:应小心的易淘屋 【欢迎大家提建设性意见】

cocos2d-x像素点击事件:精灵【FDPixelSprite】

本文为firedragonpzy原创,转载务必在明显处注明:
转载自【Softeware MyZone】原文链接: 
http://www.firedragonpzy.com.cn/index.php/archives/3233

欢迎热爱编程的朋友们参与到cocos2d-x编程中,为了给大家提供良好的交流环境,网站以开启QQ群
Software MyZone:66202765(群号,欢迎加入,若满,请加1群)
Software MyZone 1群(2dx):286504621
【加群请写:Software MyZone或者是firedragonpzy】
群论坛:火龙论坛正试运营阶段,欢迎大家多提些建设性意见……

最近研究了一下透明图片的点击事件即png图片中透明区域的点击。当时用j2me的时候,记得设置个值就可以处理是像素点击还是图片点击,不对,是碰撞检测。看了看jdk,它处理了像素,处理如下:


public void handlesinglepixel(int x, int y, int pixel) {
    
    int alpha = (pixel >> 24) & 0xff;
    
    int red   = (pixel >> 16) & 0xff;
    
    int green = (pixel >>  8) & 0xff;
    
    int blue  = (pixel      ) & 0xff;
    
    // Deal with the pixel as necessary...
    
}

这时候使用的像素格式是ARGB。

那么,在2dx中,我们也是可以这么处理的,首先看纹理中的一个函数:


bool CCTexture2D::initPremultipliedATextureWithImage(CCImage *image, unsigned int width, unsigned int height){
    
    unsigned char*            tempData = image->getData();
    
    this->setFDImageData(tempData);
    
    unsigned int*             inPixel32 = NULL;
    
    unsigned char*            inPixel8 = NULL;
    
    unsigned short*           outPixel16 = NULL;
    
    bool                      hasAlpha = image->hasAlpha();
    
    CCSize                    imageSize = CCSizeMake((float)(image->getWidth()), (float)(image->getHeight()));
    
    CCTexture2DPixelFormat    pixelFormat;
    
    size_t                    bpp = image->getBitsPerComponent();
    
     
    
    // compute pixel format
    
    if(hasAlpha){
        
        pixelFormat = g_defaultAlphaPixelFormat;
        
    } else {
        
        if (bpp >= 8) {
            
            pixelFormat = kCCTexture2DPixelFormat_RGB888;
            
        }else  {
            
            pixelFormat = kCCTexture2DPixelFormat_RGB565;
            
        }
        
    }
    
    // Repack the pixel data into the right format
    
    unsigned int length = width * height;
    
    if (pixelFormat == kCCTexture2DPixelFormat_RGB565) {
        
        if (hasAlpha) {
            
            // Convert "RRRRRRRRRGGGGGGGGBBBBBBBBAAAAAAAA" to "RRRRRGGGGGGBBBBB"
            
            tempData = new unsigned char[width * height * 2];
            
            outPixel16 = (unsigned short*)tempData;
            
            inPixel32 = (unsigned int*)image->getData();
            
            for(unsigned int i = 0; i < length; ++i, ++inPixel32){
                
                *outPixel16++ =
                
                ((((*inPixel32 >>  0) & 0xFF) >> 3) << 11) |  // R
                
                ((((*inPixel32 >>  8) & 0xFF) >> 2) << 5)  |  // G
                
                ((((*inPixel32 >> 16) & 0xFF) >> 3) << 0);    // B
                
            }
            
        }else  {
            
            // Convert "RRRRRRRRRGGGGGGGGBBBBBBBB" to "RRRRRGGGGGGBBBBB"
            
            tempData = new unsigned char[width * height * 2];
            
            outPixel16 = (unsigned short*)tempData;
            
            inPixel8 = (unsigned char*)image->getData();
            
            for(unsigned int i = 0; i < length; ++i) {
                
                *outPixel16++ =
                
                (((*inPixel8++ & 0xFF) >> 3) << 11) |  // R
                
                (((*inPixel8++ & 0xFF) >> 2) << 5)  |  // G
                
                (((*inPixel8++ & 0xFF) >> 3) << 0);    // B
                
            }
            
        }
        
    }else if (pixelFormat == kCCTexture2DPixelFormat_RGBA4444) {
        
        // Convert "RRRRRRRRRGGGGGGGGBBBBBBBBAAAAAAAA" to "RRRRGGGGBBBBAAAA"
        
        inPixel32 = (unsigned int*)image->getData();
        
        tempData = new unsigned char[width * height * 2];
        
        outPixel16 = (unsigned short*)tempData;
        
        for(unsigned int i = 0; i < length; ++i, ++inPixel32) {
            
            *outPixel16++ =
            
            ((((*inPixel32 >> 0) & 0xFF) >> 4) << 12) | // R
            
            ((((*inPixel32 >> 8) & 0xFF) >> 4) <<  8) | // G
            
            ((((*inPixel32 >> 16) & 0xFF) >> 4) << 4) | // B
            
            ((((*inPixel32 >> 24) & 0xFF) >> 4) << 0);  // A
            
        }
        
    } else if (pixelFormat == kCCTexture2DPixelFormat_RGB5A1)
        
    {
        
        // Convert "RRRRRRRRRGGGGGGGGBBBBBBBBAAAAAAAA" to "RRRRRGGGGGBBBBBA"
        
        inPixel32 = (unsigned int*)image->getData();
        
        tempData = new unsigned char[width * height * 2];
        
        outPixel16 = (unsigned short*)tempData;
        
        for(unsigned int i = 0; i < length; ++i, ++inPixel32) {
            
            *outPixel16++ =
            
            ((((*inPixel32 >> 0) & 0xFF) >> 3) << 11) | // R
            
            ((((*inPixel32 >> 8) & 0xFF) >> 3) <<  6) | // G
            
            ((((*inPixel32 >> 16) & 0xFF) >> 3) << 1) | // B
            
            ((((*inPixel32 >> 24) & 0xFF) >> 7) << 0);  // A
            
        }
        
    }else if (pixelFormat == kCCTexture2DPixelFormat_A8){
        
        // Convert "RRRRRRRRRGGGGGGGGBBBBBBBBAAAAAAAA" to "AAAAAAAA"
        
        inPixel32 = (unsigned int*)image->getData();
        
        tempData = new unsigned char[width * height];
        
        unsigned char *outPixel8 = tempData;
        
        for(unsigned int i = 0; i < length; ++i, ++inPixel32) {
            
            *outPixel8++ = (*inPixel32 >> 24) & 0xFF;  // A
            
        }
        
    }
    
    if (hasAlpha && pixelFormat == kCCTexture2DPixelFormat_RGB888){
        
        // Convert "RRRRRRRRRGGGGGGGGBBBBBBBBAAAAAAAA" to "RRRRRRRRGGGGGGGGBBBBBBBB"
        
        inPixel32 = (unsigned int*)image->getData();
        
        tempData = new unsigned char[width * height * 3];
        
        unsigned char *outPixel8 = tempData;
        
        for(unsigned int i = 0; i < length; ++i, ++inPixel32)  {
            
            *outPixel8++ = (*inPixel32 >> 0) & 0xFF; // R
            
            *outPixel8++ = (*inPixel32 >> 8) & 0xFF; // G
            
            *outPixel8++ = (*inPixel32 >> 16) & 0xFF; // B
            
        }
        
    }
    
    initWithData(tempData, pixelFormat, width, height, imageSize);
    
    if (tempData != image->getData()){
        
        delete [] tempData;
        
    }
    
     
    
    m_bHasPremultipliedAlpha = image->isPremultipliedAlpha();
    
    return true;
    
}

这里面针对kCCTexture2DPixelFormat_RGB888像素格式的图片进行了这么的处理:


*outPixel8++ = (*inPixel32 >> 0) & 0xFF; // R

*outPixel8++ = (*inPixel32 >> 8) & 0xFF; // G

*outPixel8++ = (*inPixel32 >> 16) & 0xFF; // B

那么,我们就可以考虑在点击事件中处理这个透明区域的问题了,began事件如下:


bool FDPixelSprite::ccTouchBegan(cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent){
    
    if (this->isContainTouchLocation(pTouch) ) {
        
        ccColor4B c = {0, 0, 0, 0};
        
        CCSize winSize = CCDirector::sharedDirector()->getWinSize();
        
        CCPoint touchPoint = pTouch->getLocationInView();
        
        CCSize cSize = this->getContentSize();
        
        CCPoint pos(this->getPositionX() - cSize.width/2,winSize.height-this->getPositionY()- cSize.height/2);
        
        CCPoint localPoint = ccp(touchPoint.x - pos.x,
                                 
                                 touchPoint.y -pos.y);
        
        unsigned int x = localPoint.x, y = localPoint.y;
        
        unsigned char *data_ = this->getTexture()->getFDImageData();
        
        unsigned int *pixel = (unsigned int *)data_;
        
        pixel = pixel + (y * (int)this->getContentSize().width)* 1 + x * 1;
        
        c.r = *pixel & 0xff;
        
        c.g = (*pixel >> 8) & 0xff;
        
        c.b = (*pixel >> 16) & 0xff;
        
        c.a = (*pixel >> 24) & 0xff;
        
        if (c.a == 0) {
            
            CCLog("firedragonpzy:ccTouchBegan");
            
            return false;
            
        }else {
            
            return true;
            
        }
        
    }
    
    return false;
    
}

处理原理

获取点击的像素,判断当前的a值是否为0,若为0,则是透明区域。

注意点:

图片的坐标原点在图片的左上角,这里大家需要做一下坐标的转化,把点击屏幕的坐标转化为相对于图像左上角的坐标即可。

关于图像,它的颜色表示分为:R、G、B、A四部分,各占八位。

代码中:*pixel >> (n)bit,为了得到单独的red,green,blue,alpha值,具体得到什么,目前不是很清楚,貌似什么进制【时间原因,改日谈……】不过可以给大家提供个例子。

网上例子(jdk):

比如大红色用整数表示就是00 FF 00 00
pixel >> somebits是为了得到单独的alpha,red,green,blue值
右移24与255与操作得到alpha
16位得到red……
如果不& 0xff的话,上面的int green = (pixel >>  8) & 0xff;
得到的green值是FF 00 也就是65280,当然就不是我们要得到值
与操作是为了把高位削去!!!!

【因为pixel是一个整数
图像的表示分四部分:alpha值,red,green,blue
每一个值占一个字节,一个字节有8位二进制】

求解释……欢迎拍砖……

推荐阅读http://www.cocos2d-x.org/boards/6/topics/3472

注意啦,注意啦,下面还有个赠送篇、、、、【产生赠送篇的缘由:一群友说如果设置了锚点,怎么处理啊,那代码起不是不对了,所以我又产生了赠送篇,不过使用精灵的时候,基本都是以(0.5,0.5)为锚点,我开始没考虑这问题是源于这,呵呵,假设你锚点不是(0.5,0.5)旋转什么的可能就……效果不好。不过既然有不是CCPointZero的需求,我就写了支持不同锚点的FDPixelSprite,不过建议大家使用针对锚点为(0.5,0.5)的,效率高……】

现在贴出我的FDPixelSprite,大家可以直接使用此精灵处理含有透明区域图片的点击问题:

FDPixelSprite.h


//

 

//  FDPixelSprite.h

 

//  PixelDemo

 

//

 

//  Created by firedragonpzy on 13-2-19.

 

//

 

//

 

#ifndef __PixelDemo__FDPixelSprite__

 

#define __PixelDemo__FDPixelSprite__

 

#include "cocos2d.h"

 

USING_NS_CC;

 

class FDPixelSprite : public CCSprite, public CCTargetedTouchDelegate {
    
public:
    
    FDPixelSprite();
    
    virtual ~FDPixelSprite();
    
    void onEnter();
    
    void onExit();
    
    static FDPixelSprite* create( const char *pszFileName );
    
    CCRect atlasRect();
    
    bool isContainTouchLocation(CCTouch *pTouch);
    
    bool ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent);
    
    void ccTouchMoved(CCTouch *pTouch, CCEvent *pEvent);
    
    void ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent);
    
    CC_SYNTHESIZE(const char*, m_pName,Name);
    
};

 

#endif /* defined(__PixelDemo__FDPixelSprite__) */

FDPixelSprite.cpp


//

 

//  FDPixelSprite.cpp

 

//  PixelDemo

 

//

 

//  Created by firedragonpzy on 13-2-19.

 

//

 

//

 

#include "FDPixelSprite.h"

 

FDPixelSprite::FDPixelSprite(){}

 

FDPixelSprite::~FDPixelSprite(){}

 

FDPixelSprite* FDPixelSprite::create(const char *pszFileName){
    
    FDPixelSprite *sprite = new FDPixelSprite();
    
    if (sprite && sprite->initWithFile(pszFileName)) {
        
        sprite->setName(pszFileName);
        
        sprite->autorelease();
        
        return sprite;
        
    }
    
    CC_SAFE_DELETE(sprite);
    
    sprite = NULL;
    
    return NULL;
    
}

 

void FDPixelSprite::onEnter(){
    
    CCSprite::onEnter();
    
    CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate(this, 0, true);
    
}

 

void FDPixelSprite::onExit(){
    
    CCSprite::onExit();
    
    CCDirector::sharedDirector()->getTouchDispatcher()->removeDelegate(this);
    
}

 

bool FDPixelSprite::ccTouchBegan(cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent){
    
    if (this->isContainTouchLocation(pTouch) ) {
        
        ccColor4B c = {0, 0, 0, 0};
        
        CCSize winSize = CCDirector::sharedDirector()->getWinSize();
        
        CCPoint touchPoint = pTouch->getLocationInView();
        
        CCSize cSize = this->getContentSize();
        
        CCPoint pos(this->getPositionX() - cSize.width/2,winSize.height-this->getPositionY()- cSize.height/2);
        
        CCPoint localPoint = ccp(touchPoint.x - pos.x,
                                 
                                 touchPoint.y -pos.y);
        
        unsigned int x = localPoint.x, y = localPoint.y;
        
        unsigned char *data_ = this->getTexture()->getFDImageData();
        
        unsigned int *pixel = (unsigned int *)data_;
        
        pixel = pixel + (y * (int)this->getContentSize().width)* 1 + x * 1;
        
        c.r = *pixel & 0xff;
        
        c.g = (*pixel >> 8) & 0xff;
        
        c.b = (*pixel >> 16) & 0xff;
        
        c.a = (*pixel >> 24) & 0xff;
        
        if (c.a == 0) {
            
            CCLog("firedragonpzy:ccTouchBegan");
            
            return false;
            
        }else
            
        {
            
            return true;
            
        }
        
    }
    
    return false;
    
}

 

void FDPixelSprite::ccTouchMoved(cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent){
    
    CCPoint pos = this->getPosition();
    
    CCPoint sub = pTouch->getDelta();
    
    this->setPosition(ccpAdd(pos, sub));
    
}

 

void FDPixelSprite::ccTouchEnded(cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent){
    
    CCLog("firedragonpzy:ccTouchEnded");
    
}

 

CCRect FDPixelSprite::atlasRect(){
    
    CCSize cSize = this->getContentSize();
    
    return CCRectMake(-cSize.width/2, -cSize.height/2, cSize.width, cSize.height);
    
}

 

bool FDPixelSprite::isContainTouchLocation(cocos2d::CCTouch *pTouch){
    
    return this->atlasRect().containsPoint(convertTouchToNodeSpaceAR(pTouch));
    
}


————————————–赠送篇———————————————-
为什么产生赠送篇 ,上面已经解释的很清楚了,不废话了,开门见山:
改为适配不同锚点需要改动两个地方:
(一):区域的判断:


CCRect FDPixelSprite::atlasRect(){
    
    CCSize cSize = this->getContentSize();
    
    CCPoint point = this->getAnchorPointInPoints();
    
    return CCRectMake( -point.x, -point.y, cSize.width,cSize.height);
    
}


(二)相对于图片像素的转化


bool FDPixelSprite::ccTouchBegan(cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent){
    
    if (this->isContainTouchLocation(pTouch) ) {
        
        ccColor4B c = {0, 0, 0, 0};
        
        CCSize winSize = CCDirector::sharedDirector()->getWinSize();
        
        CCPoint touchPoint = pTouch->getLocationInView();
        
        CCSize cSize = this->getContentSize();
        
        CCPoint point =this->getAnchorPointInPoints();
        
        point = ccp(cSize.width - point.x,cSize.height- point.y);
        
        CCPoint pos(this->getPositionX() - point.x,winSize.height-this->getPositionY()- point.y);
        
        CCPoint localPoint = ccp(touchPoint.x - pos.x, touchPoint.y -pos.y);
        
        unsigned int x = localPoint.x, y = localPoint.y;
        
        unsigned char *data_ = this->getTexture()->getFDImageData();
        
        unsigned int *pixel = (unsigned int *)data_;
        
        pixel = pixel + (y * (int)this->getContentSize().width)* 1 + x * 1;
        
        c.r = *pixel & 0xff;
        
        c.g = (*pixel >> 8) & 0xff;
        
        c.b = (*pixel >> 16) & 0xff;
        
        c.a = (*pixel >> 24) & 0xff;
        
        if (c.a == 0) {
            
            CCLog("firedragonpzy:ccTouchBegan----------");
            
            return false;
            
        }else{
            
            return true;
            
        }
        
    }
    
    return false;
    
}


Demo地址连接:https://github.com/firedragonpzy/PixelDemo.git

注意:

1)在Demo使用的是CCImage的方法,效率可能较低。不过demo中也提供了上述情况,在获取data的时候使用两种方法,第一种支持对称图像,第二种支持所有图像,如果你有兴趣,可以开启第一种研究下,修改为支持所有图像,完后可以M我,期待……

2)如果pDirector->setContentScaleFactor 进行了这个操作,要对你的位置和大小进行响应的操作均乘上这个参数

3)如果游戏进行了缩放操作,还牵扯到坐标转换的,还要对你的位置进行响应的操作,均处理下你的放缩参数。关于放缩处理可以参考:cocos2d-x场景缩放后的区域碰撞检测

  1. 这个到3.0的版本后,就没法直接获取到getFDImageData()了,请问楼主有办法实现吗?

  2. Pingback: cocos2d-x像素点击事件:精灵【FDPixelSprite】 - 移动端开发 - 无忧网