Tuesday, 18 September 2012

Example Game

For the morbidly interested, I have uploaded the current Alpha version of Jade as Jade.apk, available from http://amazonsystems.phpzilla.net.

This is a "game" with just six scenes, and very little to do except pick up a candle and drop it, but the music and sound effects are there, and the options.

Try it if you dare. Actually it is very benign as alpha versions go. Other than its own running space and private data, it only uses the media player, and surrenders or pauses that whenever it loses focus.

It works for me on my Android Xoom, but I'm interested in the behaviour of other platforms.

From left to right the options are Quit, Restore, Save, Music On/Off, Inventory, Pause.

Double tap to pick up and drop the candle.


Sparing no expense, the source is also available at the same address.
http://amazonsystems.phpzilla.net

Monday, 17 September 2012

New Touch protocol in place

For once, it took more time to flowchart this than it did to implement it, but I can now reliably detect click, longpress and double click. I've changed to double click for for pick up and drop. It seems to cause less finger trouble.

The next activity is definitely the prototype game. I've got all the elements I need in the engine now, I think. I just have to use these capabilities to write an acceptable mini game.

I've been a little disappointed by the appearance of some of my backgrounds on the Xoom. I'll experiment with less compressed jpg files.

Friday, 7 September 2012

Jade Sound

I have now implemented a JadeSound class that seems to be working very well. There WAS a hitch in the use of MediaPlayer. I kind of assumed that when you called MediaPlayer.release(), the instance of Media Player would be set to null. Not so. Although a failed create set mp to null, if I reset and released the mp, and then didn't create a new one, the next time I entered the method, mp would still have the value of the released mp and a new reset() caused a fault.
package uk.co.amazonsystems.jade;
import java.util.ArrayList;
import android.content.Context;
import android.media.MediaPlayer;
import android.media.SoundPool;
import android.media.AudioManager;

public class JadeSound 
{
 // This class handles all sounds. There are two sound sources. Use soundpool for short
 // transient sounds. A single soundpool does all the sounds.
 // For background music and ambient sound, Use MediaPlayer. Each sound is a new mediaplayer.
 // Only one sound can be running at a time. All such sounds are looped.
 public static final int OK_SOUND = 0;
 public static final int BAD_SOUND = 1;
 public static final int NOSUCH_SOUND = 2;
 public static final int BAD_PLAY = 3;
 
 private SoundPool soundpool = null;
 private static int CHANNS = 4;  // Sound Channels
 private String soundName; // current ambient sound name
 private int [] SFX = new int[CHANNS];  // Stream IDs
 private int priority = 1;
 private int soundid[];  // Sound IDs
 private Context thiscontext;
 private MediaPlayer mp;
 private ArrayList  soundfiles;
 private int buggins;
 private boolean soundstate = true;
 
 // Initialise
 public JadeSound(Context JadeContext, ArrayList  SFXFiles)
 {
  thiscontext = JadeContext;
  soundfiles = SFXFiles;
  if (soundpool != null) soundpool.release();
  soundpool = new SoundPool(4, AudioManager.STREAM_MUSIC, 0);
  int temp = soundfiles.size();
  soundid = new int [temp]; 
  for (int ii = 0; ii < temp; ii++)
  {
   int resid = getRaw(thiscontext, soundfiles.get(ii)); 
   if (resid == 0) 
    {
    soundid[ii] = 0;
    }
   else
    {
    soundid[ii] = soundpool.load(thiscontext, resid, priority);
    }
  }
  for (int ii = 0; ii < CHANNS; ii++)
  {
   SFX[ii] = -1;
  }
  buggins = 0;
 }
 public int playSFX(String name)
 {
  return playSFX(name, 0);
 }
 public int playSFX(String name, int Loop)
 {
  int index = soundfiles.indexOf(name);
  if (index == -1)
  {
   return NOSUCH_SOUND;
  }
  else
  {
   soundpool.stop(SFX[buggins]);
   if (soundid[index] != 0)
   {
    int reply = soundpool.play(soundid[index], 1, 1, priority, Loop, 1);
    if (reply == 0) return BAD_PLAY;
    SFX[buggins] = reply; 
    buggins++;
    if (buggins >= CHANNS)
    {
     buggins = 0;
    }
   }
   else return BAD_SOUND;
  }
  return OK_SOUND;
 }
 public int playAmbient(String name)
 {
  // if the same sound name, just exit immediately
  // we use the same mp we used last time, and keep playing the same song
  if (soundName != null)
  {
   if (soundName.equals(name))
   {
    return OK_SOUND;
   }
  }
  // if the media player still in use, reset it, release it and null it
        if (mp != null) 
        {
            mp.reset();
            mp.release();
            mp = null;
        }
        // remember the new sound name for next call
        soundName = name;
        // find the sound file in raw
        int resid = getRaw(thiscontext, name);
        // if it's not there, return
        if (resid == 0) 
         {
         return BAD_SOUND;
         }
        // Finally, create a media player
        mp = MediaPlayer.create(thiscontext, resid);
        // if it doesn't work, return
        if (mp == null) return BAD_SOUND;
        // Ambient always loops
        mp.setLooping(true);
        // start the playback
        mp.start();
        // if the song flag is false, pause it
        // we may have to start it later
        if (!getSoundState())
         pauseAmbient();
        return OK_SOUND;
 }
 public void pauseAmbient()
 {
  if (mp != null)
  {
   mp.pause();
  }
 }
 public void resumeAmbient()
 {
  if (getSoundState())
  {
   if (mp != null)
   {
    mp.start();
   }
  }
 }
 public void pauseAll()
 {
  if (soundpool != null) soundpool.autoPause();
  pauseAmbient();
 }
 public void resumeAll()
 {
  if (soundpool != null) soundpool.autoResume();
  resumeAmbient();
 }
 public void closeSound()
 {
  if (soundpool != null)
  {
   soundpool.release();
   soundpool = null;
  }
        if (mp != null) 
        {
            mp.reset();
            mp.release();
            mp = null;
        }
 }
 public void setSoundState(boolean state)
 {
  soundstate = state;
 }
 public boolean getSoundState()
 {
  return soundstate;
 }
 // Helper method that translates a filename in res/raw to a resource id
 public static int getRaw(Context context, String name)
 {

     return context.getResources().getIdentifier(name,
             "raw", context.getPackageName());
 }

}