cocos2dx+box2d实现物体爆裂效果

1.说明
整个实现参考了网上的flash代码,代码也还没有做优化
爆炸点是按照受理点随即角度裂开的,在下面例子中就是用的鼠标click点。
对于分裂后的碎块如果太小,则直接丢弃。
切分是用的box2d的raycast来实现的,切分完毕后在创建ccsprite
为了绘制纹理,修改了CCSprite类,使之可以画一个纹理的某个区域,当然也可以从其继承一个类实现。
由于自己工程的需要,原始的被切分body不能放在这儿释放,故做了下特殊处理。
如果要实现爆炸效果,需要在切分完毕后给box2d对象一个冲量,目前设置为{1,1},可根据实际情况修改
zArray为自己的用的array类,可以用类似的类或者直接用数组实现
接口函数为BodyExplosion,此处可以将分裂次数作为参数传入
[cpp] 
/*
world: b2world对象
pos:受力点
body:被切分的box2d对象
csx:被切分的cocos2dx对象
*/ 
void BodyExplosion(b2World *world, b2Vec2 &pos, b2Body* body, CCSprite* csx) 
效果

 

2.分裂代码
2.1 头文件
[cpp] 
#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 源文件
[cpp] 
#define PI 3.141295f 
[cpp] view plaincopy
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.切分调用代码
[cpp] 
/*
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); 
    } 
}<pre name="code" class="cpp">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; 
}</pre> 
<pre></pre> 
<br> 
<p></p> 
<h1>4. CCSprite类修改</h1> 
<div>修改了cocos2dx的ccSprite.h和ccSprite.cpp文件</div> 
<p></p> 
<h2>4.1 头文件添加</h2> 
<p></p> 
<pre name="code" class="cpp">   int  m_polyVertexCount; 
    ccV2F_C4F_T2F        m_polyVertexData[10]; 
 
public: 
    void setPoly(int count,CCPoint* vertex); 
    void drawPoly(); 
</pre><br> 
<p></p> 
<p>4.2 源文件添加</p> 
<p></p> 
<pre name="code" class="cpp">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); 
 
}</pre>4.3 修改CCSprite类的draw函数 
<p></p> 
<p></p> 
<pre name="code" class="cpp">void CCSprite::draw(void) 

    CCNode::draw();
 
    CCAssert(! m_bUsesBatchNode, ""); 
 
    if(m_polyVertexCount!=0) 
    { 
        drawPoly(); 
        return; 
    } 
</pre><br> 
<br> 
<p></p> 
<br> 
作者:dragoncheng

posted on 2014-04-19 21:21  无屁不成风  阅读(448)  评论(0)    收藏  举报

导航