Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use SpriteBatch and ShapeRenderer in one screen?

Tags:

libgdx

I'm using SpriteBatch to draw textures and ShapeRenderer to draw some shape.

Here is my code in an actor

@Override
public void draw(Batch batch, float parentAlpha) {
    batch.end();
    Gdx.gl.glEnable(GL20.GL_ARRAY_BUFFER_BINDING);
    Gdx.gl.glEnable(GL20.GL_BLEND);
    Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);
    shapeRenderer.begin(ShapeType.Filled);

    //change color

    shapeRenderer.setColor(color);

    shapeRenderer.rect(rectangle.x, rectangle.y, rectangle.width, rectangle.height);

    shapeRenderer.end();
    Gdx.gl.glDisable(GL20.GL_BLEND);
    batch.begin();
}

and call stage.draw() on the screen

@Override
public void render(float delta) {
    Gdx.gl20.glClearColor(0, 0, 0, 0);
    Gdx.gl20.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
    Gdx.gl20.glEnable(GL20.GL_TEXTURE_2D);
    stage.act(delta);
    stage.draw();
    //......
}

It's working but unpredictably throw exception:

STACK_TRACE=java.lang.IllegalStateException: SpriteBatch.end must be called before begin.
at com.badlogic.gdx.graphics.g2d.SpriteBatch.begin(SpriteBatch.java:164)
at com.badlogic.gdx.scenes.scene2d.Stage.draw(Stage.java:127)
at c.i.a.a(AbstractCardRoomRenderer.java:3078)
at c.i.s.a(TLMNCardRoomRenderer.java:1158)
at c.j.e.render(GameScreen.java:22)
at com.badlogic.gdx.Game.render(Game.java:46)
at com.badlogic.gdx.backends.android.AndroidGraphics.onDrawFrame(AndroidGraphics.java:422)
at android.opengl.GLSurfaceView$GLThread.guardedRun(GLSurfaceView.java:1522)
at android.opengl.GLSurfaceView$GLThread.run(GLSurfaceView.java:1239)

EDIT: For more details what I'm doing:

What I want is to draw a shape. Because the stage's batch is drawing so I have to end it for shape drawing. My code still work but sometime, another actor, I think, use stage's batch to draw something else. It make the stage begin its batch. So it conflict between begin and end.

For example, the actor draw method:

batch.end();
//drawing shapes

         batch.begin() (somewhere else) <--- I think this code is call when stage call draw on other actor

 //drawing completed
 batch.begin() 

EDIT: If others' answer not suit you, please consider my workaround I post as an answer below.

like image 434
Nathan Avatar asked Dec 14 '22 13:12

Nathan


2 Answers

@Override
public void draw(Batch batch, float parentAlpha) {
    batch.end();   <-- 
    Gdx.gl.glEnable(GL20.GL_ARRAY_BUFFER_BINDING);
    ../// other code


    shapeRenderer.end();
    Gdx.gl.glDisable(GL20.GL_BLEND);
    batch.begin(); <--

I think the error is in you is calling, batch.end () before bacth.begin (); try to change the order

on the other hand, if the draw method. It is the stage class, you call him with the arguments you require, public void draw (Batch batch, float parentAlpha)

like image 100
Angel Angel Avatar answered Feb 25 '23 07:02

Angel Angel


If you don't close all Renderers before opening a new one you will get a view without the previous ones

spriteBatch.begin()
... // render Textures
shapeRenderer.begin()
... // render Shapes
shapeRenderer.close()
spriteBatch.close()

this would cause a Screen without your spriteBatch-Textures --- you already solved this problem by resorting your code to this

    @Override
    public void draw(Batch batch, float parentAlpha) {
        batch.end(); // close A
        ...
        shapeRenderer.begin(ShapeType.Filled); // open B
        ...
        shapeRenderer.end(); // close B
        batch.begin(); // open A
 }

But in the very first batch.end() your code is not able to find any opened spriteBatch that can be closed, therefore you get an IllegalStateException

You have to call

batch.begin() one time before using the end()-method (but be aware that you shouldn't begin a batch every frame)

the most simple solution i would recommend to solve the issue is the following:

class MyActor{
      private boolean firstDraw = true;

      @Override
      public void draw(Batch batch, float parentAlpha) {
            if(firstDraw)
            {
                  batch.begin();
                  firstDraw=false;
            }
            batch.end();
            Gdx.gl.glEnable(GL20.GL_ARRAY_BUFFER_BINDING);
            Gdx.gl.glEnable(GL20.GL_BLEND);
            Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);
            shapeRenderer.begin(ShapeType.Filled);

            //change color

            shapeRenderer.setColor(color);

            shapeRenderer.rect(rectangle.x, rectangle.y, rectangle.width, rectangle.height);

            shapeRenderer.end();
            Gdx.gl.glDisable(GL20.GL_BLEND);
            batch.begin();
      }

      ...

}
like image 22
zeg Avatar answered Feb 25 '23 06:02

zeg