Learning Class : Applet

So here is the first entry in the learning class!!

Subject : What is your core applet code? The essential part that you have in every applet when you make a game.

Description

The essential parts of my applet are the following elements :

  • start() method creating the canvas, the bufferstrategy and the input listeners as well as starting the gameloop.
  • a gameloop.
  • a couple of input listeners (MouseListener, KeyListener, etc.).
  • render(Graphics2D g) method to draw on the canvas.
  • update(int deltaTime) method to update the game state.

Code

import java.applet.Applet;
import java.awt.Canvas;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.event.MouseAdapter;
import java.awt.image.BufferStrategy;

public class BasicApplet extends Applet implements Runnable{
	
	private final int width = 800;
	private final int height = 600;

	Canvas canvas;
	BufferStrategy bufferStrategy;
	Thread gameloopThread;
	
	@Override
	public void init(){
		canvas = new Canvas();
		canvas.setBounds(0, 0, width, height);
		add(canvas);
		canvas.setIgnoreRepaint(true);

		canvas.createBufferStrategy(2);
		bufferStrategy = canvas.getBufferStrategy();

		canvas.addMouseListener(new MouseControl());
		canvas.addMouseMotionListener(new MouseControl());
		canvas.requestFocus();
	}
	
	@Override
	public void start(){
		gameloopThread = new Thread(this);
		gameloopThread.start();
	}
	
	@Override
	public void stop(){
		setRunning(false);
		try {
			gameloopThread.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	private class MouseControl extends MouseAdapter{
		
	}
	
	private long desiredFPS = 60;
        private long desiredDeltaLoop = (1000*1000*1000)/desiredFPS;
    
	private boolean running = true;
	
	private synchronized void setRunning(boolean running){
		this.running = running;
	}
	
	private synchronized boolean isRunning(){
		return running;
	}
	
	public void run(){
		
		setRunning(true);
		
		long beginLoopTime;
		long endLoopTime;
		long currentUpdateTime = System.nanoTime();
		long lastUpdateTime;
		long deltaLoop;
		
		while(!isActive()){
			Thread.yield();
		}
		while(isRunning()){
			beginLoopTime = System.nanoTime();
			
			render();
			
			lastUpdateTime = currentUpdateTime;
			currentUpdateTime = System.nanoTime();
			update((int) ((currentUpdateTime - lastUpdateTime)/(1000*1000)));
			
			endLoopTime = System.nanoTime();
			deltaLoop = endLoopTime - beginLoopTime;
	        
	        if(deltaLoop > desiredDeltaLoop){
	            //Do nothing. We are already late.
	        }else{
	            try{
	                Thread.sleep((desiredDeltaLoop - deltaLoop)/(1000*1000));
	            }catch(InterruptedException e){
	                //Do nothing
	            }
	        }
		}
	}

	private void render() {
		try{
			Graphics2D g = (Graphics2D) bufferStrategy.getDrawGraphics();
			g.clearRect(0, 0, width, height);
			render(g);
			bufferStrategy.show();
			g.dispose();
		}catch(Exception e){
			e.printStackTrace();
		}
	}
	
	/**
	 * Overwrite this method in subclass
	 */
	protected void update(int deltaTime){

	}
	
	/**
	 * Overwrite this method in subclass
	 */
	protected void render(Graphics2D g){

	}

}

Notes

[Warning] There might be some tearing with the rendering.

Thanks for sharing! But please update the code to better support the Applet Lifecycle and implement all methods for best robustness.

For example the start-Method() might be called multiple times, so you should not simple spawn a new threads there (you might get multiple instances running in parallel). Better check, if the thread was already spawned and only wake it up. Also you should include means to suspend the rendering on stop() and do clean up in destroy().

Alternatively (e.g. to circumvent browsers that don’t pass correct notifications to the applet) you could simple kill and clean up in stop() and destroy() and restart the applet in start(). But you also need to check if the Thread is already there and kill it before spawning a new one.

Hello, thank you for the feedback. I had already read the applet the applet section in the past and some of my design decision were based on it. But it’s always good to refresh your memory. In the “methods” page, it said the following about the start method :

[quote]“The start method starts the execution of the applet. It is good practice to return quickly from the start method. If you need to perform computationally intensive operations it might be better to start a new thread for this purpose.”
[/quote]
That’s why I’m starting a new thread in the start method. Although, I agree with you that I need some means to kill a thread. I did some testing and I found out that effectively my thread continue to run after the stop method of the applet is called. But, the thread is then killed by the code :


if(!isActive()){
        return;
}

I could possibly replace this code by overriding the stop method with the following code, which would nearly do the same.


@Override
public void stop(){
	running = false;
}

But the following code could produce some synchronization problem if the 2 threads access the running variable at the same time. So a safer version would look like it.


@Override
public void stop(){
	setRunning(false);
}
 private synchronized void setRunning(boolean running){
	this.running = running;
}
private synchronized boolean isRunning(){
	return running;
}

In all case, the thread will stop to run (and in the process destroy itself because it is no longer in use). So starting a new one shouldn’t be a problem. But which version is better? Or any other suggestion.

Another thing, that I could/should? change is putting the initialization code in the init() method instead of the start method. I don’t think it does any difference but it might be good practice.

Btw, there is one thing that I don’t understand. How can an applet call the start method more than once? I was sure that the methods were always called in order (init(), start(), stop() and destroy())

Stop is called to suspend an applet (for example when minimizing the Browser or switching to another tab) and Start is called again to wake it up

Sorry, but I’m unable to reproduce this behavior. I tried it both in chrome and internet explorer. You can test it real quick with the applet on this page.

Applet life cycle

The only things that call the stop method are “Leaving and Returning to the Applet’s Page”, “Reloading the Applet” and “Quitting the Browser” as it is explain on the Applet life cycle page.

I heard it many times that stop was call when you change tab but it never did it for me. Was it like that in the past?

Applet life cycle have been completly broken for a while (it always have been), not sure it is really correct now

as far as I know it look something like :
init => called once before all other method
start => may be called more than one time, every time applet have to resume from a previous stop and when applet start first time
stop=> may be called more than one time, every time applet have to pause itself and once before applet destroy (browser reduced, but also sometime when going to another web page, and then start is called when returning )
destroy => once when applet is being destoryed

only sure is that init always happen and also start (at least once)

in my experience, the Official Applet life cycle is not reliable, may be it is now with the new plugin ?

@Guardian:
you can simulate pause/resume using Applet viewer but other software may display Applet, that’s why stop/start should be considered as pause/resume

Thanks dzzd. I should have made this clearer before. Bottom line is, you may not know, so make it as robust as possible even for unexpected behavior. There have been loads of problems with applets, especially with multiple invocations and multiple threads.

Ok we all agree that nobody really know how work an applet and that we should use “best practice” when we make an applet.

But I would need some help to see what an applet using best practice looks like. What’s your applet?

Alright, I made the change in my applet. Now the stop method make sure that the gameloop thread finish before exiting. So there is no way that there can be more than 2 threads running simultaneously.

I update the code in the first post.

Btw, thx for the help.

Nice code, thanks for sharing.

Small question about the delta timing. If I understand correctly, you calculate how much time the render method took and pass that to the update method in milliseconds. What should I do with it in my update method? I tried using it to determine how much to update, but isn’t the Thread.sleep already enforcing a steady frame rate?

I’m quite new to Java btw, so thanks for helping me out :wink:

No the Thread.sleep is not constant and is not reliable. It’s better to use the deltatime between 2 updates.

You can use it in your updates. Just subtract it from the time now, and if that number is over 2 seconds - update!
If not, just add it to an int, which you add to the calculation above. You now have a 2-second-interval update :smiley:

Ah, thanks guys. Had some trouble understanding the delta timing, but got it now. Game’s running smooth and consistent 8)

It is easy to test how to lifecycle works these days using println and looking at the console.
In my tests with Google Chrome, init() is treated like start()! Every time I refresh or close and reopen the tab init() is called then start().
stop() is called as soon as i refresh or close the tab and destroy() is called a while later when the JVM decides to shutdown.

Hope this helped :slight_smile: