Libgdx - How to draw filled rectangle in the right place in scene2d?
Asked Answered
I

5

12

I am using scene2d. Here is my code:

group.addActor(new Actor() {

    @Override
    public Actor hit(float arg0, float arg1) {
        return null;
    }

    @Override
    public void draw(SpriteBatch batch, float arg1) {
        batch.end();
        shapeRenderer.begin(ShapeType.FilledRectangle);
        shapeRenderer.setColor(Color.RED);
        shapeRenderer.filledRect(0, 0, 300, 20);
        shapeRenderer.end();
        batch.begin();
    }
});

The problem is that it draws this rectangular relative to screen (x = 0, y = 0), but I need it to be drawn relative to my group. But if I draw other entities with:

batch.draw(texture, 0, 0, width, height);

it correctly draws at (x = 0, y = 0) relative my group (0,0 pixels from left-bottom corner of the group).

Any suggestions how can I implement shape drawing in scene2d? And can someone can explain why these two calls work differently?

Incoherent answered 13/3, 2013 at 21:43 Comment(0)
S
19

ShapeRenderer has its own transform matrix and projection matrix. These are separate to those in the SpriteBatch that the scene2d Stage uses. If you update the ShapeRenderer's matrices to match those that scene2d is using when Actor.draw() is called then you should get the results that you want.

Sized answered 13/3, 2013 at 22:16 Comment(1)
I had to do this myself recently - I set the ShapeRenderer's transform and projection matrices from the SpriteBatch which is passed into the draw() method. It doesn't seem very efficient but it works.Nady
K
15

As Rod Hyde mentions, ShapeRenderer has its own transform matrix and projection matrix. So you would have to get the SpriteBatch's projection Matrix first. I am not sure if there is an elegant way to do it, I did it like this:

public class myActor extends Actor{

    private ShapeRenderer shapeRenderer;
    static private boolean projectionMatrixSet;

    public myActor(){
        shapeRenderer = new ShapeRenderer();
        projectionMatrixSet = false;
    }

    @Override
    public void draw(SpriteBatch batch, float alpha){
        batch.end();
        if(!projectionMatrixSet){
            shapeRenderer.setProjectionMatrix(batch.getProjectionMatrix());
        }
        shapeRenderer.begin(ShapeType.Filled);
        shapeRenderer.setColor(Color.RED);
        shapeRenderer.rect(0, 0, 50, 50);
        shapeRenderer.end();
        batch.begin();
    }
}
Kaolack answered 16/8, 2014 at 9:27 Comment(0)
V
12

The best solution for me. Cause when you using ShapeRenderer it's doesn't react on moving/zooming camera.

public class Rectangle extends Actor {

    private Texture texture;

    public Rectangle(float x, float y, float width, float height, Color color) {
        createTexture((int)width, (int)height, color);
        setX(x);
        setY(y);
        setWidth(width);
        setHeight(height);
    }

    private void createTexture(int width, int height, Color color) {
        Pixmap pixmap = new Pixmap(width, height, Pixmap.Format.RGBA8888);
        pixmap.setColor(color);
        pixmap.fillRectangle(0, 0, width, height);
        texture = new Texture(pixmap);
        pixmap.dispose();
    }
    
    @Override
    public void draw(Batch batch, float parentAlpha) {
        Color color = getColor();
        batch.setColor(color.r, color.g, color.b, color.a * parentAlpha);
        batch.draw(texture, getX(), getY(), getWidth(), getHeight());
    }
}
Veneaux answered 8/1, 2016 at 2:37 Comment(1)
This seems to work really well: create a texture of the size you want, and draw that. No messing about with shape renders.Heavyweight
A
3

You need to convert the Actor's local coordinates into screen coordinates. Assuming your stage is full-screen, you can just use Actor.localToStageCoordinates:

vec.set(getX(), getY());
this.localToStageCoordinates(/* in/out */ vec);
shapeRenderer.filledRect(vec.x, vec.y, getWidth(), getHeight());

Where vec is a private Vector2d (you don't want to allocate a new one on each render call).

This is also assuming that your ShapeRenderer is defined to be map to the full screen (which is the default).

Also, if you switch away from the ShapeRenderer and back to the SpriteBatch, note that the batch is already adjusted to Actor coordinates (and thus you can use getX() and getY() directly with batch.draw(...).

Alasdair answered 13/3, 2013 at 22:20 Comment(4)
1) Actor doesn't have .lovaltoStageCoordingates, only .toLocalCoordinates. 2) and I don't understand how to use it.. (ProgressBar is Group): batch.end(); vec.set(ProgressBar.this.x, ProgressBar.this.y); this.toLocalCoordinates(vec); shapeRenderer.begin(ShapeType.FilledRectangle); shapeRenderer.setColor(Color.BLACK); shapeRenderer.filledRect(vec.x + 11, vec.y + 11, barMaxWidth, 20); shapeRenderer.end();. I don't understand what I need to put to vec.set().Incoherent
How I understand from javadoc: "Transforms the specified point in the stage's coordinates to the actor's local coordinate system." for example I put x = 0; y = 0 for this rectangle in stage coord (it is left-bottom corner) And after I call .toLocalCoordinates() it should recalculate them and put in left-bottom Actor corner..Incoherent
I set randomly for ex. vec.set(0, 800); and I got my progress bar bunch flipped horizontaly and when I scroll my pages those progress bars fly to another side. FYI: I have scene2d structure Stage -> Group -> Table -> Group -> ActorIncoherent
You've got an older version of the Stage API, sorry didn't mean to send you off in a bad direction. The older .toLocalCoordinates is the opposite of localToStageCoordinates (its for converting from stage/screen to the Actor). You don't want to use it for rendering your Actor. Probably best to use @RodHyde's approach with older versions of Libgdx (and perhaps with newer ones, too).Alasdair
H
2

If your are using the ShapeRenderer don't forget using setProjectionMatrix() and setTransformMatrix() methods...

A sample of draw circle inside an Actor on draw method :

@Override public void draw(Batch batch, float parentAlpha) {

    batch.end();

    if (shapeRenderer == null) {
        shapeRenderer = new ShapeRenderer();
    }

    Gdx.gl.glEnable(GL20.GL_BLEND);
    shapeRenderer.setProjectionMatrix(batch.getProjectionMatrix());
    shapeRenderer.setTransformMatrix(batch.getTransformMatrix());
    shapeRenderer.setColor(mColor.r, mColor.g, mColor.b, mColor.a * parentAlpha);

    shapeRenderer.begin(ShapeRenderer.ShapeType.Filled);
    shapeRenderer.circle(getX() + getWidth()/2 , getY() + getHeight()/2 , Math.min(getWidth(),getHeight())/2 );
    shapeRenderer.end();
    Gdx.gl.glDisable(GL20.GL_BLEND);

    batch.begin();
}
Hexylresorcinol answered 10/10, 2020 at 20:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.