1

I am not sure how to really word this, but I have a game that is arrow key based. Anyways there is an options menu but after I select options when I try to hit arrow keys and move nothing happens... I am assuming it is because I am "active" in another JFrame that is now hidden (the options menu) rather than the game screen.

Is there a way I can have the program know that I want the keyboard actions to refer back to the original JFrame when I close the options menu?

And while I am at it, I am trying to figure out how to make the game window full-screened. Right now I have it setUndecorated so no border is up and I tried the code: setExtendedState(JFrame.MAXIMIZED_BOTH); But the game is shifted way off to the bottom right of the screen. I am an external monitor right now, would that matter? I also have non-resizable checked (I'm on netbeans), and I have "set" sizes for the Jframe and Jpanels, should I remove those?

I hope that makes sense, Thanks, -Austin

*All in netbeans too.

Austin
  • 3,010
  • 23
  • 62
  • 97
  • *"..back to the original JFrame.."* See [The Use of Multiple JFrames, Good/Bad Practice?](http://stackoverflow.com/q/9554636/418556). – Andrew Thompson Mar 24 '12 at 20:49

1 Answers1

0

I am assuming that you're using a KeyListener to capture key strokes, and if so, KeyListeners only work if the component being listened to has focus. Your problem is that on swapping your views, your listened to component does not have focus. One way to solve this is to call requestFocusInWindow() on the listened component after the swap.

But there's a bigger issue afoot, and that's in your use of KeyListeners to begin with, something that in general should be avoided with Swing applications. Instead use Key Bindings, a much higher level concept and thus one that should be used in favor of the low level KeyListeners.

Also, to maximize a JFrame, you'll want to call it's setExtendedState(...) method passing in Frame.MAXIMIZED_BOTH as the parameter as it appears you are doing. Are you calling pack()? Also, you're not calling setLocation(...), setBounds(...) or setSize(...) on the JFrame, right?

Edit: I see you have in fact called setSize(...) on the JFrame. Yes, remove this as it makes no sense if you're maximizing the JFrame.

Edit
Code example of what I am suggesting:

import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
import javax.swing.border.TitledBorder;

public class AnimationWithKeyBinding {
   @SuppressWarnings("serial")
   private static void createAndShowUI() {

      final JPanel cardPanel = new JPanel(new CardLayout()); 
      MenuPanel menuPanel = new MenuPanel();
      AnimationPanel animationPanel = new AnimationPanel();

      cardPanel.add(menuPanel, "Menu");
      cardPanel.add(animationPanel, "Animation");

      menuPanel.setNextBtnAction(new AbstractAction("Next") {
         {
            putValue(NAME, "Next");
            putValue(MNEMONIC_KEY, KeyEvent.VK_N);
         }
         @Override
         public void actionPerformed(ActionEvent arg0) {
            ((CardLayout)cardPanel.getLayout()).next(cardPanel);
         }
      });

      JFrame frame = new JFrame("Animation With Key Binding");
      frame.getContentPane().add(cardPanel);
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.pack();
      frame.setLocationRelativeTo(null);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      java.awt.EventQueue.invokeLater(new Runnable() {
         public void run() {
            createAndShowUI();
         }
      });
   }
}

@SuppressWarnings("serial")
class MenuPanel extends JPanel {
   private JButton nextBtn = new JButton();

   public MenuPanel() {
      TitledBorder titledBorder = BorderFactory.createTitledBorder("Menu Panel");
      titledBorder.setTitleFont(titledBorder.getTitleFont().deriveFont(Font.BOLD, 24));
      setBorder(titledBorder);
      setLayout(new GridBagLayout());
      add(nextBtn);
   }

   public void setNextBtnAction(Action action) {
      nextBtn.setAction(action);
   }
}

@SuppressWarnings("serial")
class AnimationPanel extends JPanel {
   public static final int SPRITE_WIDTH = 20;
   public static final int PANEL_WIDTH = 400;
   public static final int PANEL_HEIGHT = 400;
   private static final int MAX_MSTATE = 25;
   private static final int SPIN_TIMER_PERIOD = 16;
   private static final int SPRITE_STEP = 3;

   private int mState = 0;
   private int mX = (PANEL_WIDTH - SPRITE_WIDTH) / 2;
   private int mY = (PANEL_HEIGHT - SPRITE_WIDTH) / 2;
   private int oldMX = mX;
   private int oldMY = mY;
   private boolean moved = false;

   // an array of sprite images that are drawn sequentially
   private BufferedImage[] spriteImages = new BufferedImage[MAX_MSTATE];

   public AnimationPanel() {
      // create and start the main animation timer
      new Timer(SPIN_TIMER_PERIOD, new SpinTimerListener()).start();
      setPreferredSize(new Dimension(PANEL_WIDTH, PANEL_HEIGHT));
      setBackground(Color.white);
      createSprites(); // create the images
      setupKeyBinding();
   }

   private void setupKeyBinding() {
      int condition = JComponent.WHEN_IN_FOCUSED_WINDOW;
      InputMap inMap = getInputMap(condition);
      ActionMap actMap = getActionMap();

      // this uses an enum of Direction that holds ints for the arrow keys
      for (Direction direction : Direction.values()) {
         int key = direction.getKey();
         String name = direction.name();

         // add the key bindings for arrow key and shift-arrow key
         inMap.put(KeyStroke.getKeyStroke(key, 0), name);
         inMap.put(KeyStroke.getKeyStroke(key, InputEvent.SHIFT_DOWN_MASK),
               name);
         actMap.put(name, new MyKeyAction(this, direction));
      }
   }

   // create a bunch of buffered images and place into an array,
   // to be displayed sequentially
   private void createSprites() {
      for (int i = 0; i < spriteImages.length; i++) {
         spriteImages[i] = new BufferedImage(SPRITE_WIDTH, SPRITE_WIDTH,
               BufferedImage.TYPE_INT_ARGB);
         Graphics2D g2 = spriteImages[i].createGraphics();
         g2.setColor(Color.red);
         g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
               RenderingHints.VALUE_ANTIALIAS_ON);
         double theta = i * Math.PI / (2 * spriteImages.length);
         double x = SPRITE_WIDTH * Math.abs(Math.cos(theta)) / 2.0;
         double y = SPRITE_WIDTH * Math.abs(Math.sin(theta)) / 2.0;
         int x1 = (int) ((SPRITE_WIDTH / 2.0) - x);
         int y1 = (int) ((SPRITE_WIDTH / 2.0) - y);
         int x2 = (int) ((SPRITE_WIDTH / 2.0) + x);
         int y2 = (int) ((SPRITE_WIDTH / 2.0) + y);
         g2.drawLine(x1, y1, x2, y2);
         g2.drawLine(y1, x2, y2, x1);
         g2.dispose();
      }
   }

   @Override
   protected void paintComponent(Graphics g) {
      super.paintComponent(g);
      g.drawImage(spriteImages[mState], mX, mY, null);
   }

   public void incrementX(boolean right) {
      oldMX = mX;
      if (right) {
         mX = Math.min(getWidth() - SPRITE_WIDTH, mX + SPRITE_STEP);
      } else {
         mX = Math.max(0, mX - SPRITE_STEP);
      }
      moved = true;
   }

   public void incrementY(boolean down) {
      oldMY = mY;
      if (down) {
         mY = Math.min(getHeight() - SPRITE_WIDTH, mY + SPRITE_STEP);
      } else {
         mY = Math.max(0, mY - SPRITE_STEP);
      }
      moved = true;
   }

   public void tick() {
      mState = (mState + 1) % MAX_MSTATE;
   }

   private class SpinTimerListener implements ActionListener {
      @Override
      public void actionPerformed(ActionEvent e) {
         tick();

         int delta = 20;
         int width = SPRITE_WIDTH + 2 * delta;
         int height = width;

         // make sure to erase the old image
         if (moved) {
            int x = oldMX - delta;
            int y = oldMY - delta;
            repaint(x, y, width, height);
         }

         int x = mX - delta;
         int y = mY - delta;

         // draw the new image
         repaint(x, y, width, height);
         moved = false;
      }
   }
}

enum Direction {
   UP(KeyEvent.VK_UP), DOWN(KeyEvent.VK_DOWN), LEFT(KeyEvent.VK_LEFT), RIGHT(
         KeyEvent.VK_RIGHT);

   private int key;

   private Direction(int key) {
      this.key = key;
   }

   public int getKey() {
      return key;
   }
}

// Actions for the key binding
@SuppressWarnings("serial")
class MyKeyAction extends AbstractAction {
   private AnimationPanel draw;
   private Direction direction;

   public MyKeyAction(AnimationPanel draw, Direction direction) {
      this.draw = draw;
      this.direction = direction;
   }

   @Override
   public void actionPerformed(ActionEvent e) {
      switch (direction) {
      case UP:
         draw.incrementY(false);
         break;
      case DOWN:
         draw.incrementY(true);
         break;
      case LEFT:
         draw.incrementX(false);
         break;
      case RIGHT:
         draw.incrementX(true);
         break;

      default:
         break;
      }
   }
}
Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
  • For my keys, I have netBeans autogenerated formKeyPressed, which then goes to a class that utilizes KeyEvent? I check for up/left/right/down then move the person accordingly. So lets say I close the Options menu, I can just place something like Frame1.requestFocusInWindow() in the method that setVisible(false) is located for the Options frame? Or something like that – Austin Mar 24 '12 at 20:27
  • I am not familiar with NetBean's autogenerated "formKeyPressed". I still stand by my recommendation to use KeyBinding. I also recommend that you move away from NetBean's auto-generated code if you want to learn ***and understand*** Swing coding. – Hovercraft Full Of Eels Mar 24 '12 at 20:30
  • The only autogenerated code I use of netbeans is action/listener code for Jbuttons, or KeyEvents. Everything else I program, so all that is generated is code such as `private void jRadioButton2ActionPerformed(java.awt.event.ActionEvent evt) { }` I also tried doing this to change the focus, but it doesn't work. `GameGUI gg = new GameGUI(); setVisible(false); gg.requestFocusInWindow();` For full screen, When I run the GUI without w/o extendedstate its in top left, when I run it w/ it is partially show in bottom right. – Austin Mar 24 '12 at 20:36
  • See edit to answer above regarding full-screen option. And again, is the NetBean's autogenerated code using ***KeyListeners***, you should be able to look at that code and see. If the code shows `addKeyListener(...)` anywhere then the answer is "yes". Again, use KeyBinding. – Hovercraft Full Of Eels Mar 24 '12 at 20:38
  • Looking at my code, the way it works should be similar in that fashion, except it logs any type of UP/LEFT/RIGHT/DOWN key pressed. I edited my reply as well. – Austin Mar 24 '12 at 20:42
  • I don't understand what you mean by "it logs any type of ...". *Again*, does your code have `addKeyListener(...)` anywhere within it? Also, please see edit to my answer for an example of what I mean. – Hovercraft Full Of Eels Mar 24 '12 at 20:54
  • No. But as I mentioned I believe the method Netbeans uses, automatically bonds those keys in the background. So to your question no, but I think it is doing that in its own sense behind the scenes. I got full screen to work. Now I just have to figure this focusewindow thing out. – Austin Mar 24 '12 at 21:06