cocos2dx 实现果冻,刀光等效果

来源:互联网 发布:ubuntu rar 解压命令 编辑:程序博客网 时间:2024/03/29 13:58

         随着cocos2dx的广泛应用,大量的游戏会有一些特效效果,比如割绳子中的绳子软体效果,切水果的刀光,扑鱼达人3的开场动画,闪电链效果等等,像这些高级效果,其实在cocos2dx的示例程序有些类似的影子,但是没有具体实现。比如说box2d的web效果,他就是实现果冻效果的一个原型,shader中的模糊,变色等等,目前市面上的一些高级效果可以在cocos2dx的sample中找到一些简单实现,但是还需要自己加工下。下面作为小白的我也要叙述下一些效果的实现原理,说错的地方,望各位不要喷啊~。大笑

      首先说一下果冻效果,果冻它是变形的,而且碰撞的时候,在碰撞点有挤压,所以呢,实现它还得用物理引擎,这里我用的是box2d。具体的做法实现可以参考l示例程序中的box2d web效果,这里就不多讲了。然后要让果冻随意变形,这个可以绘制一个4个顶点的网格然后附上对应的纹理,然后随着box2d的4个刚体改变顶点的位置,就可以看到变形效果啦。

b2CircleShape shape;
  shape.m_radius=4;

  b2BodyDef bd;
  bd.type = b2_dynamicBody;

  bd.position.Set(-6.0f, 6.0f);
  m_bodies[0] = m_world->CreateBody(&bd);
  m_bodies[0]->CreateFixture(&shape, 5.0f); m_bodies[0]->SetBullet(true);


  bd.position.Set(4.0f, 4.0f);
  m_bodies[1] = m_world->CreateBody(&bd);
  m_bodies[1]->CreateFixture(&shape, 5.0f); m_bodies[1]->SetBullet(true);


  bd.position.Set(6.0f, 14.0f);
  m_bodies[2] = m_world->CreateBody(&bd);
  m_bodies[2]->CreateFixture(&shape, 5.0f); m_bodies[2]->SetBullet(true);


  bd.position.Set(-4.0f, 16.0f);
  m_bodies[3] = m_world->CreateBody(&bd);
  m_bodies[3]->CreateFixture(&shape, 5.0f); m_bodies[3]->SetBullet(true);


b2Vec2 v1=m_bodies[0]->GetWorldCenter();
 b2Vec2 v2=m_bodies[1]->GetWorldCenter();
 b2Vec2 v3=m_bodies[2]->GetWorldCenter();
 b2Vec2 v4=m_bodies[3]->GetWorldCenter();
 
 adjust(v1,v3);  adjust(v2,v4);
 Vertex Vertices[] = {
  // Front
  {{480+v1.x*32,v1.y*32, 0}, {1, 1, 0, 1}, {1, 0}},
  {{480+v2.x*32,v2.y*32, 0}, {1, 1, 1,1}, {1, 1}},
  {{480+v3.x*32,v3.y*32, 0}, {1, 1, 1, 1}, {0, 1}},
  {{480+v4.x*32,v4.y*32, 0}, {0, 1, 1, 1}, {0, 0}}
 };

  刀光效果一般根据移动的轨迹动态计算出网格,然后给纹理,或者上色什么的。

  看过shader示例的 应该都感觉很炫吧,没错,这个东东确实可以实现很多高级效果,如果你算法底子好,那么可以搞出一些 绚丽的 画面。


以上种种都需要有opengl基础额,所以呢,想实现高级效果的朋友不妨发点时间看看opengl 的知识,而且对将来实现3d场景也有很多益处。

好了,先讲到这里了。 下面贴出果冻的全部实现代码:

//HelloWorld.h

class HelloWorld : public cocos2d::Layer
{
public:
      virtual bool init(); 
      void onEnter();
      void update(float dt);
      void onDraw();
      CustomCommand _customCommand;
      CREATE_FUNC(HelloWorld);
      Vec2 m_mouseWorld;
      b2MouseJoint* m_mouseJoint;
      b2Body* m_groundBody;
      GLESDebugDraw *m_debugDraw;
      b2World* m_world;
      CCTexture2D* texture2d;
      GLuint vertexBuffer;
      GLuint indexBuffer;
      GLProgram * pg;
      b2Body* m_bodies[8];
      void adjust(b2Vec2 &v1,b2Vec2 &v2);
      bool onTouchBegan(Touch* touch, Event* event);
      void onTouchMoved(Touch* touch, Event* event);
      void onTouchEnded(Touch* touch, Event* event);
};


//HelloWorld.cpp
#include "HelloWorldScene.h"

class QueryCallback : public b2QueryCallback
{
public:
 QueryCallback(const b2Vec2& point)
 {
  m_point = point;
  m_fixture = nullptr;
 }
 bool ReportFixture(b2Fixture* fixture)
 {
  b2Body* body = fixture->GetBody();
  if (body->GetType() == b2_dynamicBody)
  {
   bool inside = fixture->TestPoint(m_point);
   if (inside)
   {
    m_fixture = fixture;
    // We are done, terminate the query.
    return false;
   }
  }
  // Continue the query.
  return true;
 }
 b2Vec2 m_point;
 b2Fixture* m_fixture;
};
Scene* HelloWorld::createScene()
{
    // 'scene' is an autorelease object
    auto scene = Scene::create();
   
    // 'layer' is an autorelease object
    auto layer = HelloWorld::create();
    // add layer as a child to scene
    scene->addChild(layer);
    // return the scene
    return scene;
}
void HelloWorld::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
{
 //
 // IMPORTANT:
 // This is only for debug purposes
 // It is recommend to disable it
 //
 Layer::draw(renderer, transform, flags);
#if CC_ENABLE_BOX2D_INTEGRATION
 GL::enableVertexAttribs( cocos2d::GL::VERTEX_ATTRIB_FLAG_POSITION );
 Director* director = Director::getInstance();
 CCASSERT(nullptr != director, "Director is null when seting matrix stack");
 director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
 _modelViewMV = director->getMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
 _customCommand.init(_globalZOrder);
 _customCommand.func = CC_CALLBACK_0(HelloWorld::onDraw, this);
 renderer->addCommand(&_customCommand);
 director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
#endif
 
}
void HelloWorld::adjust(b2Vec2 &v1,b2Vec2 &v2)
{
 float dx1=v2.x-v1.x;
 b2Vec2 midpos=b2Vec2((v2.x+v1.x)/2,(v1.y+v2.y)/2);;
 if(dx1==0)
 {
  v1=b2Vec2(v1.x,midpos.y-abs(v1.y-v2.y)/2-4);
  v2=b2Vec2(v1.x,midpos.y-abs(v1.y-v2.y)/2+4);
 }
 else
 {
  float k1=(v2.y-v1.y)/dx1;
  float nxoff=midpos.x-abs(v1.x-v2.x)/2-4;
  float pxoff=midpos.x+abs(v1.x-v2.x)/2+4;
  float ny1=k1*(nxoff-v1.x)+v1.y;
  float ny2=k1*(pxoff-v1.x)+v1.y;
  v1=b2Vec2(nxoff,ny1);
  v2=b2Vec2(pxoff,ny2);
 }
}
void HelloWorld::onDraw()

 Director* director = Director::getInstance();
 CCASSERT(nullptr != director, "Director is null when seting matrix stack");
 Mat4 oldMV;
 oldMV = director->getMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
 director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW, _modelViewMV);
 m_world->DrawDebugData();
 director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW, oldMV);
 b2Vec2 v1=m_bodies[0]->GetWorldCenter();
 b2Vec2 v2=m_bodies[1]->GetWorldCenter();
 b2Vec2 v3=m_bodies[2]->GetWorldCenter();
 b2Vec2 v4=m_bodies[3]->GetWorldCenter();
 
 adjust(v1,v3);  adjust(v2,v4);
 Vertex Vertices[] = {
  {{480+v1.x*32,v1.y*32, 0}, {1, 1, 0, 1}, {1, 0}},
  {{480+v2.x*32,v2.y*32, 0}, {1, 1, 1,1}, {1, 1}},
  {{480+v3.x*32,v3.y*32, 0}, {1, 1, 1, 1}, {0, 1}},
  {{480+v4.x*32,v4.y*32, 0}, {0, 1, 1, 1}, {0, 0}}
 };
 int vertexCount = sizeof(Vertices) / sizeof(Vertices[0]);
 GLubyte Indices[] = {
  // Front
  1, 2,0,3
 };
 glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
 glBufferData(GL_ARRAY_BUFFER,sizeof(Vertices),Vertices, GL_STATIC_DRAW);
 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
 glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Indices),Indices,GL_STATIC_DRAW);

 GLint _positionLocation = glGetAttribLocation(pg->getProgram(), "ja_position");
 GLint _colorLocation = glGetAttribLocation(pg->getProgram(), "ja_color");
 GLint _textureLocation = glGetAttribLocation(pg->getProgram(), "jjTextureCoord");
 GLint _textureUniform = glGetUniformLocation(pg->getProgram(), "jjxx");
 GLint val = glGetUniformLocation(pg->getProgram(), "val");
 GLint resolution = glGetUniformLocation(pg->getProgram(), "resolution");
 pg->use();
 pg->setUniformsForBuiltins();
 Size sz=Director::getInstance()->getWinSize();
 glUniform2f(resolution,sz.width,sz.height);
 glEnableVertexAttribArray(_positionLocation);
 glEnableVertexAttribArray(_colorLocation);
 glEnableVertexAttribArray(_textureLocation);
 glVertexAttribPointer(_positionLocation, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)offsetof(Vertex,
Position));
 glVertexAttribPointer(_colorLocation, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex),(GLvoid*)offsetof(Vertex, Color));
 glVertexAttribPointer(_textureLocation, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex),
  (GLvoid*)offsetof(Vertex, TexCoord));
 //
 ////set sampler
 GL::bindTexture2DN(0, texture2d->getName());
 glUniform1i(_textureUniform, 0); // unnecc in practice
 glEnable(GL_POINT_SMOOTH);
 glEnable(GL_BLEND);
 BlendFunc bf=BlendFunc::ALPHA_NON_PREMULTIPLIED;
 glBlendFunc(bf.src, bf.dst);
 glEnable(GL_DEPTH_TEST);
 glDrawElements(GL_TRIANGLE_STRIP,  4, GL_UNSIGNED_BYTE, 0);
 glDisable(GL_DEPTH_TEST);
}
void HelloWorld::update(float dt)
{
 m_world->Step(dt,8,1);
}
void HelloWorld::onEnter()
{
 Layer::onEnter();
}
bool HelloWorld::init()
{
    //////////////////////////////
    // 1. super init first
    Size visibleSize = Director::getInstance()->getVisibleSize();
    Vec2 origin = Director::getInstance()->getVisibleOrigin();
 texture2d = CCTextureCache::sharedTextureCache()->addImage("d.png"); 
 ct=0.1;
 pg=new GLProgram();
 pg->initWithFilenames("ccShader_Position_uColor.vert","ccShader_Position_uColor.frag");
 pg->link();
 pg->updateUniforms();
 glGenBuffers( 1, &vertexBuffer );
 glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer );
 glGenBuffers( 1, &indexBuffer );
 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer );
 //setScale(0.5);
 setAnchorPoint( Vec2(0,0) );
 setPosition( Vec2(origin.x+visibleSize.width/2, origin.y) );
 scheduleUpdate();
 m_mouseJoint=0;
 b2Vec2 gravity;
 gravity.Set(0.0f, -10.0f);
 m_world = new b2World(gravity);
 m_debugDraw=new GLESDebugDraw( 32 );
 uint32 flags = 0;
 //flags += b2Draw::e_shapeBit;
 //flags += b2Draw::e_jointBit;
 //flags += b2Draw::e_aabbBit;
 m_debugDraw->SetFlags(flags);
 m_world->SetDebugDraw(m_debugDraw);
 m_world->SetAllowSleeping(true);
 m_world->SetContinuousPhysics(true);
 b2Body* ground = NULL;
 {
  b2BodyDef bd;
  ground = m_world->CreateBody(&bd);
  b2EdgeShape shape;
  shape.Set(b2Vec2(-40.0f, 0), b2Vec2(40.0f, 0));
  ground->CreateFixture(&shape, 0.0f);
  shape.Set(b2Vec2(-480.0/32, -40), b2Vec2(-480.0/32, 40));
  ground->CreateFixture(&shape, 0.0f);
  shape.Set(b2Vec2(-40, 640.0/32), b2Vec2(40, 640.0/32));
  ground->CreateFixture(&shape, 0.0f);
  shape.Set(b2Vec2(480.0/32, -40), b2Vec2(480.0/32, 40));
  ground->CreateFixture(&shape, 0.0f);
  m_groundBody=ground;
 }
 
 {
  b2CircleShape shape;
  shape.m_radius=4;
  b2BodyDef bd;
  bd.type = b2_dynamicBody;
  bd.position.Set(-6.0f, 6.0f);
  m_bodies[0] = m_world->CreateBody(&bd);
  m_bodies[0]->CreateFixture(&shape, 5.0f); m_bodies[0]->SetBullet(true);

  bd.position.Set(4.0f, 4.0f);
  m_bodies[1] = m_world->CreateBody(&bd);
  m_bodies[1]->CreateFixture(&shape, 5.0f); m_bodies[1]->SetBullet(true);

  bd.position.Set(6.0f, 14.0f);
  m_bodies[2] = m_world->CreateBody(&bd);
  m_bodies[2]->CreateFixture(&shape, 5.0f); m_bodies[2]->SetBullet(true);

  bd.position.Set(-4.0f, 16.0f);
  m_bodies[3] = m_world->CreateBody(&bd);
  m_bodies[3]->CreateFixture(&shape, 5.0f); m_bodies[3]->SetBullet(true);
  b2DistanceJointDef jd;
  b2Vec2 p1, p2, d;
  jd.frequencyHz = 2.0f;
  jd.dampingRatio = 0.0f;
  jd.bodyA = m_bodies[0];
  jd.bodyB = m_bodies[1];
  jd.localAnchorA.Set(0.5f, 0.0f);
  jd.localAnchorB.Set(-0.5f, 0.0f);;
  p1 = jd.bodyA->GetWorldPoint(jd.localAnchorA);
  p2 = jd.bodyB->GetWorldPoint(jd.localAnchorB);
  d = p2 - p1;
  jd.length = d.Length();
  m_joints[4] = m_world->CreateJoint(&jd);
  jd.bodyA = m_bodies[0];
  jd.bodyB = m_bodies[2];
  jd.localAnchorA.Set(0.5f, 0.0f);
  jd.localAnchorB.Set(-0.5f, 0.0f);;
  p1 = jd.bodyA->GetWorldPoint(jd.localAnchorA);
  p2 = jd.bodyB->GetWorldPoint(jd.localAnchorB);
  d = p2 - p1;
  jd.length = d.Length();
  m_joints[4] = m_world->CreateJoint(&jd);
  jd.bodyA = m_bodies[1];
  jd.bodyB = m_bodies[2];
  jd.localAnchorA.Set(0.0f, 0.5f);
  jd.localAnchorB.Set(0.0f, -0.5f);
  p1 = jd.bodyA->GetWorldPoint(jd.localAnchorA);
  p2 = jd.bodyB->GetWorldPoint(jd.localAnchorB);
  d = p2 - p1;
  jd.length = d.Length();
  m_joints[5] = m_world->CreateJoint(&jd);
  jd.bodyA = m_bodies[1];
  jd.bodyB = m_bodies[3];
  jd.localAnchorA.Set(0.5f, 0.0f);
  jd.localAnchorB.Set(-0.5f, 0.0f);;
  p1 = jd.bodyA->GetWorldPoint(jd.localAnchorA);
  p2 = jd.bodyB->GetWorldPoint(jd.localAnchorB);
  d = p2 - p1;
  jd.length = d.Length();
  m_joints[4] = m_world->CreateJoint(&jd);
  jd.bodyA = m_bodies[2];
  jd.bodyB = m_bodies[3];
  jd.localAnchorA.Set(-0.5f, 0.0f);
  jd.localAnchorB.Set(0.5f, 0.0f);
  p1 = jd.bodyA->GetWorldPoint(jd.localAnchorA);
  p2 = jd.bodyB->GetWorldPoint(jd.localAnchorB);
  d = p2 - p1;
  jd.length = d.Length();
  m_joints[6] = m_world->CreateJoint(&jd);
  jd.bodyA = m_bodies[3];
  jd.bodyB = m_bodies[0];
  jd.localAnchorA.Set(0.0f, -0.5f);
  jd.localAnchorB.Set(0.0f, 0.5f);
  p1 = jd.bodyA->GetWorldPoint(jd.localAnchorA);
  p2 = jd.bodyB->GetWorldPoint(jd.localAnchorB);
  d = p2 - p1;
  jd.length = d.Length();
  m_joints[7] = m_world->CreateJoint(&jd);
 }
 
 auto listener = EventListenerTouchOneByOne::create();
 listener->setSwallowTouches(true);
 listener->onTouchBegan = CC_CALLBACK_2(HelloWorld::onTouchBegan, this);
 listener->onTouchMoved = CC_CALLBACK_2(HelloWorld::onTouchMoved, this);
 listener->onTouchEnded = CC_CALLBACK_2(HelloWorld::onTouchEnded, this);
 _eventDispatcher->addEventListenerWithFixedPriority(listener, -10);
 _touchListener = listener;
       return true;
}
bool HelloWorld::onTouchBegan(Touch* touch, Event* event)
{
 auto touchLocation = touch->getLocation();   
 Vec2 nodePosition = convertToNodeSpace( touchLocation );
 log("Box2DView::onTouchBegan, pos: %f,%f -> %f,%f", touchLocation.x, touchLocation.y, nodePosition.x,
nodePosition.y);
 m_mouseWorld = nodePosition;
 if (m_mouseJoint != nullptr)
 {
  return false;
 }
 // Make a small box.
 b2AABB aabb;
 b2Vec2 d,p(nodePosition.x/32,nodePosition.y/32);
 d.Set(0.001f, 0.001f);
 aabb.lowerBound = p - d;
 aabb.upperBound = p + d;
 // Query the world for overlapping shapes.
 QueryCallback callback(p);
 m_world->QueryAABB(&callback, aabb);
 if (callback.m_fixture)
 {
  b2Body* body = callback.m_fixture->GetBody();
  b2MouseJointDef md;
  md.bodyA = m_groundBody;
  md.bodyB = body;
  md.target = p;
  md.maxForce = 50 * body->GetMass();
  m_mouseJoint = (b2MouseJoint*)m_world->CreateJoint(&md);
  body->SetAwake(true);
  return true;
 }
 return false;
}
void HelloWorld::onTouchMoved(Touch* touch, Event* event)
{
 auto touchLocation = touch->getLocation();   
 auto nodePosition = convertToNodeSpace( touchLocation );
 log("Box2DView::onTouchMoved, pos: %f,%f -> %f,%f", touchLocation.x, touchLocation.y, nodePosition.x,
nodePosition.y);
 b2Vec2 p(nodePosition.x/32,nodePosition.y/32);
 m_mouseWorld = nodePosition;
 if (m_mouseJoint)
 {
  m_mouseJoint->SetTarget(p);
 }
}
void HelloWorld::onTouchEnded(Touch* touch, Event* event)
{
 auto touchLocation = touch->getLocation();   
 auto nodePosition = convertToNodeSpace( touchLocation );
 log("Box2DView::onTouchEnded, pos: %f,%f -> %f,%f", touchLocation.x, touchLocation.y, nodePosition.x,
nodePosition.y);
 if (m_mouseJoint)
 {
  m_world->DestroyJoint(m_mouseJoint);
  m_mouseJoint = nullptr;
 }
}
void HelloWorld::menuCloseCallback(Ref* pSender)
{
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WP8) || (CC_TARGET_PLATFORM == CC_PLATFORM_WINRT)
 MessageBox("You pressed the close button. Windows Store Apps do not implement a close button.","Alert");
    return;
#endif
    Director::getInstance()->end();
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
    exit(0);
#endif
}
效果:
以下附上刀光部分参考代码:
onTouchMoved = [&](Touch* touch, Event* event){
  Vec2 v2=touch->getLocation();touch->getDelta();
  Vertex vx={{v2.x,v2.y,0},{CCRANDOM_0_1(),CCRANDOM_0_1()+0.5,CCRANDOM_0_1(),1},{0,0}};
  varray.push_back(vx);
 };

滑动的简单算法:
 std::vector<Vertex> tarr,tarr2;
 std::vector<Vertex>::iterator iter=varray.begin();
 for(;iter!=varray.end();iter++)
 {
  Vertex vx=(*iter);
  if(tarr.empty())
  {
   vx.Color[3]=0;
    tarr.push_back(vx);
    tarr2.push_back(vx);
  }
  else
  {
   if(iter==varray.end()-1)
   {vx.Color[3]=0;
    tarr.push_back(vx);tarr2.push_back(vx);
   }
   else
   {
    float k=0,nx1,nx2,ny1,ny2;
    if(xoff!=0)
    {
     nx1=vx.Position[0]-rand()%20;xoff/2;
     nx2=vx.Position[0]+rand()%20;xoff/2;
     ny1=vx.Position[1];
     ny2=vx.Position[1];
    }
    else
    {
     nx1=vx.Position[0]-rand()%20;
     nx2=vx.Position[0]+rand()%20;
     ny1=vx.Position[1];
     ny2=vx.Position[1];
    }
    Vertex vx1={{nx1,ny1,0},
    {0,1,0,0},{0,0}};
    Vertex vx2={{nx2,ny2,0},
    {0,1,1,0},{0,0}};
    tarr.push_back(vx1); tarr.push_back(vx);
    tarr2.push_back(vx); tarr2.push_back(vx2);
   }
  }
 }
 if(varray.size()>10)
   varray.erase(varray.begin());

0 0