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
andonPause
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; } } |