Libgdx version used on this post: 0.9.2 (download)All we have by now is a splash screen that displays an static image. Wouldn't it be nice to add some fading effect on this image? Things like these improve the user experience a lot, so soon we'll deal with that. As we build our graphical user interface, we need to handle things like selecting options, cliking on buttons, giving focus to an input widget and so on so forth. Can you imagine doing that with images inside image atlases? You're lucky today. There is a very cool feature of libgdx called scene2d that basically provides useful abstractions for rendering 2D components. Let's understand it better!
As of libgdx 0.9.6 some of the classes used on this post were modified or removed. You may want to read the post #13 after reading this post, which covers those changes.
About scene2dScene2d is a module of libgdx that eases the management and rendering of 2D components, which are called Actors. These actors live on a tree structure inside a container called Stage. They keep track of many rendering attributes, such as position relative to the parent, color, visibility, dimensions, scale and rotation factors and more. They are also responsible for hit detection.
Examples of actors would be: buttons, text fields, images, enemy targets, coins, the ship we'll fly, shots and so on. We'll use scene2d a lot in our game. Also, it's possible to apply actions on the actors, like translate, rotate, scale and fade actions. If needed, you can also write your own action, but we'll get to that later.
I'll try and summarize the main concepts of scene2d below:
- Actor - A 2D component that knows how to draw itself.
- Group - An actor that contains other actors.
- Stage - An engine that requests the actors to be drawn and handles user interaction by distributing touch events to the actors.
- Action - A function that modifies the actors' properties over time.
The following UML diagram shows it graphically:
Using scene2dThe first thing to do is setup a Stage where the actors will act. A nice place for it to live is within a screen (each screen with its own stage). These are the steps for managing the stage in our game:
- Create a Stage instance on the constructor of AbstractScreen class
- Resize its viewport when the screen is resized
- Call the Stage's act and draw methods within the screen's render method
- Dispose the stage within the screen's dispose method
Modifying the Splash screenWe want the splash image to be an actor so that we can do cool things with it. Instead of extending the Actor class, we can just use the Image (com.badlogic.gdx.scenes.scene2d.ui.Image) actor that comes with scene2d. We still have to load the texture and the texture region. We should also tell the image how to be drawn, and that it should take all the screen size. Have a look at the source code for the modified SplashScreen class to see how it's done.
Regarding that "actions" object inside SplashScreen.resize(), that's the coolest thing we can do with scene2d. Each actor can be animated by actions. What we do with our splash image is to add a set of the following actions:
- Fade-in in 0.75 seconds
- Wait for 1.75 sconds
- Fade-out in 0.75 seconds
More about actionsThe three actions we used on the splash image ship with libgdx. That is:
- Fade-in action: com.badlogic.gdx.scenes.scene2d.actions.FadeIn
- Fade-out action: com.badlogic.gdx.scenes.scene2d.actions.FadeOut
- Delay action: com.badlogic.gdx.scenes.scene2d.actions.Delay
I suggest you take some time to check all the available actions inside the package com.badlogic.gdx.scenes.scene2d.actions, but basically there are two types of them:
- Concrete actions - Actions that modifies the actors directly (FadeIn, FadeOut, MoveBy, RotateBy, ScaleTo, etc).
- Support actions - Actions that group or organize other actions in specific ways (Delay, Sequence, Parallel, Repeat, Forever, etc).
FadeIn fadeInAction = FadeIn.$(2f);The delay action has a different factory method. The following code creates the FadeOut action 5 seconds delayed.
FadeOut fadeOutAction = FadeOut.$(2f); Delay delayAction = Delay.$(fadeOutAction, 5f);In order to have the complete effect we want, we need to join both actions:
Sequence sAction = Sequence.$(fadeInAction, delayAction);And finally, add the action to the actor:
actor.action(sAction);Piece of cake, isn't it? The last thing about actions I should not forget, is that you can also add interpolators to them. That means your action can start slow and them accelerate gradually, for instance. Interpolators define the rate of change for a given animation. In libgdx they also come with the "$" factory method, so you can easily create them.
Domain modelA domain model describes the entities, their attributes, roles and relationships for a given domain of interest. Most of our business logic will stay on the domain entities, because in software engineering it's a nice idea to isolate the business logic. It makes the code straightforward to other programmers, and even to ourselves later on. It also helps when switching technologies. Say we want to switch from 2D to 3D graphics. If the business logic is isolated, the impact of this change will be kept to a minimum.
With the help of scene2d we can achieve this objective rather easy, as each of the drawable domain entities can map to a scene2d actor. That's how we'll separate the business code from the presentation code. So the next task for us is to define our domain model. In order to make it simple, we're not going to implement a full clone of Tyrian, but just the main aspects of it. After analysing the game for some time, I came up with the following diagram:
You can check the complete source code for the domain model here.