(第一次翻译国外的文章,好紧张,因为英语比较菜的缘故,翻译起来有些别扭。原文: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条)