2

I'm attempting to create a simple pong game in Java, but I don't know how to have both players using the keyboard at the same time. The game is incomplete and I'm working on the paddle movement for both players currently. The problem is, when a player pushes their up key and moves their paddle up, but if the other players hits any of their keys it cancels the previous players action and causes the paddle to stop. I think I need a way to handle multiple key inputs at once. Here's my code the KeyListeners at the bottom is where I need help. I'm only a 1 year Java programmer so go easy on the rest of my code.

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;

import javax.swing.*;

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.HashSet;
import java.util.Set;

public class DrawGame extends JPanel implements ActionListener{
    public static final int XPOS = 0;
    public static final int YPOS = 0;
    public boolean xFlag = true; // true means ballx is going right
    public boolean yFlag = true; // true means bally is going down
    public int ballX = 300; // Ball starting point
    public int ballY = 400; // Ball starting point
    Timer ballTimer; // Starts balls animation

    public int leftScore;
    public int rightScore;

    public int rightPadY; // Right players paddle position
    public int leftPadY; // left players paddle position

    // Constructor
    public DrawGame(){
        addKeyListener(new RightListener());
        addKeyListener(new LeftListener());

        leftScore = 0;
        rightScore = 0;

        rightPadY = YPOS + 230;
        leftPadY = YPOS + 230;

        setBackground(Color.BLACK);
        setPreferredSize(new Dimension(800, 600));
        setFocusable(true);

        ballTimer = new Timer(10, this);
        ballTimer.start();
    }

    // Draws game
    public void paintComponent(Graphics g){
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D)g;

        //Drawing Side Boards
        g2d.setColor(Color.WHITE);
        g2d.fillRect(XPOS + 5, YPOS + 20, 775, 25); // Top Board
        g2d.fillRect(XPOS + 5, YPOS + 517, 775, 25); // Bottom board

        //Drawing the center line
        g2d.fillRect(XPOS + 377, YPOS + 45 * 1, 25, 25);
        g2d.fillRect(XPOS + 377, YPOS + 45 * 2, 25, 25);
        g2d.fillRect(XPOS + 377, YPOS + 45 * 3, 25, 25);
        g2d.fillRect(XPOS + 377, YPOS + 45 * 4, 25, 25);
        g2d.fillRect(XPOS + 377, YPOS + 45 * 5, 25, 25);
        g2d.fillRect(XPOS + 377, YPOS + 45 * 6, 25, 25);
        g2d.fillRect(XPOS + 377, YPOS + 45 * 7, 25, 25);
        g2d.fillRect(XPOS + 377, YPOS + 45 * 8, 25, 25);
        g2d.fillRect(XPOS + 377, YPOS + 45 * 9, 25, 25);
        g2d.fillRect(XPOS + 377, YPOS + 45 * 10, 25, 25);
        g2d.fillRect(XPOS + 377, YPOS + 45 * 11, 25, 25);

        //Drawing the paddles
        g2d.fillRect(XPOS + 10, leftPadY, 20, 100);// Left
        g2d.fillRect(XPOS + 755, rightPadY, 20, 100); // Right

        //Drawing the ball
        g2d.fillRect(ballX, ballY, 23, 23); 

        //Drawing the score
        switch(leftScore){
            case 0:
                g2d.fillRect(XPOS + 305, YPOS + 50, 7, 30);
                g2d.fillRect(XPOS + 325, YPOS + 50, 7, 30);
                g2d.fillRect(XPOS + 305, YPOS + 50, 25, 7);
                g2d.fillRect(XPOS + 305, YPOS + 80, 27, 7);
                break;
            case 1:
                g2d.fillRect(XPOS + 325, YPOS + 50, 7, 30);
        }

        switch(rightScore){
            case 0:
                g2d.fillRect(XPOS + 450, YPOS + 50, 7, 30);
                g2d.fillRect(XPOS + 470, YPOS + 50, 7, 30);
                g2d.fillRect(XPOS + 450, YPOS + 50, 25, 7);
                g2d.fillRect(XPOS + 450, YPOS + 80, 27, 7);
                break;
            case 1:
                g2d.fillRect(XPOS + 450, YPOS + 50, 7, 30);
        }
    }

    // Controls the animation of the ball
    public void actionPerformed(ActionEvent e){
        if(xFlag == true && ballX >= 735){
            ballX += 2;
            xFlag = false;
        } else if(xFlag == true){
            ballX += 2;
        }

        if(xFlag == false && ballX <= 25){
            ballX -= 2;
            xFlag = true;
        } else if(xFlag == false){
            ballX -= 2;
        }

        if(yFlag == true && ballY >= 500){
            ballY += 2;
            yFlag = false;
        } else if(yFlag == true){
            ballY += 2;
        }

        if(yFlag == false && ballY <= 45){
            ballY -= 2;
            yFlag = true;
        } else if(yFlag == false){
            ballY -= 2;
        }
        repaint();
        ballTimer.restart();
    }

    // Keylistener for right player
    private class RightListener implements KeyListener{

        @Override
        public synchronized void keyPressed(KeyEvent event) {
            if(event.getKeyCode() == KeyEvent.VK_UP){
                rightPadY -= 5;
            }else if(event.getKeyCode() == KeyEvent.VK_DOWN){
                rightPadY += 5;
            }
            repaint();
        }

        @Override
        public synchronized void keyReleased(KeyEvent e) {
            // TODO Auto-generated method stub

        }

        @Override
        public synchronized void keyTyped(KeyEvent e) {
            // TODO Auto-generated method stub

        }
    }

    // Keylistener for left player
    private class LeftListener implements KeyListener{
        @Override
        public synchronized void keyPressed(KeyEvent event) {
            if(event.getKeyCode() == KeyEvent.VK_W){
                leftPadY -= 5;
            } else if(event.getKeyCode() == KeyEvent.VK_S){
                leftPadY += 5;
            }
            repaint();
        }

        @Override
        public synchronized void keyReleased(KeyEvent e) {
            // TODO Auto-generated method stub

        }

        @Override
        public synchronized void keyTyped(KeyEvent e) {
            // TODO Auto-generated method stub

        }
    }




}
SHIELDHEAD
  • 45
  • 2
  • 6
  • 1
    Avoid using the `KeyListener` for non-text components. Use [Key Bindings](http://docs.oracle.com/javase/tutorial/uiswing/misc/keybinding.html) instead. – BackSlash Sep 04 '13 at 19:58
  • 2
    And please get rid of the `synchronized` key word, since 1) it's not needed since all Swing code is run on a single thread. – Hovercraft Full Of Eels Sep 04 '13 at 20:00
  • Use KeyDown and KeyUp with threads instead of KeyPressed. KeyPressed is subject to how fast your key repetetition is for the system. And once you push another key, the other stops. Try it here on Stack Overflow. Hold a, wait a few seconds and press b. The a's will stop. This uses keypressed. – Cruncher Sep 04 '13 at 20:02
  • @Cruncher There aren't `keyDown` and `keyUp` methods in the `KeyListener` class – BackSlash Sep 04 '13 at 20:04
  • @BackSlash wth. Are they supposed to be equivilent to keyPressed/Released? In that case this still holds, but with those method names. The idea is still that you don't want to do animation on every event trigger. It should be done in a thread, and the events just dictate what will happen. – Cruncher Sep 04 '13 at 20:06
  • @Cruncher Yes, `keyPressed` and `keyReleased` exist, and the OP is already using them. – BackSlash Sep 04 '13 at 20:07
  • @BackSlash He is not using released. It is there only because the interface requires it to be. – Cruncher Sep 04 '13 at 20:15

2 Answers2

6

Don't use a KeyListener. You should be using Key Bindings.

See Motion Using the Keyboard for more information.

I added the following code to the KeyboardAnimation example from the above link, which will allow you to do what you want:

JLabel label2 = new JLabel( new ColorIcon(Color.GREEN, 40, 40) );
label2.setSize( label2.getPreferredSize() );
label2.setLocation(500, 500);
contentPane.add( label2 );

KeyboardAnimation animation2 = new KeyboardAnimation(label2, 24);
animation2.addAction("A", -3,  0);
animation2.addAction("D", 3,  0);
animation2.addAction("W",    0, -3);
animation2.addAction("S",  0,  3);
camickr
  • 321,443
  • 19
  • 166
  • 288
  • I've taken a good solid hour playing with this stuff and for some reason I can only either paint the two paddles that move with the new Key Bindings or the rest of the game.(ie: score, boards, and centerline) How do I make the paddles paint over the background? – SHIELDHEAD Sep 05 '13 at 00:14
  • Search my blog for the `Background Panel`. It gives a couple of ideas on how to paint a background image. Other than that I can't give advice because I have no idea how your game works. – camickr Sep 05 '13 at 00:25
  • I fixed it! I had two JPanels in my main one for my game background and one the paddles. I forgot that my class DrawGame was a JPanel. So I swapped out DrawGame for the generic JPanel and it's now painted fine and the controls work great! – SHIELDHEAD Sep 05 '13 at 00:45
0

It's better to use a thread for animations. When you keyPressed we will tell the program that the key is down. On keyReleased we will tell the program that the key is up. In the thread we will read this value and determine if we want to move or not. This will also be much smoother.

First, you need to implement Runnable in some class. You could even do it in your main class there. Add the public void run method.

In it will be something like:

while(true)
{
    if(player1.upkeyIsDown() && player1.downKeyIsUp())
    {
         //move Player1 Up
    }
    else if(player1.downKeyIsDown() && player1.upKeyIsUp())
    {
         //move Player1 Down
    }
    //do similar for player 2
    try{
        Thread.sleep(50);
    }
    catch(InterruptedException ie)
    {
        ie.printStackTrace();
    }
}

This is semi-pseudo code, you'll have to implement how you save the key being down. In your KeyListeners, you need to call something in the main class to change these values, and then this thread will deal with the rest.

This thread also needs to be started. In your main/constructor somewhere write new Thread(this).start();

Cruncher
  • 7,641
  • 1
  • 31
  • 65