Fast & Ferocious
13 May 2024
Fast & Ferocious – Visual Demonstration
Processing Code:
import processing.sound.*;
import processing.serial.*;
Serial myPort;
int gameSpeed = 1;
float steeringValue = 0;
float rotationValue = 0;
float carsX[] = new float[2];
float carsY[] = new float[2];
float rewardX;
float rewardY;
float rewardSize = 65;
boolean rewardSpawned;
float rewardTaken = millis();
PImage bmw;
PImage obsCar;
PImage reward;
PImage loadingScreen;
PImage gameOverScreen;
int score = 0;
SoundFile sound;
SoundFile carSound;
SoundFile gameOverSong;
float carSoundTimer;
boolean started = false;
boolean gameOver = false;
void setup() {
size(800, 600);
frameRate(144);
sound = new SoundFile(this, "explode.mp3");
carSound = new SoundFile(this, "carSound.mp3");
gameOverSong = new SoundFile(this, "gameOverSong.mp3");
bmw = loadImage("BMW_M4.png");
obsCar = loadImage("obstacle_car.png");
reward = loadImage("reward_small.png");
loadingScreen = loadImage("loading.png");
gameOverScreen = loadImage("gameOver.png");
myPort = new Serial(this, "dev/cu.usbmodem1101", 9600);
for(int i = 0; i < carsX.length; i++) {
float newX = random(250, width / 2 + 150 - 40);
carsX[i] = newX;
carsY[i] = random(-80 * 2, -80);
}
}
void playCarSound() {
if(millis() - carSoundTimer >= 3000) {
carSound.play();
carSoundTimer = millis(); //repeated car noise
}
}
void reset() {
for(int i = 0; i < carsX.length; i++) {
float newX = random(250, width / 2 + 150 - 40);
carsX[i] = newX;
carsY[i] = random(-80 * 2, -80);
}
rewardTaken = millis();
rewardSpawned = false;
score = 0;
gameSpeed = 1;
gameOver = false;
}
void keyPressed() {
if(keyCode == ' ') {
if(!started) {
started = true;
reset();
return;
}
if(gameOver) {
reset();
}
}
}
//restarting and starting the game
void draw() {
if(gameOver) {
delay(5000);
gameOverSong.play(); //play see you again (F&F theme song)
push();
translate(0, 0);
image(gameOverScreen, 0, 0); //load game over screen
scale(2.0f);
text(str(score), 10.0f, 10.0f, width, 20);
pop();
return;
}
if(!started) {
push();
translate(0, 0);
image(loadingScreen, 0, 0);
pop();
//starting on the loading screen and if the game starts it would go to the road
return;
}
background(50, 205, 50); // background
if(!rewardSpawned && millis() - rewardTaken >= 15000) {
rewardX = random(250.0f, width / 2 + 150.0f - rewardSize);
rewardY = -rewardSize;
rewardSpawned = true;
}
push();
drawRoad();
pop();
push();
drawCar();
pop();
if(rewardSpawned) {
push();
drawReward();
pop();
}
push();
fill(200);
scale(2.0f);
text(str(score), 10.0f, 10.0f, width, 20);
pop();
score += 1; //if the users are able to touch the toad/reward, then it would get a point which would translate to slowing down the speed by 1
}
void drawReward() {
push();
fill(255, 0, 0);
translate(rewardX, rewardY);
rect(0, 0, 65, 65);
scale(0.2);
image(reward, 0, 0);
//translating the box underneath the toad png
pop();
rewardY += 1.0f;
if(rewardY > height + 65) {
rewardTaken = millis();
rewardSpawned = false;
}
}
void drawRoad() {
int roadWidth = 300; // the road
int roadCenter = width / 2;
int laneMarkerWidth = 5; // lane markers
int laneMarkerHeight = 40;
float laneMarkerSpacing = 60;
fill(100);
rect(roadCenter - roadWidth / 2, 0, roadWidth, height);
fill(255);
for (float y = -laneMarkerSpacing; y < height; y += (laneMarkerHeight + laneMarkerSpacing)) {
rect(roadCenter - laneMarkerWidth / 2, y + (millis() / 10 % (laneMarkerHeight + laneMarkerSpacing)), laneMarkerWidth, laneMarkerHeight);
//Lane markers moving/floating to create moving illusion
}
}
void drawCar() {
playCarSound();
int centerX = width / 2;
int carWidth = 40; // car width
int carHeight = 100; // Height of the car
int roadWidth = 300;
float mappedSteeringValue = map(steeringValue, 0, 1024, -roadWidth / 2 + carWidth / 2, roadWidth / 2 - carWidth / 2);
//car controlled by the steeringvalue or the potentiometer and limiting how far the car can go
float carX = centerX - carWidth / 2 - mappedSteeringValue;
float carY = height - carHeight - 50;
fill(255, 0, 0);
push();
translate(carX, carY);
scale(0.06);
translate(-400.0f, -80.0f);
image(bmw, 0, 0);
pop();
float carXL = carX;
float carXR = carX + carWidth;
float carYT = carY;
float carYB = carY + carHeight;
if(!(carXR < rewardX || carXL > rewardX + rewardSize || carYT > rewardY + rewardSize || carYB < rewardY)) {
rewardTaken = millis();
rewardSpawned = false;
rewardY = -rewardSize;
gameSpeed -= 1;
//reducing the speed after getting the reward
}
for(int i = 0; i < carsX.length; i++) {
float otherCarXL = carsX[i];
float otherCarXR = carsX[i] + carWidth;
float otherCarYT = carsY[i];
float otherCarYB = carsY[i] + carHeight;
if(!(carXR < otherCarXL || carXL > otherCarXR || carYT > otherCarYB || carYB < otherCarYT)) {
sound.play();
delay(300);
gameOver = true;
}
}
//two random cars falling down and if the cars touch the main car, then there would be an explosion sound
for(int i = 0; i < carsX.length; i++) {
push();
fill(0, 0, 0);
translate(carsX[i], carsY[i]);
translate(-4.0f, 4.0f);
scale(0.3);
image(obsCar, 0, 0);
pop();
carsY[i] += gameSpeed;
if(carsY[i] >= height + carHeight) {
float newX = random(250, width / 2 + 150 - 40);
carsX[i] = newX;
carsY[i] = random(-80 * 2, -80);
}
}
if(score % 1000 == 0)
gameSpeed += 1;
}
void serialEvent(Serial myPort) {
if(myPort.available() > 0) {
String input = myPort.readStringUntil('\n');
if(input == null)
return;
String[] input_separated = input.split(",");
if(!input_separated[0].equals("S"))
println(input_separated[0]);
if(input_separated[0].equals("S"))
steeringValue = Float.parseFloat(input_separated[1]);
else if(input_separated[0].equals("B")) {
if(!started) {
started = true;
return;
}
if(gameOver) {
reset();
}
}
}
}
Arduino Code:
int previousButtonInput = 0;
void setup() {
Serial.begin(9600);
pinMode(8, INPUT);
}
void loop() {
int potValue = analogRead(A0);
int resetValue = digitalRead(8);
if(previousButtonInput == 0 && resetValue == 1) {
Serial.print("B");
Serial.print(",");
Serial.println();
}
Serial.print("S");
Serial.print(",");
Serial.println(potValue);
previousButtonInput = resetValue;
delay(20);
}
CONCEPTION AND DESIGN:
Fast & Ferocious was designed as a driving game inspired by the reckless driving shown in the Fast & Furious franchise. The Fast & Furious franchise is a movie franchise based on a group of drivers completing missions and heists, during which they must avoid other cars and drive efficiently to reach their destinations or escape. Similarly, in Fast & Ferocious, I wanted users to experience reckless driving; hence, I designed a driving simulation game that mimics what it would be like to drive recklessly and dodge other cars. This allowed users to interact with the game through a steering wheel, mimicking a real driver and testing their driving skills alongside their reflexes and reaction time. The game starts with users pressing a button; the car is at its slowest speed, and the speed increases by 1 every 10 seconds. As time progresses and the car gets faster, it becomes harder to dodge the other cars. I was inspired by my friend Cameron during user testing to add some sort of reward system, enhancing the overall game. Initially, during user testing, I only had a car, other car obstacles, and a steering wheel. This was when users, including Cameron and Angela, gave me input on ways to make it more fun. They suggested it start slower, then gradually increase, include a scoring system so multiple users can play and compete with each other, and also add other game elements so it wouldn’t just be about dodging cars. This prompted me to think about other car games like Mario Kart, where cars can hit certain items and gain positive effects. This inspired me to make my game more complex and interactive; now, instead of just dodging everything, players must dodge certain things and also try to touch certain items to get a power-up. This was my inspiration for using toads from the Mario universe, which would allow the driver in Fast & Ferocious to slow down the speed by 1, helping them stay on the road longer and obtain a higher score. These inputs allowed me to create the game I have today. There wasn’t much input regarding the physical aspects; I didn’t have a steering wheel during the user testing, but I explained it to everyone and received minimal feedback. However, I did struggle with deciding whether to print a potentiometer adapter for my steering wheel, but my professor, Andy Garcia, suggested I could always just glue it together, which was the only feedback regarding the physical aspects of the project. After all these inputs from my peers and professors, I was able to create Fast & Ferocious, a game where the user drives a virtual car using a steering wheel to dodge incoming cars and get power-ups/rewards to stay on the road the longest, much like the characters from Fast & Furious.
FABRICATION AND PRODUCTION:
The two most important steps in the Fast & Ferocious production process were the physical steering wheel and the game code. For the steering wheel, I opted for a less traditional, F-1-style wheel. It took a while, but I ultimately found one on Thingiverse. However, the original designs had a bunch of holes with extra gear, such as paddle shifters and other buttons, which were unnecessary for my simpler game. So, I went on TinkerCad to tweak the wheel, which was quite challenging since I had no prior experience. Nevertheless, I managed to remove the extra pieces and fill in the holes. The hardest physical part was finding a way to connect the steering wheel to the potentiometer. I created a ring with crevasses to attach behind the steering wheel plate, but the potentiometer did not lock in place. I then realized there was a slit at the end of the potentiometer, so I used a metal wire to latch onto it and hot glued the metal wire onto the plate, securing the potentiometer to the steering wheel. The potentiometer was the most optimal sensor to use because it could spin, just like a steering wheel. All I had to do was code the potentiometer serial values and translate them to the car’s x-axis, ensuring that decreasing values would turn the car left and increasing values would move it right. I also wanted a way to start the game using an external device, rather than pressing something on my keyboard. I opted for a large button in the IMA lab, which was fairly simple to wire. I just needed to connect the positive and negative sides, alongside a 10k resistor, and wire it to the correct Arduino pin. The button acted as both a start and a reset button. When the game loads, the loading screen instructs users to press the button to start. After a player dies in a car crash, pressing the button again restarts the game. In terms of constructing the steering wheel, I ultimately decided to only print the grip and laser-cut the steering wheel plate for resource efficiency, securing the grips onto the plate with hot glue. I also wanted the steering wheel connected to a base that could be placed on a table for users to steer. I ended up laser-cutting a pentagon because I thought it was less basic than a box. Little did I know, I should have made it a box so that I could have placed my laptop or screen on top of the platform, making it more ergonomic for the user. This oversight in the design planning meant the user must now stand up and tilt the laptop screen high to see the game. However, it still worked out in the end. For future purposes, I will need to consider the ease of use and design of the product to ensure it is as convenient and ergonomic as possible for users.
The coding was the most difficult aspect of the project, especially since I didn’t have much coding experience. I started this project wanting the car to move and follow a race car track, however, to manipulate curvy roads and turns whilst also moving the car would prove to be difficult. I opted for an easier solution where the user controls the car by swerving left or right and creating an illusion as if they are moving on the road. Thus, I created a straight road on a blank canvas where the white guidelines or lane markers on the road would move down, creating an illusion the car was moving forward. This was quite easy as I simply just coded the lane markers to float down, however, I did need help with the creation of the float formula and that is when I asked Chatgpt-4 for help.
void drawRoad() {
int roadWidth = 300; // the road
int roadCenter = width / 2;
int laneMarkerWidth = 5; // lane markers
int laneMarkerHeight = 40;
float laneMarkerSpacing = 60;
fill(100);
rect(roadCenter – roadWidth / 2, 0, roadWidth, height);
fill(255);
for (float y = -laneMarkerSpacing; y < height; y += (laneMarkerHeight + laneMarkerSpacing)) {
rect(roadCenter – laneMarkerWidth / 2, y + (millis() / 10 % (laneMarkerHeight + laneMarkerSpacing)), laneMarkerWidth, laneMarkerHeight);
//Lane markers moving/floating to create a moving illusion
}
}
As for the car creation, it was pretty easy creating the car since I used a red rectangle to represent the car. However, one struggle I did have with the car was hiding the red square underneath the image of the car. I wanted to use a BMW m4 png as the car instead of the red box, but I had to make sure the box was slightly smaller than the png so that later on when the other cars crashed into the main car, the code would detect that and end the game. It took a couple of trial and error but I ended up translating the rectangle underneath the car. At first, I didn’t add a restrictor on how far the car could move so for the first try, the car went outside of the read which I did not want, so I had to add a restriction on where the car could go, making it only stay on the road.
void drawCar() {
playCarSound();
int centerX = width / 2;
int carWidth = 40; // car width
int carHeight = 100; // Height of the car
int roadWidth = 300;
float mappedSteeringValue = map(steeringValue, 0, 1024, -roadWidth / 2 + carWidth / 2, roadWidth / 2 – carWidth / 2);
//car controlled by the steeringvalue or the potentiometer and limiting how far the car can go
float carX = centerX – carWidth / 2 – mappedSteeringValue;
float carY = height – carHeight – 50;
fill(255, 0, 0);
push();
translate(carX, carY);
scale(0.06);
translate(-400.0f, -80.0f);
image(bmw, 0, 0);
pop();
As for the main objective of the game, I wanted the cars to come down so it would look like the main car was driving towards them. At first, we started with just singular cars randomly floating down, however, I thought it was too easy if it was just one, so we made it to two so it would be somewhat difficult but also doable. However, when I tried to make two cars fall down, sometimes the main car would hit the second car and it wouldn’t register fully, it would make the crash noise, but not stop the game. However, after my friend Satya Tirtha looked over it, he said that it was because my if statement part of the code was wrong and helped me adjust that so if the main car were to touch any corners or edges of the obstacle cars, then it would play the crash noise and stop the game. For aesthetic purposes, I did the same for what I did with the main car and just put a PNG over rectangles.
float carXL = carX;
float carXR = carX + carWidth;
float carYT = carY;
float carYB = carY + carHeight;
if(!(carXR < rewardX || carXL > rewardX + rewardSize || carYT > rewardY + rewardSize || carYB < rewardY)) {
rewardTaken = millis();
rewardSpawned = false;
rewardY = -rewardSize;
gameSpeed -= 1;
}
for(int i = 0; i < carsX.length; i++) {
float otherCarXL = carsX[i];
float otherCarXR = carsX[i] + carWidth;
float otherCarYT = carsY[i];
float otherCarYB = carsY[i] + carHeight;
if(!(carXR < otherCarXL || carXL > otherCarXR || carYT > otherCarYB || carYB < otherCarYT)) {
sound.play();
delay(300);
gameOver = true;
}
}
//two random cars falling down and if the cars touch the main car, then there would be an explosion sound
For the last important element, I wanted to gamify my design by adding another element. For this, I took inspiration from Mario Kart, I wanted the user to not just dodge everything, but also receive some sort of reward like you would in Mario Kart, hence I decided to add a reward toad. Now if users are able to hit this, then it would make game speed slow down by 1, allowing more time on the road. For the code, I simply just replicated the obstacle cars, but changed the if statement, if the main car hit the toad, the game would just slow the current speed by one level.
if(!(carXR < rewardX || carXL > rewardX + rewardSize || carYT > rewardY + rewardSize || carYB < rewardY)) {
rewardTaken = millis();
rewardSpawned = false;
rewardY = -rewardSize;
gameSpeed -= 1;
//reducing the speed after getting the reward
Last but not least, I wanted some interactive and funny elements to the game such as asking Chatgpt to generate a loading screen with a BMW and also an ending screen which I used. I also wanted to take heartwarming elements from the movies by adding a song that was used in Fast and Furious: See You Again by Charlie Puth because one of the main actors in the franchise died in a car crash and the song was dedicated to him. I also wanted to make the crash a little more interesting by making a crash noise from YouTube. Lastly, it is a car game and it wouldn’t make sense if the car didn’t make any car noises so I wanted the car to make revving and driving noises as the users were playing.
void playCarSound() {
if(millis() – carSoundTimer >= 3000) {
carSound.play();
carSoundTimer = millis(); //repeated car noise
}
void draw() {
if(gameOver) {
delay(5000);
gameOverSong.play(); //play see you again (F&F theme song)
CONCLUSIONS:
The goal of this project was to provide a fun driving game where users can test basic reflex and reaction time skills used in real-life driving. Fast & Ferocious allows users to fully focus on reacting to unexpected situations, such as driving in a reckless environment like the Fast & Furious franchise. To test one’s reflexes and reaction times, there would be multiple cars opposing the user, and the user must use the steering wheel to avoid the cars, but also not avoid power-ups such as the toad mushroom, because that would actually help the user slow the game down, allowing them to stay on the road longer and achieve a higher score. I expected the audience to have a fun and addictive experience, much like similar games such as Mario Kart and Subway Surfers. As expected, during user testing and the final presentation, many peers enjoyed playing the game, and I felt what made the game more immersive and fun was the physical steering wheel. A lot of games on computers and smartphones would probably have a digital control or simply require tilting your phone to control the car, but what made mine more interactive and enjoyable was that the audience was able to maneuver the vehicle with an actual steering wheel. Fast & Ferocious aligned with my definition of interaction because I believe that interaction involves the communication of two or more parties, and in this situation, when I presented this during user testing, aside from the interaction between the game and the user, the users also competed with each other. The system of scores and the enjoyability of the game made everyone who played it want to play more and naturally compete against each other. However, I believe that if I were given more time, I could definitely enhance the experience to make this game even more fun than it already is. Some improvements I’d like to incorporate include making it more physically realistic. I would want to create a chair that mimics an actual racecar, attached to the steering wheel with an actual LCD screen rather than using my laptop. Additionally, I would like to add more features such as a gas pedal, brake pedal, different gears, and an actual racetrack. I would design it to replicate a racetrack with turns, so it’s not just going straight and forward, but also having other cars coming at the user. Furthermore, I would like it to include other cars moving in the same direction as the main car, requiring the user to brake if both cars are heading in the same direction and to react accordingly to maneuver around the other cars. Some of the largest setbacks I encountered were in constructing the code itself, but I’m not too hard on myself about that because this was a complex game and I am not a coder. However, I had help from a friend who is a CS major, and together we were able to create a simplistic yet fun game. I also realized that I need to plan more thoroughly to ensure that each move I make does not negatively affect the next step. This was evident when I laser-cut a pentagon as my platform for aesthetic purposes and neglected to consider where I would place my screen. I could have designed a better platform that would support the laptop screen more effectively and provide a better ergonomic experience for the users. As for accomplishments, I was very proud of the experience I was able to provide to my audience. All my users and peers loved the game, even though it was simplistic, and mentioned that there were addictive features in the game that made them want to continuously play it and beat their previous high scores.