scene2d package是一个2D场景图结构,用来管理场景中的actors组。它能处理用户输入,并在相对于父actor的坐标系统中进行旋转和缩放。它还提供一个刷新actors的框架。scene2d.ui package对建立GUI很有用。
Stage类有一个名为"root"的group(组),可以向其中添加actors。Stage有一个camera和一个SpriteBatch。camera的viewport尺寸与屏幕相同。当需要绘制Stage时,SpriteBatch就作为参数传递给root组。
Group类派生自Actor类,其实就是一个包含其他actors的Actor。对Group的旋转和缩放操作同时影响它的chidren。Group负责把绘制操作和用户输入分配给合适的children。
Actor类提供场景结点(node)所需的全部基本函数,包含position, size, scale, rotation, origin, color这些属性,有一个可选的name,可以在debug模式下显示整个actor树形结构。
Stage类有一个viewport size的属性,该属性可以在构造函数里初始化,但最好还是在程序的resized事件中设置该属性。构造函数中还有一个名为"stretch"的参数,如果它为true,就算Stage的viewportsize和屏幕分辨率不同,stage也会被拉伸到屏幕分辨率。如果为false,stage的viewportsize中较大的那个维度会放大到和屏幕分辨率一样。如果在resized事件中把viewport size设置成和屏幕一样,则stretch值就无关紧要了。
Stage有一个"act"函数,输入参数包括两帧的间隔时间,可以使场景中所有actor都调用各自的act()函数。
private Stage stage; public void create () { stage = new Stage(0, 0, true); } public void resize (int width, int height) { stage.setViewport(width, height, true); } public void render () { Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT); stage.act(Gdx.graphics.getDeltaTime()); stage.draw(); } public void dispose() { stage.dispose(); }
请创建Actor的派生类来实现绘制、碰撞检测、处理用户输入等工作。
Drawing
当Stage::draw()函数被调用时,它会调用root group,然后root再调用它的所有children。要绘制actor,需要实现它的draw函数:
TextureRegion region; public void draw (SpriteBatch batch, float parentAlpha) { batch.setColor(1, 1, 1, parentAlpha); batch.draw(region, x, y, width, height); }actor::draw函数里收到的SpriteBatch已经被转换过了,所以actor的position, size, rotation, scale属性都是相对于父group的左下角的。这使绘制操作变得很简单。在上面的代码中,x,y,width,height都是Actor类的public属性。
如果actor的visible属性被设置为false,Group就不会调用该actor的draw()函数。
draw()函数里的parentAlpha参数是该actor的parent的alpha值。如果draw函数使用了透明度,那么整个group和它的所有children都会变得透明。
SpriteBatch::begin()在actor::draw()函数被调用前就已经调用过了。如果一个actor需要用其他方式来绘制,比如使用ShapeRenderer,好么batch应该先end()并在绘制结束后再begin()。当然,这样会使得batch被重置,所以应该谨慎使用,具体使用方法如下:
private ShapeRenderer renderer; public void draw (SpriteBatch batch, float parentAlpha) { batch.end(); renderer.setProjectionMatrix(batch.getProjectionMatrix()); renderer.setTransformMatrix(batch.getTransformMatrix()); renderer.translate(x, y, 0); renderer.begin(ShapeType.Rectangle); renderer.rect(0, 0, width, height); renderer.end(); batch.begin(); }
当Stage::hit()被调用时,它会调用root group,然后root再调用所有children。第一个返回非null值的actor将被stage返回。要进行碰撞检测,需要实现下面的函数:
public Actor hit (float x, float y) { return x > 0 && x < width && y > 0 && y < height ? this : null; }函数中用到的坐标都是actor的局部坐标系的。
要想触发手机屏幕的用户输入,需要实现touchDown, touchDragged, touchUp函数。
public boolean touchDown (float x, float y, int pointer) { return true; } public void touchDragged (float x, float y, int pointer) { } public void touchUp (float x, float y, int pointer) { }当stage::touchDown被触发时,它调用root group。root group会先调用所有children的hit()函数,然后由第一个被hit()函数返回的actor调用touchDown函数。如果touchDown返回false,表示该actor忽略touch down事件,所以group会继续调用hit(),并由被hit到的actor调用touchDown。如果touchDown()返回true,则该actor成为group的focused actor(焦点),这意味着该actor可以接收到touchDragged和touchUp事件,即使它们并没有在该actor上发生(它们会接收到事件,但如果不在它们身上发生,就可以选择忽略)。如果touchUp被调用(一旦touchDown已经触发,touchUp就一定会触发),该actor就失去焦点。
如果actor的touchable属性设置为false,则Group不会触发它的touch事件。
当前的action系统马上就会进行重构,到时候再写。