0

I am working on Air Hockey Game for my midterm project.

I have problem with handling two graphics, in this case two handles each of them consists of 3 circles. I can move only one handle because of keyPressed method.

Another problem is that I can't limit the moving domain, for example when you pressed the red handle can go beyond the frame width.

I know first problem is related to thread, but I've studied this subject from last week.

My problems are in this class:

public class StartGamePanel extends JPanel implements KeyListener, ActionListener {

    double xCircle1 = 200;
    double yCircle1 = 100;

    double xCircle2 = 200;
    double yCircle2 = 700;

    double velX = 0, velY = 0;

    public StartGamePanel() {
        Timer t = new Timer(5, this);
        t.start();
        addKeyListener(this);
        setFocusable(true);
    }

    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D) g;
        g.setColor(new Color(51, 153, 255));
        g.fillRoundRect(5, 5, 485, 790, 10, 10);

        addKeyListener(this);

        Graphics2D southArc = (Graphics2D) g;
        southArc.setColor(Color.WHITE);
        southArc.setStroke(new BasicStroke(3));
        southArc.drawArc(98, 640, 300, 300, 0, 180);

        //

        Graphics2D northArc = (Graphics2D) g;
        northArc.setColor(Color.WHITE);
        northArc.setStroke(new BasicStroke(3));
        northArc.drawArc(98, -143, 300, 300, 180, 180);

        Graphics2D line = (Graphics2D) g;
        line.setStroke(new BasicStroke(3));
        line.setColor(Color.white);
        line.drawLine(6, 395, 488, 395);

        Graphics2D dot = (Graphics2D) g;
        dot.setColor(Color.black);

        for (int j = 10; j < 800; j += 20) {
            for (int i = 6; i < 502; i += 20) {
                dot.drawLine(i, j, i, j);
            }
        }

        Graphics2D circle1 = (Graphics2D) g;
        circle1.setColor(new Color(255, 51, 51));
        Shape theCircle = new Ellipse2D.Double(xCircle1 - 40, yCircle1 - 40, 2.0 * 40, 2.0 * 40);
        circle1.fill(theCircle);

        Graphics2D circle2 = (Graphics2D) g;
        circle2.setColor(new Color(255, 102, 102));
        Shape theCircle2 = new Ellipse2D.Double(xCircle1 - 35, yCircle1 - 35, 2.0 * 35, 2.0 * 35);
        circle2.fill(theCircle2);

        Graphics2D circle3 = (Graphics2D) g;
        circle3.setColor(new Color(255, 51, 51));
        Shape theCircle3 = new Ellipse2D.Double(xCircle1 - 20, yCircle1 - 20, 2.0 * 20, 2.0 * 20);
        circle3.fill(theCircle3);

        Graphics2D circleprim = (Graphics2D) g;
        circleprim.setColor(new Color(0, 51, 102));
        Shape theCircleprim = new Ellipse2D.Double(xCircle2 - 40, yCircle2 - 40, 2.0 * 40, 2.0 * 40);
        circleprim.fill(theCircleprim);

        Graphics2D circle2prim = (Graphics2D) g;
        circle2prim.setColor(new Color(0, 102, 204));
        Shape theCircle2prim = new Ellipse2D.Double(xCircle2 - 35, yCircle2 - 35, 2.0 * 35, 2.0 * 35);
        circle2prim.fill(theCircle2prim);

        Graphics2D circle3prim = (Graphics2D) g;
        circle3prim.setColor(new Color(0, 51, 102));
        Shape theCircle3prim = new Ellipse2D.Double(xCircle2 - 20, yCircle2 - 20, 2.0 * 20, 2.0 * 20);
        circle3prim.fill(theCircle3prim);

        Graphics2D ball = (Graphics2D) g;
        ball.setColor(new Color(224, 224, 224));
        Shape theball = new Ellipse2D.Double(200 - 20, 400 - 20, 2.0 * 20, 2.0 * 20);
        ball.fill(theball);

        Graphics2D ball2 = (Graphics2D) g;
        ball2.setColor(new Color(160, 160, 160));
        Shape theball2 = new Ellipse2D.Double(200 - 15, 400 - 15, 2.0 * 15, 2.0 * 15);
        ball2.fill(theball2);

        Graphics2D goal = (Graphics2D) g;
        goal.setColor(Color.BLACK);
        goal.fill3DRect(100, 0, 300, 10, true);

        Graphics2D goal2 = (Graphics2D) g;
        goal2.setColor(Color.BLACK);
        goal2.fill3DRect(100, 790, 300, 10, true);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        repaint();
        xCircle1 += velX;
        yCircle1 += velY;
    }

    @Override
    public void keyPressed(KeyEvent e) {
        int code = e.getKeyCode();
        if (code == KeyEvent.VK_UP) {
            velY = -2;
            velX = 0;
        }
        if (code == KeyEvent.VK_DOWN) {
            velY = 2;
            velX = 0;
        }
        if (code == KeyEvent.VK_LEFT) {
            if (xCircle1 < 0) {
                velY = 0;
                velX = 0;
            } else {
                velY = 0;
                velX = -2;
            }
        }
        if (code == KeyEvent.VK_RIGHT) {
            if (xCircle1 > 200) {
                velY = 0;
                velX = 0;
            }
            velY = 0;
            velX = 2;
        }
    }

    @Override
    public void keyReleased(KeyEvent e) {
        int code = e.getKeyCode();
        if (code == KeyEvent.VK_UP) {
            velY = 0;
            velX = 0;
        }
        if (code == KeyEvent.VK_DOWN) {
            velY = 0;
            velX = 0;
        }
        if (code == KeyEvent.VK_LEFT) {
            velY = 0;
            velX = 0;
        }
        if (code == KeyEvent.VK_RIGHT) {
            velY = 0;
            velX = 0;
        }
    }

    @Override
    public void keyTyped(KeyEvent e) { }
}

Thanks!

Michael Yaworski
  • 13,410
  • 19
  • 69
  • 97

3 Answers3

1

Your key listener should be as quick as possible so it does not block following key events. Since several people press keys almost at the same time, this situation is common in games.

So the advice would be to use separate thread to listen for key presses which will quickly add events to a queue. This queue then will be processed on EDT(Swing main thread) and paint the results.

Eugene Ryzhikov
  • 17,131
  • 3
  • 38
  • 60
1

You can check out the KeyboardAnimation.java example found in Motion Using The Keyboard. It attempts to explain why Key Bindings are preferred over using a KeyListener.

The example code will animate two images. The left image controlled by W, A, S, D and the right image by Up, Down, Left and Right arrow keys. It also keeps the images within the window bounds. The code is not an actual game, it was just designed to show one way to use configurable Key Bindings.

camickr
  • 321,443
  • 19
  • 166
  • 288
0

You might want to also want to change the way the keypress's are handled. At the moment (I think) your only detecting one key down at a time.

You probably need to have an array for "heldkeys" and every time a key is down, add to that array. When a key is released, subtract that key from it.

Then, at a interval, check what keys are in the "heldkeys" array and act on them all. At least, you will probably need something like this if you plan to have two players. (who both should be able to press keys at the same time).

I don't know Swing well enough to give you exact code, but it will be something like;

//an array of numbers to hold the key-codes of the keys currently held down
HashSet<Integer> HeldKeys = new HashSet<Integer>();



@Override
public void keyDown(KeyEvent e) {

      // add key to keys currently held down
      HeldKeys.add(e.getKeyCode());


}

@Override
public void keyUp(KeyEvent e) {

       //remove key from list of things currently held
       HeldKeys.remove(e.getKeyCode());

}

@Override
public void keyPress(KeyEvent e) {

Iterator<Integer> kit = HeldKeys.iterator();

    //we loop over every key currently held down, running actions for them if 
    //there is any assigned.
    //For example, one button might move a sprite on the left down
    //Another might move a sprite on the right. One, the other, or both should be
    //able to all move without waiting for the other.
while (kit.hasNext()) {

      int keycode = kit.next();

      if (keycode==48){
          //do stuff if key with code 48 is held
          //(move one of the circles, etc)
      }

      if (keycode==46){
          //do stuff if key with code 46 is held
      }
      //...etc, for any number of keys you want to do different things for.
    }

}

Note; With this method you are not detecting a keypress as one thing, but rather detecting both the key being held down, and then the key being released as separate events. As people wont hit the keys at the exact same time, this lets you detect lots of keys at once.

This code is off the top of my head, Java, but I have a gwt background, so the names for "keydown" and "keyup" etc might be different. The "keypress" event in gwt will repeat automatically, if that's not the case in Swing you might need to use a Timer instead, running at an interval. (you might prefer this anyway as its a bit more controllable).

darkflame
  • 998
  • 1
  • 9
  • 23
  • ok, did the best code I could. I don't know Swing well, so some stuff will be different. Hopefully I have explained it well enough for you to do your own version – darkflame Dec 08 '13 at 20:57