菜鸟也能学cocos2dx3.0 物理的世界Physics-浅析篇(四)

这一系列文章主要会讲一些百度出来的博客资料中涉及不太多的地方,或者有点过时的地方,特别基础的东西,我不认为会讲得比TestCpp,TestLua好,也不认为会讲的比例如红孩儿大神的深入引擎好~好吧,开始咯。首先,在cocos2dx 3.0我们很欣喜的看到引擎组借用了chipmunk的api接口,完成了自己的一套物理引擎体系,跟cocos2dx框架的结合性自不必说,为此引擎组重构了事件派发eventdispatcher,当然不仅仅是因为physics的关系~

1.如何用physics创建物理世界;

   以helloworld为例:

Scene* HelloWorld::createScene()
{
    // 'scene' is an autorelease object
    auto scene = Scene::createWithPhysics();//创建一个携带物理世界PhysicsWorld的场景
    
    // 'layer' is an autorelease object
    auto layer = HelloWorld::create();

    layer->setPhyWorld(scene->getPhysicsWorld());//获得场景的PhysicsWorld添加到layer
    scene->getPhysicsWorld()->setDebugDrawMask(PhysicsWorld::DEBUGDRAW_ALL);//开启所有shape的debug,所有物理世界的body都会以红色渲染出来,达到世界中body的可视效果。
    // add layer as a child to scene
    scene->addChild(layer);

    // return the scene
    return scene;
}

在看引擎代码:

bool Scene::initWithPhysics()
{
    bool ret = false;
    do
    {
        Director * director;
        CC_BREAK_IF( ! (director = Director::getInstance()) );//获得导演单例
        this->setContentSize(director->getWinSize());//设置当前大小
        CC_BREAK_IF(! (_physicsWorld = PhysicsWorld::construct(*this)));//初始化physicsworld,new一个新的physicsworld,并init;
        
        this->scheduleUpdate();
        // success
        ret = true;
    } while (0);
    return ret;
}

bool PhysicsWorld::init(Scene& scene)
{
    do
    {
        _info = new PhysicsWorldInfo();//物理世界的参数,
        CC_BREAK_IF(_info == nullptr);
        
        _scene = &scene;//将参数的scene的地址传给成员_scene
        
        _info->setGravity(_gravity);//设置重力,默认为ccp(0.f,-98.f)
        
        cpSpaceSetDefaultCollisionHandler(_info->getSpace(),
                                          (cpCollisionBeginFunc)PhysicsWorldCallback::collisionBeginCallbackFunc,
                                          (cpCollisionPreSolveFunc)PhysicsWorldCallback::collisionPreSolveCallbackFunc,
                                          (cpCollisionPostSolveFunc)PhysicsWorldCallback::collisionPostSolveCallbackFunc,
                                          (cpCollisionSeparateFunc)PhysicsWorldCallback::collisionSeparateCallbackFunc,
                                          this);//调用chimpunk的api注册碰撞回调函数
        
        return true;
    } while (false);
    
    return false;
}

就这样,就创建一个带有物理世界感应的场景。


2.PhysicsBody,创建物理世界中的对象

第二个参数都是材质,最后的point则是默认初始位置,size则为大小,多边形则要有point数组创建

 static PhysicsBody* create(float mass, float moment);
    
    static PhysicsBody* createCircle(float radius, const PhysicsMaterial& material = PHYSICSBODY_MATERIAL_DEFAULT, const Point& offset = Point::ZERO);//创建一个圆
   
    static PhysicsBody* createBox(const Size& size, const PhysicsMaterial& material = PHYSICSBODY_MATERIAL_DEFAULT, const Point& offset = Point::ZERO);//创建一个矩形
    
    static PhysicsBody* createPolygon(const Point* points, int count, const PhysicsMaterial& material = PHYSICSBODY_MATERIAL_DEFAULT, const Point& offset = Point::ZERO);//创建一个多边形
    
    
    static PhysicsBody* createEdgeSegment(const Point& a, const Point& b, const PhysicsMaterial& material = PHYSICSBODY_MATERIAL_DEFAULT, float border = 1);//创建一个不受重力约束自由的线条
   
    static PhysicsBody* createEdgeBox(const Size& size, const PhysicsMaterial& material = PHYSICSBODY_MATERIAL_DEFAULT, float border = 1, const Point& offset = Point::ZERO);//创建一个不受重力约束自由的矩形
    
    static PhysicsBody* createEdgePolygon(const Point* points, int count, const PhysicsMaterial& material = PHYSICSBODY_MATERIAL_DEFAULT, float border = 1);//创建一个不受重力约束自由的多边形
   
    static PhysicsBody* createEdgeChain(const Point* points, int count, const PhysicsMaterial& material = PHYSICSBODY_MATERIAL_DEFAULT, float border = 1);//创建一个不受重力约束自由的 链条   
示例:

        Sprite* spr=Sprite::create("CloseNormal.png");
	this->addChild(spr,1,100);
	auto body=PhysicsBody::createCircle(spr->getContentSize().width/2);
	spr->setPhysicsBody(body);
	spr->setPosition(visibleSize.width/2,visibleSize.height/2);
	auto box=PhysicsBody::createEdgeBox(visibleSize/4);
	auto node=Node::create();
	node->setPhysicsBody(box);
	node->setPosition(visibleSize.width/2,visibleSize.height/2);
	this->addChild(node,1);
创建一个包围盒包裹圆形的刚体

3.碰撞

注册碰撞检测:

实例:

       auto contactListener = EventListenerPhysicsContact::create();
	contactListener->onContactBegin = CC_CALLBACK_2(HelloWorld::onContactBegin, this);
	_eventDispatcher->addEventListenerWithSceneGraphPriority(contactListener, this);

EventListenerPhysicsContactWithBodies,EventListenerPhysicsContactWithShapes ,EventListenerPhysicsContactWithGroup  可以用来监听你感兴趣的两个物体、两个形状,或者某组物体的碰撞事件

再深入点,看引擎:

void EventListenerPhysicsContact::onEvent(EventCustom* event)
{
    PhysicsContact& contact = *(PhysicsContact*)(event->getUserData());//获取eventdispather发送过来的碰撞事件
    
    switch (contact.getEventCode())
    {
        case PhysicsContact::EventCode::BEGIN://碰撞刚刚发生
        {
            bool ret = true;
            
            if (onContactBegin != nullptr
                && hitTest(contact.getShapeA(), contact.getShapeB()))
            {
                contact._begin = true;
                contact.generateContactData();//设置坐标参数,比如我们可以用normal的y<0使一个shapeA只能通过shapeB的下部,==0则上下都无法通过,>0只能通过上边。可以看testcpp的onewayplatom,感觉上会比用box2d实现oneway简单。= =
                     
            //然后再启动onContactBegin事件,说明event的优先级高于onContactbegin,由event先处理,然后我们的EventListenerPhysicsContact再接收到contactbegin。      
                if (ret)
                {
                    ret = onContactBegin(event, contact);
                }else
                {
                    onContactBegin(event, contact);
                }
            }
            
            contact.setResult(ret);
            break;
        }
        case PhysicsContact::EventCode::PRESOLVE://碰撞发生时参数计算
        {
            bool ret = true;
            
            if (onContactPreSolve != nullptr
                && hitTest(contact.getShapeA(), contact.getShapeB()))
            {
                PhysicsContactPreSolve solve(contact._begin ? nullptr : contact._contactData, contact._contactInfo);
                contact._begin = false;
                contact.generateContactData();
                
                ret = onContactPreSolve(event, contact, solve);
            }
            
            contact.setResult(ret);
            break;
        }
        case PhysicsContact::EventCode::POSTSOLVE://碰撞结束参数计算设置
        {
            if (onContactPostSolve != nullptr
                && hitTest(contact.getShapeA(), contact.getShapeB()))
            {
                PhysicsContactPostSolve solve(contact._contactInfo);
                onContactPostSolve(event, contact, solve);
            }
            break;
        }
        case PhysicsContact::EventCode::SEPERATE://shapeA与shapeB分离
        {
            if (onContactSeperate != nullptr
                && hitTest(contact.getShapeA(), contact.getShapeB()))
            {
                onContactSeperate(event, contact);
            }
            break;
        }
        default:
            break;
    }
}
而EventListenerPhysicsContact则会在init时候向eventDispatcher注册onenter。


ps:限于篇幅,就不继续详细说明了,以后有时间会补上,我感觉吧,长长的篇幅虽然很详细,但是往往是和我一样的人最不容易看完的类型了。

你可能感兴趣的:(cocos2d-x)