完美的像素碰撞检测(使用cocos2dx)
(第一次翻译国外的文章,好紧张,因为英语比较菜的缘故,翻译起来有些别扭。原文:http://blog.csdn.net/shieryueqing) 我写这篇文章的原因是,我在StackOverflow中没有发现怎么做像素碰撞检测这个问题的答案,原以为会有很多像我一样的人在搜索着答案。在大部分的游戏中碰撞检测是重要的组成部分,它能够使用在子弹打中了敌人或者你撞到了墙上等等。当做游戏的碰撞检测的时候,我们需要根据游戏去选择其中一些检测的技术。几乎所有的游戏引擎和框架都使用“Bounding Box”碰撞,这是一个默认的碰撞检测机制。简单来说,精灵或对象所用到的“Bounding Box”碰撞检测系统被视为最小的矩形,这些矩形能够完成覆盖精灵或对象,然后他们两个碰撞盒子被检查是否他们正碰到对方。 但是有时候这些简单的碰撞检测系统是不精确的,特别是当我们通过alpha值或者旋转一定角度来使用精灵的时候。看一下下面的图片: -
像素检测是一个精确的系统,他能够使我们的碰撞更精确,而不是像刚才那样使用比他们更大尺寸的Bounding Box。 警告:这个系统越精确消耗的性能越大,因此,根据你的游戏需要明智地选择不同的系统。 提示:这个系统虽然特别为cocos2dx框架写的,但是你能够轻易的明白并且使用其他的语言去实现。 我们迟早会亲自去做这件事情的。现在我们将为碰撞检测去制作一个单例类和其他一些将要做的东西,需要使用到: 1. Singleton Class – CollisionDetection
原理是: 1. 创建一个CCRenderTexture,他将作为辅助缓冲区。 2. 我们首先做一个简单的碰撞(Bounding Box)去检测是否他们两个精灵边界能够相碰。 3. 如果第二步成功了,我们将绘制两个相关的对象在我们第一步已经创建的二次缓冲中。 4. 使用openGL片段着色器我们要画其中一个对象为红色,其他的为蓝色。
5. 使用另一个openGL功能glReadPixels,我们要在boundingbox矩形区域内读取全部的像素数据。 6. 我们接着去遍历全部的像素值,检查单个像素是否有红色或者蓝色像素。如果他们有像素说明有碰撞,否则不碰撞。
现在写代码来实现以上的步骤。我已经为你写完了代码,你去看看都做了什么事情。如果有什么问题请留下评论我将用我的知识尝试去回答。
CollisionDetection.h [html] view plaincopyprint? 1. // 2. //CollisionDetection.h 3. //CreatedbyMuditJajuon30/08/13. 4. // 5. //SINGLETONclassforcheckingPixelBasedCollisionDetection 6. 7. #ifndef__CollisionDetection__ 8. #define__CollisionDetection__ 9. 10. #include<iostream> 11. #include"cocos2d.h" 12. 13. USING_NS_CC; 14. 15. classCollisionDetection{ 16. public: 17. //HandleforgettingtheSingletonObject 18. staticCollisionDetection*GetInstance(); 19. //Functionsignatureforcheckingforcollisiondetectionspr1,spr2aretheconcernedsprites 20. //ppisbool,settotrueifPixelPerfectionCollisionisrequired.Elsesettofalse 21. //_rtisthesecondarybufferusedinoursystem 22. boolareTheSpritesColliding(CCSprite*spr1,CCSprite*spr2,boolpp,CCRenderTexture*_rt); 23. private: 24. staticCollisionDetection*instance; 25. CollisionDetection(); 26. 27. //ValuesbelowareallrequiredforopenGLshading 28. CCGLProgram*glProgram; 29. ccColor4B*buffer; 30. intuniformColorRed; 31. intuniformColorBlue; 32. 33. }; 34. 35. #endif/*defined(__CollisionDetection__)*/ //
// 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__) */
[html] view plaincopyprint? 1. // 2. //CollisionDetection.cpp 3. //CreatedbyMuditJajuon30/08/13. 4. // 5. //SINGLETONclassforcheckingPixelBasedCollisionDetection 6. 7. #include"CollisionDetection.h" 8. //SingletonInstancesettoNULLinitially 9. CollisionDetection*CollisionDetection::instance=NULL; 10. 11. //HandletogetSingletonInstance 12. CollisionDetection*CollisionDetection::GetInstance(){ 13. if(instance==NULL){ 14. instance=newCollisionDetection(); 15. } 16. returninstance; 17. } 18. 19. //PrivateConstructorbeingcalledfromwithintheGetInstancehandle 20. CollisionDetection::CollisionDetection(){ 21. //CodebelowtosetupshadersforuseinCocos2d-x 22. glProgram=newCCGLProgram(); 23. glProgram->retain(); 24. glProgram->initWithVertexShaderFilename("SolidVertexShader.vsh","SolidColorShader.fsh"); 25. glProgram->addAttribute(kCCAttributeNamePosition,kCCVertexAttrib_Position); 26. glProgram->addAttribute(kCCAttributeNameTexCoord,kCCVertexAttrib_TexCoords); 27. glProgram->link(); 28. glProgram->updateUniforms(); 29. glProgram->use(); 30. 31. uniformColorRed=glGetUniformLocation(glProgram->getProgram(),"u_color_red"); 32. uniformColorBlue=glGetUniformLocation(glProgram->getProgram(),"u_color_blue"); 33. 34. //Alargebuffercreatedandre-usedagainandagaintostoreglReadPixelsdata 35. buffer=(ccColor4B*)malloc(sizeof(ccColor4B)*10000); 36. } 37. 38. boolCollisionDetection::areTheSpritesColliding(cocos2d::CCSprite*spr1,cocos2d::CCSprite*spr2,CCRenderTexture*_rt){ 39. boolisColliding=false; 40. 41. //RectangleoftheintersectingareaifthespritesarecollidingaccordingtoBoundingBoxcollision 42. CCRectintersection; 43. 44. //BoundingboxoftheTwoconcernedspritesbeingsaved 45. CCRectr1=spr1->boundingBox(); 46. CCRectr2=spr2->boundingBox(); 47. 48. //Lookforsimpleboundingboxcollision 49. if(r1.intersectsRect(r2)){ 50. //Ifwe'renotcheckingforpixelperfectcollisions,returntrue 51. if(!pp){ 52. returntrue; 53. } 54. 55. floattempX; 56. floattempY; 57. floattempWidth; 58. floattempHeight; 59. 60. //OPTIMIZEFURTHER 61. //CONSIDERTHECASEWHENONEBOUDNINGBOXISCOMPLETELYINSIDEANOTHERBOUNDINGBOX! 62. if(r1.getMaxX()>r2.getMinX()){ 63. tempX=r2.getMinX(); 64. tempWidth=r1.getMaxX()-r2.getMinX(); 65. }else{ 66. tempX=r1.getMinX(); 67. tempWidth=r2.getMaxX()-r1.getMinX(); 68. } 69. 70. if(r2.getMaxY()<r1.getMaxY()){ 71. tempY=r1.getMinY(); 72. tempHeight=r2.getMaxY()-r1.getMinY(); 73. }else{ 74. tempY=r2.getMinY(); 75. tempHeight=r1.getMaxY()-r2.getMinY(); 76. } 77. 78. //Wemaketherectanglefortheintersectionarea 79. intersection=CCRectMake(tempX*CC_CONTENT_SCALE_FACTOR(),tempY*CC_CONTENT_SCALE_FACTOR(),tempWidth*CC_CONTENT_SCALE_FACTOR(),tempHeight*CC_CONTENT_SCALE_FACTOR()); 80. 81. unsignedintx=intersection.origin.x; 82. unsignedinty=intersection.origin.y; 83. unsignedintw=intersection.size.width; 84. unsignedinth=intersection.size.height; 85. 86. //TotalpixelswhosevalueswewillgetusingglReadPixelsdependsontheHeightandWidthoftheintersectionarea 87. unsignedintnumPixels=w*h; 88. 89. //Settingthecustomshadertobeused 90. spr1->setShaderProgram(glProgram); 91. spr2->setShaderProgram(glProgram); 92. glProgram->use(); 93. 94. //ClearingtheSecondaryDrawbufferofallpreviousvalues 95. _rt->beginWithClear(0,0); 96. 97. //ThebelowtwovaluesarebeingusedinthecustomshaderstosetthevalueofREDandBLUEcolorstobeused 98. glUniform1i(uniformColorRed,255); 99. glUniform1i(uniformColorBlue,0); 100. 101. //Theblendfunctionisimportantwedon'twantthepixelvalueoftheREDcolorbeingover-writtenbytheBLUEcolor. 102. //WewantboththecolorsatasinglepixelandhencegetaPINKcolor(sothatwehaveboththeREDandBLUEpixels) 103. spr1->setBlendFunc((ccBlendFunc){GL_SRC_ALPHA,GL_ONE}); 104. 105. //Thevisit()functiondrawsthespriteinthe_rtdrawbufferitsaCocos2d-xfunction 106. spr1->visit(); 107. 108. //Settingtheshaderprogrambacktothedefaultshaderbeingusedbyourgame 109. spr1->setShaderProgram(CCShaderCache::sharedShaderCache()->programForKey(kCCShader_PositionTextureColor)); 110. //Settingthedefaultblenderfunctionbeingusedbythegame 111. spr1->setBlendFunc((ccBlendFunc){CC_BLEND_SRC,CC_BLEND_DST}); 112. 113. //SettingnewvaluesforthesameshaderbutforoursecondspriteasthistimewewanttohaveanallBLUEsprite 114. glUniform1i(uniformColorRed,0); 115. glUniform1i(uniformColorBlue,255); 116. spr2->setBlendFunc((ccBlendFunc){GL_SRC_ALPHA,GL_ONE}); 117. 118. spr2->visit(); 119. 120. spr2->setShaderProgram(CCShaderCache::sharedShaderCache()->programForKey(kCCShader_PositionTextureColor)); 121. spr2->setBlendFunc((ccBlendFunc){CC_BLEND_SRC,CC_BLEND_DST}); 122. 123. //Getcolorvaluesofintersectionarea 124. glReadPixels(x,y,w,h,GL_RGBA,GL_UNSIGNED_BYTE,buffer); 125. 126. _rt->end(); 127. 128. //Readbuffer 129. unsignedintstep=1; 130. for(unsignedinti=0;i<numPixels;i+=step){ 131. ccColor4Bcolor=buffer[i]; 132. //HerewecheckifasinglepixelhasbothREDandBLUEpixels 133. if(color.r>0&&color.b>0){ 134. isColliding=true; 135. break; 136. } 137. } 138. } 139. returnisColliding; 140. } //
// 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,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);
// 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,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;
}
[html] view plaincopyprint? 1. #ifdefGL_ES 2. precisionlowpfloat; 3. #endif 4. 5. varyingvec2v_texCoord; 6. uniformsampler2Du_texture; 7. uniformintu_color_red; 8. uniformintu_color_blue; 9. 10. voidmain() 11. { 12. vec4color=texture2D(u_texture,v_texCoord); 13. gl_FragColor=vec4(u_color_red,u_color_blue,color.a); 14. 15. } #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,color.a);
}
[html] view plaincopyprint? 1. attributevec4a_position; 2. attributevec2a_texCoord; 3. attributevec4a_color; 4. 5. #ifdefGL_ES 6. varyinglowpvec4v_fragmentColor; 7. varyingmediumpvec2v_texCoord; 8. #else 9. varyingvec4v_fragmentColor; 10. varyingvec2v_texCoord; 11. #endif 12. 13. voidmain() 14. { 15. gl_Position=CC_MVPMatrix*a_position; 16. v_fragmentColor=a_color; 17. v_texCoord=a_texCoord; 18. } 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;
}
[html] view plaincopyprint? 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); _rt = CCRenderTexture::create(visibleSize.width * 2,visibleSize.height * 2);
_rt->setPosition(ccp(visibleSize.width,visibleSize.height));
_rt->retain();
_rt->setVisible(false);
[html] view plaincopyprint? 1. if(CollisionDetection::GetInstance()->areTheSpritesColliding(pSprite,pCurrentSpriteToDrag,true,_rt)){ 2. //Codehere 3. } (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |