In the third part we have created an empty activity. We have set the engine to show "null" as our scene in onCreateScene method. If you check the examples, there is always a real scene created in this method. But we want to have multiple scenes and therefore we need a mechanism how to switch between them.
Also we will need a mechanism to pass important objects to each scene. We can do it when constructing scenes. Or we can use another class called ResourceManager.
Resource manager
This class will be used for our resources like textures, sounds and music. Because all of our scenes will be using it extensively, we will also put some important objects there. Loading the textures etc will be described in the next parts, I will be adding them as they will be needed. For now, create a really simple Resource Manager that looks like this:
package is.kul.squongtutorial.resources;
import is.kul.squongtutorial.GameActivity;
import org.andengine.engine.Engine;
import org.andengine.engine.camera.Camera;
import org.andengine.opengl.vbo.VertexBufferObjectManager;
public class ResourceManager {
private static final ResourceManager INSTANCE = new ResourceManager();
//common objects
public GameActivity activity;
public Engine engine;
public Camera camera;
public VertexBufferObjectManager vbom;
private ResourceManager() {}
public static ResourceManager getInstance() {
return INSTANCE;
}
public void init(GameActivity activity) {
this.activity = activity;
this.engine = activity.getEngine();
this.camera = engine.getCamera();
this.vbom = engine.getVertexBufferObjectManager();
}
}
Update the GameActivity to instantiate the ResourceManager:
@Override
public void onCreateResources(
OnCreateResourcesCallback pOnCreateResourcesCallback)
throws IOException {
ResourceManager.getInstance().init(this);
pOnCreateResourcesCallback.onCreateResourcesFinished();
}
This class follows what we call Singleton pattern. It means there can be only one instance in the whole program. That's why there is the private constructor, nobody else can create a new instance. It is important to have only single resource manager because due to the limitations of the hardware, you really want to keep only one instance of assets in memory.
For now, we will only use it to have a simple reference to activity, engine, camera and vertex buffer object manager.
Vertex Buffer Object is part of OpenGL. It provides methods to render vertex data. AndEngine does great job of hiding this from you. Simply use the VBO Manager all the time, at least for your first game.
Scene is basically a set of entities that are being currently displayed. You typically want to have completely different entities in menu and in the game. And that's why you create menu scene and game scene.
Scenes
We want to have the following scenes in the game:- Splash - shows your or AndEngine logo
- Menu - some kind of selector, for Squong we can put there basic settings and play button
- Game - our game
- Info - A link to this tutorial
- Loading scene - something that will be shown when we are switching the scenes and loading resources
Let's create an abstract scene first, then class for each of the scenes above and Scene Manager that will be switching between them.
Abstract Scene
In Java, abstract class is a class that can't be instantiated. It's a parent that can define some functionality, but you can't call "new". It can have some methods abstract too and that forces the children to implement them.Entity is a virtual object. It has position, a parent and children, but in can't be drawn. Some of extending classes are Shape (and it's children Rectangle, ...) and Sprite (TiledSprite, ...), which have physical representation on screen.
Scene is a hierarchy of Entities (AndEngine class Entity) shown on the screen. You can attach any Entity to Scene. And you can attach any Entity to another Entity creating a parent-child relationship. In fact, Scene is an Entity too.
Our AbstractScene defines the basic objects and important methods. It also defines default behaviour for when the user presses the back key.
package is.kul.squongtutorial.scene;
import is.kul.squongtutorial.GameActivity;
import is.kul.squongtutorial.resources.ResourceManager;
import org.andengine.engine.Engine;
import org.andengine.engine.camera.Camera;
import org.andengine.entity.scene.Scene;
import org.andengine.opengl.vbo.VertexBufferObjectManager;
import org.andengine.util.debug.Debug;
public abstract class AbstractScene extends Scene {
protected ResourceManager res = ResourceManager.getInstance();
protected Engine engine;
protected GameActivity activity;
protected VertexBufferObjectManager vbom;
protected Camera camera;
public void initialize(GameActivity activity, ResourceManager res) {
this.res = res;
this.activity = activity;
this.vbom = activity.getVertexBufferObjectManager();
this.engine = activity.getEngine();
this.camera = engine.getCamera();
}
public abstract void loadResources();
public abstract void create();
public abstract void unloadResources();
public abstract void destroy();
public void onBackKeyPressed() {
Debug.d("Back key pressed");
}
public abstract void onPause();
public abstract void onResume();
}
The methods our classes must override and implement are:
- loadResources - the scene will command the ResourceManager to load these resources.
- create - the scene will create it's entities
- unloadResources - the scene will tell the ResourceManager which resources to free (should be the same as in loadResources)
- destroy - the scene will perform any necessary cleanup
- onPause - what to do when the application is paused (home button or power button pressed, or call is received)
- onResume - what to do when the user returns to the game
Empty scenes
Create a java class for each scene and make all of them extend AbstractScene. Now to make them look at least a little bit different, put this line to each of them and change the color for each Scene:
@Override
public void create() {
getBackground().setColor(Color.BLUE);
}
SceneManager
Finally you need another singleton class that will take care of switching from one scene to another. This one is a little bit tricky and I use a more advanced concept from Android - AsyncTasks. I will explain them in a separate tutorial in detail. Here I am using the simplest form of AsyncTask. Async or Asynchronous tasks is a task that runs in a separate thread. Here is the full class. It's universal for any game and you won't need to change it. I will only add loading of common resources there in next parts.There are two important methods:
- showSplash - this is the first scene switch, we want to load the common resources there (explained later) and load & create the menu, while showing splash screen.
- showScene - this shows loading scene first, then unloads resources of scene A, loads resources of scene B and finally shows B.
package is.kul.squongtutorial;
import is.kul.squongtutorial.resources.ResourceManager;
import is.kul.squongtutorial.scene.AbstractScene;
import is.kul.squongtutorial.scene.LoadingScene;
import is.kul.squongtutorial.scene.MenuScene;
import is.kul.squongtutorial.scene.SplashScene;
import org.andengine.util.debug.Debug;
import android.os.AsyncTask;
public class SceneManager {
private static final SceneManager INSTANCE = new SceneManager();
private ResourceManager res = ResourceManager.getInstance();
private AbstractScene currentScene;
private LoadingScene loadingScene;
private SceneManager() {}
/**
* Shows splash screen and loads resources on background
*/
public void showSplash() {
Debug.i("Scene: Splash");
final SplashScene splash = new SplashScene();
setCurrentScene(splash);
splash.loadResources();
splash.create();
res.engine.setScene(splash);
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
long timestamp = System.currentTimeMillis();
// TODO later load common resources here
MenuScene menu = new MenuScene();
menu.loadResources();
menu.create();
loadingScene = new LoadingScene();
loadingScene.loadResources();
loadingScene.create();
// we want to show the splash at least SPLASH_DURATION miliseconds
long elapsed = System.currentTimeMillis() - timestamp;
if (elapsed < GameActivity.SPLASH_DURATION) {
try {
Thread.sleep(GameActivity.SPLASH_DURATION - elapsed);
} catch (InterruptedException e) {
Debug.w("This should not happen");
}
}
setCurrentScene(menu);
res.engine.setScene(menu);
splash.destroy();
splash.unloadResources();
return null;
}
}.execute();
}
public void showScene(Class<? extends AbstractScene> sceneClazz) {
if (sceneClazz == LoadingScene.class) {
throw new IllegalArgumentException("You can't switch to Loading scene");
}
try {
final AbstractScene scene = sceneClazz.newInstance();
Debug.i("Showing scene " + scene.getClass().getName());
final AbstractScene oldScene = getCurrentScene();
setCurrentScene(loadingScene);
res.engine.setScene(loadingScene);
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
if (oldScene != null) {
oldScene.destroy();
oldScene.unloadResources();
}
scene.loadResources();
scene.create();
setCurrentScene(scene);
res.engine.setScene(scene);
return null;
}
}.execute();
} catch (Exception e) {
String message = "Error while changing scene";
Debug.e(message, e);
throw new RuntimeException(message, e);
}
}
public static SceneManager getInstance() {
return INSTANCE;
}
public AbstractScene getCurrentScene() {
return currentScene;
}
private void setCurrentScene(AbstractScene currentScene) {
this.currentScene = currentScene;
}
}
Notice that the splash is shown at least SPLASH_DURATION milliseconds. Basically you should set the time so the user can see your badge (3-4 seconds), but in case the loading takes longer, this SceneManager keeps showing the Splash until the resources are loaded and Menu Scene ready. Add the following to the GameActivity:
@Override
public void onPopulateScene(Scene pScene,
OnPopulateSceneCallback pOnPopulateSceneCallback)
throws IOException {
SceneManager.getInstance().showSplash();
pOnPopulateSceneCallback.onPopulateSceneFinished();
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
try {
SceneManager.getInstance().getCurrentScene().onBackKeyPressed();
} catch (NullPointerException ne) {
// in highly unlikely situation when this there is no scene, do nothing
Debug.e("The current scene is null", ne);
}
}
return false;
}
And you are good to go. You can run the app. Your splash scene should be shown for SPLASH_DURATION milliseconds followed by your menu scene. Make sure they have different colors! Also try pressing the back key and watch the LogCat.
Note: The AsyncTask has many advantages. You can for example animate something in the current scene while the task is running on background. And the task can also send you information about progress that you can show.
Current Code
This is how your project should look like now. Download it if you have any difficulties.Next part
In the next chapter, I will cover the Splash Scene and loading and unloading resources.Further reading
My book about AndEngine covers this topic as well. You can also see the list of books about Android Game Development for further reading.Note: I had to take a break from writing this tutorial. The game is published in Google Play store now. You can still get the full source code of the final game for 99¢. Available on Sellfy, pay by PayPal:
buy
Also here's one Box2D physics problem that is directly related to the Squong game. If you find a solution, let me know!