4. Using the Mobile Media API (JSR-135)
Many games do this simply because
studies have shown that music helps create a more immersive experience.
By interspersing MIDI sequences with periods of silence we can change
the 'feel' of the game in a subtle manner which helps maintain player
interest.
To do this we use
timers, some basic parameters and a random-number generator. We also
need to detect when the active sequence has finished by implementing the
PlayerListener interface.
Whenever the current background music stops we wait for a random
(bounded) interval of time and then start the next sequence:
// from PlayerListener
public void playerUpdate(Player player, String event, Object eventData){
try{
if(event.equals(PlayerListener.END_OF_MEDIA)){
if(player == backgroundPlayer1){
activeBackgroundPlayer = backgroundPlayer2;
}
else if(player == backgroundPlayer2){
activeBackgroundPlayer = backgroundPlayer3;
}
else if(player == backgroundPlayer3){
activeBackgroundPlayer = backgroundPlayer1;
}
startBackgroundFXTimer();
}
}
catch(Exception e){
e.printStackTrace();
}
}
private void startBackgroundFXTimer(){
stopBackgroundFXTimer();
TimerTask task = new TimerTask(){
public void run(){
playBackgroundMusic();
}
};
backgroundTimer = new Timer();
backgroundTimer.schedule(task, musicDelay);
musicDelay = Utilities.random(BACKGROUND_WAIT_MIN,BACKGROUND_WAIT_MAX);
}
5. Using the Scalable 2D Vector Graphics API (JSR-226)
As explained earlier, this game
uses a menu system based on the Scalable Vector Graphics API, when it is
detected on the device. Figure 8.6a
shows the results of a very simple linear menu using scaling and
transparency. The best way to get started using JSR-226 is to read
[Nokia 2006e] and [Powers 2005].
An SVG image is simply an XML
document containing a set of drawing instructions which allows an image
to be created using high-level instructions. You can say, for example,
'draw a circle radius 5 at point 2,10'. The main benefits you get by
using SVG images instead of traditional raster images is their
scalability, ease of manipulation and the fact that they compress
extremely well in a JAR since they are just a text document.
The full SVG
specification defines support for a wide range of features but not all
of these are yet supported on mobile phones. Instead phones support a
subset of the specification called SVG-Tiny also referred to as SVG-T.
The current version of SVG-T is 1.1 and this is the version currently
shipped on Symbian OS 9.x devices. To determine from Java ME what
version of SVG-T your phone supports, you can query the system property
as follows:
String svgVersion = System.getProperty("microedition.m2g.version");
boolean useSVG = svgVersion.equals("1.1");
In order to read the XML, the
SVG-T specification defines a micro-DOM that the implementation of the
JSR must provide. This is a small XML parser specifically designed to
parse SVG elements. Note that as part of the SVG-T implementation, it
should not be considered an XML parser for general use.
There are two ways to create
SVG content: SVG images can be created programmatically through a series
of API calls but it is far more common to load and manipulate SVG
images prepared using a third-party graphics tool such as Adobe
Illustrator. If you don't have access to this, a common open-source,
free alternative for SVG content creation is Inkscape.
Before you attempt to load or
display an SVG image from a MIDlet, you need to convert it to SVG-T
format. The S60 3rd Edition SDKs come with an SVG to SVG-T conversion
tool which you can use for this. The installer can be found in the \S60Tools\
subdirectory of your SDK root. There are some incompatibilities with
support for the 'text' element on the Symbian OS implementation at this
stage so it is best to convert any text to a path (a series of line
segments and arcs) in Illustrator or Inkscape (see Figure 5)
before converting it to SVG-T. The resulting SVG file will be quite a
bit bigger, depending on how much text it contains but for simple menus
like ours this is not an issue – remember that we don't need to care too
much about JAR size on Symbian OS.
Rendering to screen is done with an instance of the ScalableGraphics class. Within our paint() method, we simply bind our Graphics
context to our scalable context, directly render SVG images to our
canvas and then release our context. By obtaining a handle to the root
element of an SVGImage instance we
can also directly scale, rotate and translate the image itself as well
as change its properties such as its CSS style attribute which defines
fills, strokes, color, and so on.
The following code snippets are taken from the BasicSVGMenu-Screen
class which forms the base class for all the SVG menus in the game. It
is surprising how easy it is to get good effects with very little code.
public abstract class BasicSVGMenuScreen extends BasicScreen {
protected ScalableGraphics sg;
protected static final float BASE_SCALE = 2.0f;
protected static final float ACTIVE_SCALE = 2.5f;
protected static final float BASE_TRANSPARENCY = 0.70f;
protected static final float ACTIVE_TRANSPARENCY = 0.90f;
...
// constructor
...
sg = ScalableGraphics.createInstance();
sg.setRenderingQuality(ScalableGraphics.RENDERING_QUALITY_HIGH);
sg.setTransparency(BASE_TRANSPARENCY);
...
public void paint(Graphics g){
Graphics context = g;
sg.bindTarget(context);
sg.setTransparency(BASE_TRANSPARENCY); // more transparent
...
// now render the active item
sg.setTransparency(ACTIVE_TRANSPARENCY); // more opaque
sg.render(x,activeY,menuOptions[selectedIndex].getImage());
sg.releaseTarget();
...
}
// load SVG content from the JAR file
protected void setImage(String imageName, boolean active){
InputStream svgStream = null;
try{
svgStream = getResourceAsStream(imageName);
image = (SVGImage) (SVGImage.createImage(svgStream, null));
image.setViewportWidth(getWidth());
image.setViewportHeight(getHeight());
setActive(active);
}
catch(Exception e){
e.printStackTrace();
}
finally{
if(svgStream != null){
try{
svgStream.close();
}
catch(IOException ioe){} // ignore
}
}
}
public void setActive(boolean active){
SVGSVGElement svgDoc = (SVGSVGElement)
image.getDocument().getDocumentElement();
if(active)
svgDoc.setCurrentScale(ACTIVE_SCALE); // bigger
else
svgDoc.setCurrentScale(BASE_SCALE); // normal
}