Converting Application into Applet

Started by DaleCantwell, July 25, 2011, 11:22:24

Previous topic - Next topic

DaleCantwell

Hi all,
As part of a project I have to create an applet that has 3D models, audio and text in it. I decided to follow the cokeandcode asteroids tutorial ( http://www.cokeandcode.com/asteroidstutorial ) as it had all those features. I have created my application but now I have to convert it into an applet, I made a basic 2D applet before but I can't manage to turn this into one.
I'm wondering if anyone has used this tutoral and converted it into an applet or could give me some help as to where to start, I asume the class below is the place to extend applet. Any help woud be much appreciated.

package org.gamescore;

import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;

import org.gamescore.sound.SoundLoader;
import org.lwjgl.LWJGLException;
import org.lwjgl.Sys;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;
import org.lwjgl.util.glu.*;

public class GameWindow {
	/** The list of game states currently registered */
	private HashMap gameStates = new HashMap();
	/** The current state being rendered */
	private GameState currentState;
	
	/**
	 * Create a new game window
	 */
	public GameWindow() {
		try {
			// find out what the current bits per pixel of the desktop is
			int currentBpp = Display.getDisplayMode().getBitsPerPixel();
			// find a display mode at 800x600
			DisplayMode mode = findDisplayMode(800, 600, currentBpp);
			
			// if can't find a mode, notify the user the give up
			if (mode == null) {
				Sys.alert("Error", "800x600x"+currentBpp+" display mode unavailable");
				return;
			}
			
			// configure and create the LWJGL display
			Display.setTitle("Collaborative Walking v0.1");
			Display.setDisplayMode(mode);
			Display.setFullscreen(false);
			
			Display.create();
			
			// initialise the game states
			init();
		} catch (LWJGLException e) {
			e.printStackTrace();
			Sys.alert("Error", "Failed: "+e.getMessage());
		}
	}

	/**
	 * Start the game
         */
	public void startGame() {
		// enter the game loop
		gameLoop();
	}	

	/**
	 * Get the current time in milliseconds based on the LWJGL
	 * high res system clock.
	 * 
	 * @return The time in milliseconds based on the LWJGL high res clock
	 */
	private long getTime() {
		return (Sys.getTime() * 1000) / Sys.getTimerResolution();
	}
	
	/**
	 * Determine an available display that matches the specified 
	 * paramaters.
	 * 
	 * @param width The desired width of the screen
	 * @param height The desired height of the screen
	 * @param bpp The desired colour depth (bits per pixel) of the screen
	 * @return The display mode matching the requirements or null
	 * if none could be found
	 * @throws LWJGLException Indicates a failure interacting with the LWJGL
	 * library.
	 */
	private DisplayMode findDisplayMode(int width, int height, int bpp) throws LWJGLException {
		DisplayMode[] modes = Display.getAvailableDisplayModes();
		DisplayMode mode = null;
		
		for (int i=0;i<modes.length;i++) {
			if ((modes[i].getBitsPerPixel() == bpp) || (mode == null)) {
				if ((modes[i].getWidth() == width) && (modes[i].getHeight() == height)) {
					mode = modes[i];
				}
			}
		}
		
		return mode;
	}
	
	/**
	 * Add a game state to this window. This state can be used via
	 * its unique name.
	 * 
	 * @param state The state to be added
	 * @see GameState.getName()
	 */
	public void addState(GameState state) {
		if (currentState == null) {
			currentState = state;
		}
		
		gameStates.put(state.getName(), state);
	}
	
	/**
	 * Initialise the window and the resources used for the game
	 */
	public void init() {
		// initialise our sound loader to determine if we can
		// play sounds on this system
		SoundLoader.get().init();
		
		// run through some based OpenGL capability settings. Textures
		// enabled, back face culling enabled, depth texting is on,
		GL11.glEnable(GL11.GL_TEXTURE_2D);
		GL11.glEnable(GL11.GL_CULL_FACE);
		GL11.glEnable(GL11.GL_DEPTH_TEST);
		GL11.glDepthFunc(GL11.GL_LEQUAL);
		GL11.glShadeModel(GL11.GL_SMOOTH);  
		
		// define the properties for the perspective of the scene
		GL11.glMatrixMode(GL11.GL_PROJECTION);
		GL11.glLoadIdentity();		
		GLU.gluPerspective(45.0f, ((float) 800) / ((float) 600), 0.1f, 100.0f);
		GL11.glMatrixMode(GL11.GL_MODELVIEW);
		GL11.glHint(GL11.GL_PERSPECTIVE_CORRECTION_HINT, GL11.GL_NICEST); 

		// add the two game states that build up our game, the menu
		// state allows starting of the game. The ingame state rendered
		// the asteroids and the player
		addState(new MenuState());
		addState(new InGameState());
		
		try {
			// initialse all the game states we've just created. This allows
			// them to load any resources they require
			Iterator states = gameStates.values().iterator();
			
			// loop through all the states that have been registered
			// causing them to initialise
			while (states.hasNext()) {
				GameState state = (GameState) states.next();
				
				state.init(this);
			}
		} catch (IOException e) {
			// if anything goes wrong, show an error message and then exit.
			// This is a bit abrupt but for the sake of this tutorial its
			// enough.
			Sys.alert("Error", "Unable to initialise state: " + e.getMessage());
			System.exit(0);
		}
	}
	
	/**
	 * The main game loop which is cycled rendering and updating the
	 * registered game states
	 */
	public void gameLoop() {
		boolean gameRunning = true;
		long lastLoop = getTime();
		
		currentState.enter(this);
		
		// while the game is running we loop round updating and rendering
		// the current game state
		while (gameRunning) {
			// calculate how long it was since we last came round this loop
			// and hold on to it so we can let the updating/rendering routine
			// know how much time to update by
			int delta = (int) (getTime() - lastLoop);
			lastLoop = getTime();
			
			// clear the screen and the buffer used to maintain the appearance
			// of depth in the 3D world (the depth buffer)
			GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
			
			// cause the game state that we're currently running to update
			// based on the amount of time passed
			int remainder = delta % 10;
			int step = delta / 10;
			for (int i=0;i<step;i++) {
				currentState.update(this, 10);
			}
			if (remainder != 0) {
				currentState.update(this, remainder);
			}
			
			// cause the game state that we're currently running to be 
			// render
			currentState.render(this,delta);
			
			// finally tell the display to cause an update. We've now
			// rendered out scene we just want to get it on the screen
			// As a side effect LWJGL re-checks the keyboard, mouse and
			// controllers for us at this point
			Display.update();
			
			// if the user has requested that the window be closed, either
			// pressing CTRL-F4 on windows, or clicking the close button
			// on the window - then we want to stop the game
			if (Display.isCloseRequested()) {
				gameRunning = false;
				System.exit(0);
			}
		}
	}
	
	/**
	 * Change the current state being rendered and updated. Note if 
	 * no state with the specified name can be found no action is taken.
	 * 
	 * @param name The name of the state to change to.
	 */
	public void changeToState(String name) {
		GameState newState = (GameState) gameStates.get(name);
		if (newState == null) {
			return;
		}
		
		currentState.leave(this);
		currentState = newState;
		currentState.enter(this);
	}
	
	/**
	 * Enter the orthographic mode by first recording the current state, 
	 * next changing us into orthographic projection.
	 */
	public void enterOrtho() {
		// store the current state of the renderer
		GL11.glPushAttrib(GL11.GL_DEPTH_BUFFER_BIT | GL11.GL_ENABLE_BIT);
		GL11.glPushMatrix();
		GL11.glLoadIdentity();
		GL11.glMatrixMode(GL11.GL_PROJECTION); 
		GL11.glPushMatrix();	
		
		// now enter orthographic projection
		GL11.glLoadIdentity();		
		GL11.glOrtho(0, 800, 600, 0, -1, 1);
		GL11.glDisable(GL11.GL_DEPTH_TEST);
		GL11.glDisable(GL11.GL_LIGHTING);  
	}

	/**
	 * Leave the orthographic mode by restoring the state we store
	 * in enterOrtho()
	 * 
	 * @see enterOrtho()
	 */
	public void leaveOrtho() {
		// restore the state of the renderer
		GL11.glPopMatrix();
		GL11.glMatrixMode(GL11.GL_MODELVIEW);
		GL11.glPopMatrix();
		GL11.glPopAttrib();
	}
	
	/**
	 * The entry point into our game. This method is called when you
	 * execute the program. Its simply responsible for creating the
	 * game window
	 * 
	 * @param argv The command line arguments provided to the program
	 */
	public static void main(String argv[]) {
		GameWindow g = new GameWindow();
		g.startGame();
	}
}

kappa

Have a read of the applet tutorials on the lwjgl wiki. Once you've got how the basic skeleton of the LWJGL applet works its not much work after that to port an LWJGL Application to an applet.

DaleCantwell

Thanks for the help, I have it running now, however when I quit the applet it seems as if the applet is never destroyed, I have to open task manager (in windows) and terminate the applet. Below is my new class extending applet, is there any noticable reason why the destroy function isn't working, perhaps I am calling display.destory from the wrong location.

package org.gamescore;

import java.applet.Applet;
import java.awt.BorderLayout;
import java.awt.Canvas;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;

import org.gamescore.sound.SoundLoader;
import org.lwjgl.LWJGLException;
import org.lwjgl.Sys;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;
import org.lwjgl.util.glu.*;

/**
 * A window to display the game in LWJGL.
 * 
 */
public class GameWindow extends Applet{
	/** The list of game states currently registered */
	private HashMap gameStates = new HashMap();
	/** The current state being rendered */
	private GameState currentState;

	/** The Canvas where the LWJGL Display is added */
	Canvas display_parent;

	/** Thread which runs the main game loop */
	Thread gameThread;

	/** is the game loop running */
	boolean running;
	
	/**
	 * Once the Canvas is created its add notify method will call this method to
	 * start the LWJGL Display and game loop in another thread.
	 */
	public void startLWJGL() {
		gameThread = new Thread() {
			public void run() {
				running = true;
				try {
					Display.setParent(display_parent);
					//Display.setVSyncEnabled(true);
					Display.create();
					initGL();
				} 
				catch (LWJGLException e) {
					e.printStackTrace();
				}
				gameLoop();
			}
		};
		gameThread.start();
	}


	/**
	 * Tell game loop to stop running, after which the LWJGL Display will be destoryed.
	 * The main thread will wait for the Display.destroy() to complete
	 */
	private void stopLWJGL() {
		running = false;
		try {
			gameThread.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	public void init() 
	{
		setSize(800, 600);
		setLayout(new BorderLayout());
		try {
			display_parent = new Canvas() {
				public void addNotify() {
					super.addNotify();
					startLWJGL();
				}
				public void removeNotify() {
					stopLWJGL();
					super.removeNotify();
				}
			};
			display_parent.setSize(getWidth(),getHeight());
			add(display_parent);
			display_parent.setFocusable(true);
			display_parent.requestFocus();
			display_parent.setIgnoreRepaint(true);
			//setResizable(true);
			setVisible(true);
		} catch (Exception e) {
			System.err.println(e);
			throw new RuntimeException("Unable to create display");
		}		
	}
	
	public void start() {}
	
	public void stop() {}
	
	public void destroy() 
	{
		remove(display_parent);
		super.destroy();
	}
	
	/**
	 * Initialise the window and the resources used for the game
	 */
	public void initGL() {
		// initialise our sound loader to determine if we can
		// play sounds on this system
		SoundLoader.get().init();
		
		// run through some based OpenGL capability settings. Textures
		// enabled, back face culling enabled, depth texting is on,
		GL11.glEnable(GL11.GL_TEXTURE_2D);
		GL11.glEnable(GL11.GL_CULL_FACE);
		GL11.glEnable(GL11.GL_DEPTH_TEST);
		GL11.glDepthFunc(GL11.GL_LEQUAL);
		GL11.glShadeModel(GL11.GL_SMOOTH);  
		
		// define the properties for the perspective of the scene
		GL11.glMatrixMode(GL11.GL_PROJECTION);
		GL11.glLoadIdentity();		
		GLU.gluPerspective(45.0f, ((float) 800) / ((float) 600), 0.1f, 100.0f);
		GL11.glMatrixMode(GL11.GL_MODELVIEW);
		GL11.glHint(GL11.GL_PERSPECTIVE_CORRECTION_HINT, GL11.GL_NICEST); 

		// add the two game states that build up our game, the menu
		// state allows starting of the game. The ingame state rendered
		// the asteroids and the player
		addState(new MenuState());
		addState(new InGameState());
		
		try {
			// initialse all the game states we've just created. This allows
			// them to load any resources they require
			Iterator states = gameStates.values().iterator();
			
			// loop through all the states that have been registered
			// causing them to initialise
			while (states.hasNext()) {
				GameState state = (GameState) states.next();
				
				state.init(this);
			}
		} catch (IOException e) {
			// if anything goes wrong, show an error message and then exit.
			// This is a bit abrupt but for the sake of this tutorial its
			// enough.
			Sys.alert("Error", "Unable to initialise state: " + e.getMessage());
			System.exit(0);
		}
	}
	
	/**
	 * Get the current time in milliseconds based on the LWJGL
	 * high res system clock.
	 * 
	 * @return The time in milliseconds based on the LWJGL high res clock
	 */
	private long getTime() {
		return (Sys.getTime() * 1000) / Sys.getTimerResolution();
	}
	
	/**
	 * The main game loop which is cycled rendering and updating the
	 * registered game states
	 */
	public void gameLoop() {
		boolean gameRunning = true;
		long lastLoop = getTime();
		
		currentState.enter(this);
		
		// while the game is running we loop round updating and rendering
		// the current game state
		while (gameRunning) {
			// calculate how long it was since we last came round this loop
			// and hold on to it so we can let the updating/rendering routine
			// know how much time to update by
			int delta = (int) (getTime() - lastLoop);
			lastLoop = getTime();
			
			// clear the screen and the buffer used to maintain the appearance
			// of depth in the 3D world (the depth buffer)
			GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
			
			// cause the game state that we're currently running to update
			// based on the amount of time passed
			int remainder = delta % 10;
			int step = delta / 10;
			for (int i=0;i<step;i++) {
				currentState.update(this, 10);
			}
			if (remainder != 0) {
				currentState.update(this, remainder);
			}
			
			// cause the game state that we're currently running to be 
			// render
			currentState.render(this,delta);
			
			// finally tell the display to cause an update. We've now
			// rendered out scene we just want to get it on the screen
			// As a side effect LWJGL re-checks the keyboard, mouse and
			// controllers for us at this point
			Display.update();
			
			// if the user has requested that the window be closed, either
			// pressing CTRL-F4 on windows, or clicking the close button
			// on the window - then we want to stop the game
			if (Display.isCloseRequested()) {
				gameRunning = false;
				System.exit(0);
			}
		}
		Display.destroy();
	}
	
	
	/**
	 * Add a game state to this window. This state can be used via
	 * its unique name.
	 * 
	 * @param state The state to be added
	 * @see GameState.getName()
	 */
	public void addState(GameState state) {
		if (currentState == null) {
			currentState = state;
		}
		
		gameStates.put(state.getName(), state);
	}
	
	/**
	 * Change the current state being rendered and updated. Note if 
	 * no state with the specified name can be found no action is taken.
	 * 
	 * @param name The name of the state to change to.
	 */
	public void changeToState(String name) {
		GameState newState = (GameState) gameStates.get(name);
		if (newState == null) {
			return;
		}
		
		currentState.leave(this);
		currentState = newState;
		currentState.enter(this);
	}
	
	/**
	 * Enter the orthographic mode by first recording the current state, 
	 * next changing us into orthographic projection.
	 */
	public void enterOrtho() {
		// store the current state of the renderer
		GL11.glPushAttrib(GL11.GL_DEPTH_BUFFER_BIT | GL11.GL_ENABLE_BIT);
		GL11.glPushMatrix();
		GL11.glLoadIdentity();
		GL11.glMatrixMode(GL11.GL_PROJECTION); 
		GL11.glPushMatrix();	
		
		// now enter orthographic projection
		GL11.glLoadIdentity();		
		GL11.glOrtho(0, 800, 600, 0, -1, 1);
		GL11.glDisable(GL11.GL_DEPTH_TEST);
		GL11.glDisable(GL11.GL_LIGHTING);  
	}

	/**
	 * Leave the orthographic mode by restoring the state we store
	 * in enterOrtho()
	 * 
	 * @see enterOrtho()
	 */
	public void leaveOrtho() {
		// restore the state of the renderer
		GL11.glPopMatrix();
		GL11.glMatrixMode(GL11.GL_MODELVIEW);
		GL11.glPopMatrix();
		GL11.glPopAttrib();
	}
	
}

broumbroum

make sure Applet.destroy() stops the gameloop thread ! ;)
start and stop would be fine if the rendering thread were suspended/resumed too.. because that handles the way the user's put the applet in the background. ;)

DaleCantwell

Thanks for the help as you said I wasn't terminating the game loop.