Penalty Shoot-out - Leo - Flora
Conception and design
As the project's name impies, the project took the idea of the penalty in soccer matches. With so many projects touching on the theme of soccer, I found most of them are either physical or digital. So the idea came to my mind that I can combine physical and digital interaction with the use of Arduino and Processing. Both have different features as physical interaction engages users via more hands-on experiences while the digital interaction delivers visual and sound effects better. So the best way of combining these two is to let users interact according to what they see and hear. As two different types of interaction like shooting and saving are quite necessary for the shooter and goalie, it would be more engaging to initiate the game through direct physical interaction using Arduino. After the whistle is blowed, the shooter choose a direction through touching sensors and a ball will shoot out to the chosen direction on the Processing. While watching the trajectory of the ball, another user is able to make adjustments to the goalie's postion by twisting the potentiometer. In this way, both physical and digital process are combined to be intuitive for users. In addition to changing the goalie's position on Processing, an actual mini goalie moves accordingly, adding more layers and fun to the overall interaction. During the user testing session, I found users are confused about when to quit and stop. Besides, because of the use of Proccessing on laptop screen placed aside, users mainly fouced on the monitor to determine the goalie's position according to the trajectory of the ball. As a result, the physical little goalie moving left and right accordingly seemed useless in terms of interaction with users' eyes fixed on the screen. Therefore, I set rounds for the game in which whoever reaches three points first wins. Then, I projected the laptop screen to an ipad which was embedded under the football field so that the user can focus on the project itself and look at the goalie's physical movement to make the save. Eventually, the interaction is more integrated and engaging.
Fabrication and production
The project started from prototyping so I used cardboards as main materials initially. For the goalie's part, I was inspired by one of the recitation project named Etch-A-Sketch that uses a potentiometer to draw lines on Processing. While for goalie, instead of drawing lines, an icon can be controlled by the potentiometer. As the goalie moves left or right to make a save, I use a rectangle on Processing that moves horizontally on the upper side to symbolize the goalie. On the physical side, the image of a goalie trying to catch the ball was attched to the servo so that the physical goalie moves according to the adjustment of potentiometer. Additionally, the front and back of the goalie are different, making it more real and detailed. To provide a more immersive user experience, I 3D printed the goal gate, crowd stands and light poles to mimic the stadium look. After furnishing the footabll field with printed paper, a mini football stadium was built and the goalie was ready inside, waiting for users to interact.
Then, as the photo showed above, I used three touching sensors for shooter to determine the direction to which the ball shoots out, self-explained by the image decorated on the sensors and their position. Because the similar interaction via touching sensors recieved positive feedbacks in midterm project and it is both effective and efficient, I adapted the approach in my final project as well. I updated it with Processing where the ball shoots out from the bottom on Processsing. Three touching sensors decide the ball to shoot to left, middle or right. As the user chooses the direction and presses the corresponding touching sensor, a ball will shoots at a random angle within the given interval. (left: 30-70; middle: 70-110; right: 110-150) I coded this way to avoid the fixed trajectory of the football so that players will not know the exact path of the ball. For the ball shooting to left and right, it may bounce back at the edge and change the direction. By doing so, the trajectory is not always straight. Both add more unpredictability and fun to the game as well as the interaction. Then a board was set up to block the sensors from the the goalie so that the goalie can not see which direction the shooter chooses. The photo on the board captures the moment when the penalty is going to be kicked, giving more excitement and tense to the shooter.
After that, I made a linear motion with servo to provide a crowd response to the result. When there is a goal, the celebrating crowd will pop up. On the other hand, the disappointing crowd comes up when the ball is saved. This is synchronized with the sound effect in Processing. Each round starts with a whistle, whenever there is a goal or the goalie stops the ball, a celebrating or disappointing sound will be played. When goalie or shooter reached three points first, the result of "Shooter/Goalie wins!" will be showed on Processing as well. For this game machinism, I achieved that with some help of ChatGPT. In this way, the digital, physical as well as visual and audio effects are combined together, contributing to the mutilayers of the project.
Finally, for a nicer look, I used laser cutting to replace the cardboard foundation with the wooden box in which circuits, electroinic and the like are hidden. I cut out the size for the Processing page so that it can be embedded under the stadium. The page is projected from the computer to a mini ipad. The background was changed to green to be better integrated in the football court. Frankly, this spent me much of time at the assembling stage because I kept struggling projecting the screen to ipad. I asked my professor Flora for help and we tried many approaches but still far from satisfactory. We didn't find the best solution until one of my classmates ingeniously suggested using Zoom.
Though a bit lagged, the project is now more portable instead of having a huge laptop inside. In general, the project reflects my love for football and my appreciation for the aesthetics of dioramas.
Conclusions
The Penalty Shoot-out project aims to immerse players in the vibe of football game, enabling them to experience the thrill, tension and excitement that binds so many football fans together. The real-time interactions and unpredictability of the project do realise the expectations to some extent. By twisting to adjust the goalie and pressing to shoot with eyes fixed on the digital part, users seem to be concentrated and they do feel joyful after scoring. The serial communication blends the digital and physical interactions. This creates a system where the project and users are constantly interacting. Moreover, the projects also incorperates the interaction between two playes as they have to react to opponent's choice and do the guessing during the process. The visual and sound effects further improve the interaction experience. While different rounds and the rule of getting three points first to win indicate where you are in an interaction, making it clear for users. If given more time, I would change the potentiometer to a slide so that the interaction for goalie would be more intuitive. Besides, I would add more interactions like aiming or timing to the shooter side as the current one only requires pressing the touching sensor. In this way, the shooter may have more fun and interact more along the processing instead of watching and waiting for the result.
In doing the project, I realised how amazing things we can generate with more tools like Processing, laser cutting, 3D printing and so on. And it can work wonders when you can connect these mutiple tools together. What's more, I had the opportunity to challenge myself and think differently about using electronics to create interactions. The way electronics process information and respond is quite different from how humans do, and exploring this transition allowed me to view everyday tools from a new perspective. It also gave me a deeper appreciation for the complex mechanisms and brainwork behind ordinary daily items.
Disassembly
Appendix
Arduino:
#include <Servo.h>
const int touch1 = 2; // First touch sensor
const int touch2 = 3; // Second touch sensor
const int touch3 = 4; // Third touch sensor
const int potentiometer = A0; // Potentiometer input
Servo myServo;
Servo leftServo; // Left servo for goal
Servo rightServo; // Right servo for save
bool leftServoActive = false;
bool rightServoActive = false;
unsigned long servoStartTime = 0;
const unsigned long servoWaitDuration = 2500; // Servo wait time (3 seconds)
void setup() {
Serial.begin(9600);
pinMode(touch1, INPUT_PULLUP);
pinMode(touch2, INPUT_PULLUP);
pinMode(touch3, INPUT_PULLUP);
myServo.attach(9); // Main servo for goalie
leftServo.attach(10); // Left servo for goal
rightServo.attach(11); // Right servo for save
// Initialize servo positions
leftServo.write(0);
rightServo.write(0);
}
void loop() {
// Read potentiometer and adjust goalie servo
int potValue = analogRead(potentiometer);
int servoAngle = map(potValue, 0, 1023, 10, 170);
myServo.write(servoAngle);
// Read touch sensors
int state1 = digitalRead(touch1);
int state2 = digitalRead(touch2);
int state3 = digitalRead(touch3);
// Send data to Processing
Serial.print(servoAngle);
Serial.print(",");
Serial.print(state1);
Serial.print(",");
Serial.print(state2);
Serial.print(",");
Serial.println(state3);
// Check for incoming serial commands
if (Serial.available()) {
char command = Serial.read();
if (command == 'G') {
startServoMovement(leftServo, true); // Goal: Left servo
} else if (command == 'S') {
startServoMovement(rightServo, false); // Save: Right servo
}
}
// Handle servo movement
handleServoMovement();
}
void startServoMovement(Servo &servo, bool isLeft) {
servo.write(100); // Rotate servo to 90 degrees
servoStartTime = millis();
if (isLeft) {
leftServoActive = true;
} else {
rightServoActive = true;
}
}
void handleServoMovement() {
unsigned long currentTime = millis();
if (leftServoActive && currentTime - servoStartTime >= servoWaitDuration) {
leftServo.write(0); // Return left servo to 0 degrees
leftServoActive = false;
}
if (rightServoActive && currentTime - servoStartTime >= servoWaitDuration) {
rightServo.write(0); // Return right servo to 0 degrees
rightServoActive = false;
}
}
Processing:
import processing.serial.*;
import processing.sound.*; // Sound library for sound effects
Serial serialPort;
// Arduino values: servo angle + 3 touch sensors
int NUM_OF_VALUES_FROM_ARDUINO = 4;
int arduino_values[] = new int[NUM_OF_VALUES_FROM_ARDUINO];
float goalieX; // Goalkeeper's horizontal position
float ballX, ballY; // Ball's position
float ballSpeedX, ballSpeedY; // Ball's speed
boolean ballActive = false; // Ball in motion
boolean allowNewShot = false; // Prevent new shots until allowed
SoundFile whistleSound, goalSound, saveSound; // Sound files for whistle, goal, and save
int playerScore = 0; // Tracks saves by the player
int computerScore = 0; // Tracks goals scored by the computer
boolean gameOver = false; // Indicates if the game is over
String winnerMessage = ""; // Message to display the winner
void setup() {
size(900, 900);
background(0);
// Setup serial communication
printArray(Serial.list());
serialPort = new Serial(this, "/dev/cu.usbmodem101", 9600); // Adjust the port
// Load sound files
whistleSound = new SoundFile(this, "whistle.mp3");
goalSound = new SoundFile(this, "goal.mp3");
saveSound = new SoundFile(this, "save.mp3");
// Initial goalkeeper position
goalieX = width / 2;
startNewRound(); // Begin the first round with a whistle
}
void draw(){
background(#2FBC4A);
stroke(255);
fill(255);
// Display scores
textSize(32);
textAlign(CENTER);
text("Shooter: " + computerScore + " | Goalie: " + playerScore, width / 2, height - 50);
if (gameOver) {
textSize(48);
text(winnerMessage, width / 2, height / 2); // Display winner message
return; // Stop gameplay if game is over
}
// Draw the goalkeeper
rectMode(CENTER);
rect(goalieX, 100, 100, 20);
// Update and draw the ball if active
if (ballActive) {
ballX += ballSpeedX;
ballY += ballSpeedY;
// Bounce off screen edges
if (ballX <= 0 || ballX >= width) {
ballSpeedX *= -1;
}
// Ball reaches the top edge (goal)
if (ballY <= 0) {
ballActive = false;
computerScore++;
if (checkGameOver()) return; // Stop further actions if game ends
playGoalAnimation(); // Simulate goal
}
// Check collision with goalkeeper
if (ballY <= 110 && ballY >= 90 && ballX >= goalieX - 50 && ballX <= goalieX + 50) {
println("Saved!");
ballActive = false;
playerScore++;
if (checkGameOver()) return; // Stop further actions if game ends
playSaveAnimation();
}
// Draw the ball
ellipse(ballX, ballY, 25, 25);
}
getSerialData(); // Receive data from Arduino
// Update goalkeeper position
int servoAngle = arduino_values[0];
goalieX = map(servoAngle, 180, 0, 0, width);
// Handle ball triggering based on touch sensor inputs
if (!ballActive && allowNewShot) {
if (arduino_values[1] == 1) { // Left touch sensor
startBall(random(radians(30), radians(70)));
} else if (arduino_values[2] == 1) { // Middle touch sensor
startBall(random(radians(70), radians(110)));
} else if (arduino_values[3] == 1) { // Right touch sensor
startBall(random(radians(110), radians(150)));
}
}
}
void startNewRound() {
allowNewShot = false; // Prevent shooting before whistle
whistleSound.play();
delay(1000); // Wait for the whistle to finish
allowNewShot = true; // Enable shooting
}
void startBall(float angle) {
ballX = width / 2; // Start at the center
ballY = height; // Start at the bottom
float speed = 20; // Ball speed
ballSpeedX = cos(angle) * speed;
ballSpeedY = -sin(angle) * speed; // Negative for upward movement
ballActive = true;
}
void playGoalAnimation() {
println("Goal!");
serialPort.write('G'); // Send goal signal to Arduino
goalSound.play();
delay(2000); // Wait for sound and servo actions
startNewRound();
}
void playSaveAnimation() {
println("Save!");
serialPort.write('S'); // Send save signal to Arduino
saveSound.play();
delay(2000); // Wait for sound and servo actions
startNewRound();
}
boolean checkGameOver() {
if (playerScore == 3 || computerScore == 3) {
gameOver = true;
allowNewShot = false;
ballActive = false;
// Play the last save/goal sound and trigger servo movement
if (playerScore == 3) {
saveSound.play();
serialPort.write('S'); // Move servo for save animation
new Thread(() -> {
delay(2000); // Wait for the save sound
winnerMessage = "Goalie Wins!";
displayWinnerAndReset();
}).start();
} else if (computerScore == 3) {
goalSound.play();
serialPort.write('G'); // Move servo for goal animation
new Thread(() -> {
delay(2000); // Wait for the goal sound
winnerMessage = "Shooter Wins!";
displayWinnerAndReset();
}).start();
}
return true; // Stop further actions
}
return false; // Continue gameplay
}
void displayWinnerAndReset() {
delay(2000); // Display the winner message for 2 seconds
resetGame(); // Reset the game state
}
void resetGame() {
gameOver = false;
playerScore = 0;
computerScore = 0;
winnerMessage = "";
startNewRound(); // Start a new game
}
// //the helper function below receives the values from Arduino
// //in the "arduino_values" array from a connected Arduino
// //running the "serial_AtoP_arduino" sketch
// //(You won't need to change this code.)
void getSerialData() {
while (serialPort.available() > 0) {
String in = serialPort.readStringUntil( 10 ); // 10 = '\n' Linefeed in ASCII
if (in != null) {
print("From Arduino:[[[ " + in);
String[] serialInArray = split(trim(in), ",");
if (serialInArray.length == NUM_OF_VALUES_FROM_ARDUINO) {
for (int i=0; i<serialInArray.length; i++) {
arduino_values[i] = int(serialInArray[i]);
}
}
}
}
}
*ChatGPT was used for making the game rules and mechanism.
Goal gate:
https://www.tinkercad.com/things/4NqGR50DtJR-copy-of-soccer-goal
Adapted from: https://www.tinkercad.com/things/9fbJJzyA7AQ-soccer-goal
Stadium:
https://www.tinkercad.com/things/7pMLnkTWmyW-copy-of-stadium
Adapted from: https://www.tinkercad.com/things/lbpd0cRkzWP-stadium
Laser cut box:
https://cuttle.xyz/@cl7476/Closed-Box-with-Finger-Joints-Z1Ui92JXPS94
Adapted from: https://cuttle.xyz/@cuttle/Closed-Box-with-Finger-Joints-blQZfaiFfZvv
Special thanks to Flora, other faculty members, IMA fellows, LAs, my excellent classmates, Perla who kept me company, and ladies who helped cleaning the studio for all the suggestions and supports.