2

I have array of sprites forming T shape, and I want to ratate them around the same origin, in my case box2D body origin, like this:

enter image description here

my shape is defined in matrix like this:

int array[][]= {{0,1,1,1,0},
                     {0,0,1,0,0},
                     {0,0,1,0,0},
                     {0,0,0,0,0},
                     {0,0,0,0,0}};

this is how I create the body:

public void setBody(int[][] blocks){
        BodyDef def = new BodyDef();
        def.type = BodyType.DynamicBody;
        def.position.set(new Vector2(0 * WORLD_TO_BOX, 0 * WORLD_TO_BOX));
        Body body = world.createBody(def);
        body.setTransform(130*WORLD_TO_BOX, 200*WORLD_TO_BOX, -90*MathUtils.degreesToRadians);
        for (int x = 0; x < 5; x++) { // HARDCODED 5
            for (int y = 0; y < 5; y++) { // HARDCODED 5
                if(blocks[x][y] == 1){
                    PolygonShape poly = new PolygonShape();
                    Vector2 v = new Vector2((x*size/BOX_TO_WORLD),(y*size/BOX_TO_WORLD));
                    poly.setAsBox(size/2 * WORLD_TO_BOX, size/2 * WORLD_TO_BOX, v, 0);
                    Sprite sprite = new Sprite(new Texture(Gdx.files.internal("data/block.png")));
                    sprite.setSize(size, size);
                    sprites.add(sprite);
                    orig.add(v);
                    body.createFixture(poly, 1);
                    poly.dispose();
                }
            }
        }
        this.body = body;
    }

this is my render method:

public void draw(SpriteBatch batch){
        for (int i = 0;i< this.body.getFixtureList().size;i++) {
            Vector2 pos = this.body.getFixtureList().get(i).getBody().getPosition();
            Sprite sprite = sprites.get(i);
            sprite.setOrigin(this.body.getWorldCenter().x,this.body.getWorldCenter().y);
            sprite.setPosition(pos.x*BOX_TO_WORLD+orig.get(i).x*32-16, pos.y*BOX_TO_WORLD+orig.get(i).y*32-16);
            sprite.setRotation(this.body.getAngle()*MathUtils.radiansToDegrees);
            sprite.draw(batch);
        }
    }
Rohit Malish
  • 3,209
  • 12
  • 49
  • 67
  • Put them into a group and try it. Else you need to create a "sprite sprite" class as one object with several sprites and rotate this as a single sprite. But youll need to rotate the sprites yourself. (Handle the positions and edges of the sprites yourself) – bemeyer Nov 30 '13 at 12:16

2 Answers2

2

As you can see in your 'right' example in the diagram, the position of the sprites depends on the angle of the body, which you are not accounting for.

b2Vec2 pos = ...; // center of the sprite relative to body (local coords)
float ang = ...;  // angle of the sprite relative to body (probably zero)

//need to rotate image local center by body angle
b2Rot rot( body->GetAngle() );
pos = b2Mul(rot, pos) + body->GetPosition();
ang += -body->GetAngle();

sprite.setRotation( ang * RADTODEG ); // RADTODEG = 57.295779513082320876f
sprite.setPosition( PTM * pos.x,  PTM * -pos.y);

Prior to that, I have also done:

sf::FloatRect rect = sprite.getLocalBounds();
sprite.setOrigin( 0.5 * rect.width, 0.5 * rect.height );
iforce2d
  • 8,194
  • 3
  • 29
  • 40
  • i dont get b2Rot rot(body-GetAngle()); part, what is this b2Rot?, could you implement your answer into my sample code? – Rohit Malish Nov 30 '13 at 15:07
  • 1
    b2Rot is part of Box2D: http://code.google.com/p/box2d/source/browse/trunk/Box2D/Box2D/Common/b2Math.h#297 – iforce2d Dec 01 '13 at 00:28
1

The easiest way to do this is to create all the fixtures, remembering what their relative position is to the body. Create a sprite for each fixture and update them using the body->GetWorldPosition(fixture center) each time the physics updates. Finally, the rotation of the sprites is the same as the rotation of the body (excepting it is the negative of the angle).

For example, to create the body:

void MainScene::CreateBody()
{
   Vec2 position(0,0);

   // Create the body.
   b2BodyDef bodyDef;
   bodyDef.position = position;
   bodyDef.type = b2_dynamicBody;
   _body = _world->CreateBody(&bodyDef);
   assert(_body != NULL);

   // Now attach fixtures to the body.
   FixtureDef fixtureDef;
   PolygonShape polyShape;
   vector<Vec2> vertices;

   const float32 VERT_SCALE = .5;
   fixtureDef.shape = &polyShape;
   fixtureDef.density = 1.0;
   fixtureDef.friction = 1.0;
   fixtureDef.isSensor = false;

   // Main Box
   vertices.clear();
   vertices.push_back(Vec2(1*VERT_SCALE,1*VERT_SCALE));
   vertices.push_back(Vec2(-1*VERT_SCALE,1*VERT_SCALE));
   vertices.push_back(Vec2(-1*VERT_SCALE,-1*VERT_SCALE));
   vertices.push_back(Vec2(1*VERT_SCALE,-1*VERT_SCALE));
   polyShape.Set(&vertices[0],vertices.size());
   _body->CreateFixture(&fixtureDef);
   _fixturePositions.push_back(CalculateAverage(vertices));

   // Down one
   vertices.clear();
   vertices.push_back(Vec2(1*VERT_SCALE,-1*VERT_SCALE));
   vertices.push_back(Vec2(-1*VERT_SCALE,-1*VERT_SCALE));
   vertices.push_back(Vec2(-1*VERT_SCALE,-3*VERT_SCALE));
   vertices.push_back(Vec2(1*VERT_SCALE,-3*VERT_SCALE));
   polyShape.Set(&vertices[0],vertices.size());
   _body->CreateFixture(&fixtureDef);
   _fixturePositions.push_back(CalculateAverage(vertices));

   // Up One
   vertices.clear();
   vertices.push_back(Vec2(1*VERT_SCALE,3*VERT_SCALE));
   vertices.push_back(Vec2(-1*VERT_SCALE,3*VERT_SCALE));
   vertices.push_back(Vec2(-1*VERT_SCALE,1*VERT_SCALE));
   vertices.push_back(Vec2(1*VERT_SCALE,1*VERT_SCALE));
   polyShape.Set(&vertices[0],vertices.size());
   _body->CreateFixture(&fixtureDef);
   _fixturePositions.push_back(CalculateAverage(vertices));

   // T Left Top
   vertices.clear();
   vertices.push_back(Vec2(-1*VERT_SCALE,3*VERT_SCALE));
   vertices.push_back(Vec2(-3*VERT_SCALE,3*VERT_SCALE));
   vertices.push_back(Vec2(-3*VERT_SCALE,1*VERT_SCALE));
   vertices.push_back(Vec2(-1*VERT_SCALE,1*VERT_SCALE));
   polyShape.Set(&vertices[0],vertices.size());
   _body->CreateFixture(&fixtureDef);
   _fixturePositions.push_back(CalculateAverage(vertices));

   // T Right Top
   vertices.clear();
   vertices.push_back(Vec2(3*VERT_SCALE,3*VERT_SCALE));
   vertices.push_back(Vec2(1*VERT_SCALE,3*VERT_SCALE));
   vertices.push_back(Vec2(1*VERT_SCALE,1*VERT_SCALE));
   vertices.push_back(Vec2(3*VERT_SCALE,1*VERT_SCALE));
   polyShape.Set(&vertices[0],vertices.size());
   _body->CreateFixture(&fixtureDef);
   _fixturePositions.push_back(CalculateAverage(vertices));

   _body->SetAngularVelocity(M_PI/8);
}

NOTE The CalculateAverage(...) function just finds the average of the vertices. For squares, it will be the center. I could manually set the centers, but I didn't want to make a simple math oopsie so I wrote a quick function to handle it.

Then create the sprites:

void MainScene::CreateSprites()
{
   Viewport& vp = Viewport::Instance();

   for(int idx = 0; idx < _fixturePositions.size(); idx++)
   {
      CCSprite* sprite = CCSprite::create("arrow.png");
       sprite->setScale(1.0*vp.GetPTMRatio()/128);
      _fixtureSprites.push_back(sprite);
      addChild(sprite);
   }
}

Then update the sprites after each physics update:

void MainScene::UpdateSprites()
{
   for(int idx = 0; idx < _fixturePositions.size(); idx++)
   {
      CCPoint spritePosition = Viewport::Instance().Convert(_body->GetWorldPoint(_fixturePositions[idx]));
      _fixtureSprites[idx]->setPosition(spritePosition);
      float32 bodyAngle = _body->GetAngle();
      bodyAngle = MathUtilities::AdjustAngle(bodyAngle);
      _fixtureSprites[idx]->setRotation(-CC_RADIANS_TO_DEGREES(bodyAngle));
   }
}

NOTE The viewport has a function Convert(..) that takes a Vec2 and converts it to a pixel position on the screen. AdjustAngle just puts the angle back in the range of [-pi,pi). All the rest should be fairly straightforward, but feel free to ask.

I have posted a solution on git hub here for Cocos2d-x (C++). Check out the code in MainScene.cpp.

And this is what it looks like on my simulator:

enter image description here

(Hopefully) Final Note: The implementation here uses a "Viewport" class to map between the Box2d world (meters) and the screen coordinates (pixels). One very useful consequence of this is that you can automatically adjust the sizes of sprites based on the size you want the bodies to be in meters and the size of the graphic in pixels. This is part of a larger set of components that I often use. You can find more information about them in this post.

Was this helpful?

FuzzyBunnySlippers
  • 3,387
  • 2
  • 18
  • 28
  • I do it with fixtures. I have one body with multiple fixtures. But do fixtures have position? I think they don't have position. – Rohit Malish Nov 30 '13 at 13:03
  • You can use the information in this post (http://stackoverflow.com/questions/4706484/box2d-multiple-fixtures-and-positioning) to position the fixtures relative to where you want them in the body. You can store off the position of the fixture relative to the body position when you create the fixture (probably as a b2PolygonShape). You can store it in the userdata member of the fixture, or store a class that contains this and other data for the shape in the userdata member – FuzzyBunnySlippers Nov 30 '13 at 23:23
  • I can create fixtures and position them successfully, the problem is with sprites. I don't know how to put them over those fixtures – Rohit Malish Nov 30 '13 at 23:26
  • If you know the position of the fixture in meters, and its orientation (the same as the body since they all rotate together), then you just use the pixel-to-meter ratio (screen pixels/meter, you define this) to convert from meters to pixels (position*pixels/meters = pixels) for the position of the sprite. – FuzzyBunnySlippers Nov 30 '13 at 23:33
  • but there is no position data in fixtures, or am I wrong? at least I cant find any position information of fixture. I updated my question and added method how I create the body – Rohit Malish Nov 30 '13 at 23:35
  • That's what I was saying...when you place the fixture, you **know** the position relative to the body. If you know the position of the center of a fixture is at (0,1) relative to the center of the body, then you can use body->GetWorldVector(Vec2(0,1))) to find the position of the fixture. But you have to store off the position of the fixture when you create it...I don't know of any API to get the position either for a polygon shape. You could make them circles (circle shapes do have a get position member)...but that would be weird. – FuzzyBunnySlippers Nov 30 '13 at 23:40
  • Yeah I have tried that, storing fixture position at creation and later using it in drawing, I get them positioned right, they dont rotate correct way. Could you check out my code and possibly modify the wrong part or tell me what is wrong so I can try to solve it myself? Thanks in advance – Rohit Malish Dec 01 '13 at 00:01
  • Hmmmm...I don't see anything obvious. The angle in box2d rotates more than +/- PI continuously, so I use a function (look for AdjustAngle here: https://github.com/NonlinearIdeas/Missile-Demo/blob/master/MissileDemo/MathUtilities.h) to put it back to the proper angle. Your call to sprite.setRotation looks ok. Are the angles completely off? Are all the boxes rotated to the same position? I may have to cook up an example myself to make sure I'm not fooling myself on this one... – FuzzyBunnySlippers Dec 01 '13 at 01:45
  • Ah, it is the negative of the body angle. I have put together a small demo code base in Cocos2d-x. I'll post the code base to the git repository. Most of the code is in a single class, the main scene. – FuzzyBunnySlippers Dec 01 '13 at 02:53
  • I'll also post part of the code as my answer, replacing above. – FuzzyBunnySlippers Dec 01 '13 at 03:12
  • I have been trying to get all this working for a week now, but to be honest I still don't get what is happening here. I don't know C++ at all. And since I started to try this I messed up rest of the code, now I have nothing – Rohit Malish Dec 06 '13 at 18:46
  • I can only tell you that the picture I took is from an iPad simulator running the code posted on the site. Did you look at the code I posted on github? It is box2d c++ and cocos2d-x; it should be similar to what you use in box2d/libgdx. I want to help...I'm just not sure what else I can do here... – FuzzyBunnySlippers Dec 06 '13 at 22:04