Physics joint distance problem

Hey devs,

i just wanted to try the joints with physics world,
so i created two physics body and tried the PhysicsJointDistance class.

but it crashes, whats there wrong in the code.
tell me.

HelloWorldScene.h

 #ifndef __HELLOWORLD_SCENE_H__
#define __HELLOWORLD_SCENE_H__

#include "cocos2d.h"

class HelloWorld : public cocos2d::Layer
{
public:
	HelloWorld():
	_tag(0) {};

    // there's no 'id' in cpp, so we recommend returning the class instance pointer
    static cocos2d::Scene* createScene();

    // Here's a difference. Method 'init' in cocos2d-x returns bool, instead of returning 'id' in cocos2d-iphone
    virtual bool init();  

    // implement the "static create()" method manually
    CREATE_FUNC(HelloWorld);

    // back button exit
    void onKeyReleased(cocos2d::EventKeyboard::KeyCode keycode, cocos2d::Event *event);

    // inline header func
    cocos2d::PhysicsWorld *sceneWorld;

    void SetPhysicsWorld(cocos2d::PhysicsWorld *world){sceneWorld = world;}

    // touches
    bool onTouchBegan(cocos2d::Touch *touch, cocos2d::Event *event);
    void onTouchMoved(cocos2d::Touch *touch, cocos2d::Event *event);
    void onTouchEnded(cocos2d::Touch *touch, cocos2d::Event *event);

    cocos2d::Sprite *sp_1;
    cocos2d::Sprite *sp_2;
    cocos2d::PhysicsBody *body_1;
    cocos2d::PhysicsBody *body_2;

private:
    int _tag;

};

#endif // __HELLOWORLD_SCENE_H__

HelloWorldScene.cpp

#include "HelloWorldScene.h"

USING_NS_CC;

Scene* HelloWorld::createScene()
{
    // 'scene' is an autorelease object
    auto scene = Scene::createWithPhysics();
    scene->getPhysicsWorld()->setDebugDrawMask(PhysicsWorld::DEBUGDRAW_ALL);
    scene->getPhysicsWorld()->setGravity(Vec2(0,0));
    
    // 'layer' is an autorelease object
    auto layer = HelloWorld::create();
    layer->SetPhysicsWorld(scene->getPhysicsWorld());

    // add layer as a child to scene
    scene->addChild(layer);

    // return the scene
    return scene;
}

// on "init" you need to initialize your instance
bool HelloWorld::init()
{
    //////////////////////////////
    // 1. super init first
    if ( !Layer::init() )
    {
        return false;
    }
    
    Size visibleSize = Director::getInstance()->getVisibleSize();
    Vec2 origin = Director::getInstance()->getVisibleOrigin();

    // physics body 1
    sp_1 = Sprite::create("reset_normal.png");
    sp_1->setPosition(Vec2(visibleSize.width/3, visibleSize.height/2));
    body_1 = PhysicsBody::createBox(sp_1->getContentSize(), PhysicsMaterial(0,1,0));
    sp_1->setPhysicsBody(body_1);
    this->addChild(sp_1);


    // physics body 2
    sp_2 = Sprite::create("menu.png");
	sp_2->setPosition(Vec2(visibleSize.width/2, visibleSize.height/2));
	body_2 = PhysicsBody::createBox(sp_2->getContentSize(), PhysicsMaterial(0,1,0));
	sp_2->setPhysicsBody(body_2);
	this->addChild(sp_2);

//	 joints
	auto joint = PhysicsJointDistance::construct(body_1, body_2, Vec2::ZERO, Vec2::ANCHOR_TOP_RIGHT);
	joint->setCollisionEnable(false);
	joint->setDistance(50);
	sceneWorld->addJoint(joint);


	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->addEventListenerWithSceneGraphPriority(listener, this);

	this->setTouchEnabled(true);
    this->setKeypadEnabled(true);
    return true;
}

bool HelloWorld::onTouchBegan(cocos2d::Touch *touch, cocos2d::Event *event)
{
	// condition for touch to the sprite
	auto pos1_point = sp_1->getPosition();
	auto pos2_point = sp_2->getPosition();

	auto pos1 = Vec2(pos1_point.x - (sp_1->getContentSize().width/2),
			(pos1_point.y - (sp_1->getContentSize().height/2)));

	auto pos2 = Vec2(pos2_point.x - (sp_2->getContentSize().width/2),
				(pos2_point.y - (sp_2->getContentSize().height/2)));

	// get the touch location in points
	Vec2 touchLoc = this->convertToWorldSpace(this->convertTouchToNodeSpace(touch));

	if( (touchLoc.x > pos1.x && touchLoc.x < (pos1.x + sp_1->getContentSize().width)) &&
			(touchLoc.y > pos1.y && touchLoc.y < (pos1.y + sp_1->getContentSize().height)) )
	{
		_tag = 1;
		return true;
	}

	else if( (touchLoc.x > pos2.x && touchLoc.x < (pos2.x + sp_2->getContentSize().width))
					&& (touchLoc.y > pos2.y && touchLoc.y < (pos2.y + sp_2->getContentSize().height)) )
	{
		_tag = 2;
		return true;
	}

return false;
}

void HelloWorld::onTouchMoved(cocos2d::Touch *touch, cocos2d::Event *event)
{
	Vec2 touchL = this->convertToWorldSpace(this->convertTouchToNodeSpace(touch));
	if(_tag == 1)
		sp_1->setPosition(touchL);
	else if(_tag == 2)
		sp_2->setPosition(touchL);
}

void HelloWorld::onTouchEnded(cocos2d::Touch *touch, cocos2d::Event *event) {}

void HelloWorld::onKeyReleased(cocos2d::EventKeyboard::KeyCode keycode, cocos2d::Event *event)
{
	Director::getInstance()->end();
}

help plz
@slackmoehrle
@Jgod
@Maxxx
@Den could you… ??

Take a look at HelloWorld::createScene() function. Here is an order of code execution:

  1. Create a scene with physics
  2. Set some parameters to that physics
  3. Call create method of HelloWorld layer
  4. Set physics world for that layer

Now let’s expand HelloWorld::create() function. It looks like this:

HelloWorld* HelloWorld::create()
{
    auto layer = new HelloWorld();
    if ( layer && layer->init())
    {
        layer->autorelease();
    }
    else
    {
        delete layer;
        layer = 0;
    }
    return layer;
}

As you can see HelloWorld::init() is called before you actually set up a physics world for it. Thus in init function you just try to access null object.

Possible solution:

#ifndef __HELLOWORLD_SCENE_H__
#define __HELLOWORLD_SCENE_H__

#include "cocos2d.h"

class HelloWorld : public cocos2d::Layer
{
public:
	HelloWorld():
	_tag(0) {};
    
    // there's no 'id' in cpp, so we recommend returning the class instance pointer
    static cocos2d::Scene* createScene();
    
    // Here's a difference. Method 'init' in cocos2d-x returns bool, instead of returning 'id' in cocos2d-iphone
    virtual bool initWithPhysicsWorld(cocos2d::PhysicsWorld* pw);
    
    static HelloWorld* createWithPhysicsWorld(cocos2d::PhysicsWorld* w);
    
    // implement the "static create()" method manually
    
    // back button exit
    void onKeyReleased(cocos2d::EventKeyboard::KeyCode keycode, cocos2d::Event *event);
    
    // inline header func
    cocos2d::PhysicsWorld *sceneWorld;
    
    void SetPhysicsWorld(cocos2d::PhysicsWorld *world){sceneWorld = world;}
    
    // touches
    bool onTouchBegan(cocos2d::Touch *touch, cocos2d::Event *event);
    void onTouchMoved(cocos2d::Touch *touch, cocos2d::Event *event);
    void onTouchEnded(cocos2d::Touch *touch, cocos2d::Event *event);
    
    cocos2d::Sprite *sp_1;
    cocos2d::Sprite *sp_2;
    cocos2d::PhysicsBody *body_1;
    cocos2d::PhysicsBody *body_2;
    
private:
    int _tag;
    
};

#endif // __HELLOWORLD_SCENE_H__
#include "HelloWorldScene.h"

USING_NS_CC;

Scene* HelloWorld::createScene()
{
    // 'scene' is an autorelease object
    auto scene = Scene::createWithPhysics();
    scene->getPhysicsWorld()->setDebugDrawMask(PhysicsWorld::DEBUGDRAW_ALL);
    scene->getPhysicsWorld()->setGravity(Vec2(0,0));
    
    // 'layer' is an autorelease object
    auto layer = HelloWorld::createWithPhysicsWorld(scene->getPhysicsWorld());
    //layer->SetPhysicsWorld(scene->getPhysicsWorld());
    
    // add layer as a child to scene
    scene->addChild(layer);
    
    // return the scene
    return scene;
}

HelloWorld* HelloWorld::createWithPhysicsWorld(cocos2d::PhysicsWorld* w)
{
    auto hw = new HelloWorld();
    if(hw && hw->initWithPhysicsWorld(w))
    {
        hw->autorelease();
    }
    else
    {
        delete hw;
        hw = 0;
    }
    return hw;
}

// on "init" you need to initialize your instance
bool HelloWorld::initWithPhysicsWorld(cocos2d::PhysicsWorld *sceneWorld)
{
    //////////////////////////////
    // 1. super init first
    if ( !Layer::init() )
    {
        return false;
    }
    
    Size visibleSize = Director::getInstance()->getVisibleSize();
    Vec2 origin = Director::getInstance()->getVisibleOrigin();
    
    // physics body 1
    sp_1 = Sprite::create("CloseNormal.png");
    sp_1->setPosition(Vec2(visibleSize.width/3, visibleSize.height/2));
    body_1 = PhysicsBody::createBox(sp_1->getContentSize(), PhysicsMaterial(0,1,0));
    sp_1->setPhysicsBody(body_1);
    this->addChild(sp_1);
    
    
    // physics body 2
    sp_2 = Sprite::create("CloseSelected.png");
	sp_2->setPosition(Vec2(visibleSize.width/2, visibleSize.height/2));
	body_2 = PhysicsBody::createBox(sp_2->getContentSize(), PhysicsMaterial(0,1,0));
	sp_2->setPhysicsBody(body_2);
	this->addChild(sp_2);
    
    //	 joints
	auto joint = PhysicsJointDistance::construct(body_1, body_2, Vec2::ZERO, Vec2::ANCHOR_TOP_RIGHT);
	joint->setCollisionEnable(false);
	joint->setDistance(50);
	sceneWorld->addJoint(joint);
    
    
	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->addEventListenerWithSceneGraphPriority(listener, this);
    
	this->setTouchEnabled(true);
    this->setKeypadEnabled(true);
    return true;
}

bool HelloWorld::onTouchBegan(cocos2d::Touch *touch, cocos2d::Event *event)
{
	// condition for touch to the sprite
	auto pos1_point = sp_1->getPosition();
	auto pos2_point = sp_2->getPosition();
    
	auto pos1 = Vec2(pos1_point.x - (sp_1->getContentSize().width/2),
                     (pos1_point.y - (sp_1->getContentSize().height/2)));
    
	auto pos2 = Vec2(pos2_point.x - (sp_2->getContentSize().width/2),
                     (pos2_point.y - (sp_2->getContentSize().height/2)));
    
	// get the touch location in points
	Vec2 touchLoc = this->convertToWorldSpace(this->convertTouchToNodeSpace(touch));
    
	if( (touchLoc.x > pos1.x && touchLoc.x < (pos1.x + sp_1->getContentSize().width)) &&
       (touchLoc.y > pos1.y && touchLoc.y < (pos1.y + sp_1->getContentSize().height)) )
	{
		_tag = 1;
		return true;
	}
    
	else if( (touchLoc.x > pos2.x && touchLoc.x < (pos2.x + sp_2->getContentSize().width))
            && (touchLoc.y > pos2.y && touchLoc.y < (pos2.y + sp_2->getContentSize().height)) )
	{
		_tag = 2;
		return true;
	}
    
    return false;
}

void HelloWorld::onTouchMoved(cocos2d::Touch *touch, cocos2d::Event *event)
{
	Vec2 touchL = this->convertToWorldSpace(this->convertTouchToNodeSpace(touch));
	if(_tag == 1)
		sp_1->setPosition(touchL);
	else if(_tag == 2)
		sp_2->setPosition(touchL);
}

void HelloWorld::onTouchEnded(cocos2d::Touch *touch, cocos2d::Event *event) {}

void HelloWorld::onKeyReleased(cocos2d::EventKeyboard::KeyCode keycode, cocos2d::Event *event)
{
	Director::getInstance()->end();
}
1 Like

the initWithPhysicsWorld function crashes at the setDistance(50) line.

without that line it looks like it is working.