How to use double buffering inside a thread and applet - java

I have a question about when paint and update method is called??
i have game applet where i want to use double buffering.But i cant use it.the problem is
In my game there is a ball which is moving inside run() method.I want to know how to use double buffering to swap the offscreen image and current image.Someone plz help.
And when there is both update() and paint() method.which are called first,when and why ???

A method you can use is to add a Canvas to the applet and then create a buffer strategy for that canvas. Abstracts the code, and you may get hardware acceleration.
The code is here: http://www.gamedev.net/community/forums/topic.asp?topic_id=405663 -- extend AppletGameCore and define your own subclass that implements the required methods.
import java.awt.Canvas;
import java.awt.Graphics2D;
import java.awt.Dimension;
import java.awt.image.BufferStrategy;
import java.applet.Applet;
/**
*AppletGameCore.java
*#author David Graham
*/
public abstract class AppletGameCore extends Applet implements Runnable
{
private BufferStrategy bufferStrategy;
private Canvas drawArea;/*Drawing Canvas*/
private boolean stopped = false;/*True if the applet has been destroyed*/
private int x = 0;
public void init()
{
Thread t = new Thread(this);
drawArea = new Canvas();
setIgnoreRepaint(true);
t.start();
}
public void destroy()
{
stopped = true;
/*Allow Applet to destroy any resources used by this applet*/
super.destroy();
}
public void update()
{
if(!bufferStrategy.contentsLost())
{
//Show bufferStrategy
bufferStrategy.show();
}
}
//Return drawArea's BufferStrategy
public BufferStrategy getBufferStrategy()
{
return bufferStrategy;
}
//Create drawArea's BufferStrategies
public void createBufferStrategy(int numBuffers)
{
drawArea.createBufferStrategy(numBuffers);
}
//Subclasses should override this method to do any drawing
public abstract void draw(Graphics2D g);
public void update(Graphics2D g)
{
g.setColor(g.getBackground());
g.fillRect(0,0,getWidth(),getHeight());
}
//Update any sprites, images, or primitives
public abstract void update(long time);
public Graphics2D getGraphics()
{
return (Graphics2D)bufferStrategy.getDrawGraphics();
}
//Do not override this method
public void run()
{
drawArea.setSize(new Dimension(getWidth(),getHeight()));
add(drawArea);
createBufferStrategy(2);
bufferStrategy = drawArea.getBufferStrategy();
long startTime = System.currentTimeMillis();
long currTime = startTime;
//animation loop
while(!stopped)
{
//Get time past
long elapsedTime = System.currentTimeMillis()-currTime;
currTime += elapsedTime;
//Flip or show the back buffer
update();
//Update any sprites or other graphical objects
update(elapsedTime);
//Handle Drawing
Graphics2D g = getGraphics();
update(g);
draw(g);
//Dispose of graphics context
g.dispose();
}
}
}

Related

How to overwrite a BufferedImage drawn to JPanel outside PaintComponent() with getGraphics()

I am working on a simple 2D game. Each tick, I want to check an effects queue that will start a thread for a certain effect(fading transitions, audio fade in and out, etc). For example, pressing "Play" on the menu screen will add a "FadeOut" message to this queue, which will be processed and start a thread to draw a black rectangle with an increasing alpha value over my GamePanel.
I'm overriding paintComponent() and sending my Graphics object to my GameStateManager, which passes along the Graphics object to the current states' draw(). I currently don't have an effects state (which maybe I should) to route the paintComponent() graphics object to, but I do pass my gamepanel to my effects thread, where I can use getGraphics() to draw on it. Drawing a rectangle to the GamePanel directly just causes flickering, as the gameloop is still rendering the game.
I found I can draw a black rectangle with increasing alpha to a BufferedImage, set the composite to AlphaComposite.Src (which causes the new draw to replace the old) then draw the BufferedImage over the game panel. The problem is the BufferedImages drawn to the game panel don't get overridden each draw, so the fade out happens really quickly because these black BufferedImages of various alphas just stack on each other.
I wrote this short program to test composite settings and see what is getting overridden. All drawing is done in the draw(), which would be my run() in the effects thread.
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class ScratchPad extends JPanel implements Runnable
{
private JFrame oFrame = null;
private Thread oGameThread = null;
private Graphics2D oPanelGraphics = null;
private Graphics2D oImageGraphics = null;
private BufferedImage oImage = null;
public static void main(String args[]) throws Exception
{
new ScratchPad();
}
public ScratchPad()
{
createFrame();
initPanel();
addAndShowComponents();
oGameThread = new Thread(this, "Game_Loop");
oGameThread.start();
}
private void addAndShowComponents()
{
oFrame.add(this);
oFrame.setVisible(true);
}
private void initPanel()
{
this.setOpaque(true);
this.setBackground(Color.cyan);
}
private void createFrame()
{
oFrame = new JFrame("Fade");
oFrame.setSize(700, 300);
oFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
oFrame.setLocationRelativeTo(null);
}
public void run()
{
oImage = new BufferedImage(200, 200, BufferedImage.TYPE_INT_ARGB);
while(true)
{
try
{
draw();
Thread.sleep(100);
}
catch(InterruptedException e)
{
}
}
}
private void draw()
{
oPanelGraphics = (Graphics2D)this.getGraphics();
oImageGraphics = oImage.createGraphics();
oImageGraphics.setComposite(AlphaComposite.Src);
oImageGraphics.setColor(new Color(0,0,0,90));
oImageGraphics.fillRect(0, 0, oImage.getWidth(), oImage.getHeight());
oPanelGraphics.drawImage(oImage, 10, 10, null);
oImageGraphics.setColor(new Color(0,0,0,60));
oImageGraphics.fillRect(0, 0, oImage.getWidth(), oImage.getHeight());
oPanelGraphics.drawImage(oImage, 220, 10, null);
oImageGraphics.setColor(new Color(0,0,0,30));
oImageGraphics.fillRect(0, 0, oImage.getWidth(), oImage.getHeight());
oPanelGraphics.drawImage(oImage, 430, 10, null);
// Drawing this image over location of first image, should overwrite first
// after setting composite to 'Src'
oPanelGraphics.setComposite(AlphaComposite.Src);
oImageGraphics.setColor(new Color(0,0,0,10));
oImageGraphics.fillRect(0, 0, oImage.getWidth(), oImage.getHeight());
oPanelGraphics.drawImage(oImage, 10, 10, null);
oImageGraphics.dispose();
oPanelGraphics.dispose();
}
} // end class
What's interesting is setting the composite on 'oPanelGraphics' causes any alpha to the BufferedImage to go away, resulting in a fully opaque black image being drawn over the image that was previously there. Even setting the color to something other than black doesn't have an effect.
What's also interesting is setting the composite for the BufferedImage to:
oImageGraphics.setComposite(AlphaComposite.SrcIn);
causes nothing to be shown. The Oracle documentation on compositing graphics in Java2D states this for 'SrcIn':
"If pixels in the source and the destination overlap, only the source pixels in the overlapping area are rendered."
So, I would expect this to have the same behavior I get with AlphaComposite.Src.
Maybe someone out there can shed some light on whats going on with these composites, and how I could achieve my desired effect.
There are a number issues with what you "seem" to be trying to do
Don't call getGraphics on a component. This can return null and only returns a snapshot of what was last painted during a Swing paint cycle. Anything you paint to it will be erased on the next paint cycle
You should also never dispose of Graphics context you did not create, doing so could effect other components that are painted by Swing
Painting is compounding, this means that painting to the same Graphics context (or BufferedImage) over and over again, will continue to apply those changes over the top of what was previously painted
You also don't seem to have a concept of how animation should work. Instead of trying to paint your fade effect in a single pass, where the results can't be applied to the screen, you need to apply a phase on each cycle and allow that to be updated to the screen before the next pass runs.
The following is a really basic example of what I'm talking about. It takes a "base" image (this could be the "base" state of the game, but I've used a static image) and the paints effects over the top.
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private Engine engine;
private Image frame;
public TestPane() {
engine = new Engine();
engine.setEngineListener(new EngineListener() {
#Override
public void updateDidOccur(Image img) {
frame = img;
repaint();
}
});
engine.start();
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
engine.addEffect(new FadeOutEffect(Color.BLACK));
}
});
}
#Override
public Dimension getPreferredSize() {
return engine.getSize();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (frame != null) {
Graphics2D g2d = (Graphics2D) g.create();
g2d.drawImage(frame, 0, 0, null);
g2d.dispose();
}
}
}
public interface EngineListener {
public void updateDidOccur(Image img);
}
public class Engine {
// This is the "base" image, without effects
private BufferedImage base;
private Timer timer;
private EngineListener listener;
private List<Effect> effects = new ArrayList<Effect>(25);
public Engine() {
try {
base = ImageIO.read(new File("/Volumes/Big Fat Extension/Dropbox/MegaTokyo/megatokyo_omnibus_1_3_cover_by_fredrin-d4oupef 50%.jpg"));
} catch (IOException ex) {
ex.printStackTrace();
}
timer = new Timer(10, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
int width = base.getWidth();
int height = base.getHeight();
BufferedImage frame = new BufferedImage(width, height, base.getType());
Graphics2D g2d = frame.createGraphics();
g2d.drawImage(base, 0, 0, null);
Iterator<Effect> it = effects.iterator();
while (it.hasNext()) {
Effect effect = it.next();
if (!effect.applyEffect(g2d, width, height)) {
it.remove();
}
}
g2d.dispose();
if (listener != null) {
listener.updateDidOccur(frame);
}
}
});
}
public void start() {
timer.start();
}
public void stop() {
timer.stop();
}
public void addEffect(Effect effect) {
effects.add(effect);
}
public void setEngineListener(EngineListener listener) {
this.listener = listener;
}
public Dimension getSize() {
return base == null ? new Dimension(200, 200) : new Dimension(base.getWidth(), base.getHeight());
}
}
public interface Effect {
public boolean applyEffect(Graphics2D context, int width, int height);
}
public class FadeOutEffect implements Effect {
private int tick = 0;
private Color fadeToColor;
public FadeOutEffect(Color fadeToColor) {
this.fadeToColor = fadeToColor;
}
#Override
public boolean applyEffect(Graphics2D context, int width, int height) {
tick++;
float alpha = (float) tick / 100.0f;
if (alpha > 1.0) {
return false;
}
Graphics2D g2d = (Graphics2D) context.create();
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha));
g2d.setColor(fadeToColor);
g2d.fillRect(0, 0, width, height);
g2d.dispose();
return true;
}
}
}
Remember, every effect or change should be applied within the same "main loop", this means you shouldn't have multiple threads, in fact, since Swing is not thread safe, you should avoid having any additional threads if possible. This example make use of a Swing Timer to act as the "main loop" because the ActionListers actionPerformed method is called within the context of the EDT, making it safe to update the UI from. It also provides a simple synchronisation method, as the UI can't be painted while the actionPerformed method is been called

Rendering a rectangle with multiple classes

I am making a simple game project and I am having a problem when trying to create a rectangle that moves across the screen.
Here is the main class:
`public class Main extends Canvas implements Runnable {
private static final long serialVersionUID = 1L;
private JFrame frame;
boolean running = false;
Graphics g;
static int HEIGHT = 500;
static int WIDTH = HEIGHT * 16 / 9;
SoundHandler sh = new SoundHandler();
//Game state manager
private GameStateManager gsm;
public Main()
{
//window
frame = new JFrame("Game");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(this, BorderLayout.CENTER);
frame.pack();
frame.setSize(WIDTH, HEIGHT);
frame.setResizable(false);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
init();
}
void init()
{
gsm = new GameStateManager();
sh.playMusic("Undertale.wav", 1);
}
public synchronized void start(){
running = true;
new Thread(this).start();
}
public synchronized void stop(){
running = false;
}
//game loop
public void run()
{
//init time loop variables
long lastLoopTime = System.nanoTime();
final int TARGET_FPS = 60;
final long OPTIMAL_TIME = 1000000000 / TARGET_FPS;
double lastFpsTime = 0;
int fps = 0;
while(running)
{
//work out how long its been since last update
//will be used to calculate how entities should
//move this loop
long now = System.nanoTime();
long updateLength = now - lastLoopTime;
lastLoopTime = now;
double delta = updateLength / ((double)OPTIMAL_TIME);
//update frame counter
lastFpsTime += updateLength;
fps++;
//update FPS counter
if(lastFpsTime >= 1000000000)
{
System.out.println("FPS " + fps);
lastFpsTime = 0;
fps = 0;
}
//game updates
update(delta);
//draw
draw(g);
try{
Thread.sleep((lastLoopTime - System.nanoTime() + OPTIMAL_TIME)/1000000 );
}catch(InterruptedException e){
System.out.println("Error in sleep");
}
}
}
private void update(double delta)
{
//updates game state code
gsm.update(delta);
}
public void draw(Graphics g)
{
gsm.draw(g);
}`
here is the class I want to draw the rectangle with
package me.mangodragon.gamestate;
import java.awt.Graphics;
public class MainState extends GameState{
int x;
public MainState(GameStateManager gsm){
this.gsm = gsm;
}
public void init() {
}
public void update(double delta) {
x += 2 * delta;
}
public void draw(Graphics g) {
g.drawRect(x, 0, 50, 50);
g.dispose();
}
public void keyPressed(int k) {
}
public void keyReleased(int k) {
}
}
I keep getting this error:
Exception in thread "Thread-4" java.lang.NullPointerException
at me.mangodragon.gamestate.MainState.draw(MainState.java:22)
at me.mangodragon.gamestate.GameStateManager.draw(GameStateManager.java:37)
at me.mangodragon.main.Main.draw(Main.java:118)
at me.mangodragon.main.Main.run(Main.java:100)
at java.lang.Thread.run(Unknown Source)
I tried to fix it, but I could not locate the problem.
Thanks!
You never assign anything to g (Graphics). Now, before you run off and try and figure out how you might do that, I'd highly, highly recommend you get rid of this variable, it's going to cause you too many issues.
Normally, when the system wants your component to painted, it calls your paint method and passes you the Graphics context which it wants you to paint to. This approach is known as passive painting, as the paint requests come at random times, which isn't really what you want. Another issue is java.awt.Canvas isn't double buffered, which will cause flickering to occur as your component is updated.
You might want to take a look at Painting in AWT and Swing and Performing Custom Painting for more details
You could use a JPanel instead, which is double buffered, but the main reason for using java.awt.Canvas is so you can make use the BufferStrategy API. This not only provides double buffering, but also provides you with a means by which you can take direct control over the painting process (or active painting).
See BufferStrategy and BufferStrategy and BufferCapabilities for more details
You defined g as such:
Graphics g;
But never gave it a value.
This is not how you should be drawing shapes, anyways. Instead, override the paint method (inherited from Canvas) in class Main:
#Override
public void paint(Graphics2D g) {
//Drawing code goes in here. This runs whenever the Canvas is rendered.
}
Then, when you want to update it, such as in your while loop, run
this.repaint(); //note that this doesn't take arguments
If you want to use the draw(Graphics g) method in the other class, call it in paint().
public void paint(Graphics2D g) {
gsm.draw(g);
}
The problem is that you have not defined g, so it is null. And for the most part, you are never supposed to create a new Graphics object, but instead get it from somewhere.
Since you are inheriting a Canvas, this can be very easily done.
First, you should change your draw method to be like this.
private void draw() {
BufferStrategy bs = this.getBufferStrategy();
if (bs == null) {
this.createBufferStrategy(3);
return;
}
Graphics g = bs.getDrawGraphics();
// Draw your game here, using the g declared above
g.dispose();
bs.show();
}
The first few lines create something called a BufferStrategy which you can read more about here but it essentially lets Java render the next couple frames ahead of schedule so that you don't see any flickering.
From the BufferStrategy, you can get the Graphics object to draw on.
And, finally, you have to dispose of the Graphics object, and then show the Buffer so that everything you did shows on the screen.

public class extends JPanel

I am getting a error that looks like this,
run:
Exception in thread "main" java.lang.ExceptionInInitializerError
at ao.Game.main(Game.java:11)
Caused by: java.lang.RuntimeException: Uncompilable source code -
ao.Panel is not abstract and does not override abstract method
keyReleased(java.awt.event.KeyEvent) in java.awt.event.KeyListener
at ao.Panel.<clinit>(Panel.java:15)
... 1 more
Java Result: 1
BUILD SUCCESSFUL (total time: 3 seconds)
I can't figure out what the problem with the public class is.
There are 2 separate files below.
Game.java
Panel.java
First File:
package ao;
import ao.Panel;
import javax.swing.JFrame;
public class Game {
public static void main(String[] args) {
JFrame frame = new JFrame("2D Shooter Pre-Alpha");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(new Panel());
frame.pack();
frame.setResizable(false);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
Next File:
package ao;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.KeyListener;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import javax.swing.JPanel;
/**
*
* #author andyoppenheimer
*/
public class Panel extends JPanel implements Runnable, KeyListener {
// panel dimensions
public static final int WIDTH = 320;
public static final int HEIGHT = 240;
public static final int SCALE = 2;
// main loop
private Thread thread;
private boolean running = false;
private int fps = 60;
private long targetTime = 1000 / fps;
// drawing
private Graphics2D g;
private BufferedImage image;
public Panel() {
setPreferredSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
setFocusable(true);
requestFocus();
}
public void addNotify() {
super.addNotify();
if (thread == null) {
running = true;
addKeyListener(this);
thread = new Thread(this);
thread.start();
}
}
public void init() {
image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
g = (Graphics2D) image.getGraphics();
}
public void update() {
}
public void draw() {
g.clearRect(0, 0, WIDTH, HEIGHT);
}
public void drawToScreen() {
Graphics g2 = getGraphics();
g2.drawImage(image, 0, 0, WIDTH * SCALE, HEIGHT * SCALE, null);
g2.dispose();
}
public void run() {
init();
long start;
long elapsed;
long wait;
while (running == true) {
start = System.nanoTime();
update();
draw();
drawToScreen();
elapsed = System.nanoTime() - start;
wait = targetTime - elapsed / 1000000;
if (wait < 0) {
wait = 5;
}
try {
Thread.sleep(wait);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public void KeyPressed(KeyEvent k) {
}
public void KeyReleased(KeyEvent k) {
}
public void KeyTyped(KeyEvent k) {
}
}
In java methods starts with lower case keyTyped keyReleased and keyPressed so you are not overriding KeyListener methods.
You can annotate a method with #Override
this causes a compile error if it doesn't actually override. Section 9.6.1.4 of the JLS says:
The annotation type Override supports early detection of such problems. If a method declaration is annotated with the annotation #Override, but the method does not in fact override any method declared in a superclass, a compile-time error will occur.
Your class definition will lead to possible potential bugs cause
public class Panel extends JPanel implements Runnable, KeyListener
Calling Panel it's confusing cause already exists java.awt.Panel so call it different. Implementing multiple interface like that brokes Single Responsability Principle . A possible solution is to make inner classes or anonymous classes. For sure if you don't override a method is not necessary to extends JPanel. Take care that if you use KeyListener components must be in focus and be focusable and bind it to all keys, instead you can use KeyBindings. Don't use requestFocus instead use requestFocusInWindow() if you read api it says it's discouraged.
The class implements the KeyListener interface but does not provide an implementation for the keyReleased, keyPressed and keyTyped methods specified on the interface. Instead it provides implementations for: KeyReleased, KeyPressed and KeyTyped which are not properly cased.

Main.GamePanel is not abstract and does not override abstract method keyReleased(java.awt.event.KeyEvent) in java.awt.event.KeyListener

I was doing a tutorial online because I wanted to make a 2d side scroller, and I got this exact error. I have googled it but came up with nothing. I tried looking for a typo and it looks clean, its not giving me an error anywere else in the code. I do not know where to start. If you could explaing to me what the error is and how i fix it then that would be amazing.
package Main;
import GameState.GameStateManager;
import javax.swing.JPanel;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
public class GamePanel extends JPanel implements Runnable, KeyListener{
public static final int WIDTH = 320;
public static final int HIGHT = 240;
public static final int SCALE = 2;
//game thread
private Thread thread;
private boolean running;
private int FPS = 60;
private long targetTime = 1000/FPS;
//image
private BufferedImage image;
private Graphics2D g;
//game state manager
private GameStateManager gsm;
public GamePanel(){
super();
setPreferredSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
setFocusable(true);
requestFocus();
}
public void addNotify(){
super.addNotify();
if (thread == null) {
thread = new Thread(this);
addKeyListener(this);
thread.start();
}
}
private void init() {
image = new BufferedImage(WIDTH, HIGHT, BufferedImage.TYPE_INT_RGB);
g = (Graphics2D) image.getGraphics();
running = true;
gsm = new GameStateManager();
}
public void run(){
init();
long start, elapsed, wait;
//game loop
while(running) {
start = System.nanoTime();
update();
draw();
drawToScreen();
elapsed = System.nanoTime() - start;
wait = targetTime - elapsed / 1000000;
try
{
Thread.sleep(wait);
}
catch(Exception e)
{
e.printStackTrace();
}//end of try catch
}
}
private void update()
{
gsm.update();
}
private void draw()
{
gsm.draw(g);
}
private void drawToScreen()
{
Graphics g2 = getGraphics();
g2.drawImage(image, 0, 0, null);
g2.dispose();
}
public void KeyPressed(KeyEvent key)
{
gsm.keyPressed(key.getKeyCode());
}
public void KeyReleased(KeyEvent key)
{
gsm.keyReleased(key.getKeyCode());
}
}
The compiler error message tells you exactly what's wrong: your class implements the KeyListener interface but does not implement all the necessary methods of the interface. Solution: be sure to implement all the necessary methods as per the KeyListener API. Also be sure to use the #Override annotation to make sure that your overrides are correct.
Having said that, I'm going to recommend that you not use KeyListeners for most key board input with Swing applications, that it is a low-level listener and should be avoided in favor of higher level constructs such as key bindings. Also, Swing GUI's should avoid use of update(...) method overrides as that is more of an AWT construct.
Your KeyReleased(KeyEvent key) method must start with small letter 'k' like keyReleased(KeyEvent key). Java is case sensitive.
You may also required to override other methods of KeyListener interface.
Also add #Override annotation (as suggested by #Hovercraft Full Of Eels) to the method when you want to override a super method. That way IDE's will give you hint's while coding.

Java Lang nullpointer exception in animation example?

import java.awt.*;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
public class Images extends JFrame {
public static void main(String[] args) {
DisplayMode dm = new DisplayMode(800,600,32, DisplayMode.REFRESH_RATE_UNKNOWN);
Images I = new Images();
I.run(dm);
}
private Screen s;
private Image bg;
private Image pic;
private boolean nLoaded;
Animation a;
public void run(DisplayMode dm)
{
nLoaded = false;
s = new Screen();
try{
s.Setfullscreen(dm, this);
LoadPics();
MovieLoop();
try{
Thread.sleep(50000);
}catch(Exception ex){}
}finally{
s.restoreScreen();
}
}
public void MovieLoop(){
long startingtime = System.currentTimeMillis();
long cumTime = startingtime;
while(cumTime-startingtime < 5000)
{
long timepassed = System.currentTimeMillis() - cumTime;
cumTime += timepassed;
a.update(timepassed);
Graphics g = s.getFullScreenWindow().getGraphics();
draw(g);
g.dispose();
try{
Thread.sleep(20);
}catch(Exception ex){}
}
}
public void draw(Graphics g)
{
g.drawImage(bg, 0,0, null);
g.drawImage(a.getImage(),0,0,null); }
//Create Load Pictures
public void LoadPics()
{
bg = new ImageIcon("C:\\Gamepics\\BackgroundImage.jpg").getImage();
pic = new ImageIcon("C:\\Gamepics\\SmileyIcon3.png").getImage();
nLoaded = true;
repaint();
}
public void paint(Graphics g){
if(g instanceof Graphics2D){
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
}
if(nLoaded)
{
g.drawImage(bg, 0, 0, null);
g.drawImage(pic, 300,300, null);
}
}
}
Im not understanding what I did wrong ive overlooked everything the best I can. Im just practicing a simple animation and I keep getting 3 null pointer exceptions.
Ive researched the best I can and apparently NullPointerExceptions in java have to do with trying to get the size of null arrays? The compiler hasn't marked any of my lists as problems so im a little confused. Any help would be greatly appreciated. All of the errors are commented out. There are three of them and they are in the Images class
Errors:
Exception in thread "main" java.lang.NullPointerException
at Images.MovieLoop(Images.java:45)
at Images.run(Images.java:26)
at Images.main(Images.java:8)
Animation Class
import java.awt.Image;
import java.util.*;
public class Animation {
private ArrayList scenes;
private int sceneindex;
private long movietime;
private long totaltime;
//CONSTRUCTOR
public Animation(){
scenes = new ArrayList();
totaltime =0;
start();
}
//Add scene to array list and sets time for each scene
//For example. If you have 3 scenes you would add t to total time three times. So if you had
//3 Scenes, one for 1s, one for 2s, one for 3s. Total time would be 6s. and you would call addscene 3 times
public synchronized void addScene(Image i, long t)
{
totaltime+=t;
scenes.add(new OneScene(i, totaltime));
}
//start animation from beggininign inignngingingnig
public synchronized void start(){
movietime = 0;
sceneindex = 0;
}
//change scenes
//movie time is the sum of all the time passed from last update
//If you have more than one scene. timepassed gets added to movietime.
//if movietime is greater than or equal to total time(ie. animation is complete) restart the animation
public synchronized void update(long timepassed)
{
if(scenes.size() > 1){
movietime += timepassed;
if(movietime >= totaltime)
{
movietime = 0;
sceneindex = 0;
}
while(movietime > getScene(sceneindex).endTime)
{
sceneindex++;
}
}
}
public synchronized Image getImage(){
if(scenes.size() == 0){
return null;}
else{
return getScene(sceneindex).pic;
}
}
//Getscene
private OneScene getScene(int x){
return (OneScene)scenes.get(x);
}
//Scenes are gonna be
private class OneScene{
Image pic;
long endTime;
public OneScene(Image pic, long endTime)
{
this.pic = pic;
this.endTime = endTime;
}
}
}
I included the animation class because the compiler is highlighting these three method calls as the source of the problem
a.update(timepassed);
MovieLoop();
I.run(dm);
Please Note: This is a really long comment
Let's start with Graphics g = s.getFullScreenWindow().getGraphics(); - getGraphics is NEVER a good idea, this can return null.
You should NEVER try and update any UI component from any thread other the EDT and you should NEVER draw directly to it in this manner, instead, you should be using paintComponent.
You should NEVER dispose of any Graphics context that you did not create yourself, this will prevent other components from been painted.
You should avoid overriding paint, especially of a top level container, if for no other reason, it's not double buffered (the top level container), and you will also be painting over any other child components.
Check out Performing Custom Painting for more details.
You should try using ImageIO instead of ImageIcon. ImageIO will throw exceptions if it can't read the file, where as ImageIcon simply fails silently, no very helpful.

Categories