Applet templates

Please paste some solid applet templates in here.

one nice thing I just realised is that since 1.5 is the minimum, we can start using System.nanoTime() instead of System.currentTimeMillis(), this should make the game loop timing much better.

1.5 was also the default JRE last year :slight_smile:

The other thread with applet templates is a bit of a mess, and there doesn’t seem to be any consensus on which ones work. Does anybody have a template that seems to work well? I could always use what I’ve used before, but since one person couldn’t start two of my applets last year, well… I’d very much appreciate a nice, reasonably stable template.

I hacked something together from L4KD :slight_smile: Just stripped it down.

import java.applet.Applet;
import java.awt.AWTEvent;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.image.BufferedImage;

public class G extends Applet implements Runnable {

	public void start() {
		enableEvents(AWTEvent.KEY_EVENT_MASK | AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK);
		new Thread(this).start();
	}

	public void run() {
		setSize(800, 600); // For AppletViewer, remove later.

		// Set up the graphics stuff, double-buffering.
		BufferedImage screen = new BufferedImage(800, 600, BufferedImage.TYPE_INT_RGB);
		Graphics g = screen.getGraphics();
		Graphics appletGraphics = getGraphics();

		// Some variables to use for the fps.
		int tick = 0, fps = 0, acc = 0;
		long lastTime = System.nanoTime();

		// Game loop.
		while (true) {
			long now = System.nanoTime();
			acc += now - lastTime;
			tick++;
			if (acc >= 1000000000L) {
				acc -= 1000000000L;
				fps = tick;
				tick = 0;
			}

			// Update
			// TODO add some update logic here.

			lastTime = now;

			// Render
			g.setColor(Color.black);
			g.fillRect(0, 0, 800, 600);
			g.setColor(Color.white);
			g.drawString("FPS " + String.valueOf(fps), 20, 30);

			// Draw the entire results on the screen.
			appletGraphics.drawImage(screen, 0, 0, null);

			do {
				Thread.yield();
			} while (System.nanoTime() - lastTime < 0);

			if (!isActive()) {
				return;
			}
		}
	}

	public void processEvent(AWTEvent e) {
		// Process the key event.
	}
}

An alternate version of Appel’s code reduces the size a bit. This trick was taken from the Preliminary thread as well.


import java.applet.Applet;
import java.awt.Event;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.image.BufferedImage;

public class G extends Applet implements Runnable {

	public void start() {
		new Thread(this).start();
	}

	public void run() {
		setSize(800, 600); // For AppletViewer, remove later.

		// Set up the graphics stuff, double-buffering.
		BufferedImage screen = new BufferedImage(800, 600, BufferedImage.TYPE_INT_RGB);
		Graphics g = screen.getGraphics();
		Graphics appletGraphics = getGraphics();

		// Some variables to use for the fps.
		int tick = 0, fps = 0, acc = 0;
		long lastTime = System.nanoTime();

		// Game loop.
		while (true) {
			long now = System.nanoTime();
			acc += now - lastTime;
			tick++;
			if (acc >= 1000000000L) {
				acc -= 1000000000L;
				fps = tick;
				tick = 0;
			}

			// Update
			// TODO add some update logic here.

			lastTime = now;

			// Render
			g.setColor(Color.black);
			g.fillRect(0, 0, 800, 600);
			g.setColor(Color.white);
			g.drawString("FPS " + String.valueOf(fps), 20, 30);

			// Draw the entire results on the screen.
			appletGraphics.drawImage(screen, 0, 0, null);

			do {
				Thread.yield();
			} while (System.nanoTime() - lastTime < 0);

			if (!isActive()) {
				return;
			}
		}
	}

        public boolean handleEvent(Event e) {
            switch (e.id) {
                  case Event.KEY_PRESS:
                  case Event.KEY_ACTION:
                      // key pressed
                      break;
                  case Event.KEY_RELEASE:
                      // key released
                      break;
                  case Event.MOUSE_DOWN:
                      // mouse button pressed
                      break;
                  case Event.MOUSE_UP:
                      // mouse button released
                      break;
                  case Event.MOUSE_MOVE:
                      break;
                  case Event.MOUSE_DRAG:
                      break;
             }
             return false;
	}
}

The one above looks like a good starting point.

The only other significant improvement worth suggesting would be to use an [Volatile]Image obtained from Component#create[Volatile]Image(width,height), rather than the BufferedImage - but obviously this is not useful for those doing any per-pixel rendering.

this is a true mess, it runs an anonymous Thread “open in the sky” where it couldn’t be checked for a termination, an aborting, … in shorter words with no control over it.
A correct impl. would handle native lnstallation and loading, with control over start-stop behaviours, window focus and a caching system.
8)
an applet is implemented through init-destroy and start-stop methods. So this template should define ignition AND shutdown otherwise anyone terminating the applet into a middle-aged browser would see its system crushing down out of memory and busy…
:slight_smile:
I like the one from LWJGL or appletLoader from JOGL which truely provide a powerful interface to such complex applet management with high-valueable capabilities.

It has an isActive() check.

Groboclown: Not including the enableEvents may not work on non Sun JVMs.

Thanks Appel for the template. Spot on in my book. 8)

Sorry, but I’ve never seen this elsewhere and I really find this code unefficient… And writing a book over such mind-less lines, is more boring than ever. :frowning:

That would be true if he was using the Java1.1+ event model, however that template is using the Java 1.0 event model callback method. (Explained & discussed in further detail here)

Woops, I thought “Spot on in my book” was a common saying. Clearly not. ::slight_smile: I just meant that it makes sense in my mind.

While the template may not be Java best practice. I can’t think of a better way of doing it for the amount of Java bytecode that it produces.

Ah, yes! Sorry, I missed the fact that Groboclown swapped it from processEvent to handleEvent.

If anyone is wanting to add music to their applet, here’s the template that I use for that. I’ve tested this on Linux, Mac, and Windows; it seems to correctly start and stop the audio, or ignore audio playback if no audio can be detected. Of course, the logic for generating sound is an exercise left to the user.


import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.FloatControl;
import javax.sound.sampled.SourceDataLine;

public class A extends Applet implements Runnable {
    private static final float SAMPLE_RATE = 44100;
    private static final int BITS_PER_SAMPLE = 8; // or 16
    private static final int OUTPUT_CHANNELS = 1;
    private static final boolean SIGNED = true;

    // adjust as necessary; this is an example of 1 second of audio
    private static final int AUDIO_BUFFER_LENGTH =
        (int) (SAMPLE_RATE * BITS_PER_SAMPLE / 8);

    private int t = 0;
    private SourceDataLine o;

    public void start() {
        try {
            this.o = AudioSystem.getSourceDataLine(
                    new AudioFormat(SAMPLE_RATE, BITS_PER_SAMPLE, OUTPUT_CHANNELS, SIGNED, true));
            this.o.open(
                    new AudioFormat(SAMPLE_RATE, BITS_PER_SAMPLE, OUTPUT_CHANNELS, SIGNED, true),
                    AUDIO_BUFFER_LENGTH * 2);
            
            // Set the volume.  Necessary on some platforms
            // Note that you may want to save this volume control
            // in a class variable if you want to allow the user to
            // control the volume
            FloatControl volume = (FloatControl) this.o.getControl(FloatControl.Type.MASTER_GAIN);
            volume.setValue(volume.getMaximum());
            this.o.start();
        } catch (Exception e) {
            // Exception - sound could not be initialized
            this.t = 1;
        }
        new Thread(this).start();
    }
    
    public void run() {
        if (this.t == 0) {
            // we're in the music playing thread
            this.t = 1;
            // start the game thread
            new Thread(this).start();
            
            while (this.o != null) {
                byte[] audio = new byte[AUDIO_BUFFER_LENGTH];
                // fill the buffer with audio samples
                this.o.write(audio, 0, AUDIO_BUFFER_LENGTH);
            }
            return;
        }
        // video thread start

        // cut-n-paste logic from above, with the exception of this shut-down block,
        // which now becomes:
        if (!isActive()) {
            this.o.stop();
            this.o.close();
            this.o = null;
            return;
        }
    }
}

Alternatively, you can eliminate the launching of the music thread and use the SourceDataLine.write as the timer for waiting for the next frame (I did this in a not-so-successful DDR-style game), but then you can run into complicated issues around buffer underruns, which are difficult to deal with in a platform independent way.

One of the rules is “The Applet may not cause “leaks” in the browser, resulting in browser instability.”. I remember there was some brief discussion on this a few weeks ago, however I can’t see any code in any of the templates that handles this.

How is this generally handled? Main reason I ask, is in my game after a few browser refreshes the game becomes choppy/slow. I guess the applet is not cleaning up my many BufferedImage objects. Last years entry suffered from a similar problem.

IIRC the main point here was that your main loop should exit and your thread finish when the user moves away from the page.

What I meant is most of the templates I have seen have a start()/init() to start the main Thread and a run() method. Do we not need a destoy() method too (to stop thread) or is this handled automatically?

If the thread terminates when isActive() goes false, then everything closes down cleanly when the player leaves the page, so we can omit destroy().

Ok thanks Alan… I get it now… The template I was using from last year didnt have the isActive check… Have added it and problem solved!

I’ve had some problems with Thread.yield-loop on some systems. It makes the CPU go to 100% which makes the applets input really slow. It works much better for me with an ordinary Thread.sleep(10) construct.
The systems I’ve had problems with were all Ubuntu 9.10 x64 with Java 1.6.0u16.

(EDIT) Here’s my edit to the template:

import java.applet.Applet;
import java.awt.Event;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.image.BufferedImage;

public class G extends Applet implements Runnable {

	public void start() {
		new Thread(this).start();
	}

	public void run() {
		setSize(800, 600); // For AppletViewer, remove later.

		// Set up the graphics stuff, double-buffering.
		BufferedImage screen = new BufferedImage(800, 600, BufferedImage.TYPE_INT_RGB);
		Graphics g = screen.getGraphics();
		Graphics appletGraphics = getGraphics();

		// Some variables to use for the fps.
		int tick = 0, fps = 0, acc = 0;
		long lastTime = System.nanoTime();

		// Game loop.
		while (true) {
			long now = System.nanoTime();
			acc += now - lastTime;
			tick++;
			if (acc >= 1000000000L) {
				acc -= 1000000000L;
				fps = tick;
				tick = 0;
			}

			// Update
			// TODO add some update logic here.

			lastTime = now;

			// Render
			g.setColor(Color.black);
			g.fillRect(0, 0, 800, 600);
			g.setColor(Color.white);
			g.drawString("FPS " + String.valueOf(fps), 20, 30);

			// Draw the entire results on the screen.
			appletGraphics.drawImage(screen, 0, 0, null);

			try {
				Thread.sleep(10);
			} catch (Exception e) { /* best practice */ };

			if (!isActive()) {
				return;
			}
		}
	}

        public boolean handleEvent(Event e) {
            switch (e.id) {
                  case Event.KEY_PRESS:
                  case Event.KEY_ACTION:
                      // key pressed
                      break;
                  case Event.KEY_RELEASE:
                      // key released
                      break;
                  case Event.MOUSE_DOWN:
                      // mouse button pressed
                      break;
                  case Event.MOUSE_UP:
                      // mouse button released
                      break;
                  case Event.MOUSE_MOVE:
                      break;
                  case Event.MOUSE_DRAG:
                      break;
             }
             return false;
	}
}