cocos2d-x快乐的做让人快乐的游戏4:像素碰撞
2014-11-03 11:36
281 查看
转自:http://blog.csdn.net/super2099/article/details/22745371
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
//
// 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
//
// 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, bool pp, 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, 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, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, 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
SolidVertexShader.vsh
For using the Collision Detection Class:
1. Initialize the CCRenderTexture object
2. Call the Singleton function whenever collision detection required
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
//
// 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
//
// 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, bool pp, 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, 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, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, 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
123456789101112131415 | #ifdef GL_ESprecision 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, 0, u_color_blue, color.a); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | attributevec4a_position; attributevec2a_texCoord; attributevec4a_color; #ifdef GL_ES varyinglowpvec4v_fragmentColor; varyingmediumpvec2v_texCoord; #else varyingvec4v_fragmentColor; varyingvec2v_texCoord; #endif voidmain() { gl_Position=CC_MVPMatrix *a_position; v_fragmentColor=a_color; v_texCoord=a_texCoord; } |
1. Initialize the CCRenderTexture object
1234 | _rt = CCRenderTexture::create(visibleSize.width * 2, visibleSize.height * 2); _rt->setPosition(ccp(visibleSize.width, visibleSize.height)); _rt->retain(); _rt->setVisible(false); |
1 2 3 | if(CollisionDetection::GetInstance()->areTheSpritesColliding(pSprite,pCurrentSpriteToDrag,true,_rt)){ //Code here } |
相关文章推荐
- cocos2d-x快乐的做让人快乐的游戏2:box2d基础
- cocos2d-x快乐的做让人快乐的游戏2:坐标系大集合
- cocos2d-x快乐的做让人快乐的游戏5:一个横版ARPG过关游戏
- <cocos2d-x for wp7>使用cocos2d-x制作基于Tile地图的游戏:碰撞检测和收集物品(二)
- Cocos2d-x 3.2 lua飞机大战开发实例(三)道具的掉落,碰撞检测,声音,分数,爆炸效果,完善游戏的功能细节
- Cocos2d-x3.0游戏实例之《别救我》第七篇——物理世界的碰撞检測
- <cocos2d-x for wp7>使用cocos2d-x制作基于Tile地图的游戏:碰撞检测和收集物品(二)
- Cocos2d-x碰撞检测原理与英雄要打死怪物--之游戏开发《赵云要格斗》(7)
- Cocos2d-x3.0游戏实例之《别救我》第七篇——物理世界的碰撞检测
- Quick-Cocos2d-x初学者游戏教程(十一) ------------------ 物体碰撞检测
- cocos2d-x游戏开发系列教程-坦克大战游戏之子弹和地图碰撞
- cocos2d-x快乐的做让人快乐的游戏3:cocos-2d 3.x中的物理世界
- 基于cocos2d-x的跑酷游戏,不同高度地面的碰撞检测demo,有兴趣可以看一看
- cocos2d-x游戏开发系列教程-坦克大战游戏之子弹和地图碰撞
- COCOS2D-X 像素级碰撞检测
- Cocos2d-x碰撞检测原理与英雄要打死怪物--之游戏开发《赵云要格斗》(7)
- Cocos2d-X 游戏之碰撞检测的方法
- cocos2d-x 3.0游戏实例学习笔记 《跑酷》第七步--物理碰撞检测(2)--主角吃金币
- 【cocos2d-x游戏开发】物体的碰撞检测
- (译)碰撞检测和收集物品:如何使用cocos2d制作基于tiled地图的游戏:第二部分