cocos2dx 3.17.2 lua 像素触摸判断


在实际应用场景时,按钮不会按照矩形这么规矩,大多数情况会出现异形按钮和图片的点击及触摸判断,比如建筑,人物等,但是实际上cocos2dx里的是按照宽高的矩形区域判断的

所以在这记录下我的解决方案,一开始我想到两个想法,一是根据像素是否为透明像素判断,因为所谓的异形图片其实就是有像素点的区域,没有像素的透明像素就是没意义,可以根据alpha是否为0判断,我也是采取的这种方案,下面提到,第二是我想到之前做捕鱼碰撞的时候用到的多边形区域,当时用polygon创建碰撞区域,添加到图片上,但是那个是物理场景,后来一想这种不太合适就算了

然后选择第一种方案之后就发现根本没有相关接口,于是乎就得去修改c++了,上网上翻了一圈发现大家基本都用的是RenderTexture实现的,于是乎借鉴一个,省时省力,何必非得去造轮子呢

参考:https://blog.csdn.net/oJianYue12/article/details/82877619

c++代码直接照搬,没啥修改

一个在CCDirector.cpp实现,文件位于frameworks\cocos2d-x\cocos\base

void Director::flushScene()
{
    _renderer->render();
}

一个在Image里面实现,文件位于frameworks\cocos2d-x\cocos\platform

Color4B Image::getPixelColor( const Vec2& pos )
{
    Color4B color = {0, 0, 0, 0};
    if ( pos.x < 0 || pos.x >= this->getWidth() || pos.y < 0 || pos.y >= this->getHeight() ) {
        return color;
    }
    auto data = this->getData();
    auto pixel = (unsigned int*) data;
    auto x = (int)pos.x;
    auto y = (int)pos.y;
    pixel = pixel + (y * this->getWidth() ) * 1 + x * 1;
    //R通道
    color.r = *pixel & 0xff;
    //G通道
    color.g = (*pixel >> 8) & 0xff;
    //B通过
    color.b = (*pixel >> 16) & 0xff;
    //Alpha通道,我们有用的就是Alpha
    color.a = (*pixel >> 24) & 0xff;
    return color;
}

c++修改之后导出到lua

然后lua的实现测试时候发现一点问题修改了下,红色为新增修改,因为实际使用时发现RenderTexture会导致image丢失,增加移动代码,使其重新渲染下

参考:https://stackoverflow.com/questions/39201877/in-cocos2dx-i-use-rendertexture-to-capture-the-screen-but-images-in-scrollview、

--@param sprite 用于判断是否点击到图片的非透明像素(Sprite,ImageView,Button测试可用,其他的有需要可以试下)
--@param x      屏幕X坐标
--@param y    屏幕Y坐标
--@return     非透明区域是否被点击
function checkIfTouchWithTransparent(sprite, x, y)
    if not checkIfTouch(sprite, x, y) then 
        -- gameprint ("没有点击图片区域") 
        return false 
    end
    local oldPos = cc.p(sprite:getPosition())
    local size = sprite:getContentSize()
    local pos = sprite:convertToNodeSpace(cc.p(x, y))
    --以下三行代码主要在ScaleFactor不为1时有效(比如高清资源)
    local scale = cc.Director:getInstance():getContentScaleFactor()
    pos.x = pos.x * scale
    pos.y = pos.y * scale
    local render = cc.RenderTexture:create(size.width, size.height)
    sprite:setPosition(cc.p(size.width/2, size.height/2))
    render:begin()
    sprite:visit()
    --自定义导出(因为cocos里的Director没有对Lua暴露getRenderer接口)
    cc.Director:getInstance():flushScene()
    render:endToLua()
    --移动一下对象重新渲染 RenderTexture to capture the screen, but images are lost
    sprite:setPosition(cc.p(0,0))
    sprite:setPosition(oldPos)
    local image = render:newImage()
    pos.y = image:getHeight() - pos.y
    --自定义导出(因为cocos里的Image没有对Lua暴露getData接口)
    local rgba = image:getPixelColor(pos)
    image:release()
    -- if rgba.a ~= 0 then 
    --     gameprint("点击了有效区域")
    -- else
    --     gameprint("点击了无效区域")
    -- end
    return (rgba.a ~= 0)
end
--@param node 用于判断是否被点击的节点
--@param x      屏幕X坐标
--@param y    屏幕Y坐标
--@return     是否被点击
function checkIfTouch(node, x, y )
    local nsp = node:convertToNodeSpace(cc.p(x, y))
    local contentSize = node:getContentSize()
    local rect = cc.rect(0, 0, contentSize.width, contentSize.height)
    return cc.rectContainsPoint(rect, nsp)
end