瘸腿蛤蟆笔记38-cocos2d-x-3.2 Box2d物理引擎Ray-cast代码介绍

瘸腿蛤蟆原创笔记,欢迎转载,转载请标明出处:

 代码可以在:http://download.csdn.net/detail/notbaron/7870335 处下载


上篇回顾

本篇名言:一个客观的艺术不只是用来看的,而是活生生的。但是你必须知道如何去靠近它,因此你必须要做静心。 [OSHO]

上篇中,蛤蟆学习了Box2d物理引擎中的Box2dTestBed代码结构,接下去蛤蟆继续学习这个Box2d引擎的使用,这次我们接着37篇的Ray-cast 使用,来介绍Ray-cast的原理。

 

理论介绍

         我们先学习这次用到的理论知识,主要是一些类和函数介绍:

b2ContactListener,这个b2ContactListener类(有源代码提供, 在b2WorldCallbacks.h文件中)是获取碰撞信息。可以使用这些信息用来声音和游戏逻辑。我们可以通过在时间步之后遍历得到接触结果。但是因为分布执行可能会错过一些接触。此外我们在一个时间步中会得到很多回调函数(我们需要高效的回调函数)。我们不能在这个回调函数中创建或摧毁Box2D的实体。

 

class b2RayCastCallback这个类是 raycasts.的回调函数类。主要实现如下这个函数

         virtualfloat32ReportFixture(  b2Fixture* fixture, constb2Vec2& point,

                                                                                   constb2Vec2&normal, float32 fraction) = 0;

参数 Fixture表示被ray击中的fixture,point表示初始交点, normal法向相量。

在查询中发现FIXTURE是被调用,返回-1,0,fraction 或者1 。-1表示忽视,0 表示停止ray case,Fraction 表示从这个点截断, 1 表示不截断 ray 并继续。

 

class DestructionListener : publicb2DestructionListener 在删除Body,隐性删除Fixture和Joint的时候会被调用。

 

class GLESDebugDraw : public b2Draw这个类实现DEBUG画图调用。在每个时间步内都会被调用。

这个Draw类被继承和注册,提供在游戏中画物理实体以供调度。由于Box2D中所有对象都是不可见的,要创建可见对象我们得用刚体定义中的userData属性来创建自己的图形,如果不创建自己的图形,则用b2DebugDraw类来实现物体的可见,方便调试。

 

void DrawDebugData(); 这个函数用来调用绘画shapes,其他debug数据。

Draw函数,是Node类的函数。

virtual void draw

(

Renderer 

renderer,

const Mat4 

transform,

bool 

transformUpdated 

)

    这个函数是Node类的虚函数,所有继承于Node的函数都可以去实现它。

官方解释如下:

重写这个方法来绘制你自己的节点。 以下的GL状态是默认开启的:

  • glEnableClientState(GL_VERTEX_ARRAY);
  • glEnableClientState(GL_COLOR_ARRAY);
  • glEnableClientState(GL_TEXTURE_COORD_ARRAY);
  • glEnable(GL_TEXTURE_2D); 并且你不能够关闭他们在绘制完你的节点之后。 但是如果你开启了其他的GL状态,那么你要关闭他们在绘制完你的节点之后。

 

CustomCommand这个类继承于RenderCommand 如下图1所示

RenderCommandRenderCommand体系的基类.渲染器(Renderer) 指定如何渲染

RenderCommands对象.

 

 

具体步骤

1、定义一个碰撞类RayCastNew

首先我们创建一个RayCastNew.h文件中,定义一个类RayCastNew,让其继承于b2ContactListener该类再做理论介绍了已经)。在该类中定义一些常用然后定义一个析构函数:析构函数主要作用是创建一个物理世界,最后增加一个Step函数用于每帧的世界变化。这个类如下:

class RayCastNew : public b2ContactListener

{

public:

         enum

         {

                   e_maxBodies = 256 //定义最多Body数量

         };

 

         enum Mode    //定义3个模式

         {

                   e_closest,

                   e_any,

                   e_multiple

         };

         RayCastNew()  //析构函数,主要是创建物理世界,只调用一次,完成第一次的世界构建。

         {

                   b2Vec2 gravity;

                   gravity.Set(0.0f,-10.0f);  //定义一个重力向量

                   m_world= new b2World(gravity);//通过向量创建一个物理世界

                   {

                            b2BodyDef bd; //定义变量,用于定义Body参数。

                            b2Body* ground = m_world->CreateBody(&bd);//创建地面Body.

                            b2EdgeShape shape; //定义变量边缘形状.

                            shape.Set(b2Vec2(-40.0f, 0.0f),b2Vec2(40.0f, 0.0f));//设置边缘形状的参数

                            ground->CreateFixture(&shape,0.0f); //关联地面Body和边缘形状(其实就是一条线)

GLESDebugDraw * _debugDraw =newGLESDebugDraw(0.5);//定义GLESDebugDraw类,用于画底面

                            m_world->SetDebugDraw(_debugDraw);//注册GLESDebugDraw

                            uint32 flags = 0;

                            flags+= b2Draw::e_shapeBit;//设置需要画的内容

                            _debugDraw->SetFlags(flags);//加载需要画内容的位

                            m_world->DrawDebugData();//画出地面Body.

                   }

                   m_angle= 0.0f; //设置角度

                   m_mode= e_closest; //设置模式

         }

         void Step(Settings*settings ) //实现step函数

         {

                   bool advanceRay =settings->pause == 0 ||settings->singleStep;                 //参看Setting变量的选项是是否为0.

                   float32 L = 11.0f; //设置射线长度

                   b2Vec2 point1(0.0f, 10.0f); //射线起点

                   b2Vec2 d(L * cosf(m_angle), L * sinf(m_angle)); //射线的长度向量

                   b2Vec2 point2 = point1 + d; //射线的终点

         if (m_mode ==e_closest) //根据模式不同选择不同的分支

                   {

                            RayCastClosestCallback callback; //定义回调函数变量

                            m_world->RayCast(&callback,point1, point2);//定义射线的起终点,和射线的回调函数,当有射线碰撞的是偶会调用callback变量中的函数。

                            if (callback.m_hit) //这个变量是RayCastClosestCallback中的,标记是否有碰撞,这个变量会在碰撞后被callback中的函数ReportFixture设置。

                            {

                                     m_debugDraw.DrawPoint(callback.m_point,5.0f,b2Color(0.4f, 0.9f, 0.4f)); //画点

                                     m_debugDraw.DrawSegment(point1,callback.m_point,b2Color(0.8f,0.8f, 0.8f)); //画线

                                     b2Vec2 head = callback.m_point + 0.5f * callback.m_normal; //得到法向量的一半长度

                                     m_debugDraw.DrawSegment(callback.m_point,head,b2Color(0.9f, 0.9f, 0.4f)); //画线,是从碰撞点到法向量长度的一半。

                            }

                            else

                            {

                                     m_debugDraw.DrawSegment(point1,point2,b2Color(0.8f, 0.8f, 0.8f));//没有碰撞就画整条直线

                            }

                   }

                   if (advanceRay)

                   {

                            m_angle+= 0.25f * b2_pi / 180.0f; //每帧都改变角度大小

                   }

         }

         b2EdgeShape m_edge; //以下是变量定义不一一解释了

         Mode m_mode;

         GLESDebugDraw m_debugDraw;

         int32 m_textLine;

         b2World* m_world;

         b2Body* m_groundBody;

         b2PolygonShape m_polygons[4];

         b2CircleShape m_circle;

         int32 m_bodyIndex;

         b2Body* m_bodies[e_maxBodies];

         float32 m_angle;

};

 

2、定义实现b2RayCastCallback类

         在RayCastNew.h函数中,加入如下相关 结构体和类定义。

struct ContactPoint //结构体,定义碰撞相关的信息变量

{

         b2Fixture* fixtureA;//碰撞物体A

         b2Fixture* fixtureB; //碰撞物体B

         b2Vec2 normal; //法向量

         b2Vec2 position; //碰撞位置

         b2PointState state; //碰撞状态

         float32 normalImpulse; //法向冲量

         float32 tangentImpulse; //切向冲量

         float32 separation; //分离

};

const int32 k_maxContactPoints = 2048;

 

class RayCastClosestCallback : publicb2RayCastCallback//该类继承于b2RayCastCallback,会在射线发生碰撞的时候被调用

{

public:

         RayCastClosestCallback()//析构函数,初始设置为FALSE。

         {

                   m_hit= false;

         }

 

         float32 ReportFixture(b2Fixture*fixture,const b2Vec2& point, const b2Vec2& normal, float32 fraction)

         {

                   b2Body* body = fixture->GetBody();//获取碰撞的Body

                   void* userData = body->GetUserData();//获取碰撞的Body的数据

                   if (userData) //如果有Body信息

                   {

                            int32 index = *(int32*)userData;//获取数据

                            if (index == 0)

                            {

                                     // By returning -1, we instruct the calling code to ignore this fixtureand

                                     // continue the ray-cast to the next fixture.

                                     return -1.0f;

                            }

                   }

                   m_hit= true;//设置碰撞为true。

                   m_point= point;//设置碰撞的点

                   m_normal= normal;//设置碰撞的法向量。

                   returnfraction;//返回fraction表示,射线从碰撞的地方截断。

         }

        

         bool m_hit; //以下三个变量

         b2Vec2 m_point;

         b2Vec2 m_normal;

};

 

#define     RAND_LIMIT 32767

#define DRAW_STRING_NEW_LINE 25

inline float32 RandomFloat() //这个是随机浮点函数

{

         float32 r = (float32)(std::rand()& (RAND_LIMIT));

         r /=RAND_LIMIT;

         r =2.0f * r - 1.0f;

         return r;

}

 

/// Random floating point number in range [lo, hi]

inline float32 RandomFloat(float32lo,float32 hi) //这个是随机浮点函数,范围在lo到hi

{

         float32 r = (float32)(std::rand()& (RAND_LIMIT));

         r /=RAND_LIMIT;

         r =(hi -lo) * r+lo;

         return r;

}

 

struct Settings//定义结构体,用于定义所有和绘图相关的信息。

{

         Settings()

         {

                   viewCenter.Set(0.0f,20.0f);

                   hz= 60.0f;

                   velocityIterations= 8;

                   positionIterations= 3;

                   drawShapes= 1;

                   drawJoints= 1;

                   drawAABBs= 0;

                   drawContactPoints= 0;

                   drawContactNormals= 0;

                   drawContactImpulse= 0;

                   drawFrictionImpulse= 0;

                   drawCOMs= 0;

                   drawStats= 0;

                   drawProfile= 0;

                   enableWarmStarting= 1;

                   enableContinuous= 1;

                   enableSubStepping= 0;

                   enableSleep= 1;

                   pause= 0;

                   singleStep= 0;

         }

 

         b2Vec2 viewCenter;

         float32 hz;

         int32 velocityIterations;

         int32 positionIterations;

         int32 drawShapes;

         int32 drawJoints;

         int32 drawAABBs;

         int32 drawContactPoints;

         int32 drawContactNormals;

         int32 drawContactImpulse;

         int32 drawFrictionImpulse;

         int32 drawCOMs;

         int32 drawStats;

         int32 drawProfile;

         int32 enableWarmStarting;

         int32 enableContinuous;

         int32 enableSubStepping;

         int32 enableSleep;

         int32 pause;

         int32 singleStep;

};

 

3、定义个视图层类Box2D类

         该类主要是调用RayCastNew类,我们只要理解Box2D是RayCastNew 类的容器即可。主要实现的函数式draw函数,这个函数是Node类的虚函数,实现可以实现Node的画图。

该类的头文件如下描述:

#pragma once

#include "cocos2d.h"//包含cocos2d头文件

#include "GLES-Render.h"//这个文件来自源码包里面,主要负责绘图功能的,不然咱们什么都看不到的了

#include "RayCastNew.h"//包含碰撞类头文件

USING_NS_CC;

class Box2D :

         publicLayer

{

public:

         staticBox2D*viewWithEntryID(); //这个是静态类,被HelloWorld.pp中的init函数调用

         bool Box2D::initWithEntryID();

         Box2D(void);

         ~Box2D(void);

         RayCastNew* m_test; //定义碰撞体类

         Settings settings;

         virtualvoid draw(Renderer *renderer,constMat4 &transform, uint32_t flags)override;//重写Node的draw函数

         CustomCommand _customCmd; //定义渲染对象类

         void Box2D::onDraw(const Mat4&transform, uint32_tflags);//被draw调用

};

CPP实现如下:

#include "Box2D.h"

Box2D::Box2D(void) //构造函数

{

}

Box2D::~Box2D(void) //析构函数

{

}

Box2D* Box2D::viewWithEntryID() //静态函数是实现

{

          Box2D*pView =newBox2D();

   pView->initWithEntryID();

   pView->autorelease();

    return pView;

}

bool Box2D::initWithEntryID()

{

         m_test= newRayCastNew(); //新建一个RayCastNew对象

         returntrue;

}

void Box2D::draw(Renderer *renderer,constMat4 &transform,uint32_t flags)//重写Node类的draw

{

    Layer::draw(renderer,transform,flags);

         _customCmd.init(_globalZOrder);//命令按深度排序

   _customCmd.func = CC_CALLBACK_0(Box2D::onDraw,this,transform, flags);//定义回调函数onDraw

    renderer->addCommand(&_customCmd);

}

void Box2D::onDraw(const Mat4 &transform, uint32_t flags)

{

    Director* director =Director::getInstance();//获取实例

    CCASSERT(nullptr !=director,"Director is null when seting matrixstack");

   director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);

   director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW,transform);

   GL::enableVertexAttribs( cocos2d::GL::VERTEX_ATTRIB_FLAG_POSITION );

   m_test->Step(&settings);//调用step

   m_test->m_world->DrawDebugData();//实现画图

    CHECK_GL_ERROR_DEBUG();   

   director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);

}

 

4、HelloWorld文件处理

首先在HelloWolrd.h头文件中,加入头文件:

#include "Box2D.h"

然后在HelloWorld.cpp文件中,找到init函数,加入如下:

       Box2D* view =Box2D::viewWithEntryID();//调用Box2D的静态函数,创建一个Layer,该Layer中包含碰撞的RayCast物理世界。

         addChild(view,0, 2);//将Layer添加到HelloWorld 层中。

         view->setScale(15);//设置放大系数

   view->setAnchorPoint( Vec2(0,0));//设置锚点

view->setPosition(Vec2(origin.x+visibleSize.width/2,origin.y+visibleSize.height/3) );//设置显示位置

         这些代码实现将试图类增加到HelloWorld类中。

         然后编译即可。

你可能感兴趣的:(瘸腿蛤蟆笔记38-cocos2d-x-3.2 Box2d物理引擎Ray-cast代码介绍)