Sign in to follow this  
deej21

[java] graphics in applets

Recommended Posts

Hey, I am trying to make a game that runs on applets. I need to know if and how to put graphics on an applet thru other Containers such as Canvases and JPanels. I know the usual way is thru paint(), but I have states that handle the graphics themselves (as well as WHEN to do the graphics, so I can't just call their methods from the applet's method). I know this is possible in JFrames by adding the Container to the JFrame, and the Container's graphics will show up in the JFrame. I tried adding the Container to the Applet, and the state's functions are all performed correctly, just the graphics are not showing up. Also, I tried making it a JApplet, which has a getContentPane() just like a JFrame. I add the Containers to it the same way, however I get the same result as with the Applet. Thanks! [Edited by - deej21 on May 21, 2006 6:30:19 PM]

Share this post


Link to post
Share on other sites
Make sure that whenever you change containers after the initial container you have a call to Component.validate(); so whenever you add a Component or swap components on a JFrame.getContentPane() you need to also call JFrame.getContentPane().validate();

Share this post


Link to post
Share on other sites
Thanks for the reply.

Hm, I thought that was only when having multiple components. I control it so there is always only one component in the pane. Regardless, I tried it and it didn't work. In my applications, I use JFrames, and I don't use validate() and it works fine.

Although, I also just tried doing a simple paint() with a fillBox() and some random stuff, and none of that showed up either... my experience with applets is, well, this, so perhaps I'm missing one of the most simple things.

Also, where is it best to call my game loop? If I don't call the game loop, the applet gets fully initialized, and my state system recieves event calls from the keylistener (which is how I know it's functioning still). However, if I call the game loop in the Applet.start(), the while(true) in the game loop takes up the whole thread. The states don't get any event calls, and the AppletViewer shows "Applet Initialized" rather than "Applet Started". I tried SwingUtilities.invokeLater(), and still got the same results.

Share this post


Link to post
Share on other sites
Try this simple example.

import java.awt.*;
import javax.swing.*;

public class Test extends JApplet {
public void init() {
getContentPane().add(new MyPanel());
}
}

class MyPanel extends JPanel {

public void paintComponent(Graphics g) {
g.fillRect(10, 10, 100, 100);
}
}


Also you are supposed to override paintComponent, not paint.

Share this post


Link to post
Share on other sites
Ok... I've been working on this for a bit.
I figured out that, yes, adding the state like that does work. The reason it didn't work for me before is because I called the game loop in Applet.start(), which means start() doesn't return, so the applet isn't officially "started", thus only initialized, and nothing is shown. I tried calling state.update() only once at the end of start(), and it drew the proper screen. Of course, it didn't draw anything else since I didn't call update() anymore.

What is the best way to have the game loop be called on a separate thread so it can work properly? Like I said, I tried SwingUtilities.invokeLater(), but I got the same results as calling the loop at the end of start().

(off subject) I was taught to use paint()... it works properly that way. What is the difference between using paint and paintComponent. Obviously different definitions in the super class, but what am I overriding?

Share this post


Link to post
Share on other sites
Under AWT it was paint. Under Swing, it is recommended that you use paintComponent. I can't remember the reasons, I just remember that that is what was recommended. Check it out in "The Java Tutorial", I am sure they explain it there.

Also, you shouldn't have a game loop in start. start is meant to return to the web browser. If you need a game loop in an applet, you need to start a new thread.

Share this post


Link to post
Share on other sites
Right... that's what I said. Would the most efficient way be to merely call

new Thread(new Runnable(){
public void run(){ loop(); }}).start();

Share this post


Link to post
Share on other sites
The way I have been calling my "render loop" has been with java.util.Timer and java.util.TimerTask which I think is much quicker and less code you have to write yourself.

You don't have to bother with Thread.wait() calls or calculating the timing. And you can add any number of Tasks to be run as you want.


java.util.Timer tasker = new java.util.Timer();
tasker.schedule(
new TimerTask() {
public void run() {
screen.paint();
}
}
1,
15 /* this is the length of time between calls */
);

The only thing to note when using threaded tasks is that you must account for concurrency issues making sure that two threads won't modify the same data.

Share this post


Link to post
Share on other sites
That's an interesting way to do it... I used to use Timers, but I don't like them that much. I prefer to use System.currentTimeMillis() and use delta / 1000 * pixelsPerSecond for movement.
Also, the code is very simple. In the update() method, just have

if(System.currentTimeMillis() - timeLastUpdate > waitTime)
{
genericUpdateEverythingMethod();

timeLastUpdate += waitTime;
}


Regardless, I got it to work, but am having problems in running the applet in a browser. I could have made a new thread, but figured it is appropriate here also.
Since browsers don't let applets read or write any files, I can't read in the images I need. I tried archiving everything into a .jar file, but still got the same problem. I would think a .jar file would work, since I thought that was kinda the point of a .jar file. One possibility I thought of was making a server-side application to load the images and send them to the game, although I'm really not wanting to do that. Is there an easier way?

Share this post


Link to post
Share on other sites
There are a couple different ways to read files in an applet. You can read property files in an applet and also get audio and images.

I pack everything into a .jar file and load the images using the Applet.getImage()

if you put everything in a .jar then all you need to do is

this.getImage( this.getCodeBase(), <image> );

and that will load the image.

The only problem I've come across is that the images don't load right away so you need to make a call to the boolean prepareImage( Image, ImageObserver ) function which will return true when the image is fully loaded.

to load property files in an applet you need to use the URLClassLoader.


URL uProp = new URL(this.getCodeBase(), jarFile);
URL arr[] = new URL[1];
arr[0] = uProp;
URLClassLoader urlLoader = URLClassLoader.newInstance(arr);
System.out.println( this.getCodeBase() );
properties.load( urlLoader.findResource(propertyFile).openStream() );



where the jarFile is just the name of your jar file
and propertyFile is just the name of the property file.

I personally like the java.util.Timer class since I don't have to worry about the timing piece of it which is especially useful for animations. But I guess if you make your own API then you only have to do it once but I still find that when making a game from scratch I don't have to bother making with that part of the game and can move on to bigger and better things. But to each his own.

Share this post


Link to post
Share on other sites
Thanks a lot.

Could you elaborate more on how to use prepareImage()? For instance, what is an ImageObserver? Also, how can I display a loading screen while the images load?

Share this post


Link to post
Share on other sites
Actually it's funny you ask that. I'm working on just the same thing right now. Haven't quiet perfected it yet got some unknown errors that I have to work out but it's getting there.

And ImageObserver is essentially a class with a single method that tells you how much of an image is loaded.

Or at least that would be my take on what it is used for with the prepareImage function. I usually just set that parameter to null;

right now I'm doing this with most of my applet games

while( !prepareImage(imgToLoad,null) );

which is potentially unsafe, hopefully I'll be able to show a solution sometime soon.

Share this post


Link to post
Share on other sites
Ok, I figured out how to make a loading screen. Here is my code.


public void start()
{
System.out.println("applet start");

paint(getGraphics());

if(!loaded)
{
new Thread(new Runnable(){
public void run()
{
load();
}
}).start();
}
else
startGameLoop();
}

private void load()
{
if(loaded)
return;

//stuff

someImage = getImage(getCodeBase(), "foo.gif");

//stuff

loaded = true;

startGameLoop();
}

public void paint(Graphics g)
{
if(loaded)
return;

g.setFont(new Font("Verdana", Font.BOLD, 24));
g.setColor(Color.black);

g.drawString("Loading...", 180, 220);
}





The reason I use paint(getGraphics()) rather than repaint() is that repaint() waits calls paint() on a new thread, which doesn't get focus until later; namely, not until after everything is loaded, so it's pointless. This way, paint() is called and must return before any of the loading is begun.

One thing is that, according to the Javadocs, Applet.getImage() always returns immediately and doesn't load the image until the applet attempts to draw it. Ideally, it would be best to have the images loaded so it doesn't take up extra time loading when it needs to be drawn to the screen. Any ideas?

edit: Ok, I added your thing using prepareImage(), and it worked out well. There was no flicker or wait between the end of the loading screen and the game itself, and it took about twice as long, which is a good sign that it loaded the images.

Share this post


Link to post
Share on other sites
Good deal, I still have had some issues with prepareImage() in different browsers. One browser just sat there and didn't do anything wouldn't load the images for some reason and he had the current version of Java too so I dunno what was up with that.

Share this post


Link to post
Share on other sites
Yeah, I found out that prepareImage() doesn't seem to work in browsers if your images are in the .jar. I tried IE and Firefox, and in both, it wouldn't work (it would, like you said, sit there waiting) although if I had the images folder in the directory, it would work.

Share this post


Link to post
Share on other sites
Yeah thats a very wierd problem because almost all the computers I've tried it on the prepareImage() returns fine and everything starts up and all my images are in the jar.

But I'll try putting them in a directory rather than the jar and see what kind of a different that makes. Personally I'd rather keep them all in the jar in one place but if it has to be done then it I guess I'll just have to do it that way.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
You might also try the ImageIcon class. It sets up a MediaTracker which blocks until the image is loaded. So all you have to do is:

Image im = new ImageIcon(getClass().getResource("someimage.gif").getImage();

and this returns the fully loaded image.

This should work as well:

im = new ImageIcon(getDocumentBase() + "someimage.gif").getImage();

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this