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

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

[html] view plaincopy

  1. //
  2. //  CollisionDetection.h
  3. //  Created by Mudit Jaju on 30/08/13.
  4. //
  5. //  SINGLETON class for checking Pixel Based Collision Detection
  6. #ifndef __CollisionDetection__
  7. #define __CollisionDetection__
  8. #include <iostream>
  9. #include "cocos2d.h"
  10. USING_NS_CC;
  11. class CollisionDetection {
  12. public:
  13. //Handle for getting the Singleton Object
  14. static CollisionDetection* GetInstance();
  15. //Function signature for checking for collision detection spr1, spr2 are the concerned sprites
  16. //pp is bool, set to true if Pixel Perfection Collision is required. Else set to false
  17. //_rt is the secondary buffer used in our system
  18. bool areTheSpritesColliding(CCSprite* spr1, CCSprite* spr2, bool pp, CCRenderTexture* _rt);
  19. private:
  20. static CollisionDetection* instance;
  21. CollisionDetection();
  22. // Values below are all required for openGL shading
  23. CCGLProgram *glProgram;
  24. ccColor4B *buffer;
  25. int uniformColorRed;
  26. int uniformColorBlue;
  27. };
  28. #endif /* defined(__CollisionDetection__) */

CollisionDetection.cpp

[html] view plaincopy

  1. //
  2. //  CollisionDetection.cpp
  3. //  Created by Mudit Jaju on 30/08/13.
  4. //
  5. //  SINGLETON class for checking Pixel Based Collision Detection
  6. #include "CollisionDetection.h"
  7. // Singleton Instance set to NULL initially
  8. CollisionDetection* CollisionDetection::instance = NULL;
  9. // Handle to get Singleton Instance
  10. CollisionDetection* CollisionDetection::GetInstance() {
  11. if (instance == NULL) {
  12. instance = new CollisionDetection();
  13. }
  14. return instance;
  15. }
  16. // Private Constructor being called from within the GetInstance handle
  17. CollisionDetection::CollisionDetection() {
  18. // Code below to setup shaders for use in Cocos2d-x
  19. glProgram = new CCGLProgram();
  20. glProgram->retain();
  21. glProgram->initWithVertexShaderFilename("SolidVertexShader.vsh", "SolidColorShader.fsh");
  22. glProgram->addAttribute(kCCAttributeNamePosition, kCCVertexAttrib_Position);
  23. glProgram->addAttribute(kCCAttributeNameTexCoord, kCCVertexAttrib_TexCoords);
  24. glProgram->link();
  25. glProgram->updateUniforms();
  26. glProgram->use();
  27. uniformColorRed = glGetUniformLocation(glProgram->getProgram(), "u_color_red");
  28. uniformColorBlue = glGetUniformLocation(glProgram->getProgram(), "u_color_blue");
  29. // A large buffer created and re-used again and again to store glReadPixels data
  30. buffer = (ccColor4B *)malloc( sizeof(ccColor4B) * 10000 );
  31. }
  32. bool CollisionDetection::areTheSpritesColliding(cocos2d::CCSprite* spr1, cocos2d::CCSprite* spr2, bool pp, CCRenderTexture* _rt) {
  33. bool isColliding = false;
  34. // Rectangle of the intersecting area if the sprites are colliding according to Bounding Box collision
  35. CCRect intersection;
  36. // Bounding box of the Two concerned sprites being saved
  37. CCRect r1 = spr1->boundingBox();
  38. CCRect r2 = spr2->boundingBox();
  39. // Look for simple bounding box collision
  40. if (r1.intersectsRect(r2)) {
  41. // If we‘re not checking for pixel perfect collisions, return true
  42. if (!pp) {
  43. return true;
  44. }
  45. float tempX;
  46. float tempY;
  47. float tempWidth;
  48. float tempHeight;
  49. //OPTIMIZE FURTHER
  50. //CONSIDER THE CASE WHEN ONE BOUDNING BOX IS COMPLETELY INSIDE ANOTHER BOUNDING BOX!
  51. if (r1.getMaxX() > r2.getMinX()) {
  52. tempX = r2.getMinX();
  53. tempWidth = r1.getMaxX() - r2.getMinX();
  54. } else {
  55. tempX = r1.getMinX();
  56. tempWidth = r2.getMaxX() - r1.getMinX();
  57. }
  58. if (r2.getMaxY() < r1.getMaxY()) {
  59. tempY = r1.getMinY();
  60. tempHeight = r2.getMaxY() - r1.getMinY();
  61. } else {
  62. tempY = r2.getMinY();
  63. tempHeight = r1.getMaxY() - r2.getMinY();
  64. }
  65. // We make the rectangle for the intersection area
  66. intersection = CCRectMake(tempX * CC_CONTENT_SCALE_FACTOR(), tempY * CC_CONTENT_SCALE_FACTOR(), tempWidth * CC_CONTENT_SCALE_FACTOR(), tempHeight * CC_CONTENT_SCALE_FACTOR());
  67. unsigned int x = intersection.origin.x;
  68. unsigned int y = intersection.origin.y;
  69. unsigned int w = intersection.size.width;
  70. unsigned int h = intersection.size.height;
  71. // Total pixels whose values we will get using glReadPixels depends on the Height and Width of the intersection area
  72. unsigned int numPixels = w * h;
  73. // Setting the custom shader to be used
  74. spr1->setShaderProgram(glProgram);
  75. spr2->setShaderProgram(glProgram);
  76. glProgram->use();
  77. // Clearing the Secondary Draw buffer of all previous values
  78. _rt->beginWithClear( 0, 0, 0, 0);
  79. // The below two values are being used in the custom shaders to set the value of RED and BLUE colors to be used
  80. glUniform1i(uniformColorRed, 255);
  81. glUniform1i(uniformColorBlue, 0);
  82. // The blend function is important we don‘t want the pixel value of the RED color being over-written by the BLUE color.
  83. // 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)
  84. spr1->setBlendFunc((ccBlendFunc){GL_SRC_ALPHA, GL_ONE});
  85. // The visit() function draws the sprite in the _rt draw buffer its a Cocos2d-x function
  86. spr1->visit();
  87. // Setting the shader program back to the default shader being used by our game
  88. spr1->setShaderProgram(CCShaderCache::sharedShaderCache()->programForKey(kCCShader_PositionTextureColor));
  89. // Setting the default blender function being used by the game
  90. spr1->setBlendFunc((ccBlendFunc){CC_BLEND_SRC, CC_BLEND_DST});
  91. // Setting new values for the same shader but for our second sprite as this time we want to have an all BLUE sprite
  92. glUniform1i(uniformColorRed, 0);
  93. glUniform1i(uniformColorBlue, 255);
  94. spr2->setBlendFunc((ccBlendFunc){GL_SRC_ALPHA, GL_ONE});
  95. spr2->visit();
  96. spr2->setShaderProgram(CCShaderCache::sharedShaderCache()->programForKey(kCCShader_PositionTextureColor));
  97. spr2->setBlendFunc((ccBlendFunc){CC_BLEND_SRC, CC_BLEND_DST});
  98. // Get color values of intersection area
  99. glReadPixels(x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
  100. _rt->end();
  101. // Read buffer
  102. unsigned int step = 1;
  103. for(unsigned int i=0; i<numPixels; i+=step) {
  104. ccColor4B color = buffer[i];
  105. // Here we check if a single pixel has both RED and BLUE pixels
  106. if (color.r > 0 && color.b > 0) {
  107. isColliding = true;
  108. break;
  109. }
  110. }
  111. }
  112. return isColliding;
  113. }

SolidColorShader.fsh

[html] view plaincopy

  1. #ifdef GL_ES
  2. precision lowp float;
  3. #endif
  4. varying vec2 v_texCoord;
  5. uniform sampler2D u_texture;
  6. uniform int u_color_red;
  7. uniform int u_color_blue;
  8. void main()
  9. {
  10. vec4 color = texture2D(u_texture, v_texCoord);
  11. gl_FragColor = vec4(u_color_red, 0, u_color_blue, color.a);
  12. }

SolidVertexShader.vsh

[html] view plaincopy

  1. attribute vec4 a_position;
  2. attribute vec2 a_texCoord;
  3. attribute vec4 a_color;
  4. #ifdef GL_ES
  5. varying lowp vec4 v_fragmentColor;
  6. varying mediump vec2 v_texCoord;
  7. #else
  8. varying vec4 v_fragmentColor;
  9. varying vec2 v_texCoord;
  10. #endif
  11. void main()
  12. {
  13. gl_Position = CC_MVPMatrix * a_position;
  14. v_fragmentColor = a_color;
  15. v_texCoord = a_texCoord;
  16. }

For using the Collision Detection Class:

1. Initialize the CCRenderTexture object

[html] view plaincopy

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

2. Call the Singleton function whenever collision detection required

[html] view plaincopy

  1. if (CollisionDetection::GetInstance()->areTheSpritesColliding(pSprite, pCurrentSpriteToDrag, true, _rt)) {
  2. //Code here
  3. }

原文出处:http://blog.muditjaju.infiniteeurekas.in/?p=1

时间: 2024-09-30 03:31:11

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

显示对象的像素级碰撞检测

mc2.gotoAndStop(1); var bmd1 = new BitmapData(mc1.width, mc1.height, true, 0); bmd1.draw(mc1); var bmd2 = new BitmapData(mc2.width, mc2.height, true, 0); bmd2.draw(mc2); stage.addEventListener(Event.ENTER_FRAME,frameFunc); function frameFunc(e:Event)

Subway Icon Set – 306个像素完美的特制图标

这个图标集是306个优化的像素完美,精雕细琢的图标.为这些设备进行了优化:iOS.Windows Phone.Windows 8 and BlackBerry 10,提供 PNG, SVG, XALM, PSD, CSH, SKETCH, PDF, AI 和 EPS 格式. 在线演示      源码下载 您可能感兴趣的相关文章 网站开发中很有用的 jQuery 效果[附源码] 分享35个让人惊讶的 CSS3 动画效果演示 十分惊艳的8个 HTML5 & JavaScript 特效 Web 开发中

分享一位国外大牛写的不规则物体像素级碰撞检测算法及详解

最近在做一个有关投篮的小游戏,需要用到像素级碰撞检测,as3自带的hitTestObject显然无法满足需要.网上搜寻了一下,在9ria挖坟挖到两篇好文章: 分享一个超高效的不规则物体碰撞检测的类~~ [Lii]超高效的不规则物体碰撞检测<效率优化> 第一篇文章介绍了一位国外大牛写的不规则物体像素级碰撞检测算法,原理是用bitmap绘制两对象不透明区域,利用混合模式计算出两对象的相交区域. 第二篇文章则在该算法的基础上进行了效率的优化,原理是判断出两对象发生hitTestObject碰撞后,将

[ActionScript 3.0] 像素级碰撞检测

1 package 2 { 3 import flash.display.BitmapData; 4 import flash.display.BlendMode; 5 import flash.display.DisplayObject; 6 import flash.display.Sprite; 7 import flash.geom.ColorTransform; 8 import flash.geom.Matrix; 9 import flash.geom.Point; 10 impo

如何保持像素的完美?ps实战经验分享

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

iOS之绘制像素到屏幕

译注:这篇文章虽然比较长,但是里面的内容还是很有价值的. 像素是如何绘制到屏幕上面的?把数据输出到屏幕的方法有很多,通过调用很多不同的framework和不同的函数.这里我们讲一下这个过程背后的东西.希望能够帮助大家了解什么时候该使用什么API,特别是当遇到性能问题需要调试的时候.当然,我们这里主要讲iOS,但是事实上,很多东西也是可以应用到OSX上面的. Graphics Stack 绘制屏幕的过程中又很多都是不被人了解的.但是一旦像素被绘制到屏幕上面,那么像素就是有3种颜色组成:红绿蓝.这3

IOS 开发:绘制像素到屏幕

像素是如何绘制到屏幕上面的?把数据输出到屏幕的方法有很多,通过调用很多不同的framework和不同的函数.这里我们讲一下这个过程背后的东西.希望能够帮助大家了解什么时候该使用什么API,特别是当遇到性能问题需要调试的时候.当然,我们这里主要讲iOS,但是事实上,很多东西也是可以应用到OSX上面的. Graphics Stack 绘制屏幕的过程中又很多都是不被人了解的.但是一旦像素被绘制到屏幕上面,那么像素就是有3种颜色组成:红绿蓝.这3个颜色单元通过特定的强弱组合形成一个特定的颜色.对于iPh

java 实现精确碰撞检测。

cnmm22 原创. [http://blog.csdn.net/cnmm22/article/details/45220551] 用我的方法,你可以在java 里实现精确的斜角矩形,平行四边形,不规则矩形,不规则多边形与圆形的碰撞检测. 我们知道,在java 里有一个类,x.getRect().intersects(x1.getRect() 可以实现规则矩形的碰撞检测: 这是无法容忍的"碰撞检测".这不是我要的游戏效果. 在使我的方法后: cnmm22 原创,转载请随便. 实现方法:

【读书笔记《Android游戏编程之从零开始》】18.游戏开发基础(碰撞检测)

1.矩形碰撞 所谓矩形碰撞,就是利用两个矩形之间的位置关系来进行判断,如果矩形的像素在另外一个矩形之中,或者之上都可以认为这两个矩形发生了碰撞. 如果单纯的去考虑哪些情况会判定两个矩形发生碰撞,倒不如反思考虑两个矩形之间不发生碰撞的几种情况.其实两个矩形不发生碰撞的情况就上下左右这四种. 下面通过实例项目来完成对应的四种判定. 先看实例效果图: 新建项目,游戏框架为 SurfaceView 游戏框架,修改 MySurfaceView 类如下: package com.example.ex4_15