像素完美碰撞检测(使用cocos2d-x)

Pixel Perfect
Collision Detection (Using Cocos2d-x)

This post found its way because I couldnt find the answer to one of the questions I asked on StackOverflow (http://stackoverflow.com/questions/18546066/pixel-perfect-collision-detection-in-cocos2dx) and thought there would be others like me in search for an
answer.

Collision detection is an integral part of almost all games. It is used to find when a bullet hits an enemy or when you bump into a wall etc.
There are many different requirements when we do collision detection and depending on our game we choose one of the many detection techniques.

The default Collision detection mechanism used by games and provided in almost all game engines and frameworks is a “Bounding Box” collision.
Simply put, a “Bounding Box” collision detection system the sprites/objects being checked for collision are treated as the smallest rectangle which completely engulfs them. Then these two Boxes are checked if they are colliding with each other.

But sometimes this very simple collision detection system is not accurate. Specially when we use sprites with alpha values (mostly png files) or when our objects are rotated by some angles. See the image below:

  
 
 

Pixel – Perfect collision detection is a system where we check if the objects concerned are actually colliding rather than just being part of a bounding box which is bigger than their size. WARNING: This system though more accurate is obviously more performance
intensive and hence depending on your game requirements choose wisely about which of the different systems you want to use.

TIP: This system though written specially for Cocos2d-x framework can be easily understood and implemented for any language/framework you are using.

So its time to get our hands dirty,

We are going to develop a Singleton Class for collision detection and just plug and play this in any project we are doing.

Things used:
1. Singleton Class – CollisionDetection
2. Opengl Vertex and Fragment Shaders
3. CCRenderTexture Class – Cocos2d-x

Theory:
1. Create a CCRenderTexture which is going to serve as a secondary draw buffer.
2. We first do a simple collision detection (Bounding Box) to check if the two sprite’s bounds are colliding
3. If step 2 is a success then we are going to draw the two concerned objects in our secondary buffer we created in step 1. (We are going to set its visibility to false, so that even though we draw something, nothing will we visible to the end user)
4. Using openGL fragment shaders we are going to draw one of the objects completely RED and the other completely BLUE!

     

5. Using another of openGL functionality glReadPixels we are going to read the pixels data of all the pixels in the Rectangular area (Intersection area) of the bounding box collision
6. We are then going to loop through all the pixel values and check if a single pixel has BOTH the RED and the BLUE pixels. If they have then the objects are actually colliding or else not.

Now here is the code for the above steps. I have commented the code for you to understand what is going on. If there are any questions please leave in the comments and I will try and answer to the best of my knowledge

CollisionDetection.h

//
//  CollisionDetection.h
//  Created by Mudit Jaju on 30/08/13.
//
//  SINGLETON class for checking Pixel Based Collision Detection

#ifndef __CollisionDetection__
#define __CollisionDetection__

#include <iostream>
#include "cocos2d.h"

USING_NS_CC;

class CollisionDetection {
public:
    //Handle for getting the Singleton Object
    static CollisionDetection* GetInstance();
    //Function signature for checking for collision detection spr1, spr2 are the concerned sprites
    //pp is bool, set to true if Pixel Perfection Collision is required. Else set to false
    //_rt is the secondary buffer used in our system
    bool areTheSpritesColliding(CCSprite* spr1, CCSprite* spr2, bool pp, CCRenderTexture* _rt);
private:
    static CollisionDetection* instance;
    CollisionDetection();

    // Values below are all required for openGL shading
    CCGLProgram *glProgram;
    ccColor4B *buffer;
    int uniformColorRed;
    int uniformColorBlue;

};

#endif /* defined(__CollisionDetection__) */

CollisionDetection.cpp

//
//  CollisionDetection.cpp
//  Created by Mudit Jaju on 30/08/13.
//
//  SINGLETON class for checking Pixel Based Collision Detection

#include "CollisionDetection.h"
// Singleton Instance set to NULL initially
CollisionDetection* CollisionDetection::instance = NULL;

// Handle to get Singleton Instance
CollisionDetection* CollisionDetection::GetInstance() {
    if (instance == NULL) {
        instance = new CollisionDetection();
    }
    return instance;
}

// Private Constructor being called from within the GetInstance handle
CollisionDetection::CollisionDetection() {
    // Code below to setup shaders for use in Cocos2d-x
    glProgram = new CCGLProgram();
    glProgram->retain();
    glProgram->initWithVertexShaderFilename("SolidVertexShader.vsh", "SolidColorShader.fsh");
    glProgram->addAttribute(kCCAttributeNamePosition, kCCVertexAttrib_Position);
    glProgram->addAttribute(kCCAttributeNameTexCoord, kCCVertexAttrib_TexCoords);
    glProgram->link();
    glProgram->updateUniforms();
    glProgram->use();

    uniformColorRed = glGetUniformLocation(glProgram->getProgram(), "u_color_red");
    uniformColorBlue = glGetUniformLocation(glProgram->getProgram(), "u_color_blue");

    // A large buffer created and re-used again and again to store glReadPixels data
    buffer = (ccColor4B *)malloc( sizeof(ccColor4B) * 10000 );
}

bool CollisionDetection::areTheSpritesColliding(cocos2d::CCSprite* spr1, cocos2d::CCSprite* spr2, bool pp, CCRenderTexture* _rt) {
    bool isColliding = false;

    // Rectangle of the intersecting area if the sprites are colliding according to Bounding Box collision
    CCRect intersection;

    // Bounding box of the Two concerned sprites being saved
    CCRect r1 = spr1->boundingBox();
    CCRect r2 = spr2->boundingBox();

    // Look for simple bounding box collision
    if (r1.intersectsRect(r2)) {
        // If we're not checking for pixel perfect collisions, return true
        if (!pp) {
            return true;
        }

        float tempX;
        float tempY;
        float tempWidth;
        float tempHeight;

        //OPTIMIZE FURTHER
        //CONSIDER THE CASE WHEN ONE BOUDNING BOX IS COMPLETELY INSIDE ANOTHER BOUNDING BOX!
        if (r1.getMaxX() > r2.getMinX()) {
            tempX = r2.getMinX();
            tempWidth = r1.getMaxX() - r2.getMinX();
        } else {
            tempX = r1.getMinX();
            tempWidth = r2.getMaxX() - r1.getMinX();
        }

        if (r2.getMaxY() < r1.getMaxY()) {
            tempY = r1.getMinY();
            tempHeight = r2.getMaxY() - r1.getMinY();
        } else {
            tempY = r2.getMinY();
            tempHeight = r1.getMaxY() - r2.getMinY();
        }

        // We make the rectangle for the intersection area
        intersection = CCRectMake(tempX * CC_CONTENT_SCALE_FACTOR(), tempY * CC_CONTENT_SCALE_FACTOR(), tempWidth * CC_CONTENT_SCALE_FACTOR(), tempHeight * CC_CONTENT_SCALE_FACTOR());

        unsigned int x = intersection.origin.x;
        unsigned int y = intersection.origin.y;
        unsigned int w = intersection.size.width;
        unsigned int h = intersection.size.height;

        // Total pixels whose values we will get using glReadPixels depends on the Height and Width of the intersection area
        unsigned int numPixels = w * h;

        // Setting the custom shader to be used
        spr1->setShaderProgram(glProgram);
        spr2->setShaderProgram(glProgram);
        glProgram->use();

        // Clearing the Secondary Draw buffer of all previous values
        _rt->beginWithClear( 0, 0, 0, 0);

        // The below two values are being used in the custom shaders to set the value of RED and BLUE colors to be used
        glUniform1i(uniformColorRed, 255);
        glUniform1i(uniformColorBlue, 0);

        // The blend function is important we don't want the pixel value of the RED color being over-written by the BLUE color.
       // We want both the colors at a single pixel and hence get a PINK color (so that we have both the RED and BLUE pixels)
        spr1->setBlendFunc((ccBlendFunc){GL_SRC_ALPHA, GL_ONE});

        // The visit() function draws the sprite in the _rt draw buffer its a Cocos2d-x function
        spr1->visit();

        // Setting the shader program back to the default shader being used by our game
spr1->setShaderProgram(CCShaderCache::sharedShaderCache()->programForKey(kCCShader_PositionTextureColor));
        // Setting the default blender function being used by the game
        spr1->setBlendFunc((ccBlendFunc){CC_BLEND_SRC, CC_BLEND_DST});

        // Setting new values for the same shader but for our second sprite as this time we want to have an all BLUE sprite
        glUniform1i(uniformColorRed, 0);
        glUniform1i(uniformColorBlue, 255);
        spr2->setBlendFunc((ccBlendFunc){GL_SRC_ALPHA, GL_ONE});

        spr2->visit();

        spr2->setShaderProgram(CCShaderCache::sharedShaderCache()->programForKey(kCCShader_PositionTextureColor));
        spr2->setBlendFunc((ccBlendFunc){CC_BLEND_SRC, CC_BLEND_DST});

        // Get color values of intersection area
        glReadPixels(x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, buffer);

        _rt->end();

        // Read buffer
        unsigned int step = 1;
        for(unsigned int i=0; i<numPixels; i+=step) {
            ccColor4B color = buffer[i];
            // Here we check if a single pixel has both RED and BLUE pixels
            if (color.r > 0 && color.b > 0) {
                isColliding = true;
                break;
            }
        }
    }
    return isColliding;
}

 SolidColorShader.fsh

#ifdef GL_ES
precision lowp float;
#endif

varying vec2 v_texCoord;
uniform sampler2D u_texture;
uniform int u_color_red;
uniform int u_color_blue;

void main()
{
	vec4 color = texture2D(u_texture, v_texCoord);
    gl_FragColor = vec4(u_color_red, 0, u_color_blue, color.a);

}

 SolidVertexShader.vsh

attribute vec4 a_position;
attribute vec2 a_texCoord;
attribute vec4 a_color;								

#ifdef GL_ES
varying lowp vec4 v_fragmentColor;
varying mediump vec2 v_texCoord;
#else
varying vec4 v_fragmentColor;
varying vec2 v_texCoord;
#endif												

void main()
{
    gl_Position = CC_MVPMatrix * a_position;
	v_fragmentColor = a_color;
	v_texCoord = a_texCoord;
}

For using the Collision Detection Class:

1. Initialize the CCRenderTexture object

    _rt = CCRenderTexture::create(visibleSize.width * 2, visibleSize.height * 2);
    _rt->setPosition(ccp(visibleSize.width, visibleSize.height));
    _rt->retain();
    _rt->setVisible(false);

2. Call the Singleton function whenever collision detection required

if (CollisionDetection::GetInstance()->areTheSpritesColliding(pSprite, pCurrentSpriteToDrag, true, _rt)) {
     //Code here
}
时间: 2024-09-19 09:10:47

像素完美碰撞检测(使用cocos2d-x)的相关文章

PS旋转技巧:让像素完美呈现

  在Photoshop 中旋转图像时(这边指顺时钟或逆时钟旋转90 度的情况),如果你的图像的长跟宽都是奇数,或者都是偶数时,并不会发生什么问题.但是当你的图像只有一边为奇数时,你会得到预期之外的结果,如下图:        小编推荐:怎样在Photoshop中保持像素的完美 为了更清楚的了解发生什么事情,我们取一张3X4 像素的图形,可以发现由于旋转中心在两像素的邻边,当我们对此图形做90度旋转后.原本的图形并没有完整落在像素范围(灰色格线)内,因此系统就必须重新计算格线内像素的色彩(我们称

怎样在Photoshop中保持像素的完美

在设计网页和程序界面的时候,为了确保每一个像素的颜色.纹理和位置都准确地呈现,大多数设计师都会被搞得疲惫不堪.一不小心,一些很常用的操作像移动.旋转和粘贴就会让你的辛勤劳动付之东流.但是,如果能对你的工作流程做些小小的改动,就能在项目中始终保持作品的高质量. 像素完美之旋转 如果你不够小心,那么在Photoshop中旋转图层就会使像素遭到明显的破坏,出现模糊. 使用自由变换工具(或其他工具)把图层精确旋转90度或270度后,像素最终的显示效果将取决于图层的尺寸.如果图层的宽和高都是偶数,你就可以

详细解析如何在PS中保持像素的完美

怎样在Photoshop中保持像素的完美 @Iamkikidong在设计网页和程序界面的时候,为了确保每一个像素的颜色.纹理和位置都准确地呈现,大多数设计师都会被搞得疲惫不堪.一不小心,一些很常用的操作像移动.旋转和粘贴就会让你的辛勤劳动付之东流.但是,如果能对你的工作流程做些小小的改动,就能在项目中始终保持作品的高质量. 像素完美之旋转 如果你不够小心,那么在Photoshop中旋转图层就会使像素遭到明显的破坏,出现模糊. 使用自由变换工具(或其他工具)把图层精确旋转90度或270度后,像素最

Illustrator像素级完美绘制经验技巧分享

给各位Illustrator软件的使用者们来详细的解析分享一下像素级完美绘制的经验技巧. 教程分享: 如我们所知,在CS5(版本号)之前,用AI这款矢量工具做到像素级的精确是非常困难的.为什么呢?因为不同于PS图像处理软件,一款基于栅格的图像处理软件(即直接面向的是像素处理).而AI是一款矢量软件,(即面向的是点.直线.和曲线这类基于数字信息绘制的图形).早期,AI还不能很完美的处理像素.现在AI不断推陈出新,随着最新版CC的发布,AI已经有了许多改进.现在Adobe Illustrator已经

Illustrator像素级完美绘制技巧教程一览

给各位Illustrator软件的使用者们来详细的解析分享一下像素级完美绘制技巧的教程. 教程分享: 自从Adobe Illustrator推出以来,人们感兴趣的是,它是否能完成过渡并且改变创作像素级作品的方式.正如我们所知,在CS5(版本号)之前,用AI这款矢量工具做到像素级的精确是非常困难的.为什么呢?因为不同于PS图像处理软件,一款基于栅格的图像处理软件(即直接面向的是像素处理).而AI是一款矢量软件,(即面向的是点.直线.和曲线这类基于数字信息绘制的图形).早期,AI还不能很完美的处理像

canvas中的碰撞检测笔记

canvas中的碰撞检测笔记 时间 2016-01-19 08:29:00  博客园精华区 原文  http://www.cnblogs.com/zichi/p/5141044.html 主题 Canvas 用 canvas 做小游戏或者特效,碰撞检测是少不了的.本文将会涉及普通的碰撞检测,以及像素级的碰撞检测.(本文的碰撞检测均以 矩形 为例)  普通碰撞检测 普通的矩形碰撞检测比较简单.即已知两个矩形的各顶点坐标,判断是否相交,如相交,则为碰撞. leetcode 有道题是给出两个矩形的坐标

响应式Web图形篇之icon fonts的探析及应用

前言 像素完美(Pixel Perfection).分辨率无关(Resolution Independent)和多平台体验一致性是设计师们的追求. 可访问性(Accessability).加载性能和重构灵活性是前端工程师们关心的主题. 当下互联网设备「风起云涌」,显示分辨率「层出不穷」,为 Web 创建者们带来越来越多的难题. 需要为高PPI(aka Retina)显示设备准备@1.5x.@2x.@3x的图片素材: 需要针对不同显示屏分辨率来调整优化排版: 需要考虑多个分辨率版本的图片的加载性能

CSS:移动网站开发的前端技术和技巧

网页制作Webjx文章简介:CSS:移动网站开发的前端技术和技巧. 上一篇我们谈到了移动网站中的标签,想必很多人也很想了解Mobile CSS的情况吧,本文将和大家一起探讨移动网站中的CSS标准. 介绍 Mobile css的标准也是有些复杂的,与前一篇文章中提到的类似,之前存在着一个W3C制定的CSS Mobile Profile 1.0以及OMA的WAP CSS 1.0,事实上它们都是CSS 2.1的子集,而且内容非常接近,不同的是,WAP CSS 1.0针对移动设备加入了一些扩展,这些扩展

Webjx.Com收集超美的扁平化网站设计20个案例

文章描述:.Com收集超美的扁平化网站设计20个案例. 扁平化设计意味着不用常用的渐变.像素完美的阴影,然后通常是近年来蔓延的用来实现看起来"平面"的用户界面." 嗯,为了能让你更快的步入扁平化设计的潮流,今天我们收集了20个超赞的示例(以及一些Dribble截图),相信它们能够阐明网页设计中的"扁平化"之美... Microsoft SpellTower Supereight Studio Hundreds Women and Tech [1] [2]