14. Paddle Object¶
In the previous section, we reviewed Daniel Shiffman’s Rain Game, Object-Oriented Game. Now, we want to customize the game to make it a bit more interesting. The next change we’ll make is to add a paddle that can catch or hit the falling objects. If we allow the objects to bounce, then that could provide a user with extra scoring opportunities. For now, let’s just look at how we can implement a paddle that moves left and right in response to keyboard input.
14.1. Pong Game¶
Dr Doane, in his online article: Thinking Through a Basic Pong Game in Processing provides a nice tutorial on how to create a pong game using processing. While it’s not an object-oriented approach, it still provides a very good overview and details of ideas we’ll want to implement. To start with, he discusses how we can use the processing keyboard functions to control movement of the paddle. Just as with mouseEvents, when a user presses a key, we can use that input to allow interaction with our program.
The initial code in Dr Doane’s tutorial describes the motion of a ball as it bounces against the
edges of the canvas. The main idea is that there are boundaries of the canvas where we need
to test to see if the ball has reached those boundries and if it has, then we need to change
the direction of the ball object’s speed. In an object oriented approach, this behavior would
be implemented in the Ball class definition, in the move( )
method.
14.2. KeyPressed Event¶
Dr. Doane then refers to the processing reference code in order to determine how to move the paddle object in response to a user’s keyboard interaction. Below is the processing example code:
// based on code from http://processing.org/reference/keyCode.html
void keyPressed() {
if (key == CODED) {
if (keyCode == UP) {
paddleY = paddleY – 30;
} else if (keyCode == DOWN) {
paddleY = paddleY + 30;
}
}
}
In the code above, the first thing is to note that we want to know if the user has interacted with our program using the keyboard. If that’s happened, then a keyPressed event is triggered. Similar to mousePressed events Processing provides a function keyPressed( ) that is triggered when the user interacts with the keyboard. Then, within the keyPressed( ) function, we need to determine how we want our program to respond to the keyPressed event. The keyPressed event stores the a key value, and it remembers the most recent key that has been pressed. For special keys like arrow keys, we need to also use the keyCode values, so we can tell if the key that was most recently pressed corresponds to a special key, the arrow keys. In the code above, keyCode == UP, is used to determine whether to move the paddle upwards.
For our project, we’ll be using a paddle that moves horizontally, so we’ll look at whether keyCode == LEFT, or KeyCode == RIGHT, and then we’ll need to create code that changes the behavior of our paddle’s movement based on these keyCode comparisons.
14.3. KeyPressed Event Handlers¶
First we need to create a Paddle class: This will be simliar to the Ball class, but we’ll have a rectangular object that moves based on the users keyboard interactions. So, instead of the move() method, we’ll have pressedLeft() and pressedRight() methods:
//this code is part of the Paddle class definition
void pressedLeft(){
if(x>0){ //check to make sure that the paddle doesn't move off the left edge
x=x-speed; // decrease x position to move the paddle left
}
}
void pressedRight(){
if(x+pWidth<width){ //make sure paddle stays within the right canvas border
x=x+speed;
}
}
The other methods and constructors are basically just like the Ball object, where we have paddle position coordinates: x,y and paddle dimensions pWidth, pHeight. We also have a speed variable that controls how fast the paddle moves.
14.4. KeyPressed Paddle Method Calls¶
For our program, we’ll actually want to use these pressedLeft() and pressedRight() methods within the keyPressed event. The pressedLeft( ) method is an event handler. It’s code that we want to be executed when the keyPressed event occurs. So, in the main program, we would create a Paddle object, for example paddle1. Then in the keyPressed event, we’d use the paddle1 object to call it’s pressedLeft( ) method as in the code below:
// This code is in the main program, below the draw() function
void keyPressed() {
if (key == CODED) {
if (keyCode == LEFT) {
paddle1.pressedLeft( );
}
else if (keyCode == RIGHT {
paddle1.pressedRight( );
}
}
}
14.5. Arrows: State Indicators¶
The example below displays left and right arrows when the user presses the arrow keys. In order to
display the correct arrow, I’ve created some additional variables as part of the paddle class, these
are state variables that keep track of the last keyPress event. I’m using int
variables, since
I want to have 3 possible values: pLEFT, pRIGHT, pNONE (which is the starting position).
Click inside the sketch to activate, then use the right and left arrows to move the paddle.
14.6. Final Keyword - Constant Values¶
This introduces 4 new instance variables in order to keep track of and display the red arrows
which indicate direction, Note the use of the final
keyword:
// new instance variables for the Paddle class
int direction; //this variable stores the current direction
final int pNONE=0; //initial direction state variable
final int pLEFT=1; // left direction state variable
final int pRIGHT=2; //right direction state variable
The final
keyword is used to indicate that this value should not be ever be changed, these
values are used as ‘constants’ within the program. The use of capital letters also indicates that
these are special values which are constants and shouldn’t be modified in the program. The constants
are used to set the value of direction, the use of int
makes it easy to use a switch statement
for our program logic. In the display()
method of the Paddle class, we use the switch statement
to determine which arrow method to call. Note that we’ve created separate display functions for each arrow
within the Paddle class, this makes our code logic easier to understand. Below is part of the display()
code
for the Paddle class, showing how we’ve used switch to control which arrow is displayed:
// this code is in the Paddle class: display() method
switch(direction){ //test the current value of direction
case(pNONE): //if the initial value, do nothing
break;
case(pLEFT): //if pLEFT, display left arrow
this.displayLeftArrow(); // call this Paddle method
break;
case(pRIGHT): //if pRIGHT, display right arrow
this.displayRightArrow(); // call this Paddle method
break;
}
14.7. Set the State Variable¶
So, next we need to figure out where to change the value of direction. We have already created
the Paddle methods: pressedLeft()
and pressedRight()
, and we know these methods are
executed when the user presses the left or right keyboard arrows, these Paddle methods are event handlers
that we have created, and they are executed in the global keyPressed( )
event by a Paddle object.
So, it makes sense that we would want to change the direction state variable when this event occurs, and we’ll want to
do that within the Paddle class itself, because a paddle object should be responsible for knowing what
behaviors need to occur when the Paddle method: pressedLeft()
event handler is executed.
Below is the new code:
// this code is in the Paddle class: pressedLeft() method
void pressedLeft(){
if(x>0){
x=x-speed;
direction=pLEFT; //here we set the direction state value to pLEFT
}
}
14.8. Intersection¶
In Shiffman’s game, both of his objects are circular so that has made testing for intersection much easier. In our game, we’re going to use a rectangular paddle and .svg PShape objects. Both of these elements have their x,y locations at the upper left corner of the object, whereas circles have x,y defined at the center. However, our paddle can’t move in the y direction, so that makes it a little easier to check for intersection.
After noticing some weird behavior when implementing the isIntersecting within the Paddle class, I have decided to move the code to the Drop classes. So, we’ll pass in a Paddle object, and call the method using the drop[i] object instance
// assume that in the Drop class we have an instance variable sWidth, sHeight that define
// the bounding box for our drop's shape
//assuming the Paddle has x,y,pWidth, pHeight
//this code is in the Drop Class definition
// this is called in the main tab as: drops[i].isIntersecting(paddle1);
boolean isIntersecting(Paddle p){
if(this.y + this.sHeight >= p.y){ //check the bottom point of our drop shape to see if it's hitting the top of the paddle
println("y > pY");
if(((this.x + this.sWidth) >= p.x) && (this.x <= ( p.x + p.pWidth))) {
println("hit ");
this.y=height + 100; // move the drop below the bottom of the visible canvas
this.isActive=false;
return true;
} //end if
} //end if
return false;
} //end method
When we use this intersection method, we’ll use it in the main tab, and if the method returns true, then we’ll want to increment the game score, and set the drop to be inactive. In addition, it’s also a good idea to just change the y position of the drop if it’s been hit, so that it’s off the screen, that prevents display issues.
14.9. Summary¶
So, in the Paddle class, we have created event handler methods: pressedLeft()
and pressedRight()
When we create a Paddle object, paddle1, then we’ll have that object call these event handler methods
within the global keyPressed( )
event. The event handler methods are used to trigger object behavior
code that we’ll need to create within the Paddle class itself, one example of this behavior is the
displayLeftArrow()
method.
Using Object-oriented programming means that we provide more structure to our code. It can be a little confusing
to figure out how to organize code when initially learning object-oriented programming. It can be helpful to think
about objects as being responsible for knowing how to implement their own behavior. From this perspective, within the
main program, either in the draw()
or setup()
functions, we want to tell objects when to implement behavior, either
as part of a sequence of functions, or as the result of some event being triggered, but then we want to let the object
itself be responsible for knowing how to implement it’s own behavior, so that code should be contained within the
Class definition.
14.10. Questions:¶
Why have we decided to use
int
as the type for the state variabledirection
?- What is the benefit of creating simple methods like displayLeftArrow( ) which do one
specific task instead of just writing that additional code within the pressedLeft( ) method?