完美的像素碰撞检测(使用cocos2dx)

前端之家收集整理的这篇文章主要介绍了完美的像素碰撞检测(使用cocos2dx)前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

(第一次翻译国外的文章,好紧张,因为英语比较菜的缘故,翻译起来有些别扭。原文:http://blog.csdn.net/shieryueqing

我写这篇文章的原因是,我在StackOverflow中没有发现怎么做像素碰撞检测这个问题的答案,原以为会有很多像我一样的人在搜索着答案。在大部分的游戏中碰撞检测是重要的组成部分,它能够使用在子弹打中了敌人或者你撞到了墙上等等。当做游戏的碰撞检测的时候,我们需要根据游戏去选择其中一些检测的技术。几乎所有的游戏引擎和框架都使用“Bounding Box”碰撞,这是一个默认的碰撞检测机制。简单来说,精灵或对象所用到的“Bounding Box”碰撞检测系统被视为最小的矩形,这些矩形能够完成覆盖精灵或对象,然后他们两个碰撞盒子被检查是否他们正碰到对方。

但是有时候这些简单的碰撞检测系统是不精确的,特别是当我们通过alpha值或者旋转一定角度来使用精灵的时候。看一下下面的图片

­

像素检测是一个精确的系统,他能够使我们的碰撞更精确,而不是像刚才那样使用比他们更大尺寸的Bounding Box

警告:这个系统越精确消耗的性能越大,因此,根据你的游戏需要明智地选择不同的系统。

提示:这个系统虽然特别为cocos2dx框架写的,但是你能够轻易的明白并且使用其他的语言去实现。

我们迟早会亲自去做这件事情的。现在我们将为碰撞检测去制作一个单例类和其他一些将要做的东西,需要使用到:

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

原理是:

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__) */


CollisionDetection.cpp

[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;
}


SolidColorShader.fsh

[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);
}


SolidVertexShader.vsh

[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; 
}


For using the Collision Detection Class:

1. Initialize the CCRenderTexture object

[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);


2. Call the Singleton function whenever collision detection required

[html] view plaincopyprint?

1. if(CollisionDetection::GetInstance()->areTheSpritesColliding(pSprite,pCurrentSpriteToDrag,true,_rt)){

2. //Codehere

3. }

猜你在找的Cocos2d-x相关文章