Rotating multiple sprites as one ( around same origin )

前端 未结 2 1950
醉酒成梦
醉酒成梦 2021-01-21 14:59

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:

相关标签:
2条回答
  • 2021-01-21 15:00

    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 );
    
    0 讨论(0)
  • 2021-01-21 15:18

    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?

    0 讨论(0)
提交回复
热议问题