Archive for May, 2008

Shredz64 – PSX64 PCB checks out!

First off, I want to apologize for not updating this blog for a few weeks, I’ve started a new job and things have been hectic getting up to speed on things. But life is starting to calm down, and more importantly, I’ve received the first PSX64 PCB, soldered on the chips, uploaded the firmware, and it works!

There are a few changes to make to the board, mainly due to the pinout of available parts (I have a large stock of voltage regulators that are a different pinout than how the board is configured). I’m also going to use rectangular headers with stress relief connectors for the PSX and DB9 cables so they can take more tugging (as opposed to soldering them directly to the board). However, these changes aren’t major and don’t require another single-board run. The next run will be a batch of 10-30 boards, which will then be available for purchase! I suspect it will be about 4 weeks or so.

Thanks to everyone who has signed up on the notification list, it won’t be too much longer now!

Creating a Blackberry Game – Part 6

Looking for part 5?

Let’s Hear It!

Our game is almost done – the only part left is the sound (and vibration) processing. This is a fairly simple class as well – again, most of the low level processing is done already by the Blackberry. However – you may want to play with the methods in this class. We have functionality for playing a midi file – and we could also have functionality for playing a wav file as well, but I haven’t included it. The reason – on my 8830, the sound engine (at least the way I was using it), could only seem to mix one sound at a time, and completely stopped the other sound if a second one was played. There may be specific methods to mix two sounds together that I did not research, or otherwise it’s a limitation/bug of the 8830. So, if music was playing, and I then played a wav sound effect, the music would stop.

I overcame this by using the Alert.startAudio method. This takes frequency/duration pairs from an array and plays simple sounds with it. When this method is used, it does indeed mix the audio with the midi playing in the background, so I stuck with it. It makes for less sophisticated sound effects, but it helps us for now.

Additionally, I wanted to include vibration in the game, so I included a small method for triggering this off. Vibration is used for when spaceships explode, either yours or the enemy’s.

SND.java

package com.synthdreams.GalacticBlast;

import net.rim.device.api.ui.component.Dialog;
import java.io.InputStream;
import java.lang.Class;
import javax.microedition.media.Manager;
import javax.microedition.media.Player;
import net.rim.device.api.system.Alert;

// Sound engine
class SND {

    Player _musicPlayer; // Java media player

    SND() { }

    // Play a midi file for background music
    void playMusic(String passMusic)
    {
        try
        {
            // Set InputStream to a midi file included as resource, as specified by
            // passMusic
            InputStream in = getClass().getResourceAsStream("/" + passMusic);            

            // Create a media player with mime type of audio/midi using our inputstream
            _musicPlayer = javax.microedition.media.Manager.createPlayer(in, "audio/midi");

            // Ready the data and start playing it.  To loop indefinitely, we set loopcount
            // to -1.
            _musicPlayer.realize();
            _musicPlayer.prefetch();
            _musicPlayer.setLoopCount(-1);
            _musicPlayer.start();

        }
        catch (Exception e)
        {
            Dialog.alert("Error playing music");
        }
    }

    // Stop playing music
    void stopMusic()
    {
        try
        {
            // Tell player to stop playing
            _musicPlayer.stop();

        }
        catch (Exception e)
        {
            Dialog.alert("Error stopping music");
        }

        // Then release the data and close out the player
        _musicPlayer.deallocate();
        _musicPlayer.close();
    }

    // The Playsound method plays a simple combinations of tones to simulate a firing
    // noise.  This was necessary, as due to a bug or limitation of the BlackBerry 8830
    // (the phone I do my testing on), playing a WAV file stopped the midi player and
    // any other sound effects.  Player doesn't appear to mix properly (if at all).  However,
    // a midi file can be played while using the Alert objects startAudio method which
    // can play a sequence of tones, so this is what we've done for now.
    void playSound()
    {
        // A sequence of frequencies and durations (eg 1400hz for 15ms, 1350hz for 15ms, etc)
        short[] fire = {1400, 15, 1350, 15, 1320, 20, 1300, 20, 1250, 25, 1200, 35};

        try
        {
            Alert.startAudio(fire, 100);

        }
        catch (Exception e)
        {
            Dialog.alert("Error playing sound effect.");
        }   

    }

    // Activates the phone's vibration functionality for a specific number of ms
    void vibrate(int passMilli)
    {
        Alert.startVibrate(passMilli);
    }
}

Nothing too complex at all going on here. You may wonder what all the player initialization methods are doing – they deal mainly with making sure the sound data is available and buffered before playing. Just call them in order and you’re good to go.

That’s All Folks!

At this point, you have all the basic functionality necessary for making whatever kind of Blackberry game you’d like. Your logic may have to be much more complex, and you may have to include additional classes to accommodate everything, but the basic principles stay the same. Also – all the classes used here are unsigned and available without a license from Research in Motion, which means you can make, play, and distribute these games for free.

Feel free to comment if you have any questions, or even suggestions! These were my own experiences and suggestions with creating a Blackberry game, but you might have your own! Thanks for checking out this tutorial, good luck – and most importantly, have fun!

Creating a Blackberry Game – Part 5

Looking for part 4?

Let’s See Our Game

Now that our logic is done, we have a few holes in the program where calls are made to graphics and sound processing. By isolating (for the most part, as much as our program allows) graphics and sound processing from the rest of the program, it offers for a little more portability. In general, this isn’t too big of an issue in Java since Java itself is cross platform, but even so we run into areas where certain libraries might be available for one setup and not for others (for example, our program in which we use Blackberry specific libraries, wouldn’t be valid in a non-Blackberry environment). For systems where processor speed and memory are in ample supply (which are most systems these days – as compared to an MCU or retro environment when you’re dealing with 64K of RAM), it’s a good practice to isolate things out like this – it makes porting a lot easier (Think Windows -> Mac & Linux, or PS3 -> XBox & Wii, that kind of thing).

Anyway, game programming philosophy aside, let’s dig into the graphics functionality of GFX. GFX is actually fairly simple since most of the low level graphic processing is done by the Blackberry itself. We don’t have a lot of ways to get close to the hardware, which is a good and bad thing – but for us here, the built in graphics functionality of the Blackberry is good enough and saves us work.

Besides some initialization routines, the GFX class consists mainly of the process method, which takes a handle to the graphics of the current screen from Gameplay. This handle indirectly allows us to write to the screen memory through simple graphic methods like drawBitmap and drawText. The overall algorithm is to first write the background (aligned properly, it moves every update) to the screen, then draw all objects, then the score and our hero’s health meter. There is some math for doing coordinate and dimension calculations, but everything is fairly straightforward.

GFX.java

package com.synthdreams.GalacticBlast;

import net.rim.device.api.system.Bitmap;
import net.rim.device.api.system.Display;
import net.rim.device.api.ui.*;
import java.util.Vector;

// The GFX class is our game's graphics engine with all the drawing routines
class GFX
{
    int _backPos, _backSpeed; // Background position and speed
    Bitmap _backgroundBM; // The bitmap for the background
    Bitmap _healthBM; // The bitmap for the health meter
    Font _gameFont; // The font used for drawing score and lives

    GFX()
    {
        // First see if we can get the BBCondensed font at size 16 for our game font.
        // If not, we just go with the default, but all Blackberrys should have this font.
        try
        {
            _gameFont = FontFamily.forName("BBCondensed").getFont(FontFamily.SCALABLE_FONT,16);
        }
        catch (Exception e) { }

        // Load the health bitmap from health.png
        _healthBM = Bitmap.getBitmapResource("health.png");
    }

    // Get current background position
    public int getBackPos() { return _backPos; }

    // Method that sets the bitmap for the background and sets the scroll speed
    public void initBackground(String passBackground, int passSpeed)
    {
       _backgroundBM = Bitmap.getBitmapResource(passBackground);
       _backPos = 0;
       _backSpeed = passSpeed;
    }

    // Primary function for the graphics engine, draws all the objects, text, health, etc
    public void process(Graphics passGraphics, Vector passObjects, int passScore, int passLives)
    {
        // Draw our background at the correct position
        passGraphics.drawBitmap(0,0, Graphics.getScreenWidth(), Graphics.getScreenHeight(), _backgroundBM, 0, _backPos);

        // Move our background.
        _backPos -= _backSpeed;

        //If we're at the beginning of our bitmap, reset to end
        if (_backPos < 0)
        {
           _backPos = _backgroundBM.getHeight() - _backSpeed;
        }

        // If we're within screenheight of the end of our bitmap, we need to draw another copy to fill in the blank
        if (_backgroundBM.getHeight() - _backPos < Graphics.getScreenHeight()+_backSpeed)
        {
           passGraphics.drawBitmap(0, _backgroundBM.getHeight() - _backPos - _backSpeed, Graphics.getScreenWidth(), Graphics.getScreenHeight(), _backgroundBM, 0, 0);
        }

        // Now draw each of our objects to the screen
        for (int lcv = 0 ; lcv < passObjects.size() ; lcv++)
        {
            passGraphics.drawBitmap(((OBJ) passObjects.elementAt(lcv)).getX(),
                                    ((OBJ) passObjects.elementAt(lcv)).getY(),
                                    ((OBJ) passObjects.elementAt(lcv)).getBitmap().getWidth(),
                                    ((OBJ) passObjects.elementAt(lcv)).getBitmap().getHeight(),
                                    ((OBJ) passObjects.elementAt(lcv)).getBitmap(),
                                    0, 0);
        }

        // Draw score
        String zeroPad;

        // We want to pad the score with 0s
        zeroPad = "";
        if (passScore < 10000)
          zeroPad += "0";
        if (passScore < 1000)
          zeroPad += "0";
        if (passScore < 100)
          zeroPad += "0";
        if (passScore < 10)
          zeroPad += "0";

        // Draw score and lives in white with the game font
        passGraphics.setColor(0xFFFFFF);
        passGraphics.setFont(_gameFont);
        passGraphics.drawText("Score: " + zeroPad + passScore, Graphics.getScreenWidth()-93, 2);
        passGraphics.drawText("Lives: " + passLives, 7, Graphics.getScreenHeight() - 20);

        //Draw health, with width dependent on hero's life
        if (((OBJ) passObjects.elementAt(0)).getLife() > 0)
            passGraphics.drawBitmap(7, 7, _healthBM.getWidth() * ((OBJ) passObjects.elementAt(0)).getLife() / 5, _healthBM.getHeight(), _healthBM, 0, 0);
    }

}

As can be seen, nothing major happening here. The only weird part is how we draw the background. We basically have a long bitmap that scrolls in the background. When it hits the end of the bitmap, it needs to repeat it. It does this by continuing to scroll the original bitmap, and then filling in the whitespace with a second bitmap, placed flush to the first bitmap. Since we’re dealing with random stars, its hard to make out any seams on the screen. If you were doing something with a more ordered/textured background, you’d need to make sure the top of the bitmap flowed well with the bottom of the bitmap.

Graphics are done – you should be able to disable your audio calls and get a playable game at this point! It should look something like this (only with your graphics, of course)

Onto our final part, audio…

Next Page »