The Start screen and disposal

So, here we are back with Flappee Bee. The first thing we should tackle is having a Start screen, or menu screen, depending on what you want to call it. This Screen class will be built entirely with Scene2d.ui components and you will be mesmerized by how awesome all this is.

First things first; let's decide what we will have on our start screen. We will need a play button and some text, perhaps also a title image on the screen.

Let's create our StartScreen class and add the Stage classes to it:

public class StartScreen extends ScreenAdapter {

  private static final float WORLD_WIDTH = 480;
  private static final float WORLD_HEIGHT = 640;

  private Stage stage;

  public void show() {
    stage = new Stage(new FitViewport(WORLD_WIDTH, WORLD_HEIGHT));
    Gdx.input.setInputProcessor(stage);
  }

  public void resize(int width, int height) {
    stage.getViewport().update(width, height, true);
  }

  public void render(float delta) {
    stage.act(delta);
    stage.draw();
  }
}

As you can see, we are using concepts that should now be familiar to you - the use of the Viewport class and our world sizes. We tell LibGDX to use our Stage as the InputProcessor, we have our Stage instance call act() and draw(). I would say we are all set to add our first actor.

Our first actor is going to be the background image, this means we will use the Image class. To do this, we will need to create a reference to our background texture, then pass it to the Image class, and then add it to the Stage instance, as follows:

private Texture backgroundTexture;
public void show() {
  stage = new Stage(new FitViewport(WORLD_WIDTH, WORLD_HEIGHT));
  Gdx.input.setInputProcessor(stage);

  backgroundTexture = new Texture(Gdx.files.internal("bg.png"));
  Image background = new Image(backgroundTexture);

  stage.addActor(background);
}

Before we run our project, we will need to update our FlappeeBeeGame class to point to our new Screen class:

public void create() {
  setScreen(new StartScreen());
}

Excellent! Now let's fire it up and see what we get.

Hopefully, you would have just seen the background on its own, which is awesome. Now look back at the code. You haven't had to worry about the rendering code as all the stage has taken care of that.

Next, let's add a play button to the screen. To do this, we will use the ImageButton class. We have two images on hand for the play button; one when the button is in a normal state and one when it is pressed in. If you look at the code for the ImageButton class, you will see there is a constructor that looks like it was made just for us! Let's take a look at the following code:

public ImageButton (Drawable imageUp, Drawable imageDown)

Aha! You might be wondering why these Drawable classes are here, you were probably expecting the Texture/TextureRegion classes. Well, textures can come in a variety of different ways, so by LibGDX being done this way, they aren't tied to the Texture class.

Ok, so then, how do we use it? Well, it is pretty straightforward; let's update our StartScreen with the following code:

private Texture playTexture;
private Texture playPressTexture;
public void show() {
  /** Code omitted for brevity **/
  playTexture = new Texture(Gdx.files.internal("play.png"));
  playPressTexture = new Texture(Gdx.files.internal("playPress.png"));
  ImageButton play = new ImageButton(new TextureRegionDrawable(new TextureRegion(playTexture)), new TextureRegionDrawable(new TextureRegion(playPressTexture)));
  stage.addActor(play);
}

Here, we create our textures, then we create TextureRegionDrawables, a Drawable subclass, to hold our textures in. We then instantiate the ImageButton class and add it to the stage.

Let's start the game and see how it looks!

The Start screen and disposal

Yay! Provided you see what I see, you should have the button there. Also, if you click on it, you will see that it switches the texture for use to the depressed texture. Looking pretty smart! Even if the placement is not where we want it to be yet.

Let's fix the positioning before we move on to adding the title. The Actor class has a really interesting method signature for setting a position. There is the standard:

public void setPosition (float x, float y)

This will position the actor relative to its bottom-left corner. However, there is also this method:

public void setPosition (float x, float y, int alignment)

This, with the added alignment parameter, allows us to change the anchor point from the bottom-left corner to somewhere else. Check out the Align class here to see the options:

public class Align {
  static public final int center = 1 << 0;
  static public final int top = 1 << 1;
  static public final int bottom = 1 << 2;
  static public final int left = 1 << 3;
  static public final int right = 1 << 4;

  static public final int topLeft = top | left;
  static public final int topRight = top | right;
  static public final int bottomLeft = bottom | left;
  static public final int bottomRight = bottom | right;
}

We will look at using the center alignment. Update our StartScreen class with the following code:

play.setPosition(WORLD_WIDTH / 2, WORLD_HEIGHT / 4, Align.center);

We can add it just after we instantiate the ImageButton class. So, what we are saying here is, place the button at the center of the width and a quarter of the way up the screen relative to the center point. If we didn't have this functionality, we would have to work it out ourselves, with something like the following code:

play.setPosition(WORLD_WIDTH / 2 - play.getWidth() / 2, WORLD_HEIGHT / 4 - play.getHeight() / 2);

But yay for less code now! Plus the less code means less chance of making mistakes.

I hope you can see by now, how easy it is to build a UI with the Scene2d toolkit. Admittedly, we have only added a single button, but it's a small step in the right direction.

Our final act for artwork on the screen is placing the title texture. So, going through the same process as earlier, with the background, we need to update our StartScreen class with a title texture and then add it to the stage:

private Texture titleTexture;
public void show() {
  /* Code omitted for brevity */
  titleTexture = new Texture(Gdx.files.internal("title.png"));
  Image title = new Image(titleTexture);
  title.setPosition(WORLD_WIDTH /2, 3 * WORLD_HEIGHT / 4, Align.center);
  stage.addActor(title);
}

Here, we are just placing it above the play button.

If we run the project, we should get the following output:

The Start screen and disposal

We are almost there! Now, we just need to hook up what to do when you tap the play button, as you are going to want to start the game.

To do this, we need to add a listener to the play button and react to when it is called. LibGDX has an interface called EventListener that is used throughout Scene2d to propagate the events through the graph. Now, we can just take this interface to create our own implementation, where we check for a mouse click and then react. However, thankfully, someone has already created a useful class that does the bulk of the heavy lifting!

It is the ActorGestureListener class, which contains lots of methods we can chose to override if we wish to react to them, as follows:

touchDown (InputEvent event, float x, float y, int pointer, int button) {}

touchUp (InputEvent event, float x, float y, int pointer, int button) {}

tap (InputEvent event, float x, float y, int count, int button) {}
boolean longPress (Actor actor, float x, float y) { return false;}

fling (InputEvent event, float velocityX, float velocityY, int button) {}

pan (InputEvent event, float x, float y, float deltaX, float deltaY) {}

zoom (InputEvent event, float initialDistance, float distance) {}

pinch (InputEvent event, Vector2 initialPointer1, Vector2 initialPointer2, Vector2 pointer1, Vector2 pointer2) {}

We are interested in the tap method. As we would like to do something when the user taps the button. To do this, we need to instantiate and override the method and then add this listener to the play button object, as follows:

play.addListener(new ActorGestureListener() {
  @Override
  public void tap(InputEvent event, float x, float y, int count, int button) {
    super.tap(event, x, y, count, button);
    }
});

Now, every time the button is tapped, that method will get called. We just now need to decide what we are going to do with it. This, thankfully, is a pretty simple answer, as we want to start the game!

To do this, we need to change the Screen object that the Game object currently has referenced. This means we will need access to the Game object in our code. Let's do this by adding it as a constructor parameter to our StartScreen class:

private final Game game;
public StartScreen(Game game) {
  this.game = game;
}

Next, let's update our tap method to change the screen:

public void tap(InputEvent event, float x, float y, int count, int button) {
  super.tap(event, x, y, count, button);
  game.setScreen(new GameScreen());
}

Perfect. Finally, we need to update our FlappyBeeGame class to pass in a reference to itself:

public void create() {
  setScreen(new StartScreen(this));
}

Done!

Run the project and see what happens; tapping on Play will start the game.

We are not using Scene2D to its fullest potential, yet, you can see how easy it is to create a UI that is simple and clear code it; just imagine if we had to do all that by hand!

The dispose() method

Before we close this chapter, I want to quickly show you how to dispose of your assets in an appropriate manner. We haven't done this so far as we have been continually using our textures throughout the life of the game. However, here we have a that a player will see, then go into the game, and probably never see this screen again in this instance of the game. This means we are giving away vital memory allocation to textures that can be freed up! Also, our Stage instance needs to clean up after itself.

Doing this is pretty basic stuff, so we should add it now. In our StartScreen class, let's override the dispose() method and call dispose on everything we wish to clear:

@Override
public void dispose() {
  stage.dispose();
  backgroundTexture.dispose();
  playTexture.dispose();
  playPressTexture.dispose();
  titleTexture.dispose();
}

As you can see, all the textures and the stage are disposed of. Next, since dispose isn't called for us automatically, we need to explicitly say when we want to clear up. Luckily, we know when this will happen—when we switch screens, of course! Let's take a look at the following code snippet:

play.addListener(new ActorGestureListener() {
  @Override
  public void tap(InputEvent event, float x, float y, int count, int button) {
    super.tap(event, x, y, count, button);
    game.setScreen(new GameScreen());
    dispose();
  }
});

Excellent. With what we are doing, the impact will be small; however, it is a good practice to get into the habit of cleaning up after yourself, as when you get on to making bigger and more demanding games—this could be a time saver!

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset