1.说明
- 整个实现参考了网上的flash代码,代码也还没有做优化
- 爆炸点是按照受理点随即角度裂开的,在下面例子中就是用的鼠标click点。
- 对于分裂后的碎块如果太小,则直接丢弃。
- 切分是用的box2d的raycast来实现的,切分完毕后在创建ccsprite
- 为了绘制纹理,修改了CCSprite类,使之可以画一个纹理的某个区域,当然也可以从其继承一个类实现。
- 由于自己工程的需要,原始的被切分body不能放在这儿释放,故做了下特殊处理。
- 如果要实现爆炸效果,需要在切分完毕后给box2d对象一个冲量,目前设置为{1,1},可根据实际情况修改
- zArray为自己的用的array类,可以用类似的类或者直接用数组实现
- 接口函数为BodyExplosion,此处可以将分裂次数作为参数传入
/* world: b2world对象 pos:受力点 body:被切分的box2d对象 csx:被切分的cocos2dx对象 */ void BodyExplosion(b2World *world, b2Vec2 &pos, b2Body* body, CCSprite* csx)
- 效果
2.分裂代码
2.1 头文件
#ifndef __BodyExplosion_h__
#define __BodyExplosion_h__
#include <Box2D/Box2D.h>
#include "jb2d.h"
#include "cocos2d.h"
#include "zarray.h"
class JoySplitRayCastCallback:public b2RayCastCallback
{
public:
JoySplitRayCastCallback(b2World* pworld, b2Body* body=NULL);
void FloatCompareAccuracy()
{
m_minx-=0.5;
m_miny-=0.5;
m_maxx+=0.5;
m_maxy+=0.5;
}
virtual float32 ReportFixture( b2Fixture* fixture, const b2Vec2& point,
const b2Vec2& normal, float32 fraction);
public:
void splitObj(b2Body* sliceBody, b2Vec2& A, b2Vec2& B);
void resetCB();
void addPoint(b2Body* body, const b2Vec2& p);
void removePoint(b2Body* body);
void CreateBody( b2Body* sliceBody, zArrayT<b2Vec2> &vecs);
bool PointInBody(const b2Vec2& point);
bool CheckClockwise(zArrayT<b2Vec2> &vecs);
public:
b2World* world;
b2Body* clickBody;
bool destroyed;
float m_minx;
float m_miny;
float m_maxx;
float m_maxy;
zArrayT<b2Body*> explodingBodies;
zArrayT<b2Vec2> enterPointsVec;
zArrayT<b2Body*> enterPointsVecBody;
zArrayT<b2Body*> slicedBodies;
};
#endif
2.2 源文件
#define PI 3.141295f
float32 JoySplitRayCastCallback::ReportFixture( b2Fixture* fixture, const b2Vec2& point, const b2Vec2& normal, float32 fraction )
{
b2Body* body=fixture->GetBody();
if (explodingBodies.find(body) == -1)
{
return 1;
}
int idx=enterPointsVecBody.find(body);
if (idx==-1)
{
addPoint(body, point);
}
else
{
splitObj(fixture->GetBody(), enterPointsVec[idx], (b2Vec2&)point);
}
return 1;
}
float det(float x1, float y1, float x2, float y2, float x3, float y3) {
// This is a function which finds the determinant of a 3x3 matrix.
// If you studied matrices, you'd know that it returns a positive number if three given points are in clockwise order, negative if they are in anti-clockwise order and zero if they lie on the same line.
// Another useful thing about determinants is that their absolute value is two times the face of the triangle, formed by the three given points.
return x1*y2+x2*y3+x3*y1-y1*x2-y2*x3-y3*x1;
}
int comp1(const void* a1,const void* b1)
{
b2Vec2& a=*(b2Vec2*)a1;
b2Vec2& b=*(b2Vec2*)b1;
// This is a compare function, used in the arrangeClockwise() method - a fast way to arrange the points in ascending order, according to their x-coordinate.
if (a.x*10000>b.x*10000) {
return 1;
}
else if (a.x*10000<b.x*10000) {
return -1;
}
return 0;
}
void arrangeClockwise(zArrayT<b2Vec2>& vec) {
// The algorithm is simple:
// First, it arranges all given points in ascending order, according to their x-coordinate.
// Secondly, it takes the leftmost and rightmost points (lets call them C and D), and creates tempVec, where the points arranged in clockwise order will be stored.
// Then, it iterates over the vertices vector, and uses the det() method I talked about earlier. It starts putting the points above CD from the beginning of the vector, and the points below CD from the end of the vector.
// That was it!
int n=vec.count();
float d;
int i1=1,i2=n-1;
zArrayT<b2Vec2> tempVec;
b2Vec2 C;
b2Vec2 D;
qsort(vec.getDataPtr(), vec.count(), sizeof(b2Vec2), comp1);
tempVec.setSize(n);
tempVec[0]=vec[0];
C=vec[0];
D=vec[n-1];
for (int i=1; i<n-1; i++)
{
d=det(C.x,C.y,D.x,D.y,vec[i].x,vec[i].y);
if (d<0) {
tempVec[i1++]=vec[i];
}
else {
tempVec[i2--]=vec[i];
}
}
tempVec[i1]=vec[n-1];
for (int i=0; i<n; i++)
{
vec[i]=tempVec[i];
}
return ;
}
float getArea(zArrayT<b2Vec2>& vs)
{
float area=0.0;
float p1X=0.0, p1Y=0.0;
float inv3=1.0/3.0;
int n=vs.count();
for (int i=0; i < n; i++)
{
b2Vec2 p2=vs[i];
b2Vec2 p3=vs[i+1<n?i+1:0];
float e1X=p2.x-p1X;
float e1Y=p2.y-p1Y;
float e2X=p3.x-p1X;
float e2Y=p3.y-p1Y;
float D=(e1X * e2Y - e1Y * e2X);
float triangleArea=0.5*D;
area+=triangleArea;
}
return area;
}
JoySplitRayCastCallback::JoySplitRayCastCallback( b2World* pworld, b2Body* body/*=NULL*/ ):
world(pworld),clickBody(body),destroyed(false)
,m_minx(0xffff),m_miny(0xffff),m_maxx(-0xffff),m_maxy(-0xffff)
{
if (body)
{
explodingBodies.push(body);
b2Fixture* fixture=body->GetFixtureList();
while(fixture)
{
if (fixture->GetType()==b2Shape::e_polygon)
{
b2PolygonShape* poly=(b2PolygonShape*)fixture->GetShape();
int numVertices=poly->GetVertexCount();
b2Vec2 v;
for(int i=0; i < poly->GetVertexCount(); i++)
{
v=poly->GetVertex(i);
m_minx=MIN(m_minx, v.x);
m_miny=MIN(m_miny, v.y);
m_maxx=MAX(m_maxx, v.x);
m_maxy=MAX(m_maxy, v.y);
}
}
fixture=fixture->GetNext();
}
}
FloatCompareAccuracy();
}
float32 sb2Cross(const b2Vec2& a, const b2Vec2& b)
{
return a.x * b.y - a.y * b.x;
}
void ShowVect(const char* name, zArrayT<b2Vec2> &vecs)
{
printf("%s\n",name);
for (unsigned int i=0; i < vecs.count(); i++)
{
printf("(%f %f) ",vecs[i].x, vecs[i].y);
}
printf("\n");
}
bool JoySplitRayCastCallback::CheckClockwise(zArrayT<b2Vec2> &vecs)
{
int cnt=vecs.count();
for (int32 i = 0; i < cnt; ++i)
{
int32 i1 = i;
int32 i2 = i + 1 < cnt ? i + 1 : 0;
b2Vec2 edge = vecs[i2] - vecs[i1];
if (edge.LengthSquared() < b2_epsilon * b2_epsilon)
{
return false;
}
if(!PointInBody(vecs[i]))
{
printf("illeage data (%f,%f)\n",vecs[i].x, vecs[i].y);
return false;
}
for (int32 j = 0; j < cnt; ++j)
{
// Don't check vertices on the current edge.
if (j == i1 || j == i2)
{
continue;
}
b2Vec2 r = vecs[j] - vecs[i1];
// If this crashes, your polygon is non-convex, has colinear edges,
// or the winding order is wrong.
float32 s = sb2Cross(edge, r);
if (s<0)
{
return false;
}
}
}
return true;
}
void JoySplitRayCastCallback::splitObj( b2Body* sliceBody, b2Vec2& A, b2Vec2& B )
{
if (clickBody==sliceBody && destroyed)
{
return;
}
b2Fixture* origFixture=sliceBody->GetFixtureList();
b2PolygonShape* poly=(b2PolygonShape*)origFixture->GetShape();
int numVertices=poly->GetVertexCount();
zArrayT<b2Vec2> shape1Vertices;
zArrayT<b2Vec2> shape2Vertices;
float d;
A=sliceBody->GetLocalPoint(A);
B=sliceBody->GetLocalPoint(B);
shape1Vertices.push(A);
shape1Vertices.push(B);
shape2Vertices.push(A);
shape2Vertices.push(B);
for (int i=0; i < numVertices; i++)
{
b2Vec2 v=poly->GetVertex(i);
d=det(A.x,A.y,B.x,B.y,v.x, v.y);
if (d>0)
{
shape1Vertices.push(v);
}
else
{
shape2Vertices.push(v);
}
}
//ShowVect("shap1", shape1Vertices);
//ShowVect("shap2", shape2Vertices);
arrangeClockwise(shape1Vertices);
arrangeClockwise(shape2Vertices);
// creating the first shape, if big enough
if (getArea(shape1Vertices)>=0.05 && CheckClockwise(shape1Vertices))
{
CreateBody( sliceBody, shape1Vertices);
}
// creating the second shape, if big enough
if (getArea(shape2Vertices)>=0.05 && CheckClockwise(shape2Vertices)) {
CreateBody( sliceBody, shape2Vertices);
}
destroyed=true;
if (clickBody!=sliceBody)
{
world->DestroyBody(sliceBody);
int idx=slicedBodies.find(sliceBody);
if (idx!=-1)
{
slicedBodies.remove(idx);
}
}
}
void JoySplitRayCastCallback::resetCB()
{
enterPointsVec.setSize(0);
enterPointsVecBody.setSize(0);
}
void JoySplitRayCastCallback::addPoint( b2Body* body, const b2Vec2& p )
{
enterPointsVecBody.push(body);
enterPointsVec.push(p);
}
void JoySplitRayCastCallback::removePoint( b2Body* body )
{
int n=enterPointsVecBody.find(body);
if (n>=0)
{
enterPointsVecBody.remove(n);
enterPointsVec.remove(n);
}
}
void JoySplitRayCastCallback::CreateBody( b2Body* sliceBody, zArrayT<b2Vec2> &vecs)
{
b2Fixture* origFixture=sliceBody->GetFixtureList();
b2BodyDef bodyDef;
bodyDef.type=b2_dynamicBody;
bodyDef.position=sliceBody->GetPosition();
b2FixtureDef fixtureDef;
fixtureDef.density=origFixture->GetDensity();
fixtureDef.friction=origFixture->GetFriction();
fixtureDef.restitution=origFixture->GetRestitution();
b2PolygonShape polyShape;
b2Body* body=NULL;
polyShape.Set(vecs.getDataPtr(), vecs.count());
fixtureDef.shape=&polyShape;
removePoint(body);
body=world->CreateBody(&bodyDef);
//body->SetAngularVelocity(sliceBody->GetAngle());
body->CreateFixture(&fixtureDef);
// setting a velocity for the debris
b2Vec2 v(1,1);
body->SetLinearVelocity(v);
// the shape will be also part of the explosion and can explode too
explodingBodies.push(body);
slicedBodies.push(body);
return ;
}
bool JoySplitRayCastCallback::PointInBody( const b2Vec2& point )
{
return (point.x<=m_maxx && point.x >=m_minx && point.y <=m_maxy && point.y>=m_miny);
}
3.切分调用代码
/*
world: b2world对象
pos:受力点
body:被切分的box2d对象
csx:被切分的cocos2dx对象
*/
void BodyExplosion(b2World *world, b2Vec2 &pos, b2Body* body, CCSprite* csx)
{
if (body==NULL)
{
return;
}
float cutAngle=0.0;
b2Vec2 p1,p2;
float scale=32;
JoySplitRayCastCallback cb(world, body);
int i;
for (i=0; i<5; i++)
{
cutAngle=rand()*PI*2;
p1.x=(pos.x + i/10.0 - 2000*cos(cutAngle))/scale;
p1.y=(pos.y - 2000*sin(cutAngle))/scale;
p2.x=(pos.x + 2000*cos(cutAngle))/scale;
p2.y=(pos.y + 2000*sin(cutAngle))/scale;
world->RayCast(&cb, p1, p2);
world->RayCast(&cb, p2, p1);
cb.resetCB();
}
for(i=0; i<cb.slicedBodies.count(); i++)
{
b2Body* bd=cb.slicedBodies[i];
CCSprite* sp=CreateSprintByBody(bd, csx, scale);
}
}
CCSprite* CreateSprintByBody(b2Body* body, CCSprite* csx, float metor)
{
b2Fixture* fixture=body->GetFixtureList();
b2PolygonShape* poly=(b2PolygonShape*)fixture->GetShape();
int numVertices=poly->GetVertexCount();
CCPoint points[10];
float scalex=csx->getScaleX();
float scaley=csx->getScaleY();
CCPoint ap=csx->getAnchorPoint();
unsigned long ow=csx->getTexture()->getContentSize().width;
ow*=ap.x;
unsigned long oh=csx->getTexture()->getContentSize().height;
oh*=ap.y;
for (int i=0; i<numVertices; i++)
{
b2Vec2 v=poly->GetVertex(i);
points[i].x=v.x*metor/scalex+ow;
points[i].y=v.y*metor/scaley+oh;
}
CCSprite* sp=CCSprite::spriteWithTexture(csx->getTexture());
sp->setPoly(numVertices, points);
sp->setScaleX(scalex);
sp->setScaleY(scaley);
sp->setAnchorPoint(CCPointMake(0.5,0.5));
return sp;
}
4. CCSprite类修改
修改了cocos2dx的ccSprite.h和ccSprite.cpp文件
4.1 头文件添加
int m_polyVertexCount;
ccV2F_C4F_T2F m_polyVertexData[10];
public:
void setPoly(int count,CCPoint* vertex);
void drawPoly();
4.2 源文件添加
void CCSprite::setPoly( int count,CCPoint* vertex )
{
if (count<=0 || count>10)
{
m_polyVertexCount=0;
return;
}
m_polyVertexCount=count;
float w=getTexture()->getPixelsWide();
float h=getTexture()->getPixelsHigh();
float x,y;
for (int i=0; i<count; i++)
{
x=vertex[i].x;
y=vertex[i].y;
m_polyVertexData[i].vertices.x = x;
m_polyVertexData[i].vertices.y = y;
m_polyVertexData[i].texCoords.u = x/w;
m_polyVertexData[i].texCoords.v = y/h;
m_polyVertexData[i].colors.r=255;
m_polyVertexData[i].colors.g=255;
m_polyVertexData[i].colors.b=255;
m_polyVertexData[i].colors.a=255;
}
}
void CCSprite::drawPoly()
{
CCNode::draw();
CCAssert(! m_bUsesBatchNode, "");
// Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
// Needed states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
// Unneeded states: -
bool newBlend = m_sBlendFunc.src != CC_BLEND_SRC || m_sBlendFunc.dst != CC_BLEND_DST;
if (newBlend)
{
glBlendFunc(m_sBlendFunc.src, m_sBlendFunc.dst);
}
/// ========================================================================
// Replaced [texture_ drawAtPoint:CGPointZero] with my own vertexData
// Everything above me and below me is copied from CCTextureNode's draw
if (m_pobTexture)
{
glBindTexture(GL_TEXTURE_2D, m_pobTexture->getName());
}
else
{
glBindTexture(GL_TEXTURE_2D, 0);
}
glVertexPointer(2, GL_FLOAT, sizeof(ccV2F_C4F_T2F), &m_polyVertexData[0].vertices);
glTexCoordPointer(2, GL_FLOAT, sizeof(ccV2F_C4F_T2F), &m_polyVertexData[0].texCoords);
//glColorPointer(4, GL_FLOAT, sizeof(ccV2F_C4F_T2F), &m_polyVertexData[0].colors);
glDrawArrays(GL_TRIANGLE_FAN, 0, m_polyVertexCount);
}
4.3 修改CCSprite类的draw函数
void CCSprite::draw(void)
{
CCNode::draw();
CCAssert(! m_bUsesBatchNode, "");
if(m_polyVertexCount!=0)
{
drawPoly();
return;
}