如何在一个屏幕中同时使用SpriteBatch和ShapeRenderer?

4

我将使用SpriteBatch绘制纹理和ShapeRenderer绘制某些形状。

这是我的演员中的代码:

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

在屏幕上调用stage.draw()函数。
@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();
    //......
}

它能够工作,但会不可预测地抛出异常:

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)

编辑:更多关于我的操作细节:

我想要画一个形状。因为舞台的批次正在进行中,所以我必须结束它来进行形状的绘制。我的代码仍然有效,但有时候,其他的演员使用舞台的批次来绘制其他东西。这会使舞台开始新的批次,与结束批次产生冲突。

举个例子,演员的绘制方法:

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

编辑: 如果其他答案不适合您,请考虑我在下面发布的解决方法。

4个回答

9
@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(); <--

我认为错误在于您调用了batch.end()之前调用了batch.begin(),请尝试更改顺序。
另一方面,如果draw方法是在Stage类中,您可以使用所需的参数调用它:public void draw(Batch batch, float parentAlpha)

绘图方法位于actor中。我想要的是结束阶段的batch(绘制某些形状),以便稍后重新开始绘制。我的代码仍然可以工作,但有时候另一个actor使用stage的batch来绘制其他东西。这使得stage开始了它的batch。因此在begin和end之间存在冲突。 batch.end() ... batch.begin()(某个地方).. batch.begin() - Nathan

3

如果在打开新的Renderers之前不关闭所有Renderers,则会得到没有先前Renderers的视图。

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

这会导致一个没有你的spriteBatch-Textures的屏幕 --- 你可以通过重新排列代码来解决这个问题。
    @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
 }

但是在第一个batch.end()中,你的代码无法找到任何可以关闭的已打开spriteBatch,因此会出现IllegalStateException异常。

在使用end()方法之前,你必须先调用batch.begin()一次(但要注意不要在每帧都开始一个batch)。

我建议最简单的解决方案是:

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

      ...

}

0
就像Angel * 2所说的那样,你的错误来自于在调用.begin之前调用了.end。使用多个绘图批次是完全可行的,并且经常被使用,但是你必须按照正确的顺序使用它们并正确地开始/结束它们。以下代码是有效的:
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();

0

我知道这是我的旧问题,但我可以看到仍有新人使用libgdx也面临此错误。因此,我将我的解决方法发布为答案:

问题在于在两者之间有一些东西被破坏了。

batch.begin()

并且

batch.end()

当舞台绘制时

因此,如果您使用舞台来管理批处理,则try-catch可以节省您的时间:

@Override
public void render(float delta) {
    try {
        stage.act(delta)
        stage.draw()
    } catch (Exception ex) {
        if(stage.batch.isDrawing)
            stage.batch.end()
    }
}

** 这只是一个解决方法,用于绕过帧中的一些意外错误(例如字形布局),并且在下一帧中应该可以正常工作。如果您的代码或资源存在任何真正的问题,则渲染代码将最终进入catch{}块。


所有其他答案都是错误的,因为它们没有注意到您的代码位于Actor的绘制方法内部,但是哇,如果这不是一个丑陋的黑客。您最初的问题几乎肯定是由于未调用batch.end()或多次调用batch.begin()引起的。您在原始问题中发布的实际代码不会是罪魁祸首。LibGDX不执行任何异步绘图。 - Tenfour04
@Tenfour04确实,我的代码问题是由于较旧版本的GlyphLayout引起的,但已经解决了。它破坏了阶段绘制方法中的循环,因此阶段无法调用batch.end(),所有错误都发生在下一帧调用batch.begin()时。 - Nathan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接