
(第一次翻译国外的文章,好紧张,因为英语比较菜的缘故,翻译起来有些别扭。原文:http://blog.csdn.net/shieryueqing)
我写这篇文章的原因是,我在StackOverflow中没有发现怎么做像素碰撞检测这个问题的答案,原以为会有很多像我一样的人在搜索着答案。在大部分的游戏中碰撞检测是重要的组成部分,它能够使用在子d打中了敌人或者你撞到了墙上等等。当做游戏的碰撞检测的时候,我们需要根据游戏去选择其中一些检测的技术。几乎所有的游戏引擎和框架都使用“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. }
总结以上是内存溢出为你收集整理的完美的像素碰撞检测(使用cocos2dx)全部内容,希望文章能够帮你解决完美的像素碰撞检测(使用cocos2dx)所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)