A couple questions about applets and games
I'm relatively new to Java and I'm working on making a standard, simple game. Now I've looked through the Java Applet tutorials and I can make basic Applets that show graphics and whatnot. But I am interested in making an executable version of this run by a Driver Class. I'm not able to make the paint(Graphics g) function return say, a BufferedImage, and I'm not sure how to make an instance of the applet and use it like a frame - for example Frame frame = new Frame("blah") and then set the graphics to that via Graphics g = frame.getGraphics();
I'm also curious what other people think in terms of what is best for a game. I have heard Applets are good, but there are of course the Frame options and so forth. I actually had an easier time with JFrame, but it was skipping and such(I know there's a way around that, I just didn't get around to implementing it). I would like to get the Applet working better, however.
Any advice would be helpful, thank you.
- Bob
# 1
If you want your program to be an application that runs outside the browser then do not create applets just create a standerd java application (a class with a 'public static void main') and use Swing to make the GUI.
If you want to mount an applet in to a Frame in an standallone application you can just add the applet in to the content pane of the frame becouse applets are components. But then some applet specific methods of applet class will not work (Ex:- getAppletContext). So if you are using such functions you will have to use workarrounds.
Whether to use an applet or application for a game depends on what kind of a game that you are planing to build. If it is a game that you plan to allow the players to visit your website and play on their browser then you can use applets. If you are developing a game that should run directly on the players desktop (after downloading from web or distributed using a CD) then choose applications.
# 2
Thanks a lot, that helps =) I will try the above suggestions. - Bob
# 3
So anyway I decided to go with the applet, but I have an additional question. I've been working on a way to put in a background along with the little character on the screen in a way where the screen does not flicker. I've tried a few different methods, but none that seem to get rid of the flickering. I can put my character in and move him around freely without flickering, but when I put the second buffered image in it starts flickering. Here's some of the code(cut down some of the graphics portion to make it postable) -
public class TheGame extends Applet implements Runnable, KeyListener
{
public volatile Thread timer;
boolean downmov = false;
boolean upmov = false;
boolean leftmov = false;
boolean rightmov = false;
int appWidth;
int appHeight;
double moveX = 1;
double moveY = 1;
double moveSpeed = 40;
Graphics g;
Graphics dublbuff;
BufferedImage playermodel;
BufferedImage background;
double fpsx = 0.3333;
double fpsy = 0.6666;
public void init()
{
appWidth = getWidth();
appHeight = getHeight();
setLayout(new BorderLayout());
background = (BufferedImage) createImage(appWidth, appHeight);
BufferedImage offscrn = (BufferedImage) createImage(appWidth, appHeight);
dublbuff = offscrn.getGraphics();
Graphics2D backg = (Graphics2D) dublbuff;
backg = background.createGraphics();
backg.setColor(Color.green);
backg.fill(new Rectangle(222, 185, 50, 50));
}
public void paint(Graphics g)
{
update(g);
}
public void pause()
{
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void run()
{
Thread check = Thread.currentThread();
while (timer == check)
{
//g.drawImage(background, 0, 0, null);
repaint();
try {Thread.currentThread().sleep(20);
} catch (InterruptedException e) {
}
}
}
public void start()
{
timer = new Thread(this);
timer.start();
addKeyListener(this);
}
public void stop()
{
timer = null;
}
public void keyPressed(KeyEvent e)
{
int keyID = e.getKeyCode();
if (keyID == KeyEvent.VK_UP)
{
upmov = true;
}
if (keyID == KeyEvent.VK_DOWN)
{
downmov = true;
}
if (keyID == KeyEvent.VK_LEFT)
{
leftmov = true;
}
if (keyID == KeyEvent.VK_RIGHT)
{
rightmov = true;
}
paint(g);
}
public void keyReleased(KeyEvent e)
{
}
public void keyTyped(KeyEvent e)
{
}
public void update(Graphics g)
{
playermodel = (BufferedImage) createImage(appWidth, appHeight);
Graphics2D g2 = (Graphics2D) g;
g2 = playermodel.createGraphics();
if (downmov == true)
{
double tempMove = moveY;
tempMove = moveY + (moveSpeed * fpsx);
g.drawImage(playermodel, (int)moveX, (int)tempMove, null);
pause();
tempMove = moveY;
tempMove = moveY + (moveSpeed * fpsy);
g.drawImage(playermodel, (int)moveX, (int)tempMove, null);
pause();
moveY = moveY + moveSpeed;
downmov = false;
}
if (upmov == true)
{
double tempMove = moveY;
tempMove = moveY - (moveSpeed * fpsx);
g.drawImage(playermodel, (int)moveX, (int)tempMove, null);
pause();
tempMove = moveY;
tempMove = moveY - (moveSpeed * fpsy);
g.drawImage(playermodel, (int)moveX, (int)tempMove, null);
pause();
moveY = moveY - moveSpeed;
upmov = false;
}
if (leftmov == true)
{
double tempMove = moveX;
tempMove = moveX - (moveSpeed * fpsx);
g.drawImage(playermodel, (int)tempMove, (int)moveY, null);
pause();
tempMove = moveX;
tempMove = moveX - (moveSpeed * fpsy);
g.drawImage(playermodel, (int)tempMove, (int)moveY, null);
pause();
moveX = moveX - moveSpeed;
leftmov = false;
}
if (rightmov == true)
{
double tempMove = moveX;
tempMove = moveX + (moveSpeed * fpsx);
g.drawImage(playermodel, (int)tempMove, (int)moveY, null);
pause();
tempMove = moveX;
tempMove = moveX + (moveSpeed * fpsy);
g.drawImage(playermodel, (int)tempMove, (int)moveY, null);
pause();
moveX = moveX + moveSpeed;
rightmov = false;
}
g.drawImage(playermodel, (int)moveX, (int)moveY, null);
g.drawImage(background, 0, 0, null); // comment this out and it works fine, but no background
}
public void repaint(Graphics g)
{
paint(g);
}
public void paintComponent(Graphics g)
{
paint(g);
}
}
--
Now, the problem arises when I employ the g.drawImage the second time for the background. I have an idea of why this might obviously be a problem(it's drawing with the same "g" graphics context and overwriting the first over and over I suspect), I just don't know another way to do it.
Any help would be appreciated, thank you.
- Bob
# 4
The way to do a back buffer is to have a BufferedImage. You can call getGraphics() on that to get a Graphics object.
# 5
Thanks for the response but... there's already a BufferedImage for the second graphic. I use g.drawImage(background, 0, 0, null) to draw it. I've tried using a seperate graphics context for it such as Graphics dublbuff and then going dublbuff.drawImage(background, 0, 0, null) but it does not display.
- Bob
# 6
I can set it as: dublbuff = this.getGraphics() but it results in pretty much the same thing with the screen flickering as it redraws each image over and over. I've made no call for a repaint().
# 7
For a tutorial on double-buffering and other animation techniques have a look at the [url http://www.ddj.com/dept/cpp/184403907?pgno=3]Java Animation Techniques[/url] article. It's a bit dated but still relevant.
# 8
Thanks for the link, appreciate it. That being said I read through it and I'm not seeing a mention of using 2 buffered image using g.drawImage. If anyone has more information on how to put in 2 buffered images in this manner it would be appreciated. - Bob
# 9
I've been struggling with this for several days now and I just can't come up with a solution. I've read all the links and tutorials regarding the subject that I can find and tried all of the proposed solutions but none of them seem to work for me. If anyone has experience with making an animation where you have a background directly behind a moving animation any help would be appreciated. It seems to me like using 2 calls of g.drawImage would be the easiest solution but I just can't get that flicker to go away. If I just use one call of g.drawImage the image is completely steady and does not appear to repaint every time. The very second I put another g.drawImage in there everything goes haywire. I'm certain there must be an easy way out there to make a buffered image that can move around the screen with a background without flickering.
edit - here is the entire working program as I have it right now.
/**
* Write a description of class TheGame here.
*
* @author (your name)
* @version (a version number or a date)
*/
import java.awt.event.*;
import java.lang.*;
import java.applet.Applet;
import javax.swing.JApplet;
import java.awt.*;
import java.awt.geom.*;
import java.awt.image.*;
public class TheGame extends Applet implements Runnable, KeyListener
{
public volatile Thread timer;
boolean downmov = false;
boolean upmov = false;
boolean leftmov = false;
boolean rightmov = false;
int appWidth;
int appHeight;
double moveX = 1;
double moveY = 1;
double moveSpeed = 40;
Graphics g;
Graphics dublbuff;
BufferedImage playermodel;
BufferedImage backtest;
BufferedImage background;
BufferedImage playertesters;
double fpsx = 0.3333;
double fpsy = 0.6666;
public void init()
{
appWidth = getWidth();
appHeight = getHeight();
setLayout(new BorderLayout());
/*background = (BufferedImage) createImage(appWidth, appHeight);
BufferedImage offscrn = (BufferedImage) createImage(appWidth, appHeight);
dublbuff = offscrn.getGraphics();
Graphics2D backg = (Graphics2D) dublbuff;
backg = background.createGraphics();
backg.setColor(Color.green);
backg.fill(new Rectangle(222, 185, 50, 50));*/
}
public BufferedImage makeChar(Graphics g)
{
return playermodel;
}
public void paint(Graphics g)
{
appWidth = getWidth();
appHeight = getHeight();
playermodel = (BufferedImage) createImage(appWidth, appHeight);
Graphics2D g2 = (Graphics2D) g;
g2 = playermodel.createGraphics();
g2.setColor(Color.black);
// original 233, 197, 233, 185
g2.draw(new Line2D.Double(233, 197, 233, 185)); // left helm
g2.draw(new Line2D.Double(247, 197, 247, 185)); // right helm
g2.setColor(Color.blue);
g2.fill(new QuadCurve2D.Double(233, 185, 240, 175, 247, 185)); // top helm curve
g2.setColor(Color.black);
g2.draw(new Line2D.Double(237, 195, 237, 187)); // inside left
g2.draw(new Line2D.Double(243, 195, 243, 187)); // inside right
g2.draw(new Line2D.Double(237, 187, 243, 187)); // inside top
g2.draw(new Line2D.Double(243, 195, 253, 200)); // right arm
g2.draw(new Line2D.Double(246, 201, 257, 207)); // right arm2
g2.draw(new Line2D.Double(237, 195, 223, 203)); // left arm
g2.draw(new Line2D.Double(234, 201, 223, 207)); // left arm2
g2.draw(new Line2D.Double(234, 201, 234, 212)); // body left
g2.draw(new Line2D.Double(246, 201, 246, 212)); // body right
g2.draw(new Line2D.Double(246, 212, 250, 227)); // right leg
g2.draw(new Line2D.Double(234, 212, 230, 227)); // left leg
g2.draw(new Line2D.Double(230, 227, 238, 227)); // top left shoe
g2.draw(new Line2D.Double(250, 227, 242, 227)); // top right shoe
g2.draw(new Line2D.Double(242, 227, 240, 214)); // inner right leg
g2.draw(new Line2D.Double(238, 227, 240, 214)); // inner left leg
g2.draw(new Line2D.Double(223, 210, 223, 180)); // sword vert
g2.draw(new Line2D.Double(222, 210, 222, 180)); // sword vert
g2.setColor(Color.blue);
g2.fill(new Rectangle(253, 200, 10, 13)); // shield
g2.fill(new QuadCurve2D.Double(253, 213, 258, 219, 263, 213));
g2.fill(new Rectangle(234, 185, 3, 13));
g2.fill(new Rectangle(244, 185, 3, 13));
g2.fill(new Rectangle(234, 185, 10, 3));
g2.fill(new Rectangle(234, 195, 10, 3));
Color skin = new Color(255, 228, 196);
g2.setColor(skin);
g2.fill(new Rectangle(238, 188, 5, 8));
if (downmov == true)
{
double tempMove = moveY;
tempMove = moveY + (moveSpeed * fpsx);
g.drawImage(playermodel, (int)moveX, (int)tempMove, null);
pause();
tempMove = moveY;
tempMove = moveY + (moveSpeed * fpsy);
g.drawImage(playermodel, (int)moveX, (int)tempMove, null);
pause();
moveY = moveY + moveSpeed;
}
if (upmov == true)
{
double tempMove = moveY;
tempMove = moveY - (moveSpeed * fpsx);
g.drawImage(playermodel, (int)moveX, (int)tempMove, null);
pause();
tempMove = moveY;
tempMove = moveY - (moveSpeed * fpsy);
g.drawImage(playermodel, (int)moveX, (int)tempMove, null);
pause();
moveY = moveY - moveSpeed;
}
if (leftmov == true)
{
double tempMove = moveX;
tempMove = moveX - (moveSpeed * fpsx);
g.drawImage(playermodel, (int)tempMove, (int)moveY, null);
pause();
tempMove = moveX;
tempMove = moveX - (moveSpeed * fpsy);
g.drawImage(playermodel, (int)tempMove, (int)moveY, null);
pause();
moveX = moveX - moveSpeed;
}
if (rightmov == true)
{
double tempMove = moveX;
tempMove = moveX + (moveSpeed * fpsx);
g.drawImage(playermodel, (int)tempMove, (int)moveY, null);
pause();
tempMove = moveX;
tempMove = moveX + (moveSpeed * fpsy);
g.drawImage(playermodel, (int)tempMove, (int)moveY, null);
pause();
moveX = moveX + moveSpeed;
}
leftmov = false;
rightmov = false;
upmov = false;
downmov = false;
g.drawImage(playermodel, (int)moveX, (int)moveY, null);
//g.drawImage(makeBackground(g), 0, 0, null); // unncomment here to see flicker
//g.drawImage(playermodel, -41, 0, null); // 222 to 263
}
public void paintNow(Graphics g, BufferedImage x)
{
g.drawImage(x, 0, 0, null);
}
public void pause()
{
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void run()
{
Thread check = Thread.currentThread();
while (timer == check)
{
repaint();
try {Thread.currentThread().sleep(20);
} catch (InterruptedException e) {
}
}
}
public void start()
{
timer = new Thread(this);
timer.start();
addKeyListener(this);
}
public void stop()
{
timer = null;
}
public void keyPressed(KeyEvent e)
{
int keyID = e.getKeyCode();
if (keyID == KeyEvent.VK_UP)
{
upmov = true;
}
if (keyID == KeyEvent.VK_DOWN)
{
downmov = true;
}
if (keyID == KeyEvent.VK_LEFT)
{
leftmov = true;
}
if (keyID == KeyEvent.VK_RIGHT)
{
rightmov = true;
}
paint(g);
}
public void keyReleased(KeyEvent e)
{
}
public void keyTyped(KeyEvent e)
{
}
public BufferedImage makeBackground(Graphics g)
{
background = (BufferedImage) createImage(appWidth, appHeight);
Graphics2D g3 = (Graphics2D) g;
g3 = background.createGraphics();
g3.setColor(Color.green); // make background
g3.fill(new Rectangle(222, 185, 50, 50)); // background
return background;
}
public void update(Graphics g)
{
paint(g);
}
public void repaint(Graphics g)
{
paint(g);
}
public void paintComponent(Graphics g)
{
paint(g);
}
}
To view the flickering problem, uncomment the line g.drawImage(background...) near the bottom of the paint function. Feel free to copy/paste and try it out, this version should be workable in terms of moving the guy around the screen.
Note: A few of the functions (such as paintNow) and variables are not used so just ignore them.
Thanks again for any additional help. I have assigned some duke points for this particular question now.
- Bob
# 10
A snippet from the article follows:
The animation thread's sole responsibility is to effect a change on the drawn image's state (e.g., change coordinates, change image) and to call the Component class's repaint method.
Your code is mixing drawing and movement inside the paint method which is not ideal. All animation effects should be performed in the animation thread only. I've refactored your code (using the code from the article as a template) to simplify it quite a bit and to remove the flickering effect you are witnessing.
import java.applet.*;
import java.awt.event.*;
import java.awt.*;
import java.awt.geom.*;
import java.awt.image.*;
public class TheUpdatedGame extends Applet implements Runnable {
private Thread thread;
private Image offscreenImage;
private Graphics2D offscreenGraph;
private Dimension gameDim;
private Color skin = new Color(255, 228, 196);
private boolean downmov;
private boolean upmov;
private boolean leftmov;
private boolean rightmov;
int moveY;
int moveX;
public void init()
{
if (getBackground().equals(Color.white))
{
setBackground(Color.GREEN);
}
gameDim = getSize();
/* Create the offscreen image and graphics context */
offscreenImage = createImage(gameDim.width,gameDim.height);
offscreenGraph = (Graphics2D) offscreenImage.getGraphics();
addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent evt)
{
int keyId = evt.getKeyCode();
switch(keyId)
{
case KeyEvent.VK_UP:
upmov = true;
break;
case KeyEvent.VK_DOWN:
downmov = true;
break;
case KeyEvent.VK_LEFT:
leftmov = true;
break;
case KeyEvent.VK_RIGHT:
rightmov = true;
break;
}
}
});
requestFocus();
}
public void start()
{
if (thread == null)
{
/* Start the animation thread */
thread = new Thread(this);
thread.start();
}
}
public void stop()
{
/* Stop the animation thread */
if (thread != null)
{
thread = null;
}
}
public synchronized void run()
{
while(thread != null)
{
if (downmov)
{
moveY += 5;
}
else if (upmov)
{
moveY -= 5;
}
else if (leftmov)
{
moveX -= 5;
}
else if (rightmov)
{
moveX += 5;
}
repaint();
leftmov = false;
rightmov = false;
upmov = false;
downmov = false;
try
{
wait();
Thread.sleep(10); //optional
}
catch(InterruptedException ie)
{
//no op
}
}
}
public void update(Graphics g)
{
/* Clear the offscreen graphics context */
offscreenGraph.setColor(getBackground());
offscreenGraph.fillRect(0,0,gameDim.width,gameDim.height);
/* Draw on the offscreen graphics context */
// original 233, 197, 233, 185
offscreenGraph.draw(new Line2D.Double(233, 197, 233, 185)); // left helm
offscreenGraph.draw(new Line2D.Double(247, 197, 247, 185)); // right helm
offscreenGraph.setColor(Color.blue);
offscreenGraph.fill(new QuadCurve2D.Double(233, 185, 240, 175, 247, 185)); // top helm curve
offscreenGraph.setColor(Color.black);
offscreenGraph.draw(new Line2D.Double(237, 195, 237, 187)); // inside left
offscreenGraph.draw(new Line2D.Double(243, 195, 243, 187)); // inside right
offscreenGraph.draw(new Line2D.Double(237, 187, 243, 187)); // inside top
offscreenGraph.draw(new Line2D.Double(243, 195, 253, 200)); // right arm
offscreenGraph.draw(new Line2D.Double(246, 201, 257, 207)); // right arm2
offscreenGraph.draw(new Line2D.Double(237, 195, 223, 203)); // left arm
offscreenGraph.draw(new Line2D.Double(234, 201, 223, 207)); // left arm2
offscreenGraph.draw(new Line2D.Double(234, 201, 234, 212)); // body left
offscreenGraph.draw(new Line2D.Double(246, 201, 246, 212)); // body right
offscreenGraph.draw(new Line2D.Double(246, 212, 250, 227)); // right leg
offscreenGraph.draw(new Line2D.Double(234, 212, 230, 227)); // left leg
offscreenGraph.draw(new Line2D.Double(230, 227, 238, 227)); // top left shoe
offscreenGraph.draw(new Line2D.Double(250, 227, 242, 227)); // top right shoe
offscreenGraph.draw(new Line2D.Double(242, 227, 240, 214)); // inner right leg
offscreenGraph.draw(new Line2D.Double(238, 227, 240, 214)); // inner left leg
offscreenGraph.draw(new Line2D.Double(223, 210, 223, 180)); // sword vert
offscreenGraph.draw(new Line2D.Double(222, 210, 222, 180)); // sword vert
offscreenGraph.setColor(Color.blue);
offscreenGraph.fill(new Rectangle(253, 200, 10, 13)); // shield
offscreenGraph.fill(new QuadCurve2D.Double(253, 213, 258, 219, 263, 213));
offscreenGraph.fill(new Rectangle(234, 185, 3, 13));
offscreenGraph.fill(new Rectangle(244, 185, 3, 13));
offscreenGraph.fill(new Rectangle(234, 185, 10, 3));
offscreenGraph.fill(new Rectangle(234, 195, 10, 3));
offscreenGraph.setColor(skin);
offscreenGraph.fill(new Rectangle(238, 188, 5, 8));
/* Draw the offscreen image to the visible graphics context */
g.drawImage(offscreenImage,moveX,moveY,this);
synchronized(this)
{
notifyAll();
}
}
public void paint(Graphics g)
{
update(g);
}
public void destroy()
{
/* Explicitly dispose of the offscreen graphics context */
if (offscreenGraph != null)
{
offscreenGraph.dispose();
}
}
}
# 11
Thank you very much for the reply, but there is still the issue of drawing a second image. For the background you used:
if (getBackground().equals(Color.white))
{
setBackground(Color.GREEN);
}
I'm trying to figure out how to put something else in there, say an object within the game that is part of the background. When I try to draw within the init(), I always get a null error, and like you said I'm not really supposed to draw outside the paint() method. Regardless you earned some dukes there for the time you spent working on that(and it is a step in the right direction), thank you again.
- Bob
# 12
So to sum up and ask the original question - anyone know how to draw 2 images seperately or something similar without causing the screen to flicker? - Bob
# 13
What you are trying to achieve is cast-based (sprite) animation. You'll have to create an image file of the game character. Do the same for the background you wish to display. Ensure the game character image has a transparent background. Then simply draw both images on the graphics context.
import java.applet.*;
import java.awt.event.*;
import java.awt.*;
public class SpriteGame extends Applet implements Runnable {
private Thread thread;
private Image offscreenImage;
private Graphics2D offscreenGraph;
private Dimension gameDim;
private boolean downmov;
private boolean upmov;
private boolean leftmov;
private boolean rightmov;
private int moveY = 233;
private int moveX = 233;
private Image backgroundImage;
private Image playerImage;
public void init()
{
MediaTracker tracker = new MediaTracker(this);
backgroundImage = getImage(getCodeBase(),"background.gif");
playerImage = getImage(getCodeBase(),"player.gif");
tracker.addImage(backgroundImage,0);
tracker.addImage(playerImage,0);
gameDim = getSize();
/* Create the offscreen image and graphics context */
offscreenImage = createImage(gameDim.width,gameDim.height);
offscreenGraph = (Graphics2D) offscreenImage.getGraphics();
addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent evt)
{
int keyId = evt.getKeyCode();
switch(keyId)
{
case KeyEvent.VK_UP:
upmov = true;
break;
case KeyEvent.VK_DOWN:
downmov = true;
break;
case KeyEvent.VK_LEFT:
leftmov = true;
break;
case KeyEvent.VK_RIGHT:
rightmov = true;
break;
}
}
});
requestFocus();
}
public void start()
{
if (thread == null)
{
/* Start the animation thread */
thread = new Thread(this);
thread.start();
}
}
public void stop()
{
/* Stop the animation thread */
if (thread != null)
{
thread = null;
}
}
public synchronized void run()
{
while(thread != null)
{
if (downmov)
{
moveY += 5;
downmov = false;
}
else if (upmov)
{
moveY -= 5;
upmov = false;
}
else if (leftmov)
{
moveX -= 5;
leftmov = false;
}
else if (rightmov)
{
moveX += 5;
rightmov = false;
}
repaint();
try
{
wait();
Thread.sleep(10); //optional
}
catch(InterruptedException ie)
{
//no op
}
}
}
public void update(Graphics g)
{
/* Clear the offscreen graphics context */
offscreenGraph.setColor(getBackground());
offscreenGraph.fillRect(0,0,gameDim.width,gameDim.height);
/* Draw on the offscreen graphics context */
offscreenGraph.drawImage(backgroundImage,0,0,this);
offscreenGraph.drawImage(playerImage,moveX,moveY,this);
/* Draw the offscreen image to the visible graphics context */
g.drawImage(offscreenImage,0,0,this);
synchronized(this)
{
notifyAll();
}
}
public void paint(Graphics g)
{
update(g);
}
public void destroy()
{
/* Explicitly dispose of the offscreen graphics context */
if (offscreenGraph != null)
{
offscreenGraph.dispose();
}
}
}
# 14
http://www.geocities.com/javagameapi/
# 15
Thanks paternostro! That works =) Points awarded. - Bob