The first thing I did was to add a KeyListener to my JFrame. Java's KeyListener requires three methods: keyPressed, keyReleased, and keyTyped. For my purposes, I really only care about keyPressed, but all three have to be defined. Here is my code for adding the KeyListener.
public static void main(String[] args)
{
GameWindow g = new GameWindow();
frame = new JFrame("Sword Sword Revolution");
frame.add(g);
frame.addKeyListener(new KeyListener(){
public void keyPressed(KeyEvent e) { processKey(e.getKeyCode()); }
public void keyReleased(KeyEvent e) { }
public void keyTyped(KeyEvent e) { }
});
frame.setSize(600,600);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
I chose to just add a new KeyListener() call directly in the addKeyListener call, but you can also create a KeyListener object separately by writing a custom class that implements KeyListener.
I just ignore keyReleased and keyTyped by not putting anything in their brackets. However, when the user first presses a key down, I want to register what key they pressed and then do some action. Therefore, in keyPressed, I make a call to a function processKey, and pass it an integer representation of the key that was pressed.
public static void processKey(int key)
{
switch(key)
{
case 37:
mainWindow.setIcon(left);
break;
case 38:
mainWindow.setIcon(forward);
break;
case 39:
mainWindow.setIcon(right);
break;
case 40:
mainWindow.setIcon(backward);
break;
}
frame.repaint();
}
My processKey function takes an integer, and then uses a switch statement to decide what to do. The arrow keys represent numbers 37-40, so I have specific cases for each of those. I do not care if the user presses any other button, so I do not need a default case. This is fairly simple, I just want to change the animated GIF being displayed. I also have to make sure to repaint the frame so the change appears on screen. There is just one problem now. When I press a button, the picture changes, but it stays changed. What I want is for the new animation to be run once, and then return to the waiting animation.
In order to handle this, Java has something called a ScheduledExecutorService. This is a class that allows you to schedule a future event, and it requires two objects: a ScheduledExecutorService object and a ScheduledFuture object. I create both of these objects at the top of my class, and then instantiate them after my switch statement.
scheduledExecutorService = Executors.newScheduledThreadPool(1);
scheduledFuture = scheduledExecutorService.schedule(new Runnable() {
public void run() {
mainWindow.setIcon(ready);
}
},
1,
TimeUnit.SECONDS);
scheduledExecutorService.shutdown();
Basically, what this code does is creates a thread, waits 1 second, and then runs the run function of my Runnable object. Once that completes I am free to shutdown the thread.
There is still an issue, however. If I press more than one arrow key during that 1 second, the animation is off. The second animation interrupts the first one, but the scheduler is still running in the background. So 1 second after the first button gets pressed, the last button pressed gets interrupted. I want to cancel the scheduled run if it exists and a new one is being created, so at the top of my processKey function I add:
try {
scheduledFuture.cancel(true);
} catch(Exception ex){}
The reason for the try...catch statement is because when you first press a button, there is nothing scheduled. This results in cancel() throwing a NullPointerException. We want to safely catch that Exception and just move on if there is nothing to cancel. Voila! I can now change images at the press of a button!
Another thing to note, in order to call functions and reference variables from within a static method, those methods and variables also have to be static. Here is the full code of the game so far:
import java.awt.BorderLayout;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class GameWindow extends JPanel{
private static JLabel mainWindow;
private static ImageIcon forward;
private static ImageIcon backward;
private static ImageIcon left;
private static ImageIcon right;
private static ImageIcon ready;
private static ImageIcon miss;
protected static JFrame frame;
protected static ScheduledFuture scheduledFuture;
protected static ScheduledExecutorService scheduledExecutorService;
public GameWindow(){
forward = new ImageIcon("src/images/forward.gif");
backward = new ImageIcon("src/images/backward.gif");
left = new ImageIcon("src/images/left.gif");
right = new ImageIcon("src/images/right.gif");
ready = new ImageIcon("src/images/ready.gif");
miss = new ImageIcon("src/images/miss.gif");
mainWindow = new JLabel(ready);
this.setSize(600,600);
this.setLayout(new BorderLayout());
this.add(mainWindow, BorderLayout.CENTER);
}
public static void main(String[] args)
{
GameWindow g = new GameWindow();
frame = new JFrame("Sword Sword Revolution");
frame.add(g);
frame.addKeyListener(new KeyListener(){
public void keyPressed(KeyEvent e) { processKey(e.getKeyCode()); }
public void keyReleased(KeyEvent e) { }
public void keyTyped(KeyEvent e) { }
});
frame.setSize(600,600);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
public static void processKey(int key)
{
try {
scheduledFuture.cancel(true);
} catch(Exception ex){}
switch(key)
{
case 37:
mainWindow.setIcon(left);
break;
case 38:
mainWindow.setIcon(forward);
break;
case 39:
mainWindow.setIcon(right);
break;
case 40:
mainWindow.setIcon(backward);
break;
}
frame.repaint();
scheduledExecutorService =
Executors.newScheduledThreadPool(1);
scheduledFuture =
scheduledExecutorService.schedule(new Runnable() {
public void run() {
mainWindow.setIcon(ready);
}
},
1,
TimeUnit.SECONDS);
scheduledExecutorService.shutdown();
}
}
No comments:
Post a Comment