2

I haven't worked with LibGdx in a couple of years, but I remember figuring this out at some point. I'm trying to create a button, and use ShapeRenderer to draw a Rectangle around the edge of the button (to outline it).

So far, I've attempted to create my own custom Actor and use ShapeRenderer in the draw method, but it won't update the position of the rectangle, opacity etc. In other words, it doesn't take advantage of any of the Actor class benefits. Is there any way to draw a line or shape using the Actor classes which will then get updated with applied Actions etc?

Here is an example of what I was doing in my custom Actor class:

public class Shape extends Actor {

ShapeRenderer sr;
float x, y, w, h;

public Shape (float x, float y, float w, float h) {
    sr = new ShapeRenderer();
    this.x = x;
    this.y = y;
    this.w = w;
    this.h = h;
}

@Override
public void draw(Batch batch, float parentAlpha) {
    super.draw(batch, parentAlpha);
    batch.end();
    sr.begin(ShapeRenderer.ShapeType.Line);
    sr.setColor(Color.BLACK);
    sr.rect(x, y, w, h);
    sr.end();
    batch.begin();
}}

Any help would be greatly appreciated; thanks in advance!

EDIT 4/21 2:39PM EST Made recommended changes, shape still doesn't move / rotate with actions. Any other suggestions?

public class Shape extends Actor {

ShapeRenderer sr;

public Shape (ShapeRenderer sr, float x, float y, float w, float h) {
    this.sr = sr;
    setX(x);
    setY(y);
    setWidth(w);
    setHeight(h);
}

@Override
public void draw(Batch batch, float parentAlpha) {
    super.draw(batch, parentAlpha);
    batch.end();
    sr.begin(ShapeRenderer.ShapeType.Line);
    sr.setColor(Color.BLACK);
    sr.rect(getX(), getY(), getWidth(), getHeight());
    sr.end();
    batch.begin();
}}

EDIT 4/21 2:46PM EST Here's how I'm implementing my custom actor, I add it to a parent group and set actions on the group...

//Generate buttons
    ImageTextButton.ImageTextButtonStyle style = new ImageTextButton.ImageTextButtonStyle();
    parameter.size = game.labelButtonSize;
    style.font = generator.generateFont(parameter);
    style.fontColor = Color.BLACK;
    buttonYes = new ImageTextButton(Consts.CAMERA_STRING_BUTTON_YES, style);
    buttonYes.align(Align.left);
    buttonYes.addListener(new ClickListener() {
        @Override
        public void clicked(InputEvent event, float x, float y) {
            Gdx.app.log("TAG", "Clicked Yes");
        }
    });
    Group bY = new Group();
    bY.addActor(buttonYes);
    bY.addActor(new Shape(sr, buttonYes.getX(), buttonYes.getY(), buttonYes.getWidth(), buttonYes.getHeight()));
    bY.addAction(Actions.rotateTo(90, 2));
sngreco
  • 1,138
  • 11
  • 16

2 Answers2

2

Actor already has fields for x, y, width, height, and color. You have to use the getter methods to access them. By using your own, you're ignoring the built-in functionality.

Also, you probably want to have only one ShapeRenderer in the whole app because each ShapeRenderer instance has a big vertex data array and compiles a shader.

Note that you create a lot of extra draw calls because you keep having to flush the Batch and the ShapeRenderer for each actor that does this. If your performance begins to suffer, you may want to replace this rectangle with a NinePatch, which you can draw with the Batch.

Alternatively, if you are not using sprites at all, you might consider overriding drawDebug instead of draw so you can use SpriteBatch's built in ShapeRenderer and avoid all the extra flushing.

Tenfour04
  • 83,111
  • 11
  • 94
  • 154
  • I used the get / set methods...still didn't update the position etc (see above). Any other tips? – sngreco Apr 21 '16 at 18:42
  • In regards to the NinePatch thought, my goal is to eventually set the width of the line outlining the button to a value that is relative to the resolution of the screen (ie 1% screen width). If I was to do this with NinePatch, I would need to create a handful of them for different resolutions correct? – sngreco Apr 21 '16 at 18:52
  • I did not know a ShapeRenderer is such a big object. Shouldn't that info be in the wiki somehwere? – Barodapride Apr 21 '16 at 20:05
  • You've sized your Shape to the Button, but the Button has a size of 0, 0 since you never called setWidth or setHeight on the button. You can still see the button because it draws at its minWidth and minHeight if they are less than its stored width and height. You can call pack() on the button to transfer its minSize to its actual size so it will report its size correctly. But perhaps you could subclass the button and add your outline code there to avoid the complexity of putting an extra actor in the mix. – Tenfour04 Apr 21 '16 at 20:28
  • ShapeRenderer does not support variable width lines on all devices anyway, to the best of my knowledge. Some GPUs simply ignore the line width parameter and always draw 1-pixel wide lines. With NinePatch, you can call the setLeftWidth, setTopHeight, etc methods to set the size of the non-stretchable region to something other than their original size in the image. So it would work perfectly fine for what you want to do. – Tenfour04 Apr 21 '16 at 20:36
0

I have a similar scenario. What I noticed is that I had to take the TransformMatrix from the Batch and apply it to the ShapeRenderer. See code below.

@Override
public void draw(Batch batch, float parentAlpha) {
    batch.end();
    shapeRenderer.setTransformMatrix(batch.getTransformMatrix());
    shapeRenderer.begin(ShapeRenderer.ShapeType.Filled);
    Color color = getColor();
    shapeRenderer.setColor(color.r, color.g, color.b, color.a * parentAlpha);
    shapeRenderer.rect(getX(), getY(), getWidth(), getHeight());
    shapeRenderer.end();
    batch.begin();
}
Jose Martinez
  • 11,452
  • 7
  • 53
  • 68