Pong game

Im tring to create a java version of pong for a final project of my intro to java programming class. I have about 80% of the planning done and about ready to start making some skeleton code. Im a bit stuck on a few things. Animation is one of them. If someone could link me to a good animation tutiorial i would be in your bedt. Also, i also dont know how to code when the ball hits the paddle. Any help would be great.

When the ball hits the paddle, the angles of reflection (is that what they’re called?) will be the same, so all you need is some simple sin, cos stuff.

There probably is a smarter way, but I just can’t think of one right now… :-/

I don’t know if you have seen it yet, but kevglass of these forums wrote up a really nice tutorial for beginners such as yourself…

http://grexengine.com/sections/externalgames/

Edit I just realized blah^3 won’t allow linking to the specific tutorial…bah hum bug =p…just go to Articles and check out the Space Invaders 101

[quote]When the ball hits the paddle, the angles of reflection (is that what they’re called?) will be the same, so all you need is some simple sin, cos stuff.
[/quote]
I’ve you’ve got a horizontal or vertical paddle its even easier, you just need to invert the x or y speed. But I seem to remember the pong/breakout games I’ve played bounce the ball at a different angle depending on how near the middle of the paddle the ball hits.

I’ve just written some text about a timing and animation, I don’t if it’d be any help, not its still in review stage.

http://www.cokeandcode.com/info/tut2d-2.html

Kev

No, you just copied and pasted wrongly :). Sounds like your web-browser displays the URL for the frameset rather than anything else, so if you want to link to a page within that, you have to right click the link and get the URL off it. It is the same for all sites; there’s nothign special about JGF.

We’re hoping at some point to make it so that articles can be easily referenced/bookmarked, but with automatic links back to JGF (e.g. a custom header/footer). At the moment though we don’t even have a custom CSS for articles (would like to borrow some parts of Kev’s CSS he uses in articles, but whilst not clashing too badly with the JGF colour scheme ;))

I actually wrote a networked pong game like that some months ago. Well, I nearly finished it, then I got too busy doing other things (j2me atm) I will probably never finish it. The netcode sucks, but the rest is ok… Shout if you want the source and Ill find it in my collection of never-meant-to-be projects :slight_smile:

[quote]… If someone could link me to a good animation tutiorial i would be in your bedt
[/quote]
Best. Typo. Ever.

Well guys were back with some problems. After lots of coding and crap, we got the ball to move and kinda bounce off the wall but one problem. The paddles dont move. The movement of the ball somehow cancles out the movement of the paddles/


import java.awt.*;
import java.awt.Graphics;
import java.applet.Applet;
import java.awt.event.*;

public class Pong extends Applet implements ActionListener
{
      //creates the ball
      Ball Ball1=new Ball();
      
      //creates paddles
      pongpaddle Paddle1=new pongpaddle();      
      pongpaddle Paddle2=new pongpaddle();      

      //buttons
      Button btnStart=new Button("Start Game");
      Button btnRestart=new Button("Restart Game");

      //variables
      int Counter1=0;
      int Counter2=0;
      int MovX;
      int MovY;
      int Ballx=Ball1.getBall_xLoc();
      int Bally=Ball1.getBall_yLoc();
      int Xinc=1;
      int Yinc=1;
      Image offscreen;
      Graphics buffer;

      public void init()
      {
            //stuff for key listener
            requestFocus();
            addKeyListener(new DirectionKeyListener());

            //set locations for paddles 1 and 2
            Paddle1.setLocation(0,140);
            Paddle2.setLocation(680,140);
            
            btnStart.addActionListener(this);
            btnRestart.addActionListener(this);
            add(btnStart);
            add(btnRestart);

            offscreen=createImage(700,350);
            buffer=offscreen.getGraphics();
            //start();
      }

      private class DirectionKeyListener implements KeyListener
      {
            public void keyPressed(KeyEvent event)
            {
                  if(event.getKeyCode()==KeyEvent.VK_W & Paddle1.getyLoc1()>0)
                  {
                        Paddle1.moveUp();
                        repaint();
                  }

                        if(event.getKeyCode()==KeyEvent.VK_S & Paddle1.getyLoc1()<280)
                  {
                        Paddle1.moveDown();
                        repaint();
                  }

                  if(event.getKeyCode()==KeyEvent.VK_P & Paddle2.getyLoc1()>0)
                  {
                        Paddle2.moveUp();
                        repaint();
                  }

                  if(event.getKeyCode()==KeyEvent.VK_L & Paddle2.getyLoc1()<280)
                  {
                        Paddle2.moveDown();
                        repaint();
                  }
            }
            public void keyTyped(KeyEvent event){}
            public void keyReleased(KeyEvent event){}
      }

      public void drawPaddle(Graphics g)
      {
            //set color and draw paddle1
            g.setColor(Color.RED);
            Paddle1.drawPaddle(g);

            //set color and draw paddle2
            g.setColor(Color.BLUE);
            Paddle2.drawPaddle(g);
      }      

      public void actionPerformed(ActionEvent e)
      {
            if(e.getSource()==btnStart)
            move();
      }

      public void move()
      {
            Ballx=Ballx+Xinc;
            Bally=Bally+Yinc;
            Ball1.setLocation(Ballx+1,Bally+1);

            if(Ball1.getBall_xLoc()==0)
            {
                  Xinc=1;
                  repaint();
            }

            if(Ball1.getBall_xLoc()==680)
            {
                  Xinc=-1;
                  repaint();
            }

            if(Ball1.getBall_yLoc()==0)
            {
                  Yinc=1;
                  repaint();
            }

            if(Ball1.getBall_yLoc()==330)
            {
                  Yinc=-1;
                  repaint();
            }
            //repaint();
      }

      public void update(Graphics g)
      {
            paint(g);
      }      

      public void paint(Graphics g)
      {
            buffer.setColor(Color.BLACK);
            buffer.fillRect(0,0,700,350);            

            drawPaddle(buffer);  //draws paddle
            Ball1.drawBall(buffer);  //draws ball
            
            g.drawImage(offscreen,0,0,this);
            
            Ballx=Ballx+Xinc;
            Bally=Bally+Yinc;
            Ball1.setLocation(Ballx+1,Bally+1);
            move();
            repaint();
      }
}

Posted the whole code. If anyone can help, please do, this is my final >.<

I think I found your error. In your key listener, it looks like you put & between your statements. I hope you wanted a bitwise and, because otherwise, you should have put &&. Is that the problem?

[quote]I think I found your error. In your key listener, it looks like you put & between your statements. I hope you wanted a bitwise and, because otherwise, you should have put &&. Is that the problem?
[/quote]
No, “&” means boolean “and”. It is overriden to also mean bitwise and.

The “&&” operator is a special form of the “&” operator, part of a family that includes “||” (just a special form of “|” which would also work everywhere that “||” is used) which was designed partly for high performance and partly so that you can write code like this (a standard trick):


if( object != null
&& object.toString() != null )
   // do something

The difference between & and && is that the latter STOPS EXECUTING as soon as it finds somethign that is “false” - because it doesn’t need to check the rest, it knows that “false & [anything]” is always false.

The trick above is that the second half of the AND will only execute if the first half is true - so you won’t get a null pointer exception when you try to call object.toString().

This is an especially useful trick in Java, where null is a frequent cause of bugs.

Your code is very advanced. No, seriously - most people never use inner classes. There are two reasons:

[] It took sun SEVERAL YEARS to understand their own code enough to actually implement them properly! Especially windows IIRC did NOT have fully working inner classes (and friends) until java 1.1.8 or 1.2.0 or similar. Some people feel you cannot trust your JVM provider to get them right either - so few people really understand/remember them without getting out a book and looking up how they work.
[
] They are sufficiently hard to understand and test that they cause lots of problems. Many people got used to not using them in that time :slight_smile: and discovered you don’t need them - they aren’t really object oriented, they are a hangover from an older programming style that is now out of date

Which is not to claim they aren;t useful. But I would STRONGLY advise anyone who is relatively new to java to avoid them - there are enough subtleties and weird features that it’s likely to be painful for you.

Rewrite your code either into two classes, and the KeyListener will need to take arguments so that it has references to the paddle etc, or else merge it all into one class that implements KeyListener etc.

This may help you track down the problem(s); certainly it will remove one extra piece of complexity that you don’t need right now.

PS I do like inner classes, but not in java. They are emasculated in java I sometimes think because Sun realised too late that they were about to undermine the OOP of Java and went “STOP! NO MORE inner class features!” but by then had already put them in the JLS. Add a few more bits and bobs and they could become full-blow closures, with tonnes of expressive power, but … that’s taking us back into C++ world where you have powerful-but-dangerous stuff. IMHO.

Oh… Learn something new everyday. So much good my comp sci degree will be when my instructors keep telling me the wrong things to “keep it simple” Thanks Blah^3. Nice to know how things actually work.

[quote]Oh… Learn something new everyday. So much good my comp sci degree will be when my instructors keep telling me the wrong things to “keep it simple” Thanks Blah^3. Nice to know how things actually work.
[/quote]
No probs. Most of my lecturers weren’t even sure how inner classes worked themslves (although it didn’t stop my professor from being a mean git and setting questions requiring you to compare and contrast the 4 different types - he admitted he was going to have to ask someone else to mark them because he wasn’t sure himself).

If you’re really comfortable with OOP they’re worth looking at, but I’d suggest you go get some experience with a poorer language that uses closures instead of OOP beforehand, so you have a better idea where some of the ideas behind inner classes come from and how you can use them.

And…most java compilers and bytecode systems (including all the java-based scripting languages like Jython, Beanshell, Groovy etc) always seem to have a significant number of alarmingly serious outstanding bugs on their inner-class support. It’s like a “standard part of java that too few people understand and many think is useless so it often gets implemented badly or not at all”.

So, if you get too used to inner classes, you can get smacked down when you start using java (both language and runtimes) outside of the sun JVM. Sad, but true.

Alright, (this is snp2k / member of craz’s “Team Pong”) Huge update here, we got the paddles to work, put in a score board, and made it so that the ball would reflect off the paddle at a different speed if it hit the top 20 pixels or the bottom 20 pixels of the paddle.

For all intents and purposes, the game is done, however, we need to make a few relatively minor improvements:

  • Decrease the keyboard repeat delay (the amt. of time the key has to be held down before repeating). How would we go about doing this?

  • Figure out a different method for reflecting the ball off the paddles (as the ball seems to follow a similar path the whole time) it’s not the exact same path, but after every few hits, it usually ends up in the same position.

  • When the key that commands paddle1 to move up or down is held down, and another key for paddle2 is pressed, it interrupts paddle1 from moving, thus stopping paddle1, and lets paddle2 move; and vice versa. How can we code this so that both paddles will move at the same time when their respective keys are held down?

There may be a few other tweaks that we will need help on, but that’s it for now.

Here’s our current code:


//imports the appropriate packages
import java.awt.*;
import java.awt.Graphics;
import java.applet.Applet;
import java.awt.event.*;

public class Pong extends Applet implements ActionListener 
{
      //creates the ball
      Ball Ball1=new Ball();
      
      //creates paddles
      pongpaddle Paddle1=new pongpaddle();
      pongpaddle Paddle2=new pongpaddle();

      //creates buttons
      Button btnStart=new Button("Start Game");
      Button btnRestart=new Button("Restart Game");

      //variables
      int Counter1=0;
      int Counter2=0;
      int MovX;
      int MovY;
      int Ballx=Ball1.getBall_xLoc();
      int Bally=Ball1.getBall_yLoc();
      int Xinc=1;
      int Yinc=1;
      Image offscreen;
      Graphics buffer;

      public void init()
      {
            //stuff for key listener
            requestFocus();
            addKeyListener(new DirectionKeyListener());

            //set locations for paddles 1 and 2
            Paddle1.setLocation(20,140);
            Paddle2.setLocation(660,140);

            //start button and restart button action listeners
            //btnStart.addActionListener(this);
            //btnRestart.addActionListener(this);
            //add(btnStart);

            //double buffer
            offscreen=createImage(700,350);
            buffer=offscreen.getGraphics();
            start();
      }

      public  void start()
      {
            //move ball
            move();
      }

      //key listener
      private class DirectionKeyListener implements KeyListener
      {
            public void keyPressed(KeyEvent event)
            {
                  //if "W" key is pressed, paddle1 will move up
                  if(event.getKeyCode()==KeyEvent.VK_W & Paddle1.getyLoc1()>0)
                  {
                        Paddle1.moveUp();
                        repaint();
                  }

                  //if "S" is pressed, paddle1 will move down
                  if(event.getKeyCode()==KeyEvent.VK_S & Paddle1.getyLoc1()<280)
                  {
                        Paddle1.moveDown();
                        repaint();
                  }

                  //if "P" is pressed, paddle2 will move up
                  if(event.getKeyCode()==KeyEvent.VK_P & Paddle2.getyLoc1()>0)
                  {
                        Paddle2.moveUp();
                        repaint();
                  }

                  //if "L" is pressed, paddle2 will move down
                  if(event.getKeyCode()==KeyEvent.VK_L & Paddle2.getyLoc1()<280)
                  {
                        Paddle2.moveDown();
                        repaint();
                  }
            }
            public void keyTyped(KeyEvent event){}
            public void keyReleased(KeyEvent event){}
      }

      public void drawPaddle(Graphics g)
      {
            //set color and draw paddle1
            g.setColor(Color.RED);
            Paddle1.drawPaddle(g);

            //set color and draw paddle2
            g.setColor(Color.BLUE);
            Paddle2.drawPaddle(g);

            //set color and draw string with score info
            g.setColor(Color.YELLOW);
            g.drawString("Player 1 Score: " + Counter1,20,330);
            g.drawString("Player 2 Score: " + Counter2,590,330);
      }      
      
      public void actionPerformed(ActionEvent e)
      {
            if(e.getSource()==btnStart)
                  move();
      }

      //moves ball
      public void move()
      {
            Ballx=Ballx+Xinc;
            Bally=Bally+Yinc;
            Ball1.setLocation(Ballx+1,Bally+1);
            repaint();
      }

      //double buffer
      public void update(Graphics g)
      {
            paint(g);
      }      

      public void paint(Graphics g)
      {
            //sets color and fills rectangle
            buffer.setColor(Color.BLACK);
            buffer.fillRect(0,0,700,350);            

            //draws paddle and ball in buffer
            drawPaddle(buffer);
            Ball1.drawBall(buffer);
            
            g.drawImage(offscreen,0,0,this);

            //moves ball
            move();

            if(Ballx<=0)
            {
                  Xinc=1;
                  Counter2++; //increases player2's score by one
                  repaint();
            }

            if(Ballx>=680)
            {
                  Xinc=-1;
                  Counter1++;  //increases player1's score by one
                  repaint();
            }

            if(Bally<=0)
            {
                  Yinc=1;
                  repaint();
            }

            if(Bally>=330)
            {
                  Yinc=-1;
                  repaint();
            }

            //ball hits paddle1
            if(Ballx<=60 && Ballx>20)
            {
                  //if ball hits top 20 pixels of paddle1, it bounces back at 2x the speed
                  if(Bally>=Paddle1.getyLoc1() && Bally<=Paddle1.getyLoc1()+20)
                  {
                        Xinc=2;
                        repaint();
                  }

                  //if ball hits middle 30 pixels of paddle1, ball bounces back at normal speed
                  if(Bally>=Paddle1.getyLoc1()+20 && Bally<=Paddle1.getyLoc1()+50)
                  {
                        Xinc=1;
                        repaint();
                  }

                  //if ball hits bottom 20 pixels of paddle1, ball bounces back at 2x speed
                  if(Bally>=Paddle1.getyLoc1()+50 && Bally<=Paddle1.getyLoc1())
                  {
                        Xinc=2;
                        repaint();
                  }
            }

            //ball hits paddle2
            if(Ballx>=660 && Ballx<700)
            {
                  //if ball hits top 20 pixels of paddle2, bounces back at 2x speed
                  if(Bally>=Paddle2.getyLoc1() && Bally<=Paddle2.getyLoc1()+20)
                  {
                        Xinc=-2;
                        repaint();
                  }

                  //if ball hits middle 30 pixels of paddle2, bounces back at normal speed
                  if(Bally>=Paddle2.getyLoc1()+20 && Bally<=Paddle2.getyLoc1()+50)
                  {
                        Xinc=-1;
                        repaint();
                  }

                  //if ball hits bottom 20 pixels of paddle2, bounces back at 2x speed
                  if(Bally>=Paddle2.getyLoc1()+50 && Bally<=Paddle2.getyLoc1())
                  {
                        Xinc=-2;
                        repaint();
                  }
            }
      }
}

Note: the coordinates at which the ball hits the walls/paddles will be changed slightly through the next few days as it will be tweaked so the ball actually hits the paddle on it’s surface (rather than assimilating into the paddle and then bouncing “out” of it…basically what is happening now is the “back edge” of the ball is hitting the paddle and bouncing off rather than the “front edge” of the ball.

As a side note, we do have the code for buttons that will start/pause/restart/ the game, we just need to put them in here, so don’t worry about that right now. We may add another button for credits or something (to impress the teacher :stuck_out_tongue: ).

Don’t.

Instead what you should be doing is to have some flags that represent the key being pressed. Set the flags in keyPressed(). Reset them the flags in keyReleased(). In the main loop you move the paddles according to the flags set.

shmoove

how

You set up boolean flags with names like upPressed, downPressed, etc, whatever you need to control the game. In keyPressed(), if the key is “up” then you you set upPressed = true. In keyReleased again you check what key it is and reset the appropiate boolean to false.
Then in your main loop (OK, I notice you don’t exactly have a main loop but you’re handling all the logic in the paint() routine, but that’s another topic), or basically wherever you’re moving the ball, you add a call to a method that moves the paddles. In this method you check the state of the different boolean flags and move the paddle depending on which flag is true…

shmoove