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