将多个精灵旋转为一个(围绕同一原点)

2022-01-12 00:00:00 box2d sprite java libgdx

我有一组形成 T 形的精灵,我想围绕同一个原点对它们进行评级,在我的例子中是 box2D 身体原点,如下所示:

我的形状在矩阵中定义如下:

int 数组[][]= {{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}};

这就是我创建身体的方式:

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++) {//硬编码 5for (int y = 0; y < 5; y++) {//硬编码 5如果(块[x][y] == 1){PolygonShape 多边形 = 新的 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(大小,大小);sprites.add(精灵);orig.add(v);body.createFixture(poly, 1);poly.dispose();}}}this.body = 身体;}

这是我的渲染方法:

public void draw(SpriteBatch batch){for (int i = 0;i

解决方案

最简单的方法是创建所有的灯具,记住它们与身体的相对位置.为每个夹具创建一个精灵,并在每次物理更新时使用 body->GetWorldPosition(fixture center) 更新它们.最后,精灵的旋转与身体的旋转相同(除了它是角度的负数).

例如,创建主体:

void MainScene::CreateBody(){Vec2位置(0,0);//创建身体.b2BodyDef 身体定义;bodyDef.position = 位置;bodyDef.type = b2_dynamicBody;_body = _world->CreateBody(&bodyDef);断言(_body!= NULL);//现在将固定装置附加到主体上.FixtureDef 夹具定义;PolygonShape 多边形;载体<Vec2>顶点;常量 float32 VERT_SCALE = .5;fixtureDef.shape = &polyShape;夹具定义密度 = 1.0;fixtureDef.friction = 1.0;fixtureDef.isSensor = false;//主框顶点.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));//下一个顶点.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));//上一个顶点.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 左上角顶点.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 右上顶点.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);}

注意 CalculateAverage(...) 函数只求顶点的平均值.对于正方形,它将是中心.我可以手动设置中心,但我不想做一个简单的数学 oopsie,所以我写了一个快速函数来处理它.

然后创建精灵:

void MainScene::CreateSprites(){视口&vp = 视口::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(精灵);addChild(精灵);}}

然后在每次物理更新后更新精灵:

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

注意 视口有一个函数 Convert(..),它接受 Vec2 并将其转换为屏幕上的像素位置.AdjustAngle 只是将角度放回到 [-pi,pi) 的范围内.其余的都应该相当简单,但请随时提出.

我在 git hub 中找到有关它们的更多信息.

这有帮助吗?

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:

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

解决方案

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:

(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?

相关文章