Saturday, September 22, 2012

SSR 3 - Java Animation

Check out the first two parts to follow along from the beginning:
Part 1 - Building the GUI
Part 2 - Key Listener and Scheduler

Continuing with the Sword Sword Revolution game, today I decided to tackle animation. Specifically, I needed the ability to randomly generate the arrow images, and then scroll them along the bottom of the screen, right to left. This proved to be much more difficult than I had originally imagined, and I owe a huge thanks to the folks over at StackOverflow, specifically Hovercraft Full of Eels and Aurelien Ribon.

When it comes to animating in Java, there are a few options. Many people try to create their own Layout Manager or use the null layout option and just set the location of a component and then repaint the container. I even tried the second one, but learn from my mistake. Null layout is NOT a good idea and both of these options just make things more difficult. Other options include using an external library such as SlidingLayout or The Universal Tween Engine, or the route that I decided on: overwriting the existing paintComponent function.

The first thing I needed to do was create a new class that extended JPanel. The reason for this is that in order to customize the paintComponent function, you have to have a custom class. This class is very specific to my exact needs, but can easily be modified for other projects.

First, I needed to manage the necessary imports and instantiate the necessary instance variables.

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.LinkedList;
import java.util.Random;

import javax.imageio.ImageIO;
import javax.swing.JPanel;
import javax.swing.Timer;

public class AnimatedPanel extends JPanel {

 private static final long serialVersionUID = -7564181037729291253L;
 private static final int PREF_W = 600;
 private static final int PREF_H = 100;
 private static final int TIMER_DELAY = 15;
 private LinkedList images = new LinkedList();
 private BufferedImage rightarrow, leftarrow, uparrow, downarrow;
 private LinkedList xLocs = new LinkedList();
 private int moveSpeed = 3;
 private long totalTime = 0, nextNewTime = 0;
 private static Random r;
}


Next, I created the constructor. Here I needed to instantiate a few variables, load my image files, and then schedule a timer thread to add new arrow images, and move the arrow images currently on the screen.

 public AnimatedPanel() {
  r = new Random();
  try {
   rightarrow = ImageIO.read(new File("src/images/arrowright.gif"));
   leftarrow = ImageIO.read(new File("src/images/arrowleft.gif"));
   uparrow = ImageIO.read(new File("src/images/arrowup.gif"));
   downarrow = ImageIO.read(new File("src/images/arrowdown.gif"));
  } catch (IOException e) {
   System.exit(0);
  }

  new Timer(TIMER_DELAY, new ActionListener(){
   public void actionPerformed(java.awt.event.ActionEvent e) {
    int index = 0;
    totalTime += TIMER_DELAY;
    if (totalTime >= nextNewTime)
     addArrow();
    for(int imgX : xLocs)
    {
     xLocs.set(index, imgX - moveSpeed);
     index++;
    }
    repaint();
   };
  }).start();
 }


The Timer used here is the javax.swing.Timer, NOT the java.util.Timer class. The TIMER_DELAY parameter is passed in milliseconds, and then the ActionListener which calls its actionPerformed function whenever the delay is reached. This Timer runs indefinitely until you manually cancel it, which we don't particularly care about for the purposes of this project.

The function I wrote to add an arrow image to the screen uses a random number generator to decide which of the four arrow directions should be added. It also sets the initial x location to be 600 which is just off the edge of the screen.

 private void addArrow() {
  int arrow = r.nextInt(4);
  nextNewTime = totalTime + r.nextInt(1000) + 300 + (300 / moveSpeed);
  switch(arrow){
  case 0:
   images.add(rightarrow);
   xLocs.add(600);
   break;
  case 1:
   images.add(leftarrow);
   xLocs.add(600);
   break;
  case 2:
   images.add(downarrow);
   xLocs.add(600);
   break;
  case 3:
   images.add(uparrow);
   xLocs.add(600);
   break;
  }
 }


Finally, I need to override the existing paintComponent function with my own, custom version. Essentially, I just need to determine the proper location for each of the images on the screen, and then display them.

 @Override
 protected void paintComponent(Graphics g) {
  super.paintComponent(g);
  Graphics2D g2 = (Graphics2D) g;
  g.setColor(Color.LIGHT_GRAY);
  g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
    RenderingHints.VALUE_TEXT_ANTIALIAS_ON);

  int index = 0;
  for(BufferedImage image : images)
  {
   if (image != null) 
    g.drawImage(image, xLocs.get(index), 10, 65, 65, this);
   index++;
  }
 }   


And with that, we have animated arrows scrolling across the bottom of the screen.

Photobucket

Sword Sword Revolution - Part 2

Continuing from yesterday, I have added a few new features to my Sword Sword Revolution game. For today, I decided to tackle user input. Specifically, my goal was to handle listening for the user to press an arrow key, and then change the animated GIF accordingly. After a short delay, I would return to the original waiting GIF.

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!

Photobucket

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();
 }

}

Friday, September 21, 2012

Sword Sword Revolution

It is, once again, time for a new project. I got my hands on a neat little toy called the Makey Makey, which is basically a custom Arduino board that allows you to turn anything into a keyboard. The official Makey Makey website shows some neat ideas, such as playing a computer piano with bananas or Pacman with drawings on a piece of notebook paper, but I had my own idea. You see, I also got another new toy, a sword. It's just for decoration, but if I can use my new sword as a keyboard, you know I am going to. The question then became, what should I use my sword to do? And, of course, the answer is, "Program your own game!"

After going through a few ideas, I decided to make my own version of Dance Dance Revolution. With swords. Sword Sword Revolution. Played by swinging an actual sword. This is going to be awesome.

So, the first problem to address: what programming language should I use? I have created games in the past in a few different programming languages, notably Java, Python, and VB.NET, and I thought of all of those, as well as giving PHP a try since that is what I program in professionally. I had originally decided on Python, but after some research and a few sample programs, I decided against it. Pygame is great for working with sprites, but an artist I am not. Instead, I had found some animated GIFs to cover all possible actions for my game, and unfortunately, Pygame can not, to the best of my knowledge, display animated GIFs. At least not simply and natively. Java, on the other hand, can display them just fine inside of an ImageIcon object, so that is what I ultimately chose.

Now, I have not touched Java in over a year, so I knew this project could be a bit of a challenge. That is why I started out with the basics. I knew I wanted a general game screen, with a place to display the animation and a place to show the commands, i.e. which button to press. Since this is pretty basic, I decided to use a JPanel with BorderLayout.

Creating just a basic Java Class, I needed to create the JPanel, a JFrame to hold the JPanel, load the images, and store the required image in a JLabel, which would be displayed in the JPanel. So, basically, JFrame > JPanel > JLabel > ImageIcon.


import java.awt.BorderLayout;

import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;

public class GameWindow extends JPanel{
 
        //Create the necessary variables to be used throughout the game
 private JLabel mainWindow;
 private ImageIcon forward;
 private ImageIcon backward;
 private ImageIcon left;
 private ImageIcon right;
 private ImageIcon ready;
 
 public GameWindow(){
                //Load the images
  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");
                //Create a JLabel with the initial image
  mainWindow = new JLabel(ready);
  this.setSize(600,600);
  this.setLayout(new BorderLayout());
                //Add the JLabel with image. This will be the main focus of the window, so use the CENTER of the BorderLayout
  this.add(mainWindow, BorderLayout.CENTER);
 }
 
 public static void main(String[] args)
 {
                //Instantiate our game window on initial run
  GameWindow g = new GameWindow();
                //We need a frame to put the panel in
  JFrame frame = new JFrame("Sword Sword Revolution");
  frame.add(g);
  frame.setSize(600,600);
  frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  frame.setVisible(true);
 }
}


And with that, I had the basic beginnings of my game. Sure, for now all that it did was display an animated gif, but soon, soon it would do so much more.

Photobucket

Next, there are four major things that need to happen.

1) I need to change the image in the JLabel when an arrow key is pressed. After a set amount of time, I need to revert the image back to the starting image.

2) I need to add a component to the SOUTH section (or perhaps EAST or WEST) to hold the arrow commands.

3) I need to randomly generate the arrows that come through, and animate them across their box.

4) I need to add a listener that will determine if the user pressed the correct arrow key or not, and the game should react accordingly.

There are also some optional things that would be nice to add:

1) It would be cool if there were enemies animated on the screen. Perhaps coming from the direction you were supposed to swing the sword from. This might be rather challenging, especially since I have the sword swinging left, right, forward, and backward, but would be really neat if I could figure it out.

2) If the player misses a key press or presses incorrectly, I would like a different animation to play.

3) Some background music could be a nice addition.