Monday, 12 November 2012

Ignore this entry - I'm talking to myself.

Things to resolve:

  • How to dispense rockets one by one only.
  • Caves. Probably start again with new .pov file.
  • Candle Illumination - cave dark without candle or with candle in inventory.
  • Mirror the junkani pictures for speed.
  • Stationary / moving junk - triggered by rocket.
  • Rocket ignition without tube - explosion.

Sunday, 4 November 2012

Composite Sprites (contd.)

Oh, and I might well have persevered with composite sprites had I not encountered the SAVE / RESTORE implications.

Tuesday, 30 October 2012

Composite Sprites

I was planning a new Class - a composite sprite - which would enable a number of sprites to be considered as a cluster which always moved together, but might have different schedules - e.g. a static main sprite, such as a boat, with animated bow wave, only when moving, and an animated flag which might wave or droop.

My first draft of this class quickly showed me the error of my ways, as the coordination of the multiple sub-sprites would be extremely difficult, due to scaling and flipping, for example. Especially scaling.

Unless.. the multiple subsprites were co-located - i.e. had the same position (which is a CENTRE position), even if this meant they had a lot of transparent content in them.

Once you do that, however, there's no longer any need for a composite class. You just mirror certain characteristics of the main sprite (position, scale etc.) on the subsprites, eliminating the need for a composite class.

Easier still, make the main sprite and the principal subsprite as a single sprite with all the necessary animations - say, the boat and its wake, and use a separate sub sprite for each of the other animations required.

So, long story short, no composite sprite class.

Sunday, 28 October 2012

Another Idle Month

I've done no more on Jade in the last month - Real Life intervened.

I'm now ready to start setting up a more satisfactory demo version with some interesting action, so I'll be working in Povray a lot - a condition that isn't conducive to much reporting from me.

I'd like again to appeal to anyone who wants to collaborate on the Povray side. I'd be delighted to share the load and the credit with you.

Meanwhile, and this is the problem with me, I've seen another possible use of the Jade engine and I keep thinking about that instead of getting on with what I should be doing!

(below) Junk sprite with bluescreening.

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());
 }

}

Friday, 31 August 2012

I'm horrified...

... to see that it's taken a whole month to get a new Mythaxis out. However, here we are today, ready to roll.
  • First up, a new sound class. Sound was giving a lot of trouble.
  • Next, a better long press implementation.
  • I've been giving consideration to the concept of never displaying a background bigger that the original, to avoid jpg artefacts.

Saturday, 28 July 2012

A Hesitation

I must get on with a new edition of Mythaxis, so I'm pausing a while.



I still have to sort out Music/Sound Effects. I'm also uncomfortable with the feel of long press as I've implemented it, so I'll have another go at that when I return.

Monday, 23 July 2012

Music Design Unsuccessful

Well, my sound implementation was not a great success. Some of it worked, some didn't, despite close monitoring on debug that the right things were being called with the right parameters. Specifically, setLoop didn't seem to work, nor did pause nor autopause. Most of the problems were with the background music and ambience sounds. The simple sound effects were fine.

I note a lot of such mysterious problems on Stackoverflow, and I may change policy here.

Also, I think it should have been a class rather than a method, and I'll re-write it like that. I may also have to use the MediaPlayer for longer music and the Soundpool just for immediate sound effects. We'll see. MediaPlayer is facility-rich, but seems rather complex to use.

Wednesday, 18 July 2012

Music Design Revised

There was no need for a separate thread to do the music. Once the sounds are loaded, it's a low usage thing, and I'll call it with the ticks.


Friday, 13 July 2012

Some Progress and a Design for Music and SFX

The Progress:
I realised I hadn't accounted for save and restore of holdable items. If it's already held, then all we need remember is the sprite that's being held. This can be achieved at the top level, where the player's current location is remembered. If, however, the sprite has been picked up and dropped away from its home screen, then the scene and x y have to be remembered. Code to do this is in test.

Design for Music and SFX:
It's currently my plan to use the SoundPool facility. This allows you to store a number of sound files with the application and also allows several channels to play at once. In this case, I would use one channel for the theme music or soundscape (e.g. birdsong, waves, wind etc.) of each scene, and another two for individual sound effects.
I think I can put this in a thread, which first loads the sounds, then is wakened up every 200 ms, say, which checks a variable in JadeView for each of the sound channels. Once it has started the channel, it sets the variable to "done". It can then see the next sound request when "done" is replaced. If the soundscape is unaltered, it just continues to play.

Tuesday, 10 July 2012

More on Cursors and Pick up & Drop

Cursors still have a few glitches. They tend to linger longer than they should. I shall try to make them only appear on screen when a touch is taking place - i.e. between ACTION_DOWN and ACTION_UP.





I implemented  Pick up & Drop using (as recommended by Android Developers) Long Press, rather than Double Click, which I had planned. So far, the Long Press seems too short at 384ms. It will be tuned.

I went for the complicated option - a gettable sprite that is also animated and scaled. That bit works fine

Monday, 2 July 2012

Cursor Implemented

I've now done the cursor. It's a Sprite, but with special characteristics. It only appears while you are touching the screen (ie between a down and an up event). It is invisible unless there is a zone or an active sprite under it. Actually, it was a little inconvenient as an entity. It might have been more "architectural" to have it as a separate class, but it had so much in common with a sprite that it was convenient, despite all the exceptions I had to introduce to its behaviour. There are currently 7 cursor shapes :
  • Default - blank
  • The four direction pointers
  • The crosshairs
  • The Hand - indicating something that can be operated or picked up. 
It's just over three months since I started the project. It feels good progress so far.
Still TODO:
  • Picking up and dropping items
  • Music and sounds. Probably using SoundPool.
  • Putting a Beta version together

Thursday, 28 June 2012

Useful Stage Reached

I've now done all the Menu items as follows:
Quit : finishes the game
Music On/Off : tweaks the music flag, though no music is currently implemented
Inventory: enters the Inventory scene (which cannot be saved)
User Save/Restore : uses 4 possible local files to save/restore any position
 also:
Android Save/Restore :  to deal with situations where Android OS does something outside the game's control - e.g. Change orientation; Pull a different Activity into foreground; Destroy game for lack of resources.
In this case,  the game saves its current position with the Android's own memory of the game, and restores it when the game is restarted.

All tested on both the PC-based emulator and on a Xoom device.

Thursday, 21 June 2012

Save and Restore

I think I've done all the necessary stuff for life-cycle save and restore. Tests so far are encouraging, but there are a number of life-cycle incidents I'm not able readily to reproduce on the emulator. It'll take a test on real hardware to make me comfortable.

Next task is doing the user save / restore options. Then I'll implement the QUIT function.

Thursday, 14 June 2012

The GUI

Now implemented the skeleton of the "Settings" menu. The Pause/Restart works, as do the Inventory entry/exit and Music on/off.



Still to do in Settings - Save, Restore.

Tuesday, 12 June 2012

Scale, Flip and Mirror on Sprites

Wow! That was easy.

The following snatch of code takes image im and transforms it to scaledbitmap.
 scale (x and y values) are based on 100 as full size.
flip x and y values are either 1 or -1.
 1,1 means as original bitmap
-1, 1 means mirrored in the y axis
1, -1 means flipped on the x-axis
-1, -1 means mirrored on both axes - i.e. inverted
 
// scale image before return
Matrix matrix = new Matrix();
// scale and flip
matrix.postScale((float)flip.x*scale.x/100, (float)flip.y*scale.y/100);
// create a new bitmap from the original using the matrix to transform the result
Bitmap scaledbitmap = Bitmap.createBitmap(im , 0, 0,
           im.getWidth(), im.getHeight(), matrix, true);
return scaledbitmap;  

Monday, 11 June 2012

The GUI

Having further considered the matter of the GUI, I've decided to base it all on sprites, rather than a special screen. It is probably neater, and certainly more efficient. So another carefully designed backdrop hits the wastebin.



So, what's still to do?
  • Scale, flip and mirror on sprites;
  • Cursor;
  • More detailed touch control;
  • Pick-up and drop;
  • The save / restore for Android OS operations. The game has to be revivable if it is re-oriented, de-focussed etc. by the user.  It's an Android preoccupation;
  • Similarly, and, I hope, using the same compression technology, the user save / restore;
  • Music,which I hope to implement by using the built-in MP3 player;
  • The Jade game itself, for which I have a lot of scenery and virtually no plot;
  • The various delivery activities to get it to Beta Test and Android Market.
In short, still a lot to do, but the worst is over, I think. Actually getting a limp-mode game on screen has been an achievement. This time, as opposed to the original applet-based software, I've compartmentalised the code so that it will be easy to expand the game, or even base another game on the same engine.

Saturday, 9 June 2012

Progress to Date

Well, where am I?
  •  Scaling for whatever size of playfield is available works perfectly.
  • After pestering with invalidateRect() for weeks to deal with the "dirty" rectangles on the screen, and finding it doesn't work the way I expected, I now just repaint the whole playfield (the image part of the View) every tick. In tests, both on emulator and Xoom, it's fast enough, even with a couple of active sprites. My ticks are approximately 18/second;
  • I can detect a touch in various aspects;
  • I can click a sprite and change screen, I've dealt with the problem of re-entrant popscreens;
  • I'm wrestling with the GUI concept. Basically, there's an icon on every screen, which, if clicked, takes you to an options screen where you can choose to select the inventory screen, quit, save, restore or switch the music on and off (the music is not yet incorporated);
  • I can display moving sprites (the gulls in the illustration below) with multiple (24, in this case) images.

Tuesday, 29 May 2012

Scaling and Displacement

Scaling and displacement are tricky areas. The old browser-based Java engine could use absolute values for background and sprite size. Everything was based on a 640 x 480 playfield starting at position 0,0, with appropriately sized sprites, and sprite / zone positions locked to the 640 x 480 setup.

With the multi-platform Android version, any given game background size need not be based on 640 x 480, but may take any rectangular size. In addition, the eventual playfields can be many sizes and usually will not not start at position 0, 0. Sprites will be scaled and their position on the screen scaled appropriately. Touch positions will be similarly displaced.


All collisions, zone effects, sprite movements and other screen events will be computed in the program on a game model which is at the basic background dimensions.

So, for drawing and detection of user touch, all positions, including background, sprite positions, dirty rectangle positions and touch positions, we have to make a transformation.

Rule 1. All images are pre-transformed to the correct scale. (This to save processing time during animation)

Rule 2. All position calculations are initially made on the base dimensions of the background playfield and proportionate sprite positions, and are only resized and displaced at draw time.

Rule 3  All touch co-ordinates are similarly transformed to base positions (by rescaling in reverse and subtracting the displacement) before being applied to the base model.

For example, the background. In any given scene, the background image does not vary. It is efficient to scale the image at the outset of the scene. Once scaled, and its displacement from position 0,0 determined, it can be drawn, in whole or in part, without having to scale the bitmap every time.


Similarly, sprite images will be resized, but all dimensions and positions will be reported to the base game model on the (in this case) 640x480 basis.

The actual placement and size of the sprite will be deternined in the draw stage.

There is a slight complication. Sprites can be scaled, flipped and mirrored , and this is done prior to the general scaling transformation.

It's not really so complex. Is it?


Sunday, 20 May 2012

A Little More Progress

I can't see very much more that needs to be saved - the current scene is the major factor.

Just mopping up some of the display manager functions, and checking out thread behaviour for the timer.

Saturday, 19 May 2012

More Struggle

Google is keen to get to know me and help out. Perhaps they could improve their user interface on this Xoom.

Anyway, I've made a little progress with the game but I don't want to go too far without testing my changes on the emulator which I can't do right now.

re. Bundle management

For simplicity, it's best if the game restore string for orientation is the same structure as the optional game save file that's stored locally.

Therefore, I am not going to make use of the Key:value pair structure in the Bundle except to label the whole save string.

In addition, since the game may be updated/altered, the save string must always be forwards/backwards compatible.

Must watch out for possible duplicate keywords e.g. spritename/scenename both called "pause"; imagenames will often be the same as sprite/scene names. Partially identical names in the savestring could also be problem, as we search it for spritenames among other things. Perhaps all keywords in the string should be uppercase and preceded by a type letter. e.g. S for sprite, R for scene(room), G for general state markers.

re. Scene alteration

Mostly, changed or additional zones with new destinations. Occasionally, delete zone. Sometimes, different image(s) or music.

So, for any alteration - like an open door with a new exit, we just have a key in the save string that causes us, on restore, to call a method in JadeData which makes the alteration.

Thursday, 3 May 2012

Unecessary Struggle

I found that all the menu items on this blog had been rendered into Greek, and simultaneously my bluetooth keyboard malfunctioned, so wasted some time, but the good news is that I made useful inroads into Save and Restore both for users and for Androidy purposes.

Wednesday, 25 April 2012

A Few More Topics


Coding:
  • Background transitions: It should be easy to slide one background over another, one doDraw() at a time.
  • Sprite scale, flip and mirror: Must do some experiments with this, but it looks as if a single bitmapFactory call with a suitable matrix could do the whole modification on a Sprite  image.
  • onPause and onResume: A save string could be attached to the Bundle, just to restore the current state. The deliberate saves (by User) would use the same save string format, but stored in a local file.
Game:
  • Lighting the Rocket: A candle in a cave.

Monday, 23 April 2012

More Design

Showing the limp mode version to a friend yesterday alerted me to the fact that I ought to centralise the image in the view. No problem. Just a displacement in the x or y axis of the destination.

There will also be a need for cutting off a portion of any sprite that strays across the background boundary.

Both of these can be handled in the doDraw portion of the code.


--------------------------

Some scenes will be labelled "no popscene", eg Inventory, save, limbo.

---------------------------

Design suggestion: So that we don't need to use text to label the various saves, icons of jade Chinese horoscope characters  (eg dragon, tiger, ox, monkey) will be used.

Sunday, 22 April 2012

Quick Design Points

Decision 1:

Sprite images will be collected full size. On scale change in the sprite, they will be re-scaled in the sprite's image list.

Decision 2:

View scaling will be done at doDraw time. After all, it's only one rescale per sprite.

Thursday, 19 April 2012

Some Decisons

Wrestling with the Rect (x1, y1, x2, y2) versus Rectangle (x1, y1, width, height), I eventually decided there'd be less code wasted by going over to the Rect as standard, particularly appropriate for Zones, and making the position (x1, y1) and size (width, height) of the Sprite as Points.

Another decision is that Sprite position will be the position of the Sprite centre.

When to scale the Sprite image(s) for screen size? This should be done at scene change, not "just in time".

There is also a scale factor for Sprites which is independent of screen size, and can happen during a scene. This should be done "just in time".

The new Pause facility will act like an Inventory scene, with all the features previously supplied by Javascript buttons - music, save/restore, Inventory.

Wednesday, 18 April 2012

Optimism

Really making progress with Jade now. After a successful limp mode with background and moving partially transparent sprite, which readjusted and restarted on orientation, I was confident enough to bring in a lot of the Bridge code. A few matters giving me grief right now:
  • The difference between Android Java's Rect class and the Java Rectangle class;
  • The fact that the cursor is going to have to be a sprite;
  • How to implement flip and mirror- will Android Java accept a back-to-front or upside-down Rect as a parameter in canvas.drawBitmap? To be tested.

Thursday, 12 April 2012

Breakthrough

After remaining baffled for a while that I couldn't get my Android Jade app to display anything, I discovered:
  • by a roundabout route, without ever finding out what had been wrong, how to display and change a background of a different size to the original image;
  • how to move a sprite over it (with transparency);
  • how to detect touches and flings on the touch screen;
  • how to detect and compensate for changes in orientation;
  • how to superimpose a diagnostic display.
I now, therefore, have a framework on which to build the various functions from Bridge. The apk (application package) even works on the Xoom.

Wednesday, 4 April 2012

Still Struggling

I am now seriously planning Jade, as the Android version of Bridge will be called, and here are a few of the considerations I've been working with.

Contrary to my previous assumption, I think quite a lot of Bridge code will transfer across to Jade.

It appears that most graphics Android apps consist of a kind of basic Activity, and a separate Class, a subclass of View, for the work and the display. I'm currently wondering whether I should do the same, and include all the Sprite / Scene / Data Management etc. classes within the View subclass. I'm revising scoping rules etc.

I was originally planning to put the initialise data in an xml file, but I now feel that my BridgeData class is actually more efficient, involving less iteration and parsing at startup and restart.

I found the Vector class very handy in Bridge, but I guess it's time to use these smart list classes.

I shall retain the off-screen buffer model from Bridge, do all the original artwork at full size, do vector movement etc. off-screen, then blit it into the View canvas when it's done. So at each screen size and each orientation, I'll calculate suitable View dimensions. Then I'll leave it to Java to scale the OSB to the actual screen. This is instead of doing the screen construction inside a Thread.

As for the GUI, there will be a "Jade" button (actually a Sprite) on every screen. Touching it will pause the game and allow the user to make various choices (it will actually be a virtual scene, like Inventory and Limbo). Choices include music, save, restore.

There will be differences in the cursor and carrying GUI, due to touch rather than mouse screens.

I think I can retain the cookie format of save data.

Tuesday, 6 March 2012

Getting started with Android

It's hard to find a really appropriate example on which to base Jade, but I today managed to write a tiny program that draws one of my backgrounds.

Thursday, 1 March 2012

Is it REALLY eight months?

It's not that I haven't done anything since last July. For a month or so, I built scenes for the Junk voyage. But then... I discovered Android, in the form of a Motorola Xoom, and I'm looking seriously at mounting the adventure on an Android base.

A development system and three books downstream, I still haven't written a line of code. Actually, although Android coding is mostly in Java, there's very little of the existing code that I'll be able to use.