I am trying out Soft Body physics using the inbuilt physics engine, I created a ball sprite and used PhysicsSpringJoint according to a tutorial (it was originally written in Objective-C). Please look in my code below.
Original ball sprite:
As rendered in Physics world
here’s my ball sprite class
bool SoftBall::init(PhysicsWorld* w){
bool bref = false;
try{
Sprite::init();
this->initWithFile("ball.png");
float bubbleRadius = this->getContentSize().width/2;
PhysicsBody* phyBody = PhysicsBody::createCircle(4, PHYSICSBODY_MATERIAL_DEFAULT , CCPointMake(bubbleRadius, bubbleRadius));
this->setPhysicsBody(phyBody);
this->getPhysicsBody()->setRotationEnable(false);
this->getPhysicsBody()->setDynamic(true);
// Distance between main body and outer children
float childDist = bubbleRadius - PHYSICS_BODY_RADIUS;
// Create child bodies connected to the main body with inner springs
for(int i=0; i<NUM_SEGMENTS; i++) {
float childAngle = i * 2 * M_PI / NUM_SEGMENTS;
Node* child = Node::create();
PhysicsBody* phyBody = PhysicsBody::createCircle(PHYSICS_BODY_RADIUS, PHYSICSBODY_MATERIAL_DEFAULT, CCPointMake(0,0));
child->setPhysicsBody(phyBody);
child->getPhysicsBody()->setRotationEnable(false);
child->setTag(i);
child->setPosition(Vec2(bubbleRadius + childDist * cosf(childAngle), bubbleRadius + childDist * sinf(childAngle)));
this->addChild(child);
PhysicsJointSpring* spring = PhysicsJointSpring::construct(this->getPhysicsBody(), child->getPhysicsBody(), Vec2(bubbleRadius, bubbleRadius), Point::ZERO, INNER_STIFFNESS, INNER_DAMPING);
spring->setRestLength(childDist);
w->addJoint(spring);
}
// Connect child bodies together with outer springs
for(int i=0; i<NUM_SEGMENTS; i++) {
Node* previous = i == 0 ? this->getChildren().at(NUM_SEGMENTS-1) : this->getChildren().at(i-1);
Node* child = (Node*)this->getChildren().at(i);
PhysicsJointSpring* spring = PhysicsJointSpring::construct(child->getPhysicsBody(), previous->getPhysicsBody(), CCPointMake(0,0), CCPointMake(0,0), OUTER_STIFFNESS, OUTER_DAMPING);
spring->setRestLength(childDist*2*M_PI/NUM_SEGMENTS);
w->addJoint(spring);
}
bref = true;
}catch(int e){
}
return bref;
}
the draw method to map the texture to the physics body
void SoftBall::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
{
int nVertices = NUM_SEGMENTS+2;
float deltaAngle = (2.f * M_PI) / NUM_SEGMENTS;
float bubbleRadius = this->getContentSize().width/2;
// Triangle fan vertices
Vertex2D vertices[nVertices];
vertices[0] = (Vertex2D){bubbleRadius, bubbleRadius};
for (int i = 0; i < NUM_SEGMENTS; i++) {
Node *child = this->getChildren().at(i);
vertices[i+1] = (Vertex2D){
child->getPositionX() + PHYSICS_BODY_RADIUS * (child->getPositionX()-bubbleRadius)/bubbleRadius,
child->getPositionY() + PHYSICS_BODY_RADIUS * (child->getPositionY()-bubbleRadius)/bubbleRadius
};
}
vertices[NUM_SEGMENTS+1] = vertices[1];
// Corresponding texture coordinates
Vertex2D texCoords[nVertices];
texCoords[0] = (Vertex2D){0.5f, 0.5f};
for (int i = 0; i < NUM_SEGMENTS; i++) {
GLfloat coordAngle = M_PI + (deltaAngle * i);
texCoords[i+1] = (Vertex2D){(GLfloat)0.5 + (GLfloat)cosf(coordAngle)*0.5, (GLfloat)0.5 + (GLfloat)sinf(coordAngle)*0.5};
}
texCoords[NUM_SEGMENTS+1] = texCoords[1];
// Default colors
Color4F colors[nVertices];
for (int i = 0; i <= NUM_SEGMENTS+1; i++) {
colors[i] = (Color4F){1, 1, 1, 1};
}
auto texture = Director::getInstance()->getTextureCache()->addImage("ball.png");
// kmGLPushMatrix();
GL::bindTexture2D(this->getTexture()->getName());
texture->getGLProgram()->use();
texture->getGLProgram()->setUniformsForBuiltins();
GL::blendFunc(this->getBlendFunc().src, this->getBlendFunc().dst);
GL::enableVertexAttribs(GL::VERTEX_ATTRIB_FLAG_POS_COLOR_TEX);
glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_POSITION, 2, GL_FLOAT, GL_FALSE, 0, vertices);
glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_TEX_COORDS, 2, GL_FLOAT, GL_FALSE, 0, texCoords);
glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_COLOR, 4, GL_FLOAT, GL_FALSE, 0, colors);
glDrawArrays(GL_TRIANGLE_FAN, 0, (GLsizei)nVertices);
}
The physics scene
Scene* MainScene::createScene(){
sn = Scene::createWithPhysics();
auto w = sn->getPhysicsWorld();
Layer* layer = MainScene::createWithPhysicsWorld(w);
sn->addChild(layer);
return sn;
}
bool MainScene::initWithPhysicsWorld(cocos2d::PhysicsWorld* pw){
bool bret = false;
try{
//world = pw;
pw->setDebugDrawMask(pw->DEBUGDRAW_ALL);
Size visibleSize = Director::getInstance()->getVisibleSize();
auto body = PhysicsBody::createEdgeBox(visibleSize, PHYSICSBODY_MATERIAL_DEFAULT, 3);
auto edgeNode = Node::create();
edgeNode->setPosition(Vec2(visibleSize.width/2,visibleSize.height/2));
edgeNode->setPhysicsBody(body);
this->addChild(edgeNode);
SoftBall* ball = SoftBall::create(pw);
ball->setPosition(100, 700);
//ball->setPosition(edgeNode->getContentSize().width/2,edgeNode->getContentSize().height/2);
//ball->getPhysicsBody()->getNode()->setPosition(Vec2(500,400));
this->setPhysicsBody(ball->getPhysicsBody());
this->addChild(ball);
for(int i; i<ball->getChildrenCount(); i++){
auto child = ball->getChildByTag(i);
this->setPhysicsBody(child->getPhysicsBody());
}
bret = true;
}catch(int e){
}
return bret;
}
But I was expecting it to behave like this
[see video]
