现在的位置: 首页 > 综合 > 正文

cocos2dx+box2d实现物体爆裂效果

2013年10月04日 ⁄ 综合 ⁄ 共 10844字 ⁄ 字号 评论关闭

1.说明

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

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;
	}

抱歉!评论已关闭.