How to use SpriteBatch and ShapeRenderer on the same screen?
I am using SpriteBatch to paint textures and ShapeRenderer to paint some shape.
Here is my code in the 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 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();
//......
}
Works, but throws an exception unpredictably:
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 on what I am doing:
I want to draw a shape. Because the stage part is being drawn, so I have to finish it to draw the figure. My code still works, but at one time, another actor, I think, used a batch of the scene to paint something else. This forces the scene to begin its part. So this is a conflict between the beginning and the end.
For example, the method for drawing an actor:
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 doesn't work for you, please consider my workaround, which I post as an answer below.
source to share
@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 in you is causing, batch.end ()
before bacth.begin ();
try to change the order
on the other hand, if the draw. This is the scene class, you call it the arguments you need,public void draw (Batch batch, float parentAlpha)
source to share
If you don't close all Renderers before opening a new one, you will get a view without previous
spriteBatch.begin()
... // render Textures
shapeRenderer.begin()
... // render Shapes
shapeRenderer.close()
spriteBatch.close()
this will lead to the screen without your spriteBatch-Textures --- you already solved this problem by applying 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 () file, your code cannot find an open spriteBatch that can be closed, so you get an IllegalStateException
You need to call
batch.begin () once before using the end () method (but keep in mind that you don't have to start the batch every frame)
The simplest solution I would recommend solving is this:
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();
}
...
}
source to share
As Angel * 2 says, your error comes from calling .end before .begin. Multiple batches of blueprints are quite possible and often used, but you must use them in order and start / end correctly. The following code is valid:
spriteBatch.begin();
spriteBatch.draw(..);
//more draw calls for this spritebatch
spriteBatch.end();
shapeRenderer.begin(..);
shapeRenderer.line(..);
//more draw calls for shaperenderer go here
shapeRenderer.end();
anotherSpriteBatch.begin();
anotherSpriteBatch.draw(..);
anotherSpriteBatch.end();
//You can also use the same batch again.
shapeRenderer.begin(..);
shapeRenderer.circle(..);
shapeRenderer.close();
source to share
I know this is an old question of mine, but I see that there are still new people using libgdx after facing this error. So I am posting my workaround as an answer:
The problem is that there is something in between
batch.begin()
and
batch.end()
when drawing a scene
so if you are using Stage to control the package try-catch can save you time:
@Override
public void render(float delta) {
try {
stage.act(delta)
stage.draw()
} catch (Exception ex) {
if(stage.batch.isDrawing)
stage.batch.end()
}
}
** This is just a workaround for some random error (like glyphlayout) in a frame, and it should work fine on the next frame. If there is any real problem in your code or resource, your rendering code will end up in catch {}
source to share