Monday, August 29, 2016

Pong in Java: Mouse Controlled Paddle

The previous post discussed command line Java.  I went a little deeper to make two versions of the classic game Pong.  This first one uses the mouse to control one of the paddles.  The other paddle is controlled automatically by the computer.


Java is an object oriented language.  Pong lends itself to programming in this manner because the game is based on objects... specifically a ball and two paddles.  Objects in Java are instances of a class.  A class can contain its own variables, own methods (functions), and requires a constructor which describes how an instance of the class should be created.  For example, the following is the class for a ball:

Each instance of the Ball class has an x-coordinate, y-coordinate, heading, speed, and a diameter.  The diameter is always the same, but the other variables are set when the Ball instance is created.
Elsewhere in the program, an instance of Ball called "ball" is created using the command:

Ball ball = new Ball(200, 100, 30, 10);

The constructor assigns the variables for "ball" (eg its xcor equals 200, etc).

There is also a method for the Ball called "move", which changes the values of xcor and ycor depending on the Ball's speed and heading.



Another object is a Paddle.  The following is the class for a paddle.


This class is similar in many ways to a ball; it has several variables which describe its geometry, but it also interacts with a Ball, and so one variable describes a property of a Ball.

The constructor requires an instance of a Ball to be passed to it:

Paddle leftPaddle = new Paddle(0, 100, 20, 100, ball);

This is important because the "overlaps" method for a Paddle checks to see if the middle of the ball (ballycor + ballDiameter/2) is below the bottom of the paddle (this.ycor + height) or above the top of the paddle (this.ycor).  The relevant geometry is shown below.


The real action for the game happens in the GamePanel class, which includes the methods "paintComponent" and "animate".  "paintComponent" colors two rectangles and an oval at the locations of the left and right paddles, and the ball.  When an instance of GamePanel is created, the constructor requires two Paddles and a Ball.  paintComponent is a method of the class JPanel.  GamePanel extends JPanel, and I have overridden the default method paintComponent.



The "animate" methods causes the ball to move, to deflect off the upper/lower values, and to deflect off of a paddle.  It also causes the right paddle to have the same height location as the ball: rightPaddle.ycor = ball.ycor - rightPaddle.height/2;

The deflection works as follows:  we first check if the ball is within the paddle's width of the left side of the JPanel:  if( ball.xcor  < leftPaddle.width). If so, we want to know if the ball's y-coordinate overlaps with the location of the left paddle.  If so, we do some fancy math to change the heading of the ball so it looks like it bounced off the paddle.   Note that this code does not currently end the game if the paddle and ball don't overlap... the ball goes off the screen to the left, but then it will eventually "bounce" offscreen because the original "if" statement is still true, and the ball will "overlap" with the paddle at some point.  This is a bug.

Next we check to see if the ball is within the paddle's width of the right side of the JPanel:

if(( ball.xcor + ball.ballDiameter)  > getWidth() - rightPaddle.width).  

getWidth() is a built in method for a JPanel which returns the width of the JPanel.  If so, we then check to see if the ball's y-coordinate overlaps with the location of the right paddle, in which case the ball's heading is changed to Pi minus its original heading.

Finally, we check to see if the ball is either at the top of the JPanel (ball.ycor < 0) or at the bottom of the JPanel (ball.ycor > getHeight() - ball.ballDiameter).  In either case, a vertical bounce is done by making the original heading negative.

The class Listener allow the user to interact with the game through the mouse.  Listener implements the interfaces ActionListener and MouseMotionListener.  The latter gives us access to the methods mouseDragged and mouseMoved.  I only use mouseMoved (although you have to override mouseDragged ...my method for mouseDragged does nothing).



When the mouse if moved, it triggers a MouseEvent called e.  We can read the vertical location of the mouse (e.getY()) and then we position the left paddle accordingly.  If the mouse is at the bottom or below the JPanel, the left paddle stays at the bottom of the JPanel.  If the mouse is at the top or above the JPanel, the left paddle stays at the top.  Otherwise, the position of the paddle is set according to the vertical location of the mouse:

gamePanel.leftPaddle.ycor = e.getY() - gamePanel.leftPaddle.height/2;






The ActionListener interface includes the method actionPerformed, which is invoked anytime an action occurs.  Actions are occurring all the time, and so this method occurs constantly.  I have overridden the method to have it call the GamePanel.animate method discussed above.







There is another ActionListener called ButtonListener which includes a timer which is started and stopped by two buttons in the main JPanel, called startButton and stopButton.









The main class for the pong game is a JFrame class called PongGame.  It has no constructor and no instances; everything in the class occurs in the method main.  Main creates a JFrame with two JPanels.  The "top" JPanel (called settingsPanel) includes start and stop buttons.   The other JPanel is the gamePanel already discussed.

In main, we create the ball and two paddles for the left and right sides of the screen.  We create a timer which runs when the start button is pressed and stops when the stop button is pressed.  We create an instance of ButtonListener. We create a Listener so we can have the game animate and detect mouse movement.

I am still confused by aspects of the code.  I know that they work but I just stole them from online.  In particular, these lines are tough:

           Listener listener = new Listener(gamePanel);
      Timer timer = new Timer(50, listener);
      gamePanel.addMouseMotionListener(listener);
      gamePanel.requestFocusInWindow();
   
      ButtonListener buttonListener = new ButtonListener(startButton, stopButton, timer);
      startButton.addActionListener(buttonListener);
      stopButton.addActionListener(buttonListener);


The PongGame class is shown below:
Full code for this game can be found here.

Learning Java: Command Line and LeJos

FIRST switched to Android Studio for robotics coding for the 2015-16 season.  Android Studio is a Java based platform used to create Apps for Android phones.  I spent part of last summer trying to get up to speed with Java.  Rather than dealing with Android Studio (which is a whole different can of worms), I worked with command line Java, and then with Java in NetBeans.

The command line programs described below are on GitHub at https://github.com/gcronin/LearningJava.

Program 1: Parabola
This program uses the scanner to take in  the coefficients of a parabola written in standard form, and converts to vertex form.












Here's an example of the output; the parabola y = x^2 + 2x + 5 is equivalent to y = (x+1)^2 + 4 with a vertex at (-1, 4).



Program 2: Distance Formula
Using the scanner again, the user inputs the x and y coordinates of two points, and the program calculates the distance between the points.









Here's an output showing that the distance between (0,0) and (1,1) is the square root of 2.





Program 3: Add Text to a File
This very simple program requires dealing with exceptions, which is a big part of Java.  When you open and write to a file, you must catch two exceptions... the first is given if the file doesn't exist, and the second is given if the program cannot write to the file.  We deal with exceptions by using the Try... Catch syntax.

A much more complex version of this program with user input can be found here (I didn't write it).



Using LeJos:  Java for NXT:
You can program a LEGO NXT in Java.  One of my students setup the compiler for me.  Once LeJos is installed on your computer, you first need to turn .java files into .class files using "nxjc".  Then you need to use "nxjlink" to create an "nxj" file from one of your class files.  Finally, you use "nxjupload" to upload the .nxj file.  In total, this series of commands will do the work for you:

nxjc *.java  //create .class files from .java files
set /p mclass= "Please enter the name of the main class file: " %=%  //chose a .class file
nxjlink -o linked.nxj %mclass%  //create linked.nxj from the .class file
nxjupload -r *nxj   //upload linked.nxj to the NXT


Here are a few Java programs for the NXT:
 This program just prints "Hello World" to the screen until any button is pressed.
This program shows the value of a light sensor attached to port 1 in several different formats.
This program moves motors attached to ports B and C at 50% power.
This program is a line follower using PID control.  I am not sure that it has been tuned correctly, as it was written by a former student.



Sunday, August 28, 2016

Fun with H-Bridges

This summer in our school's robotics camp, participants worked with the Adafruit Mini Rover Chassis Kit.  For $25, it looks like a steal.  However, the motors turn out to be tiny DC motors and not continuous servos, and so speed control requires circuitry beyond a microprocessor.

There are three basic ways to control a motor.  If you want to simply turn the motor on and off, you can use a switch wired directly to the power for the motors.  The breadboard below shows that setup on a Parallax Board of Education Shield.




If you want to digitally control the speed of a motor (in addition to turning it on or off), you can use a transistor.  In the picture below, a bipolar 2N2222 is wired to a PWM output of a microprocessor to allow for speed control via the duty cycle.










Finally, if you want full speed AND direction control over motors, you need an H-bridge.  I've made H-bridges before (see the Explorer Bot), but I figured it was time to make a simpler one which could be used for educating students using the Mini Rover Chassis Kit.















The result is shown in the picture above.  It uses two 2N2907 PNP transistors in the upper legs, and
two 2N2222 NPN transistors in the lower legs.  There are two H-bridges, one for the left motor and one for the right motor.  All eight transistors are wired to separate digital outputs on an Arduino, with the NPN transistors controlled by PWM pins.  This allows for speed control.  Potentiometers control the speed and direction in the test code.


Schematics, Eagle CAD board files, and test code is published on GitHub at https://github.com/gcronin/H-bridge.

I also measured some voltages and currents for one leg of an H-bridge, and then analyzed the circuit from the point of view of both a physicist and an electrical engineer.