cocos2dx3.5 新增物理引擎分析

World 世界

物理刚体被添加到一个叫世界(World)的容器里,这也是它们被模拟的场所。将bodies,shapes,constraints这些对象添加到物理世界中,将整个物理世界作为一个整体进行更新。物理世界决定了所有这些部件在一起的互动方式。其中,用物理API实现的许多互动都是与PhysicsWorld这个对象有关的。

物理世界

物理世界(PhysicsWorld)对象是进行物理模拟时的一个核心部件。物理世界(PhysicsWorld)与场景(Scene)紧密整合在一起。让我们来看一个我们都会涉及到的例子吧。你住的房子里有厨房吗?你想这个问题的时候,就像是在想你的物理世界一样!现在,你的世界里拥有一些物理刚体(PhysicsBody)对象,就跟食物、刀具、电器这些东西一样!在这个世界中,这些刚体相互作用。它们相互接触,并且对相互的接触做出反应。例如:用刀子切开食物,并把它放到电器中。刀子切到食物了吗?可能切到了。也可能还没有。还可能这个刀子根本就不适合做这个。

  PhysicsWorld相关的函数:

/** Adds a joint to the physics world.*/
    virtual void
addJoint(PhysicsJoint* joint);
    /** Remove a joint from physics world.*/
    virtual void removeJoint(PhysicsJoint* joint, bool destroy = true);
    /** Remove all joints from physics world.*/
    virtual void removeAllJoints(bool destroy = true);
    
    /** Remove a body from physics world. */
    virtual void removeBody(PhysicsBody* body);
    /** Remove body by tag. */
    virtual void removeBody(int tag);
    /** Remove all bodies from physics world. */
    virtual void removeAllBodies();

  /** set the gravity value */
    void
setGravity(const Vect& gravity);

 /**
     * set the update rate of physics world, update rate is the value of EngineUpdateTimes/PhysicsWorldUpdateTimes.
     * set it higher can improve performance, set it lower can improve accuracy of physics world simulation.
     * default value is 1.0
     * Note: if you setAutoStep(false), this won't work.
     */
    inline void
setUpdateRate(int rate) { if(rate > 0) { _updateRate = rate; } }

每一个物理世界(PhysicsWorld)都具有与之相关的属性:

-重力(gravity):全局重力,应用于整个物理世界。默认值为Vec2(0.0f,-98.0f)。

 -速度(speed):设定了物理世界的速度。这里,速度指的是这个模拟世界运动的一种比率。默认值为1.0。 

-刷新率:设定了物理世界的刷新率,这里刷新率指的是EngineUpdateTimes/PhysicsWorldUpdateTimes的比值。

 -子步(substeps):设定了物理世界中每次刷新的子步数量。刷新物理世界的过程也被称为步进(stepping)。按照默认设置,物理世界会不停地进行自动刷新。这被称为“自动步进(auto stepping)”,它会自动地进行。你可以通过设定PhysicsWorld::setAutoStep(false)禁用一个物理世界的auto step,然后通过设定PhysicsWorld::step(time)来手动刷新PhysicsWorld。substeps使用比单一框架更加精确的时间增量来刷新物理世界。使用它,我们可以实现更加细致地实现对步进过程的控制,包括更加流畅的运动。

创建一个带有物理世界的场景:

auto scene=Scene::createWithPhysics();

参考:

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)));
        
        // success
        ret = true;
    } while (0);
    return ret;
}

获得场景的物理世界

scene->getPhysicsWorld()

不过首先要保证:

CC_USE_PHYSICS==1.这个宏,控制物理世界的开和关。

物理刚体

物理刚体(PhyicsBody)对象具有位置(position)和速度(velocity)两个属性。可以在PhysicsBody上应用forces、movement、damping和impulses。物理刚体可以是静态的,也可以是动态的。静态的刚体在模拟世界中不会移动,看起来就像它拥有无限大的质量一样。动态的刚体则是一种完全仿真的模拟。它可以被用户手动移动,但更常见的是它们受到力的作用而移动。动态刚体可以与所有的刚体类型发生碰撞。

Cocos2d-x提供了Node::setPhysicsbody()来将物理刚体与一个节点对象关联在一起。

创建一个物理刚体

auto physicsBody = PhysicsBody::createBox(Size(65.0f, 81.0f),PhysicsMaterial(0.1f, 1.0f, 0.0f));

physicsBody还有以下函数
  static PhysicsBody* createCircle(float radius, const PhysicsMaterial& material = PHYSICSBODY_MATERIAL_DEFAULT, const Vec2& offset = Vec2::ZERO);
    /** Create a body contains a box shape. */
    static PhysicsBody* createBox(const Size& size, const PhysicsMaterial& material = PHYSICSBODY_MATERIAL_DEFAULT, const Vec2& offset = Vec2::ZERO);
    /**
     * @brief Create a body contains a polygon shape.
     * points is an array of Vec2 structs defining a convex hull with a clockwise winding.
     */
    static PhysicsBody* createPolygon(const Vec2* points, int count, const PhysicsMaterial& material = PHYSICSBODY_MATERIAL_DEFAULT, const Vec2& offset = Vec2::ZERO);
    
    /** Create a body contains a EdgeSegment shape. */
    static PhysicsBody* createEdgeSegment(const Vec2& a, const Vec2& b, const PhysicsMaterial& material = PHYSICSBODY_MATERIAL_DEFAULT, float border = 1);
    /** Create a body contains a EdgeBox shape. */
    static PhysicsBody* createEdgeBox(const Size& size, const PhysicsMaterial& material = PHYSICSBODY_MATERIAL_DEFAULT, float border = 1, const Vec2& offset = Vec2::ZERO);
    /** Create a body contains a EdgePolygon shape. */
    static PhysicsBody* createEdgePolygon(const Vec2* points, int count, const PhysicsMaterial& material = PHYSICSBODY_MATERIAL_DEFAULT, float border = 1);
    /** Create a body contains a EdgeChain shape. */
    static PhysicsBody* createEdgeChain(const Vec2* points, int count, const PhysicsMaterial& material = PHYSICSBODY_MATERIAL_DEFAULT, float border = 1);
 virtual PhysicsShape* addShape(PhysicsShape* shape, bool addMassAndMoment = true);

physicsBody->setDynamic(false);
 
//create a sprite
auto sprite = Sprite::create( "whiteSprite.png" );
sprite->setPosition(s_centre);
addChild(sprite);
 
精灵设置刚体
//apply physicsBody to the sprite
sprite->setPhysicsBody(physicsBody);
 
//add five dynamic bodies
for ( int i = 0; i < 5; ++i)
{
     physicsBody = PhysicsBody::createBox(Size(65.0f, 81.0f),
     PhysicsMaterial(0.1f, 1.0f, 0.0f));
 
     //set the body isn't affected by the physics world's gravitational force
     physicsBody->setGravityEnable( false );
 
     //set initial velocity of physicsBody
     physicsBody->setVelocity(Vec2(cocos2d::random(-500,500),
     cocos2d::random(-500,500)));
     physicsBody->setTag(DRAG_BODYS_TAG);
 
     sprite = Sprite::create( "blueSprite.png" );
     sprite->setPosition(Vec2(s_centre.x + cocos2d::random(-300,300),
    s_centre.y + cocos2d::random(-300,300)));
     sprite->setPhysicsBody(physicsBody);
 
     addChild(sprite);
}

物理调试

打开
#if CC_USE_PHYSICS
    getPhysicsWorld()->setDebugDrawMask( PhysicsWorld::DEBUGDRAW_ALL);
#endif
关闭
#if CC_USE_PHYSICS
    getPhysicsWorld()->setDebugDrawMask(PhysicsWorld::DEBUGDRAW_NONE);
#endif


连接/关节

关节是一种把接触点联结在一起的方式。每一个关节都有一个从PhysicsJoint对象获得的定义。在两个不同的刚体之间,所有的关节都是联结在一起的。刚体可以是静态的。你可以使用joint->setCollisionEnable(false)来避免相关联的刚体相互碰撞。很多关节的定义需要你提供一些几何数据。很多情况下,关节由锚点来定义。其余的关节定义数据取决于关节的类型。

  • PhysicsJointFixed:固定关节在一个特定的点上,将两个刚体结合在了一起。如果要创建一些以后会断裂的复杂形状,固定关节是非常有用的。

  •   PhysicsJointFixed* joint = PhysicsJointFixed::construct(sp1->getPhysicsBody(), sp2->getPhysicsBody(), offset);
       _scene->getPhysicsWorld()->addJoint(joint);

  • PhysicsJointLimit:一种限制关节,它利用了两个刚体间最大距离,就如同两个刚体被绳子连在一起一样。

  •   PhysicsJointLimit* joint = PhysicsJointLimit::construct(sp1->getPhysicsBody(), sp2->getPhysicsBody(), Point::ZERO, Point::ZERO, 30.0f, 60.0f);
                        _scene->getPhysicsWorld()->addJoint(joint);

  • PhysicsJointPin:针式关节可以让两个刚体独立地围绕锚点进行旋转,就如同它们被钉在一起了一样。

  •  PhysicsJointPin* joint = PhysicsJointPin::construct(sp1->getPhysicsBody(), sp2->getPhysicsBody(), offset);
      _scene->getPhysicsWorld()->addJoint(joint);

  • PhysicsJointDistance:设定两个刚体间的固定距离。

  • PhysicsJointDistance* joint = PhysicsJointDistance::construct(sp1->getPhysicsBody(), sp2->getPhysicsBody(), Point::ZERO, Point::ZERO);
                        _scene->getPhysicsWorld()->addJoint(joint);

  • PhysicsJointSpring:用弹簧来联结两个物理刚体

  •   PhysicsJointSpring* joint = PhysicsJointSpring::construct(sp1->getPhysicsBody(), sp2->getPhysicsBody(), Point::ZERO, Point::ZERO, 500.0f, 0.3f);
                        _scene->getPhysicsWorld()->addJoint(joint);

  • PhysicsJointGroove:将一个刚体连到线上,另一个连到点上。

  • PhysicsJointGroove* joint = PhysicsJointGroove::construct(sp1->getPhysicsBody(), sp2->getPhysicsBody(), Vec2(30, 15), Vec2(30, -15), Vec2(-30, 0));
                        _scene->getPhysicsWorld()->addJoint(joint);

  • PhysicsJointRotarySpring:与弹簧关节相似,但是增加了自旋

  •   _scene->getPhysicsWorld()->addJoint(PhysicsJointPin::construct(sp1->getPhysicsBody(), box, sp1->getPosition()));
                        _scene->getPhysicsWorld()->addJoint(PhysicsJointPin::construct(sp2->getPhysicsBody(), box, sp2->getPosition()));
                        PhysicsJointRotarySpring* joint = PhysicsJointRotarySpring::construct(sp1->getPhysicsBody(), sp2->getPhysicsBody(), 3000.0f, 60.0f);
                        _scene->getPhysicsWorld()->addJoint(joint);

  • PhysicsJointRotaryLimit:与限制关节相似,但是增加了自旋

  •  _scene->getPhysicsWorld()->addJoint(PhysicsJointPin::construct(sp1->getPhysicsBody(), box, sp1->getPosition()));
                        _scene->getPhysicsWorld()->addJoint(PhysicsJointPin::construct(sp2->getPhysicsBody(), box, sp2->getPosition()));
                        PhysicsJointRotaryLimit* joint = PhysicsJointRotaryLimit::construct(sp1->getPhysicsBody(), sp2->getPhysicsBody(), 0.0f,(float) M_PI_2);
                        _scene->getPhysicsWorld()->addJoint(joint);

  • PhysicsJointRatchet:与套筒扳手的工作类似

  •  _scene->getPhysicsWorld()->addJoint(PhysicsJointPin::construct(sp1->getPhysicsBody(), box, sp1->getPosition()));
                        _scene->getPhysicsWorld()->addJoint(PhysicsJointPin::construct(sp2->getPhysicsBody(), box, sp2->getPosition()));
                        PhysicsJointRatchet* joint = PhysicsJointRatchet::construct(sp1->getPhysicsBody(), sp2->getPhysicsBody(), 0.0f, (float)M_PI_2);
                        _scene->getPhysicsWorld()->addJoint(joint);

  • PhysicsJointGear:使一对刚体的角速度比率保持是一个常数。

  •     _scene->getPhysicsWorld()->addJoint(PhysicsJointPin::construct(sp1->getPhysicsBody(), box, sp1->getPosition()));
                        _scene->getPhysicsWorld()->addJoint(PhysicsJointPin::construct(sp2->getPhysicsBody(), box, sp2->getPosition()));
                        PhysicsJointGear* joint = PhysicsJointGear::construct(sp1->getPhysicsBody(), sp2->getPhysicsBody(), 0.0f, 2.0f);
                        _scene->getPhysicsWorld()->addJoint(joint);

  • PhysicsJointMotor:使一对刚体的相对角速度保持是一个常数。 
  •   _scene->getPhysicsWorld()->addJoint(PhysicsJointPin::construct(sp1->getPhysicsBody(), box, sp1->getPosition()));
                        _scene->getPhysicsWorld()->addJoint(PhysicsJointPin::construct(sp2->getPhysicsBody(), box, sp2->getPosition()));
                        PhysicsJointMotor* joint = PhysicsJointMotor::construct(sp1->getPhysicsBody(), sp2->getPhysicsBody(), (float)M_PI_2);
                        _scene->getPhysicsWorld()->addJoint(joint);


  std::unordered_map<int, Node*> _mouses

bool PhysicsDemo::onTouchBegan(Touch* touch, Event* event)
{
    auto location = touch->getLocation();
    auto arr = _scene->getPhysicsWorld()->getShapes(location);
    
    PhysicsBody* body = nullptr;
    for (auto& obj : arr)
    {
        if ((obj->getBody()->getTag() & DRAG_BODYS_TAG) != 0)
        {
            body = obj->getBody();
            break;
        }
    }
    
    if (body != nullptr)
    {
        Node* mouse = Node::create();
        mouse->setPhysicsBody(PhysicsBody::create(PHYSICS_INFINITY, PHYSICS_INFINITY));
        mouse->getPhysicsBody()->setDynamic(false);
        mouse->setPosition(location);
        this->addChild(mouse);
        PhysicsJointPin* joint = PhysicsJointPin::construct(mouse->getPhysicsBody(), body, location);
        joint->setMaxForce(5000.0f * body->getMass());
        _scene->getPhysicsWorld()->addJoint(joint);
        _mouses.insert(std::make_pair(touch->getID(), mouse));
        
        return true;
    }
    
    return false;
}


void PhysicsDemo::onTouchMoved(Touch* touch, Event* event)
{
    auto it = _mouses.find(touch->getID());
    
    if (it != _mouses.end())
    {
        it->second->setPosition(touch->getLocation());
    }
}


void PhysicsDemo::onTouchEnded(Touch* touch, Event* event)
{
    auto it = _mouses.find(touch->getID());
    
    if (it != _mouses.end())
    {
        this->removeChild(it->second);
        _mouses.erase(it);
    }
}

RayCast:

  /** Searches for physics shapes that intersects the ray. */
    void rayCast(PhysicsRayCastCallbackFunc func, const Vec2& start, const Vec2& end, void* data);

  Vec2 point3 = point2;
   auto func = CC_CALLBACK_3(PhysicsDemoRayCast::anyRay, this);       
   _scene->getPhysicsWorld()->rayCast(func, point1, point2, &point3);

   _node->drawSegment(point1, point3, 1, STATIC_COLOR);


contact:

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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值