Various

Adding Sprites and ConcurrentModificationException

As rule of a thumb you should never add sprites within a state in an event method. That can lead to ConcurrentModificationException by a SpriteGroup.

Only add sprites in the State#updateLogic() method. The reason: the State#onTouchEvent(View, MotionEvent) runs in a different thread. And if you add sprites to a sprite group within this method it's very likely that you run into those kinds of exceptions.

You should use a flag or message which is set in the event methods. If the update phase is running you can evaluate those flags and create the appropriate sprites and add them to the root node.

Units and Conversion

For rendering graphics it's important to consider the units that are used to resize a sprite, or change the font size for instance. Internally the engine uses pixel values for all the calculations. If you want to use Density Independent Pixels (DP / DIP) you have to convert those values accordingly by yourself.

For more information on this topic have a look in the section Helper classes.

How to Handle more than one State

An app has probably more than one activity. You can also define many graphical scenes by creating multiple State classes. This section describes how one activity can handle multiple defined State classes.

Look at the following scenario. We have an activity with a RenderThread and a DrawView component (we call it GraphicsActivity) and many states. Each state defines a different graphical scene, animation or mini game. Beside that we also have other "normal" activities with standard Android components (for instance an activity showing the settings).

Every activity can start the GraphicsActivity (with the RenderThread and DrawView component). To define which state should be started is evaluated through the intent bundle of the GraphicsAcitivty. So every other activity should start the GraphicsActivity with a android.os.Bundle object to define the state to start. And if no arguments are supplied then a default state is used.

The code for starting the GraphicsActivity could look like this in the onCreate() method:

1
2
3
4
5
6
Bundle b = new Bundle();
b.putSerializable("currentState", MiniGameState.class);
//Start the GraphicsActivity with Intent
Intent myIntent = new Intent(getContext(), GraphicsActivity.class);
myIntent.putExtras(b);
startActivityForResult(myIntent, 0);

MiniGameState is the class that extends the State class. Via the android.os.Bundle every activity can select the next state for the GraphicsActivity.

The activity GraphicsActivity should then implement something like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
RetroEngine.init(this);
DrawView drawView = (DrawView) findViewById(R.id.graphics);
RenderThread renderThread = new RenderThread(drawView);

// add all possible states
renderThread.addStates(new Intro(), new Gameplay(), new MiniGameState());
drawView.setRenderThread(renderThread);

// Now decide which state should be loaded
Bundle b = getIntent().getExtras();
Class<?> tmp = Intro.class;
if (b != null) {
    // Get State from bundle if available
    Class<?> tmp2 = (Class<?>) b.get("currentState");
    if (tmp2 != null) {
        tmp = tmp2
    }
}

renderThread.setCurrentState(tmp); // default state

You see, the GraphicsActivity evaluates the Bundle object to decide which state should be loaded. With that approach only one activity is necessary to draw different graphical scenes. And only one RenderThread is used which saves resources!

Get Android Handler from State

The Android Handler can be obtained in every State class. For that use the manager instance variable to get the handler with getHandler().

Example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
        if (skipIntro && isActive()) {
            RetroEngine.shouldWait = true;
            manager.getHandler().postDelayed(new Runnable() {
                public void run() {
                    //cleanUp();
                    Intent myIntent = new Intent(manager.getParentActivity(),
                            MenuActivity.class);
                    manager.getParentActivity().startActivityForResult(
                            myIntent, 0);
                }
            }, 0);
            setActive(false);
        }

This is useful if you need to use the method post or postDelayed from the Handler.

Combine RetroGraphicsEngine with other components

The DrawView component is a special implementation of the android.view.SurfaceView thus a standard Android component and can be used as such. You can integrate this component in your layout just like any other component.

Correct Handling of the RenderThread

What we've implemented so far is working. But what if the activity is switched, the app minimized and re-entered? The thread is still running and will overwrite your next renderings. We have to handle different things: - clear all sprites - stop the RenderThread - remove all states (this may apply if you have more activites with different states) - because the StateManager is a Singleton it will hold all previous states

The listed points are common pitfalls which have to be taken care of.

State clean method

  • use the cleanUp() method of the state which you have to override. This is every time called if a state is terminated or switched. You can use clearSprites from State to handle basic sprite removing from the rootGroup.
1
2
3
4
5
    @Override
    public void cleanUp() {
        clearSprites();
        manager.clearStates();
    }

Activity

  • override onDestroy and onPause in each activity to stop the renderThread
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@Override
protected void onDestroy() {
    super.onDestroy();
    RetroEngine.isRunning = false;
    try {
        if (renderThread != null) renderThread.join();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

@Override
protected void onResume() {
    super.onResume();

    if (renderThread != null) {
        RetroEngine.shouldWait = false;
    }
}

@Override
protected void onPause() {
    super.onPause();

    if (renderThread != null) {
        RetroEngine.isRunning = true;
    }
}