Problem with dynamically setting friction

Hi all,
Currently I’m facing a strange bug with Box2D - Cocos2dx v3.0.

I’m following this tutorial (http://www.raywenderlich.com/28606/how-to-create-a-breakout-game-with-box2d-and-cocos2d-2-x-tutorial-part-2) but modify it a little bit, just playing around since I’m new with Box2d.

In this simple game I’ve created a paddle with very big mass and no friction to reflect the ball, prevent it from colliding the bottom edge of screen. When playing on phone, I realized it’d be somehow stubborn if the paddle continue to slide freely on the bottom edge (player will lose track of it in an instant, which will easily lead to game over), so when TouchesBegin and TouchesMoved, I’ve set the friction of that paddle to 0.0f; when player end with the touches, the friction is set to 0.2f (which slow down then stop the paddle).

Problem is my paddle gradually become slow and slow even when I’m touching and dragging… then it completely stop! I’ve tried to print down the paddleFicture->friction but have no clue. I thought “setting” a variable will not cause it to be increased or decreased unwantedly… ?

Here’s my code:

#include "HelloWorldScene.h"

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;
}

// 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();

    /////////////////////////////
    // 2. add a menu item with "X" image, which is clicked to quit the program
    //    you may modify it.

    // add a "close" icon to exit the progress. it's an autorelease object
    auto closeItem = MenuItemImage::create("CloseNormal.png", "CloseSelected.png",
                                           CC_CALLBACK_1(HelloWorld::menuCloseCallback, this));
    
	closeItem->setPosition(Vec2(origin.x - closeItem->getContentSize().width/2 + visibleSize.width,
                                origin.y + closeItem->getContentSize().height/2));

    // create menu, it's an autorelease object
    auto menu = Menu::create(closeItem, NULL);
    menu->setPosition(Vec2::ZERO);
    this->addChild(menu, 1);

    /////////////////////////////
    // 3. add your codes below...

    // add a label shows "Hello World"
    // create and initialize a label
    
    auto label = LabelTTF::create("Hello World", "Arial", 24);
    
    // position the label on the center of the screen
    label->setPosition(Vec2(origin.x + visibleSize.width/2,
		origin.y + visibleSize.height - label->getContentSize().height));

    // add the label as a child to this layer
    this->addChild(label, 1);

    // add "HelloWorld" splash screen"
    //auto sprite = Sprite::create("HelloWorld.png");

    // position the sprite on the center of the screen
    //sprite->setPosition(Vec2(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y));

    // add the sprite as a child to this layer
    //this->addChild(sprite);

	// Add sprites of objects to the layer
	ball = Sprite::create("Ball_2.png");
	ball->setPosition((visibleSize.width - ball->getContentSize().width)/2,
					  (visibleSize.height - ball->getContentSize().height)/2); // Center of the window
	this->addChild(ball);

	Sprite* paddle = Sprite::create("Paddle.png");
	paddle->setPosition((visibleSize.width - paddle->getContentSize().width)/2,
						paddle->getContentSize().height/2);	// Bottom of window
	this->addChild(paddle);

	// Create a world
	//b2Vec2 gravity = b2Vec2(0.0f, -9.81f);	// Gravity of Earth
	b2Vec2 gravity = b2Vec2(0.0f, 0.0f);	// Gravity of some God-live planets
	world = new b2World(gravity);

	// Draw borders for physical testing
	world->SetContinuousPhysics(true);
	this->addChild(B2DebugDrawLayer::create(world, PTM_RATIO), PTM_RATIO);

	// Ball body and shape
	b2BodyDef ballBodyDef;
	ballBodyDef.type = b2_dynamicBody;
	ballBodyDef.position = b2Vec2(ball->getPosition().x/PTM_RATIO,
								  ball->getPosition().y/PTM_RATIO);
	ballBodyDef.userData = ball;
	body = world->CreateBody(&ballBodyDef);

	// Paddle body and shape
	b2BodyDef paddleBodyDef;
	paddleBodyDef.type = b2_dynamicBody;
	paddleBodyDef.position = b2Vec2(paddle->getPosition().x/PTM_RATIO,
									paddle->getPosition().y/PTM_RATIO);
	paddleBodyDef.userData = paddle;
	paddleBody = world->CreateBody(&paddleBodyDef);

	// Physical shape of objects. Better the same as their respective graphical shape
	b2CircleShape circle;
	circle.m_radius = (ball->getContentSize().width/2)/PTM_RATIO;
	b2FixtureDef ballShapeDef;
	ballShapeDef.shape = &circle;
	ballShapeDef.density = 0.5f;
	ballShapeDef.friction = 0.0f;
	ballShapeDef.restitution = 1.0f;
	body->CreateFixture(&ballShapeDef);

	b2PolygonShape rect;
	rect.SetAsBox((paddle->getContentSize().width/PTM_RATIO)/2,
				  (paddle->getContentSize().height/PTM_RATIO)/2);
	b2FixtureDef paddleShapeDef;
	paddleShapeDef.shape = ▭
	paddleShapeDef.density = 1.0f;
	paddleShapeDef.friction = 0.2f;
	paddleShapeDef.restitution = 0.1f;
	paddleFixture = paddleBody->CreateFixture(&paddleShapeDef);

	// Create edge around the entire screen
	b2BodyDef groundBodyDef;
	groundBodyDef.position.Set(0, 0);

	groundBody = world->CreateBody(&groundBodyDef);
	b2EdgeShape groundEdge;
	b2FixtureDef boxShapeDef;
	boxShapeDef.shape = &groundEdge;

	// Wall definition
	groundEdge.Set(b2Vec2(0, 0), b2Vec2(visibleSize.width/PTM_RATIO, 0));
	groundBody->CreateFixture(&boxShapeDef);
	groundEdge.Set(b2Vec2(0, 0), b2Vec2(0, visibleSize.height/PTM_RATIO));
	groundBody->CreateFixture(&boxShapeDef);
	groundEdge.Set(b2Vec2(visibleSize.width/PTM_RATIO, 0), b2Vec2(visibleSize.width/PTM_RATIO, visibleSize.height/PTM_RATIO));
	groundBody->CreateFixture(&boxShapeDef);
	groundEdge.Set(b2Vec2(0, visibleSize.height/PTM_RATIO), b2Vec2(visibleSize.width/PTM_RATIO, visibleSize.height/PTM_RATIO));
	groundBody->CreateFixture(&boxShapeDef);
	
	// Set NULL to prevent the "empty created" case.
	mouseJoint = NULL;

	// Prismatic joints let us restrict the movement
	// of one body to another, along a specified axis.
	b2PrismaticJointDef prisJointDef;
	b2Vec2 xAxis = b2Vec2(1.0f, 0.0f);
	prisJointDef.collideConnected = true;
	prisJointDef.Initialize(paddleBody, groundBody, paddleBody->GetWorldCenter(), xAxis);

	world->CreateJoint(&prisJointDef);

	// Using acceleromater. The deprecated message for Scene isn't used here
	//Device::setAccelerometerEnabled(true);
	//auto accelerateListener = EventListenerAcceleration::create(CC_CALLBACK_2(HelloWorld::onAcceleration, this));
	//this->getEventDispatcher()->addEventListenerWithSceneGraphPriority(accelerateListener, this);

	// Using touch
	auto touchListener = EventListenerTouchAllAtOnce::create();
	touchListener->onTouchesBegan = CC_CALLBACK_2(HelloWorld::onTouchesBegan, this);
	touchListener->onTouchesMoved = CC_CALLBACK_2(HelloWorld::onTouchesMoved, this);
	touchListener->onTouchesEnded = CC_CALLBACK_2(HelloWorld::onTouchesEnded, this);
	touchListener->onTouchesCancelled = CC_CALLBACK_2(HelloWorld::onTouchesCancelled, this);
	this->getEventDispatcher()->addEventListenerWithSceneGraphPriority(touchListener, this);

	// Initialize interval vars
	tickInterval = 1.0/60.0;
	kickInterval = 2.0;

	// Update the graphic to the physic every 1/60 sec
	this->schedule(schedule_selector(HelloWorld::tick), tickInterval);

	// Apply force to the ball every 2 sec
	//this->schedule(schedule_selector(HelloWorld::kick), kickInterval);

	// Just kick once to start the game
	kick(0.0);

    return true;
}

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
}

void HelloWorld::tick(float dt)	{
	world->Step(tickInterval, 10, 10);
	for (b2Body* b = world->GetBodyList(); b; b = b->GetNext())	{
		if (b->GetUserData() != NULL)	{
			Sprite* ballData = (Sprite*)b->GetUserData();
			ballData->setPosition(Vec2( b->GetPosition().x * PTM_RATIO,
									    b->GetPosition().y * PTM_RATIO ));
			ballData->setRotation(-1 * CC_RADIANS_TO_DEGREES(b->GetAngle()));


			b2Vec2 velocity = b->GetLinearVelocity();
			float32 speed = velocity.Length();
			static int maxSpeed = 10;

			if (speed > maxSpeed) {
				//b->SetLinearDamping(0.5);
			} else if (speed < maxSpeed) {
				b->SetLinearDamping(0.0);
			}
		}
	}
}

void HelloWorld::kick(float dt)	{
	b2Vec2 force = b2Vec2(100, 100);
	body->ApplyLinearImpulse(force, body->GetPosition(), true);
}

void HelloWorld::onAcceleration(Acceleration* acc, Event* anEvent)	{
	// Landscape left values
	//b2Vec2 accGravity = b2Vec2(acc->y * 30, -acc->x * 30);
	//world->SetGravity(accGravity);
}

void HelloWorld::onTouchesBegan(const std::vector<Touch*>& touches, Event *touchEvent)	{
	// On Windows, this mouseJoint is created but empty.
	// That's why if we return here, its m_bodyB will still NULL
	// and cause crashing later.

	// On phone it is not created at all (as we expected it should be)
	// so this check will not cause any problem.

	CCLOG("Now friction = %f", paddleFixture->GetFriction());
	paddleFixture->SetFriction(0.0f);

	///*
	if (mouseJoint != NULL)	{
		return;
	}
	//*/

	// Get the first available touch.
	auto myTouch = new Touch();
	for(auto &item: touches)	{
		if (item)	{
			myTouch = item;
			break;
		}
	}
	
	Point location = myTouch->getLocationInView();
	location = Director::getInstance()->convertToGL(location);
	b2Vec2 worldLoc = b2Vec2(location.x/PTM_RATIO, location.y/PTM_RATIO);

	//CCLOG("Touch began at x = %f, y = %f", location.x, location.y);

	if (paddleFixture->TestPoint(worldLoc))	{
		//CCLOG("Tested point. Now init the mouseJoint.");
		b2MouseJointDef mouseJointDef;
		mouseJointDef.bodyA = groundBody;
		mouseJointDef.bodyB = paddleBody;
		mouseJointDef.target = worldLoc;
		mouseJointDef.collideConnected = true;
		mouseJointDef.maxForce = 10000.0f * paddleBody->GetMass();

		mouseJoint = (b2MouseJoint*)world->CreateJoint(&mouseJointDef);
		paddleBody->SetAwake(true);
	}
}

void HelloWorld::onTouchesMoved(const std::vector<Touch*>& touches, Event *touchEvent)	{
	paddleFixture->SetFriction(0.0f);

	if (mouseJoint == NULL)	{
		return;
	}

	auto myTouch = new Touch();
	for(auto &item: touches)	{
		if (item)	{
			myTouch = item;
			break;
		}
	}

	Point location = myTouch->getLocationInView();
	location = Director::getInstance()->convertToGL(location);
	b2Vec2 worldLoc = b2Vec2(location.x/PTM_RATIO, location.y/PTM_RATIO);

	//CCLOG("Touch moved to x = %f, y = %f", location.x, location.y);

	mouseJoint->SetTarget(worldLoc);
	paddleBody->SetAwake(true);	// Sometime it *mysteriously* asleep - so set it like this
}

void HelloWorld::onTouchesEnded(const std::vector<Touch*>& touches, Event *touchEvent)	{
	paddleFixture->SetFriction(0.2f);

	if (mouseJoint != NULL)	{
		world->DestroyJoint(mouseJoint);
		mouseJoint = NULL;
	}
}

void HelloWorld::onTouchesCancelled(const std::vector<Touch*>& touches, Event *touchEvent)	{
	paddleFixture->SetFriction(0.2f);

	if (mouseJoint != NULL)	{
		world->DestroyJoint(mouseJoint);
		mouseJoint = NULL;
	}
}

P/S: when let it be, the paddle will slowly slide to the bottom right corner of the screen (though I 've set the gravity to 0, 0. I still have no clue about this too >.<