Phorms

Introduction

In this post, we are going to look at creating some form fields (with a dive into more object oriented methods) , which have been used to create a menu and an options screen to change some of the settings.

Getting Started

Recommended

I recommend that you go through the previous posts for this project, although completing the Arduino part of the project is not essential.

The Code

Download

Processing

What’s New

The first thing I wanted to do in this stage was to move some of the game operations away from pronguino sketch into a new class Game. Also added a starting Menu, which allows us to change Options before we play.

FileDescription
GameClass to encapsulate game operations.
– Options
– State
– Begin
– Pause
– Resume
FormContains a number of form control classes
– Frame
– ToggleControl
– InputControl
– SliderControl
– ButtonControl
MenuClass to encapsulate menu operation
– Play
– Options
– Exit
– Status Message
What’s Changed

Most of the files have had a minor change due to moving Options into Game as a child property, below are the significant changes …

FileDescription
pronguinoImplemented new Game class.
Implemented new Menu class
Handle serial errors as status messages
Added further mouse/keyboard events
Added game events
BallAdded applyOptions() method
ConstantsAdded new menu/option states
Added new status message colours
Add new colours constants
DebugCleared constructor
OptionsAdded form controls for options
Add file handling for options file
Add various methods/events
PaddleAdded applyOptions() method
PlayerAdded name property
Added applyOptions() method
Added serve() method
ScoreboardRemove score property
Added player property
Display player.score on scoreboard
Display player.name above scoreboard

Challenge: I have started adding TODO’s at top of some of the files, adding items I’d like to do, should do, or may do in the future. Sometimes when I am putting the code together for these posts it is easy to get carried away, but I have find the cut-off point, so feel free to scan these and have a go yourself.

Play

The rules haven’t change, keys and controls are still the same, now you can select Options from the Menu and change your name(s), set speed, or switch on debugging without changing the code!

Forms

When a Processing application starts it runs setup() once, and then runs the code continuously in the loop draw() method, where the objects get displayed. With two new “forms” being added I have added some further state constants ….

... static final int STATE_MENU = 5; // Game menu showing static final int STATE_OPTIONS = 6; // Game options showing ...

… which get set accordingly … at the start …

void setup() { ... ... game.state = Constants.STATE_MENU; ... }

… and when Options button clicked …

void optionsButtonClicked() { game.state = Constants.STATE_OPTIONS; }

.. and these are used to determine what gets displayed during draw() method …

// Display .. switch(game.state) { case Constants.STATE_MENU: background(game.options.backgroundColour); menu.display(); break; case Constants.STATE_OPTIONS: background(game.options.backgroundColour); game.options.display(); break; default: ... // Game Objects ... break; }

In preparation for adding controls onto the form, we need to know when certain events happen while the form is shown, these events can only be triggered in the main sketch (pronguino) and need to be cascaded down to the form … i.e.

void mouseClicked() { ... ... if (game.state == Constants.STATE_MENU) { // Cascade down menu.clicked(); } }

.. and so the clicked() method is called in Menu class … it is in this method, the form still doesn’t know what has been clicked so checks each controls clicked() method to identify, and act accordingly.

void clicked() { ... // Check if options button clicked .. if(this.optionsButton.clicked()) { // Call event method optionsButtonClicked(); } ... }

If any of the controls used on a form have the validate() method and valid property implemented, a valid() method can be added to check all the relevant controls, which in this case is used to disabled the OK button on the options form, to prevent invalid data being saved …

boolean valid() { // Currently only input controls need validation ... for (int id=0; id < Constants.PLAYER_COUNT; id++) { // ... this will return false if any of the controls are invalid if (!this.playerNameInputs[id].valid || !this.playerUpInputs[id].valid || !this.playerDownInputs[id].valid) { return false; } } // returns true on completion of loop, nothing invalid return true; }

Controls

Processing is great for fast visual imagery and for games too, sometimes though you need to be able to interact with an application to adjust settings or enter data etc. There are a number of libraries available for such form controls, these would have been written in Java (which is what Processing language is based on).

As an exercise I decided to put some together using the “out-of-the-box” language, (will have a look at building own Java library another time) this allows me to further explore keyboard and mouse events, graphic/text combinations, and is also a good example to demonstrate some object-oriented methods.

ControlDescriptionEnabledDisabled

Button
Simple push button.

Input
Simple text entry.
ToggleUsed for true/false values
SliderChoose value within a range by dragging slider along gauge.
FrameUsed to group related controls / text.N/A

In this section will look into behavior and drawing of the controls, a further look into how they are put together will be covered more in Object-Orientation

All the controls that extend the Control class will have the following properties … although not all are necessarily used …

PropertyTypeDescription
xfloatX co-ordinate
yfloatY co-ordinate
wfloatWidth
hfloatHeight
labelStringLabel
hasFocusbooleanHas control got focus
disabledbooleanIs control disabled

Also all the controls will share common functionality through these Control methods … although not all are necessarily called … and some may even override implementation …

MethodReturnsDescription
over()booleanReturns true if the mouse cursor is over the control.
display()voidDraws the control.
clicked()booleanReturns true if the mouse button is clicked over the control.
Button

… the ButtonControl requires an additional property …

PropertyTypeDescription
captionStringtext displayed on button

.. and overrides the following method from Control

MethodReturnsDescription
display()voidDraws the button

After declaring the ButtonControl, we create it in the containing “form” class … in this case Options

... ButtonControl okButton; ... this.okButton = new ButtonControl(305,450,75,25,"OK"); ...

… the additional property is set in the controls constructor, the other properties are set by calling super(...) which calls the relevant constructor in the Control class.

class ButtonControl extends Control { ... String caption; ... ButtonControl(float x, float y, float w, float h, String caption) { // Call the constructor of the superclass super(x, y, w, h); // Sets own properties this.caption = caption; } ... }

When the mouseClicked() event is triggered in the pronguino sketch, and the state is set to STATE_OPTIONS, the following method is called in the Options class …

void clicked() { ... if(this.okButton.clicked()) { this.okButtonClicked(); } ... }

.. which will then check with the following method in the Control class whether this control has been clicked()

boolean clicked() { this.hasFocus = this.over(); return this.hasFocus; }

.. it will do this by calling the over() method in the Control class, which will check to see if the mouseX and mouseY are within the boundary of the control, ignoring this check if control disabled…

boolean over() { if(this.disabled) { return false; } if (mouseX >= this.x && mouseX <= this.x+this.w && mouseY >= this.y && mouseY <= this.y+this.h) { return true; } else { return false; } }

When the button gets drawn, it uses the disabled property and over() method to determine which colours to use, one notable method begin used here is textAlign() … it tells the following text() method how to interpret the x, y parameters .. in this case the CENTER point is in the middle of the text.

void display() { // Border colour depending on disabled state stroke(this.disabled ? Constants.CONTROL_COLOUR_DISABLED : Constants.CONTROL_COLOUR_ENABLED); // Draw button, colour depending on whether mouse over fill(this.over() ? Constants.CONTROL_COLOUR_ENABLED : Constants.CONTROL_COLOUR_BACKGROUND); rectMode(CORNER); rect(this.x, this.y, this.w, this.h, 5); // Text, colour depending on disabled state / or mouse over fill(this.over() ? Constants.CONTROL_COLOUR_BACKGROUND : this.disabled ? Constants.CONTROL_COLOUR_DISABLED : Constants.CONTROL_COLOUR_ENABLED); textAlign(CENTER,CENTER); text(this.caption, this.x+(this.w/2), this.y+(this.h/2)-2); }
Input

… the InputControl requires additional properties …

PropertyTypeDescription
valueStringValue being input/edited
maxLengthfloatMaximum length of value
validbooleanIs value a valid input

… and the following additional methods …

MethodReturnsDescription
value(String) voidSets the value property with a String
value(char)voidSets the value property with a String converted from a char
validate()voidSets the valid property
typed(char) voidEvent method called when key typed.

.. and overrides the following method from Control

MethodReturnsDescription
display()voidDraws the input control

After declaring the InputControl, in this case an array, we create it in the containing “form” class … in this case Options

... InputControl[] playerNameInputs; ... this.playerNameInputs[Constants.PLAYER_ONE] = new InputControl(450,120,200,20, "Name", 10, this.names[Constants.PLAYER_ONE]); ...

… with the exception of valid , … the additional properties are set in the controls constructor, the other properties are set by calling super(...) which calls the relevant constructor in the Control class.

InputControl(float x, float y, float w, float h, String label, int maxLength, String value) { super(x, y, w, h, label); this.maxLength = maxLength; this.value(value); }

… a setter method value(...) is used to set the value

void value(String value) { this.value = value; this.validate(); }

… which also calls the validate() method, which sets the valid property accordingly …

void validate() { this.valid = (this.value.length() > 0); }

When the mouseClicked() event is triggered in the pronguino sketch, and the state is set to STATE_OPTIONS, the following method is called in the Options class …

void clicked() { ... for (int id=0; id < Constants.PLAYER_COUNT; id++) { this.playerNameInputs[id].clicked(); ... } ... }

.. which will then check with the following method in the Control class whether this control has been clicked() … however, note how this method is called slightly differently, we are not interested in the result of method, as we don’t have to do anything at this level.

Note: This is a good way to get around the fact that the following method implementations are not allowed i.e. two methods of the same name that have different return types … void clicked() { ... } boolean clicked() { ... }

.. as with ButtonControl the clicked() method in Control class will get called .. however, this time we are more interested in the hasFocus property …

boolean clicked() { this.hasFocus = this.over(); return this.hasFocus; }

When the keyTyped() event is triggered in the pronguino sketch, and the state is set to STATE_OPTIONS, the following method is called in the Options class …

void typed(char typedKey) { ... for (int id=0; id < Constants.PLAYER_COUNT; id++) { this.playerNameInputs[id].typed(typedKey); ... } }

.. which will then call typed(char typedKey) in the InputControl class, if the control has focus, adds typedKey to value, checking the maxLength, or if BACKSPACE is typed, removes last character from value, continuously checking if value is valid…

void typed(char typedKey) { if(this.hasFocus) { if(typedKey==BACKSPACE && this.value.length() > 0) { this.value = this.value.substring(0, this.value.length()-1); } else { if(this.value.length() < this.maxLength && typedKey != BACKSPACE) { this.value += typedKey; } } } this.validate(); }

Using basic drawing methods, coloured based on disabled and valid properties, note when drawing the value, adds a _ afterwards when focused, acting as a cursor…

void display() { // Label fill(Constants.CONTROL_COLOUR_LABEL); textAlign(RIGHT, TOP); text(this.label, this.x, this.y); // Border if(this.valid) { stroke(this.disabled ? Constants.CONTROL_COLOUR_DISABLED : Constants.CONTROL_COLOUR_ENABLED); } else { stroke(Constants.CONTROL_COLOUR_INVALID); } // Input area fill(Constants.CONTROL_COLOUR_BACKGROUND); rectMode(CORNER); rect(this.x, this.y, this.w, this.h, 5); // Value fill(this.disabled ? Constants.CONTROL_COLOUR_DISABLED : Constants.CONTROL_COLOUR_ENABLED); textAlign(LEFT,CENTER); // Add a 'cursor' after value if it has focus text(this.value + (this.hasFocus ? '_' : ""), this.x+10, this.y+(this.h/2)-1); }

Challenge: Have a go at either amending or extending the InputControl, to allow only number relating characters i.e. 0-9, ‘.’, ‘-‘ etc, extending is probably the better option, then you don’t ‘break’ InputControl .. just create a new one called say ‘NumberInputControl’ ? You could add a FrameControl titled ‘Net’ and put all the net related options here ?

Toggle

… the ToggleControl requires an additional property …

PropertyTypeDescription
valuebooleanOn / Off

… and the following additional method …

MethodReturnsDescription
value(boolean) voidSets the value property with a boolean

.. and overrides the following methods from Control

MethodReturnsDescription
clicked()booleanReturns true if the mouse button is clicked over the control.
display()voidDraws the toggle control

After declaring the ToggleControl, we create it in the containing “form” class … in this case Options

... ToggleControl debugEnabledToggle; ... this.debugEnabledToggle = new ToggleControl(220,120,40,20, "Enabled", this.debug.enabled);

… the additional property is set in the controls constructor, the other properties are set by calling super(...) which calls the relevant constructor in the Control class.

ToggleControl(float x, float y, float w, float h, String label, boolean value) { super(x, y, w, h, label); this.value(value); }

… a setter method value(...) is used to set the value

void value(boolean value) { this.value = value; }

When the mouseClicked() event is triggered in the pronguino sketch, and the state is set to STATE_OPTIONS, the following method is called in the Options class …

void clicked() { ... this.debugEnabledToggle.clicked(); ... }

.. when the clicked() method in the ToggleControl class gets called .. first it checks with the superclass to see if control has been clicked, if it has, it uses the logical NOT operator to invert the current boolean value.

boolean clicked() { if(super.clicked()) { this.value(!this.value); return true; } else { return false; } }

Using basic drawing methods, coloured based on disabled, valid and value properties…

void display() { // Label fill(Constants.CONTROL_COLOUR_LABEL); textAlign(RIGHT, TOP); text(this.label, this.x, this.y); // Outline stroke(this.disabled ? Constants.CONTROL_COLOUR_DISABLED : Constants.CONTROL_COLOUR_ENABLED); fill(Constants.CONTROL_COLOUR_BACKGROUND); rectMode(CORNER); rect(this.x, this.y, this.w, this.h, this.h/2); // Switch noStroke(); ellipseMode(CORNER); if(this.value) { fill(this.disabled ? Constants.CONTROL_COLOUR_DISABLED : Constants.CONTROL_COLOUR_ON); ellipse(this.x+(this.w/2)+2, this.y+2, this.h-3, this.h-3); } else { fill(this.disabled ? Constants.CONTROL_COLOUR_DISABLED : Constants.CONTROL_COLOUR_OFF); ellipse(this.x+2, this.y+2, this.h-3, this.h-3); } }
Slider

… the SliderControl requires additional properties …

PropertyTypeDescription
valuefloatValue being set
minValuefloatMinimum value allowed.
maxValuefloatMaximum value allowed.
gaugeWfloatWidth of ‘gauge’ in pixels
sliderButtonSliderButtonControlSlider button

… and the following additional methods …

MethodReturnsDescription
value(float) voidSets the value property with a float
pressed()voidEvent method called when mouse button pressed.
released()voidEvent method called when mouse button released.
dragged() voidEvent method called when mouse button pressed and dragged.

.. and overrides the following method from Control

MethodReturnsDescription
display()voidDraws the slider control

After declaring the SliderControl, we create it in the containing “form” class … in this case Options

... SliderControl ballSpeedSlider; ... this.ballSpeedSlider = new SliderControl(140, 340, 30, 20, "Ball", 1.0, 10.0, this.ballSpeed); ...

… the additional properties are set in the controls constructor, the other properties are set by calling super(...) which calls the relevant constructor in the Control class.

SliderControl(float x, float y, float w, float h, String label, float minValue, float maxValue, float value) { super(x, y, w, h, label); this.minValue = minValue; this.maxValue = maxValue; this.value(value); this.gaugeW = ((this.maxValue - this.minValue) / 2) * this.w; this.sliderButton = new SliderButtonControl(this, map(this.value, this.minValue, this.maxValue, this.x+(this.w/2), this.x+this.gaugeW-(this.w/2)), this.y, this.w, this.h); }

… a setter method value(...) is used to set the value

void value(float value) { this.value = value; }

When the mousePressed() , mouseReleased() or mouseDragged() events are triggered in the pronguino sketch, and the state is set to STATE_OPTIONS, the following methods are called in the Options class …

void pressed() { ... this.ballSpeedSlider.pressed(); ... } void released() { ... this.ballSpeedSlider.released(); ... } void dragged() { ... this.ballSpeedSlider.dragged(); ... }

.. which will then call the pressed(), released() or dragged() respectively in the SliderControl class … as it its the child class SliderButtonControl that is more interested in these events happening the call is passed on …

void pressed() { this.sliderButton.pressed(); } void released() { this.sliderButton.released(); } void dragged() { this.sliderButton.dragged(); }

.. and then the display() method draws the control, note the call to the child sliderButton.display()method.

void display() { // Label fill(Constants.CONTROL_COLOUR_LABEL); textAlign(RIGHT, CENTER); text(this.label, this.x, this.y); // Gauge stroke(this.disabled ? Constants.CONTROL_COLOUR_DISABLED : Constants.CONTROL_COLOUR_ENABLED); line(this.x, this.y, this.x+this.gaugeW, this.y); // Slider Button this.sliderButton.display(); }
SliderButtonControl

The SliderButtonControl is a child class of SliderControl, and only used by its parent to manage the buttons behaviour … it has no use outside of this …

class SliderControl extends Control { private class SliderButtonControl extends Control { // So we can access parent properties SliderControl parent; ... } ... }

… the SliderButtonControl requires additional properties …

PropertyTypeDescription
grabbedbooleanSet when mouse button pressed or released.
parentSliderControlAccess to parents properties i.e. value, x, y etc.

… and the following additional methods …

MethodReturnsDescription
pressed()voidEvent method called when mouse button pressed.
released()voidEvent method called when mouse button released.
dragged() voidEvent method called when mouse button pressed and dragged.

.. and overrides the following methods from Control

MethodReturnsDescription
over()booleanTrue if mouse cursor over control.
display()voidDraws the slider control

After declaring the SliderButtonControl, we create it in its parent class … SliderControl

... SliderButtonControl sliderButton; ... this.sliderButton = new SliderButtonControl(this, map(this.value, this.minValue, this.maxValue, this.x+(this.w/2), this.x+this.gaugeW-(this.w/2)), this.y, this.w, this.h); ...

… the additional properties are set in the controls constructor, the other properties are set by calling super(...) which calls the relevant constructor in the Control class.

SliderButtonControl(SliderControl parent, float x, float y, float w, float h) { super(x, y, w, h); this.parent = parent; this.grabbed = false; }

.. when the pressed() method in the SliderButtonControl class gets called .. the grabbed flag gets set according to result of over()

void pressed() { this.grabbed = this.over(); }

.. when the released() method in the SliderButtonControl class gets called .. the grabbed flag gets set to false …

void released() { this.grabbed = false; }

.. when the dragged() method in the SliderButtonControl class gets called .. the x co-ord of the button gets continuously calculated by keeping the mouseX value within the bounds of its parent, using the constrain() method, and then calculates the new value for SliderControl, based on the new x co-ord …

void dragged() { if(this.grabbed) { this.x = constrain(mouseX, this.parent.x+(this.w/2), this.parent.x+this.parent.gaugeW-(this.w/2)); this.parent.value(round(map(this.x, this.parent.x+(this.w/2), this.parent.x+this.parent.gaugeW-(this.w/2), this.parent.minValue, this.parent.maxValue))); } }

… due to this controls x,y being at the center, a slightly different version of the over() method was required, so it gets overridden here …

boolean over() { if(this.disabled) { return false; } if (mouseX >= this.x-(this.w/2) && mouseX <= this.x+(this.w/2) && mouseY >= this.y-(this.h/2) && mouseY <= this.y+(this.h/2)) { return true; } else { return false; } }

.. and then the display() method draws the slider button …

void display() { // Slider Button fill(this.parent.disabled ? Constants.CONTROL_COLOUR_DISABLED : Constants.CONTROL_COLOUR_SLIDER); rectMode(CENTER); rect(this.x, this.y, this.w, this.h, this.h/2); // Value fill(this.parent.disabled ? Constants.CONTROL_COLOUR_BACKGROUND : Constants.CONTROL_COLOUR_ENABLED ); textAlign(CENTER, CENTER); text(str(this.parent.value), this.x, this.y); }

Challenge: Have a go at creating a new, more specific control to adjust the size of the ball, you could create a new file to add this into, called, say … GameForm, keeping Form to be used for generic type controls. You could call this control something like BallSliderControl, that extends SliderControl (also adding BallSliderButtonControl, which would be the ball itself), adding additional properties such as ballRadius, and overriding methods such as over() and display(), where, instead of showing value, change the size of ‘ball’ etc. You could also do something similar with the the paddleHeight, drawing the paddle as the ‘gauge’ ?

Frame

The FrameControl requires an additional property …

PropertyTypeDescription
titleStringSet when mouse button pressed or released.

… no additional methods are required, just the following overridden method …

MethodReturnsDescription
display()voidDraws the frame control

After declaring the FrameControl, we create it in the containing “form” class … in this case Options

... FrameControl debugFrame; ... this.debugFrame = new FrameControl(80,100,300,200,"Debugging"); ...

… the additional property is set in the controls constructor, the other properties are set by calling super(...) which calls the relevant constructor in the Control class.

FrameControl(float x, float y, float w, float h, String title) { super(x, y, w, h); this.title = title; }

.. the display method simply draws a rounded rectangle, note when drawing the title, a background coloured rectangle is drawn first, based on the width of the title in pixels, based on the current font size using the method textWidth().

void display() { // Border stroke(Constants.CONTROL_COLOUR_FRAME); noFill(); rectMode(CORNER); rect(this.x, this.y, this.w, this.h, 5); // Title background noStroke(); fill(Constants.CONTROL_COLOUR_BACKGROUND); rect(this.x+10, this.y-10, textWidth(this.title)+2, 20); // Title fill(Constants.CONTROL_COLOUR_FRAME); textAlign(LEFT,CENTER); text(this.title, this.x+10, this.y); }

Object-Orientation

Object-Orientation is a huge subject … and definitely not completely covered here, however, will cover some of the concepts within the context of this project.

Object

An object is an entity that has state and behaviour, in this project for example we have the following objects along with examples of some of their state and behaviour…

ObjectStateBehaviour
BallColour
Speed
can move.
can bounce.
GameState
In play ?
can pause.
can resume.
Input ControlValue
Has focus ?
can be clicked.
can be typed in.

.. each one of these is an instance of a class

Class

A class is basically a blueprint from which an object is instantiated … each class can include fields, methods and constructors, these are some examples of classes used in this project …

ClassFieldsMethodsConstructors
Playerscore
name
serve()Player(index) {…}
Paddleplayer
speed
move()Paddle(Player player) {…}

Note: I have used the term ‘fields’, however there are other terms used with different meaning depending on the context i.e. properties, attributes etc, as Processing ignores scoping keywords such as private and public etc, any field declared in a class will be exposed as properties anyway.

.. in most cases objects are instantiated with the new keyword … so for players to be created …

for (int index=0; index < Constants.PLAYER_COUNT; index++) { players[index] = new Player(index); }

..and the paddle …

Player(int index) { ... // Create Paddle this.paddle = new Paddle(this); ... }

Encapsulation

The aim of encapsulation is to prevent some internal fields of the object being changed directly by “hiding” them, and then provide (or not provide, as they maybe fields that are just used internally) the means to access them via ‘getter’ and ‘setter’ methods.

As mentioned in the note above, Processing ignores scoping keywords such as private and public , so we can not really implement encapsulation, however, I will still go through some examples, as I am hoping to convert the form controls into a Java library, and this is where encapsulation will really matter.

A good example to show is the InputControl class …

class InputControl extends Control { String value; boolean valid; ... // Constructor InputControl(float x, float y, float w, float h, String label, int maxLength, String value) { ... this.setValue(value); } ... // Setter Method void setValue(String value) { this.value = value; this.validate(); } ... // Validation void validate() { this.valid = (this.value.length() > 0); }

… here, I know that I can ‘get’ the value quite easily from an object of this class …

for(int id = 0; id < Constants.PLAYER_COUNT; id++) { this.names[id] = this.playerNameInputs[id].value; ... }

.. however, the trouble starts if I wanted to ‘set’ the value directly, for example …

for(int id = 0; id < Constants.PLAYER_COUNT; id++) { this.playerNameInputs[id].value(this.names[id]); }

.. if I were to implement the above, the validate() method would not get called, allowing invalid data to be entered, not good! So we add a ‘setter’ method …

void setValue(String value) { this.value = value; this.validate(); }

.. now, when I use the following, which has been implemented, the validate() method gets called …

for(int id = 0; id < Constants.PLAYER_COUNT; id++) { this.playerNameInputs[id].setValue(this.names[id]); }

If this class was to be written in Java, it should look something like this …

class InputControl extends Control { private String value; // Hide the value, preventing access directly ... private boolean valid; // This should only be set internally ... // Constructor InputControl(float x, float y, float w, float h, String label, int maxLength, String value) { ... this.setValue(value); } ... // Setter method for value public void setValue(String value) { this.value = value; this.validate(); } // Getter method for value public String getValue() { return this.value; } ... // Getter method for valid public boolean isValid() { return this.valid; } // Validation, only to be called internally private void validate() { this.valid = (this.value.length() > 0); }

Abstraction

Abstraction is a bit like saying, here is what you have got to do, I don’t care how you’re going to do it ,but I might do some of it for you.

Interface

An interface is in essence a contract for any class that implements it has to follow, in this project we have the following interface …

interface IControl { boolean over(); boolean clicked(); void display(); }

.. so any class that implements this interface, has to create each method, whether it be abstract or non-abstract…

abstract class Control implements IControl {...}

It maybe that in the future, I added some kind of read-only or visual control that doesn’t need over() and clicked(), just display() … I could then split the above interface to something like …

interface IControl { void display(); } interface IClickableControl { boolean over(); boolean clicked(); }

.. and the classes that implemented these would look like this …

// A visual only control abstract class Control implements IControl {...} // A Clickable control, but also needs to be a visual one abstract class ClickableControl implements IControl, IClickableControl {...}

.. using interfaces is useful for expanding on classes without breaking existing code, and also if we were to have had a method that needs to do something with any of our controls, we could use …

void someMethod(IControl someControl) { ... }

… which would accept as an argument any object that was created from a class that implements IControl.

Abstract Class

An abstract class is basically creating our base (or subclass) class from which other classes will be derived from, in the case of this project, we have the Control class, which is ….

  • defining the common fields (i.e. x, y, w, h).
  • defining and implementing non-abstract methods with common functionality (i.e. over, clicked)
  • defining abstract method, that needs to be implemented by derived classes (i.e. display)
abstract class Control implements IControl { float x; float y; float w; float h; String label; boolean hasFocus = false; boolean disabled = false; ... boolean over() { if(this.disabled) { return false; } if (mouseX >= this.x && mouseX <= this.x+this.w && mouseY >= this.y && mouseY <= this.y+this.h) { return true; } else { return false; } } ... boolean clicked() { this.hasFocus = this.over(); return this.hasFocus; } ... abstract void display(); }

Abstract classes cannot be instantiated, so doing something like this would not be allowed …

Control control = new Control(...);

Inheritance

Inheritance is where one class extends another, inheriting all of its properties and methods, these are the various types of inheritance …

Single Inheritance

There isn’t an example of single inheritance in this project, where one class is extended only once by another class.

Multiple Inheritance

Processing or Java does not support multiple inheritance, where one class can extend two or more other classes.

Hierarchical Inheritance

Hierarchical inheritance, is where many classes are derived from the same class, in the Forms file, all of the controls here are extended from the Control class.

Below is a simple example, showing the relevant code of the ButtonControl class, in this case all of the properties will be available, the over() and clicked() methods do not need to be implemented, but their functionality is inherited from the Control class, the only code that needs to be added is any additional properties of its own i.e. caption, and to implement the abstract method display() ..

class ButtonControl extends Control { // Extra property required for button control String caption; ... ... // Implements the abstract method defined in Control void display() { ... // Code to draw button ... }
Multilevel Inheritance

Multilevel inheritance is where a class is derived from another already derived class, although there are no examples of this type, a challenge was set earlier in post that would use this type of inheritance, here is a bit of a spoiler …

class BallSliderControl extends SliderControl {...}

Polymorphism

Polymorphism, the word, is Greek, which translates roughly to “multiple or many forms”, so in the context of object-orientation we are transforming a class into different (but related) classes by changing the underlying behaviour of its methods. There are two types of polymorphism …

Compile Time

Compile time polymorphism is acheived using overloading, below, shows two overloaded constructors for the Control class, this allows any controls extending this to implement in two different ways, in this example a control with a label, and a control without …

Control(float x, float y, float w, float h, String label) { ... } Control(float x, float y, float w, float h) { ... }

..so in the case of the ToggleControl, for example, the constructor calls super (…) which uses the relavant constructor of Control class (the superclass), and then sets its own properties…

class ToggleControl extends Control { boolean value; ... ToggleControl(float x, float y, float w, float h, String label, boolean value) { super(x, y, w, h, label); this.value(value); } ... }

.. and in the case of the FrameControl, for example, which doesn’t display a label, it has it’s own property title, it only requires the use of x, y, w and h, which get set by calling super(...) which uses the relevant constructor of Control

class FrameControl extends Control { String title; ... FrameControl(float x, float y, float w, float h, String title) { super(x, y, w, h); this.title = title; } ... }

As well as constructors other methods can be overloaded, as in this case with the InputControl, we have setValue( ... ), the first overload accepts a String, and then validates, the second one allows a char to be passed in, and calls the other with char converted…

void setValue(String value) { this.value = value; this.validate(); } ... void setValue(char value) { this.setValue(str(value)); }
Run Time

Run time polymorphism is acheived by overriding methods, where a subclass provides the specific implementation of a method declared in its superclass.

One example is in the SliderButtonControl, where the over() method needs to do the check for mouse over a little different from what was in Control class …

boolean over() { // code to do something different than what was implemented on Control }

… another example is in the ToggleControl, with clicked() , however, this time instead of replacing the implementation completely, it checks with the parent method first using super.clicked(), and continues with setting the value if true …

boolean clicked() { if(super.clicked()) { this.value(!this.value); return true; } else { return false; } }

.. and finally, with all the classes dervied from Control, they are to be drawn differently by overriding the method display(), which in the Control class had no implementation at all …

void display() { // draw the specific control here }

File Handling (JSON)

Up until now all the options of the game have been hard coded into the Options class constructor, and we now have form to change some of them, but we need to keep these settings for when we close game and play again next time.

Below is the contents of options.json file located in the /data folder, it is formatted using JSON (JavaScript Object Notation) …

{ "debug": { "playerHitsBall": true, "paddleHitsBoundary": false, "ballReposition": true, "ballHitsBoundary": false, "enabled": false }, "players": [ { "name": "Bill", "id": 0, "up": "q", "down": "a" }, { "name": "Ben", "id": 1, "up": "p", "down": "l" } ], "ballSpeed": 5, "paddleSpeed": 6 }
Reading

.. the the Options class, the method read() loads the file into a JSONObject

... JSONObject optionsData; ... this.optionsData = loadJSONObject("data/options.json");

.. and uses various methods to get the values of the keys from optionsData

... JSONObject debugOptions = optionsData.getJSONObject("debug"); this.debug.enabled = debugOptions.getBoolean("enabled"); ... this.ballSpeed = optionsData.getFloat("ballSpeed"); ... JSONArray playersOptions = optionsData.getJSONArray("players"); ...
Writing

.. the the Options class, the method write() sets the values of the keys in optionsData

this.optionsData.getJSONObject("debug").setBoolean("enabled",this.debug.enabled); ... this.optionsData.setFloat("paddleSpeed", this.paddleSpeed); ... playerOption.setString("name", names[id]); ...

… and saves optionsData back to the file …

saveJSONObject(this.optionsData, "data/options.json");

Conclusion

Ok, so that was quite a chunky post, but hopefully quite a useful one, learning that Processing is not all about using graphics for games and art, and an exploration of object-orientation in Processing.

What next? There is something missing, something that all video games should have …