Ball Drop Game Controller

Ball drop game is a multiplayer game that the players collaborate to keep a ball from hitting the ground. Since this is a multiplayer game and a network application, we need to create a controller that can connect to IP address of the computer which the game is running. For this assignment, I just wanted to make a simple game controller that the user can easily use without think about it. And also, since I need to learn more how the controller would connect using TCP socket connection.


How to make a game controller that is easy to use.


I just wanted to make a simple controller that easy to understand and use. Since the player needs to keep a ball from hitting the ground, the player can control the game by moving the paddle on the screen to the left & right and up & down. For that, I need 2 controllers, 1 controller to control right & left and the other one control up & down movement. I also need a button to start the game. In this case, this button is to connect and disconnect to the game.

Controller Design
Controller Design


I noticed that this Ball Drop Game is actually similar to Pong Game. I also asked some friends how they play Pong Game, and what they think about the Ball Drop Game. Surprisingly, they have a similar mental model. Since the goal of both games is to keep the ball in the field, they tended to move only right & left (on Ball Drop Game). They didn’t really move the paddle to move between up & down.

Since the player mostly control the paddle to right & left, I designed the interaction to be a continuous movement for right & left and discrete movement for up & down. The players will be very frustrated if the movement for right & left is discrete, since the ball movement is fast.

Movement: Continuous vs Discrete
Movement: Continuous vs Discrete


To make a continuous movement for right & left, I used Slide Pot Module. This module is really easy to setup and works smoothly to map the paddle movement on the screen.

Slide Pot Module
Slide Pot Module

For a discrete movement, I used buttons. Each button controls each direction. Once the player hit the button, the paddle will move one step up or down.


Control Mechanism

The challenge of making this controller is to make the paddle moves smoothly. It was quite hard to map the controller and the paddle movement well. I didn’t want to make the controller neither very sensitive nor hard to move. I tried several range of values, from 0 to 1023 mapped to -1 to 1, and several ranges in between. But I felt the range from -2 to 2 is quite perfect.

Then since the player control right & left using slide pot module, I needed to make the module move naturally like how the player would expect. Previously, I had -2 to 0 would make the paddle moved to left, and 0 to 2 to the right. But I felt it wasn’t that natural. That’s why I used this mechanism below. If the previous position is less (by value) than current position, the paddle will move to the right, and if the previous position is higher than current position, it will move to the left. But if the previous and current position have the same value, it will stay the same place (don’t move).Mechanism: Right & Left Controller

Mechanism: Right & Left Controller


I used Arduino MKR1000, because it already has a built-in Wifi. Also, I own one since I did an assignment for Tangible Interaction class. MKR1000 is easy to setup to connect to Wifi. I have had my MKR1000 setup since last semester, and I have some examples code for MKR1000 from previous class. To connect MKR1000 to Wifi, it has its own library, Wifi101 Library, which has a simple configuration to connect to the network.



  • Arduino MKR1000
  • Slide Pot Module
  • Buttons
  • Bread board
  • Wires


Ball Drop Controller Wiring


I followed Tom’s example code for joystick using Arduino MKR1000. But, since I didn’t use a joystick, I made some changes for buttons and the slide pot module. I made the pseudo-code based on the movement mechanism above.

#include <SPI.h>
#include <WiFi101.h>

const char ssid[] = ""; // your network SSID (name)
const char pass[] = ""; // your network password

WiFiClient client; // instance of the WIFi client library
int status = WL_IDLE_STATUS; // WiFi status
IPAddress server(172,22,151,125); // address of the server

const int connectButton = 5; // the pushbutton for connecting/disconnecting
const int downButton = 4; // the pushbutton for DOWN
const int upButton = 3; // the pushbutton for UP

const int sendInterval = 100; // minimum time between messages to the server
const int debounceInterval = 15; // used to smooth out pushbutton readings
const int debounceIntervalDown = 15;
int prevButtonState = 0; // previous state of the pushbutton
int prevDownButtonState = 0; // previous state of the pushbutton DOWN
int prevUpButtonState = 0; // previous state of the pushbutton UP
long lastTimeSent = 0; // timestamp of the last server message

void setup() {
//Initialize serial and wait for port to open:
// initialize digital inputs and outputs:
//pinMode(connectionLED, OUTPUT);
pinMode(connectButton, INPUT_PULLUP);
pinMode(downButton, INPUT_PULLUP);
pinMode(upButton, INPUT_PULLUP);

while (!Serial); // wait for serial port to connect.

// attempt to connect to Wifi network:
while ( status != WL_CONNECTED) {
Serial.print("Attempting to connect to SSID: ");
// Connect to WPA/WPA2 network:
status = WiFi.begin(ssid, pass);

// wait 3 seconds for connection:

// you're connected now, so print out the status:


void loop() {
// message to send to the server:
char message = 0;
// note the current time in milliseconds:
long now = millis();
// check to see if the pushbutton's pressed:
boolean buttonPushed = buttonRead(connectButton);

//if the button's just pressed:
if (buttonPushed) {
// if the client's connected, disconnect:
if (client.connected()) {
} // if the client's disconnected, try to connect:
else {
client.connect(server, 8080);

int xCurrentSensor = analogRead(A0);
xCurrentSensor = map(xCurrentSensor, 0, 1023, 2, -2);

int xPrevSensor = 0;

prevDownButtonState = digitalRead(downButton);
prevUpButtonState = digitalRead(upButton);

if (xCurrentSensor > xPrevSensor && prevDownButtonState == HIGH && prevUpButtonState == HIGH) {
message = 'r';
xPrevSensor = xCurrentSensor;
} else if (xCurrentSensor < xPrevSensor && prevDownButtonState == HIGH && prevUpButtonState == HIGH) {
message = 'l';
xPrevSensor = xCurrentSensor;
} else if (prevDownButtonState == LOW && (xCurrentSensor < xPrevSensor || xCurrentSensor > xPrevSensor || xCurrentSensor == xPrevSensor) && prevUpButtonState == HIGH) {
message = 'd';
} else if (prevUpButtonState == LOW && (xCurrentSensor < xPrevSensor || xCurrentSensor > xPrevSensor || xCurrentSensor == xPrevSensor) && prevDownButtonState == HIGH) {
message = 'u';

if (client.connected() // if the client's connected
&& now - lastTimeSent > sendInterval // and the send interval has elapased
&& message != 0) { // and there's a message to send
client.print(message); // send a message
//save this moment as last time you sent a message:
lastTimeSent = now;

// if there's incoming data from the client, print it serially:
if (client.available()) {
char c =;


// this method reads the button to see if it's just changed
// from high to low, and debounces the button:
boolean buttonRead(int thisButton) {
boolean result = false;
int currentState = digitalRead(thisButton); // current button state

if (currentState != prevButtonState // if the button's changed
&& currentState == LOW) { // and it's low
result = true; // result is true

delay(debounceInterval); // debounce delay
prevButtonState = currentState; // save the button state for next time
return result;

void printWifiStatus() {
// print the SSID of the network you're attached to:
Serial.print("SSID: ");

// print your WiFi shield's IP address:
IPAddress ip = WiFi.localIP();
Serial.print("IP Address: ");

// print the received signal strength:
long rssi = WiFi.RSSI();
Serial.print("signal strength (RSSI):");
Serial.println(" dBm");

Final Product

Ball Drop Controller Final


(Will upload the video later)

Leave a Reply

Your email address will not be published. Required fields are marked *