Midterm Project

Concept and Inspiration:

I wanted to share the inspiration behind my game. It all started when I delved into the captivating world of folklore, specifically the stories of Sindbad the Sailor and Behula from Bangladeshi culture. Sindbad’s adventurous spirit, sailing through the vast oceans, I mean, who doesn’t love a good tale of adventure on the high seas?

Then there’s Behula, a fierce and determined woman who braves the ocean’s challenges to bring back her husband. Her journey is filled with trials and tribulations, showcasing strength, love, and resilience. These stories are so rich and deep, and I wanted to weave that essence into my game.

As I started to brainstorm, I thought, “Why not create a surfer game set against this backdrop?” The ocean is such a dynamic environment, and surfing adds an exciting twist. I wanted to capture the thrill of riding the waves while subtly nodding to these legendary tales.

Of course, I realized that not everyone might connect with the heavier themes of folklore, especially younger audiences. So, I decided to give the game a fun, cartoonish vibe. This way, it can appeal to all ages while still honoring those timeless stories. It’s all about adventure, overcoming challenges, and having a great time on the waves!

Code Explanation:

In my game, I’ve focused on creating an engaging surfing experience, and the wave mechanics play a crucial role in bringing that to life. There are some key elects that I would like to mention, especially how I generate those smooth, dynamic waves that define the gameplay.

1. Wave Creation

One of the standout features of my game is how I create the waves. I use a combination of beginShape() and endShape()to draw the wave’s outline:

beginShape();
let xOffset = waveXOffset;
for (let x = 0; x <= width; x += 10) {
    let y = map(noise(xOffset, yOffset), 0, 1, height / 2 - waveAmplitude, height / 2 + waveAmplitude);
    vertex(x, y);
    xOffset += waveFrequency;
}
endShape(CLOSE);
  • Creating Vertex Points: Inside this loop, I utilize the vertex() function to establish a series of points that define the wave’s shape. By iterating across the entire width of the canvas, I can create a flowing wave profile that enhances the surfing experience.
  • Using Perlin Noise: The magic happens with the noise() function. I chose Perlin noise because it generates smooth, natural variations, making the waves look more realistic. Unlike random values that can create jarring changes, Perlin noise ensures that the wave transitions are fluid, which adds to the game’s aesthetic appeal.
  • Mapping Values: I then use the map() function to rescale the noise output, allowing me to set the wave height within specific bounds. By centering the waves around the middle of the canvas (height / 2), I ensure they oscillate up and down, making the gameplay more visually engaging.2.

    2. Player Interaction with Waves

    The way the waves interact with the swimmer is equally important. I calculate the wave’s height and adjust the swimmer’s position accordingly:

    if (isJumping) {
        verticalVelocity += gravity;
        swimmerY += verticalVelocity;
        if (swimmerY >= waveY - swimmerHeight / 2) {
            swimmerY = waveY - swimmerHeight / 2;
            isJumping = false;
            verticalVelocity = 0;
        }
    } else {
        swimmerY = waveY - swimmerHeight / 2;
    }
    • Jump Mechanics: I implement a jumping mechanic where the swimmer’s vertical position changes based on gravity and jump forces. When the swimmer is in the air, I adjust their position based on the wave height, allowing them to ride the wave realistically.
    • Wave Height Adjustment: By ensuring the swimmer’s position is linked to the wave’s current height, I can create a seamless experience where the player feels like they are truly surfing on the waves.3. Window Resizing and Obstacles: 
      • The windowResized() function allows the canvas to resize dynamically if the window size changes, maintaining the game’s responsiveness.
        • Instantiation of Obstacles: Every time spawnObstacle() is called, a new instance of the Obstacle class is created and added to the obstacles array. This ensures that there are multiple obstacles on the screen for players to avoid, making the gameplay more challenging.
        • Continuous Challenge: By calling this function regularly within the main game loop (in the playGame function), I ensure that obstacles keep appearing as the player progresses. This continuous spawning simulates a moving ocean environment, where new challenges arise as players navigate the waves.
        • P5.js Sketch:

      Features  and Game Mechanics:

      • The game kicks off with an engaging start screen featuring the title “Surfer Game”, inviting players to click to begin their surfing.
      • Players control a lively surfer character using intuitive keyboard commands, specifically the spacebar to jump over incoming obstacles like sharks, enhancing the gameplay’s interactivity.
      • As players navigate through the waves, they encounter a dynamic wave system that adjusts in amplitude and frequency, creating a realistic surfing experience. This effect is achieved through Perlin noise, giving the waves a smooth, natural movement.
      • Collectible coins are scattered throughout the waves, adding an exciting layer of challenge. When the player collides with a coin, it disappears, contributing to the score, which is displayed on the screen.
      • The game includes a health mechanic where if the surfer collides with an obstacle, such as a shark, the game transitions to a “Game Over” state, prompting players to either restart or return to the main menu.
      • Upon reaching specific milestones, such as collecting a set number of coins, players are rewarded with a “Level Up” screen that highlights their achievements and encourages them to continue.
      • Players can easily restart the game after it ends by clicking anywhere on the screen, offering a seamless transition between attempts.

      Additional AudioVisual:

      • Sound effects enhance the immersive experience, with cheerful tunes playing when coins are collected and exciting sound bites when the surfer jumps over obstacles.
      • Background music plays continuously throughout the game, creating an engaging atmosphere. Players can enjoy a unique soundtrack that fits the theme of the game, enriching their adventure.
      • The graphics have a cartoonish style, making it appealing for all age groups while still paying homage to the folklore inspirations behind the game.

Reflection and Challenges

When I first set out to create a game, my vision was to develop something based on neuroscience, exploring complex concepts in an engaging way. However, as I delved deeper into that idea, I realized it was more challenging than I anticipated. I then pivoted to creating a 2048 game, but that also didn’t quite hit the mark for me. I felt it lacked the excitement needed to captivate players of all ages.

This experience taught me a valuable lesson: sometimes, taking a step back can provide clarity. Rather than getting bogged down in intricate designs, I opted for a simpler concept that would appeal to a wider audience. Thus, I decided to create an ocean-themed surfing game, inspired by folklore like Sindbad the Sailor and the tale of Behula from Bangladeshi culture.

I see a lot of potential for upgrading this game. Adding a storyline could further engage young players and introduce them to fascinating narratives from folklore. Additionally, I plan to enhance the wave mechanics by incorporating realistic gravity effects, making the surfing experience more immersive. These improvements could really elevate the gameplay and provide an enjoyable adventure for everyone.

Midterm Project: “Cubix”

Sketch
http://165.232.166.95:5500
Concept
I decided to implement a clicker/incremental game with a unique twist, combining elements from popular titles like To The Core, Cookie Clicker, and Adventure Capitalist. The game revolves around hunting flying shapes using a cursor with an attached box (scope/aim). Players damage enemy shapes when they’re inside this box, earning resources to upgrade skills in a talent tree. The game also features levels for long-term progression and bonuses.

Game Mechanics and Achievements
The core gameplay involves killing enemies that appear on the screen using an aiming device attached to the cursor. Each enemy defeated rewards the player with experience and money. Leveling up releases resources, while money allows for upgrades in the talent tree. One of the most challenging and rewarding aspects of development was balancing the game’s economy. After extensive experimentation and research, I developed formulas that strike a balance between challenge and progression, ensuring the game is neither too easy nor too difficult. Here is the mathematical framework for the economics of the “Cubix” game.

Core Mechanics

Player Progression
The player’s power increases through two main avenues:
1. Killing mobs to gain experience and resources
2. Upgrading skills to become more effective at killing

Experience and Leveling
XP_{gained} = Base_{XP}  *  (1 + Level_{mob} * 0.1)
Where Base_{XP} is a constant value for each mob type, and Level_{mob} is the level of the mob killed.

The experience required to level up follows an exponential curve:
XP_{required} = 100 * 1.1^{Level_{player}}

This ensures that leveling becomes progressively more challenging.

Resource Generation

Resources (e.g., gold) dropped by mobs calculated similarly:
Gold_{dropped} = Base_{gold} * (1 + Level_{mob} * 0.15)

Skill Upgrades

Each skill has multiple levels, with increasing costs and effects. The cost to upgrade a skill follows this formula:
Cost_{upgrade} = Base_{cost} * 1.5^{Level_{skill}}

The effect of the skill (e.g., damage increase) is:
Effect_{skill} = Base_{effect} * (1 + Level_{skill} * 0.2)

Mob Scaling

To keep the game challenging, mob health and damage can scale with the player’s level:
Health_{mob} = Base_{health} * 1.2^{Level_{player}}
Damage_{mob} = Base_{damage} * 1.15^{Level_{player}}

A significant technical achievement is the implementation of local progress saving using localStorage. This feature required considerable effort but greatly enhances the player experience by allowing seamless continuation of gameplay across sessions.

class Storage {
    constructor() {
        this.spawnRate = [1000, 2000];
        this.spawnChances = [0.5, 0.3, 0.1];
        this.maxEnemies = 100;

        this.balance = 0;

        this.level = 0;
        this.xp = 0;

        this.damage = 20;
        this.hp = 20;
        this.regen = 1;
        this.interval = 1000;
        this.crit = 10;
        this.critChance = 0.12;
        this.armor = 0;

        this.skills = defaultSkills;

        this.scopeSize = 100;
    }

    getSkillEffect(id) {
        let skill = this.skills.find(skill => skill.id === id);
        if (skill.level === 0) {
            return 0;
        }
        return int(skill.baseEffect * (1 + skill.level * 0.2))
    }

    reset() {
        this.spawnRate = [1000, 2000];
        this.spawnChances = [0.5, 0.3, 0.1];
        this.maxEnemies = 100;

        this.balance = 0;

        this.level = 0;
        this.xp = 0;

        this.damage = 20;
        this.hp = 20;
        this.regen = 1;
        this.interval = 1000;
        this.crit = 10;
        this.critChance = 0.12;
        this.armor = 0;

        this.skills = defaultSkills;

        this.scopeSize = 100;

        this.save();
    }

    load() {
        let save = localStorage.getItem("save");
        if (!save) {
            this.save();
        } else {
            let data = JSON.parse(save);
            this.spawnRate = data.spawnRate;
            this.spawnChances = data.spawnChances;
            this.maxEnemies = data.maxEnemies;
            this.balance = data.balance;
            this.damage = data.damage;
            this.hp = data.hp;
            this.level = data.level;
            this.xp = data.xp;
            this.regen = data.regen;
            this.interval = data.interval;
            this.crit = data.crit;
            this.critChance = data.critChance;
            this.armor = data.armor;
            this.skills = data.skills;
            this.scopeSize = data.scopeSize
        }
    }

    save() {
        localStorage.setItem("save", JSON.stringify(this));
    }
}

Additionally, the game’s performance has been optimized to handle multiple moving elements without significant FPS drops, with only minor issues arising when enemy rotation was introduced (disabled for the FPS sake).

Design

Areas for Improvement and Challenges
While the project has made significant progress, there are several areas for future improvement. The settings button on the home screen is currently non-functional and needs to be implemented. Expanding the variety of enemies and introducing the ability to prestige levels are also planned enhancements that will add depth to the gameplay.

Also I encountered a significant scaling challenge when testing the game on devices with different screen sizes and resolutions. The game elements, including flying shapes, cursor box, and UI components, were not displaying consistently across various machines. To resolve this, I implemented a scaling solution using scale factor based on canvas size and baseWidth with baseHeight, that ensures optimal display across different screen sizes.

Any way one of the main challenges encountered was optimizing performance, particularly when adding rotation to the enemies. This issue highlighted the need for careful consideration of graphical elements and their impact on game performance. Moving forward, further optimization and potentially exploring alternative rendering techniques could help address these performance concerns and allow for more complex visual elements in the game.

Midterm: 5R Photo Lab!

Final product 

Make sure to open on the P5 web editor for sound and press f for full screen please!

Concept

My main concept for my midterm was based on the photo lab that I work at in New York. I wanted to create an interactive lab experience in which the user could go through some of the steps of developing/processing film while also hopefully learning a bit more about film in the process. My main goal when designing the program was to make something that was useful to the lab and could be used by my customers.  Therefore, I ended up creating a ‘behind the curtain’ experience for customers to see what developing film is like and get small experiences in the different activities.

How it works

My program functions solely on mouse interaction and features 2 mini-games and 2.5 ‘resource tabs. The first mini-game is what I’ve considered as stage 1 of the development process, making the chemistry. Users are able to reveal the recipe card and then have to memorize the ratios for each chemical in order to make the chemistry needed for the development process. They have only 2 chances to refresh their memory but if they over pour it at all they immediately lose (because in the real world we’d have to throw the chemistry out). The second mini game is the fourth station where they are editing scans. Basically users have 15 seconds to edit 10 randomly generated pieces of ‘dust’ from the scans to prepare them to be sent off to the customer.

The two middle stations, splicing and feeding the machine do not have any major interactive piece. I made this decision for multiple reasons. The first, of course being time management and having figure out what I’d actually have time to compelte, and the second being that these stages don’t really have much that could be digitally represented like with the other two games. Step 2 typically just requires cutting a piece of film and labelling it while step 3 is literally just putting it in the machine. Therefore, by giving them this informative, resource functionality I believe it further instills how this program can be a resource for film photographers.

I also have a small easter egg which appears when you click on the film negatives hanging on the wall that just feature some photos, some of which were shot on film so that were not. While this doesn’t really add anything to the program I thought it was on theme with the design and added another layer of creativity to the project.

What I’m proud of

I am particularly proud of the mix chemicals station because it took many many hours of trial and error to reach the stage its at currently. Originally I wanted to incorporate the g, r, b, and y keys to represent filling up the vials but decided that jumping between input functions might be too much for the user. Also, while working on this station I completely deleted the relevant functions three times which I am honestly quite proud of because it took quite a bit of guts to just start over like that, but I am glad I did.

if (grow.blue && blueHeight < maxHeight) blueHeight += growthRate;
  if (grow.red && redHeight < maxHeight) redHeight += growthRate;
  if (grow.green && greenHeight < maxHeight) greenHeight += growthRate;
  if (grow.yellow && yellowHeight < maxHeight) yellowHeight += growthRate;

  // drawing rectangles
  fill('#4ba0f7');
  rect(windowWidth * 0.2 + 1.5, baseY - blueHeight, 57, blueHeight);
  fill('#f12721');
  rect(windowWidth * 0.31 - 5.5, baseY - redHeight, 57, redHeight);
  fill('#52c204');
  rect(windowWidth * 0.41 + 2, baseY - greenHeight, 57, greenHeight);
  fill('#ffeb67');
  rect(windowWidth * 0.52 - 3.75, baseY - yellowHeight, 57, yellowHeight);

The algorithm I ended up with is actually pretty simple it was just hard for me to conceptualize because I was originally unable to breakdown the mini-game into each separate requirement for what I wanted to happen. Above you can see the main part of the function that illustrates the rectangle growth.

I am also proud of the fact that everything is appropriately ratioed in full screen (or at least on my 13 inch MacBook Air screen it is) because it was a topic of growing anxiety for me as I continued to ratio everything in a split screen when I was actually implementing my ideas. Furthermore, while this is technically an area of improvement, I think that that way I problem solved was particularly effective. Due to poor design and planning I quickly became overwhelmed with the various errors I was dealing with and didn’t really know how to approach them but eventually just went back to the basic and wrote down whatever needed to be changed and I think that really helped reset  my mentality and also the direction of my program.

Please excuse my awful handwriting, it was nearing 3 AM when I wrote this…

Areas of improvement 

Although I am very happy with how my program turned out, I also have a lot of areas of improvement that I’d like to see happen in the future. During the implementation process my code was extremely disorganized and messy which cause for a lot of unnecessary confusion and frustration while I was debugging errors. Also, while the worst of it has since been improved, I did quite a bit of hardcoding that I think with the right approach could be simplified in some creative ways that I’d like to test out. Lastly, I would like to think of some more ways I can further develop the user experience. In conceptualizing this project I considered having some drag and drop features, using the the laptops camera to take a picture, and other unique components that I would like to further explore eventually.

The Flame Boy: Becoming a Robinson (Midterm Project)

Concept

link to full sketch: https://editor.p5js.org/takuthulani/full/gdL68sjHc

This project is an adaptation of a fictional world I’ve created, designed to offer an immersive storytelling experience. Instead of passively observing, users actively engage with the narrative, making it more like an interactive movie or book. The story centers around the protagonist and key events from his life. To preserve the element of surprise and keep the experience enjoyable, I encourage you to explore the story firsthand!The narrative also takes place in a sci-fi setting, featuring an alien planet inhabited by human-like beings with enhanced abilities. This concept influenced the design, with fire playing a central role and red as the dominant color theme.

How the Game Works:

The game starts by greeting the user with a cover page that plays The Flame Boy’s theme song (it’s catchy, so you might get lost in it—don’t forget to progress, though you’re welcome to just listen!). The theme song was created using Suno AI (credit to them). On the cover image, there are instructions to click on the screen, which takes the user to a menu page (with its own theme song as well from Pixabay). The menu presents two options: “His Story” and “His Home,” and the user is expected to click on one.

If the user clicks on “His Home,” the page transitions to an image showing his home—simple and straightforward. This image was made using DALL-E. To exit this view and return to the menu, the user can press the escape button. If the user clicks on “His Story,” a video begins to play, so sit back and enjoy! After the video, the user must make a choice: either “Trust” or “Don’t Trust” the character they interact with. Clicking “Don’t Trust” progresses the game to the conclusion and eventually to a thank you message. The user can return to the cover screen by pressing the mouse key.

If the user chooses “Trust,” the game transitions to a section where they need to earn 100 points to reach the story’s conclusion. The experience continues until the user decides to stop the sketch.

Parts I’m Proud of:

There are several aspects of this project that I’m truly proud of. First and foremost is the video element, which made everything possible. Initially, I planned to use images and recordings to narrate the story, but p5.js kept crashing, so integrating video became the best solution. Even though it’s technically “out of scope” based on what we covered in class, it was a practical way to bring the project to life. I turned to resources like Stack Overflow, the p5.js help section, and ChatGPT to guide me through the video implementation. Below is an example of the code I used to incorporate video into the project:

// Video elements
let storyVideo; // Video for the story sequence
let conclusionVideo; // Video for the conclusion sequence
let decisionImage; // Image for the decision screen where the player chooses their path
...
function playStory() {
  // Stop the story video if it's currently playing to reset the playback
  storyVideo.stop(); 
  
  // Reset the playback time of the story video to the beginning (0 seconds)
  storyVideo.time(0);  
  
  // Start playing the story video from the beginning
  storyVideo.play();  
}
...
function playConclusion() {
  // Stop the conclusion video if it is currently playing
  conclusionVideo.stop();
  
  // Reset the video playback time to the start (0 seconds)
  conclusionVideo.time(0);
  
  // Start playing the conclusion video from the beginning
  conclusionVideo.play();
}

Secondly, I’m particularly proud of the shooting game and its mechanics. I’ll include snippets of the code for the parts I’m most proud of below, and I’ll also provide a link to the full sketch so you can explore the many amazing functions I’ve implemented. Here’s a sample of the code for the game within the game:

function playShootingGame() {
  // Draw the background night sky for the shooting game
  drawNightSky();
  
  // Set the fill color to white for the score text
  fill(255);
  
  // Set the text size for the score display
  textSize(16);
  
  // Align text to the right and top of the canvas
  textAlign(RIGHT, TOP);
  
  // Display the current score at the top-right corner of the canvas
  text("Score: " + score, width - 10, 10);

  // Check if 2 seconds have passed since the last star was spawned
  if (millis() - starTimer > 2000) {
    // Spawn new stars for the shooting game
    spawnGameStars();
    
    // Update the star timer to the current time
    starTimer = millis();
  }

  // Loop through the stars array in reverse order to avoid issues with splicing
  for (let i = stars.length - 1; i >= 0; i--) {
    // Get the current star object from the stars array
    let star = stars[i];
    
    // Set the fill color to yellow for larger stars, white for smaller stars
    fill(star.size === 40 ? 'yellow' : 'white'); 
    
    // Draw the star as an ellipse at its specified x and y coordinates with its size
    ellipse(star.x, star.y, star.size);

    // Check if the star has been on screen for more than 2 seconds
    if (millis() - star.appearTime > 2000) {
      // Remove the star from the array if it has been displayed long enough
      stars.splice(i, 1);
    }
  }

  // Check if the score has reached 100 points
  if (score >= 100) {
    // Change the game state to 'conclusion'
    state = 'conclusion';
    
    // Play the conclusion video or sequence
    playConclusion();
  }
}

link to the sketch: <iframe src=”https://editor.p5js.org/takuthulani/full/gdL68sjHc”></iframe>

Problems I encountered and possible solutions:

The main challenge I faced was implementing video into the sketch after my original plan didn’t work out. I found solutions using the online resources mentioned earlier. Another problem was integrating the mini-game within the story. The best approach was to treat the game as a separate entity and then integrate it into the sketch, rather than seeing it as one large game (which added unnecessary stress while coding). Additionally, I encountered performance issues, as I used too many audio and visual files. Optimizing the game speed and performance became a key focus. Below is a screenshot showing some of the media I used:

files and resources used to generate the story for the game

This includes various assets like fonts, PowerPoint presentations, a mini script for the game, and a massive 500MB PSD file (trust me, you don’t want to know how many images are in that!). I also went through multiple iterations of the menu and cover page designs.

Since I’m not the best at drawing, I used Meta AI to generate the images, which I then manually edited in Photoshop. I recorded the vocal narrations using Audacity and assembled the video using CapCut before uploading it into my program. Some of the images, sounds, and music were sourced from Pixabay.com, which allows usage for projects like this.

Areas needing improvements:

The overall workflow of the game could use some enhancements. For example, the “His Home” area could feature more interactive functions to increase engagement and enjoyment. Additionally, the story’s flow would benefit from a clearer rationale for why the user needs to earn 100 points to advance to the conclusion. While some of these creative flaws may have been overlooked, they can be incorporated as features in future updates.

Overall, I thoroughly enjoyed working on this project and was fully invested, as I was creating something I loved, among other reasons. It provided a fun and technical way to learn, and I am excited for you all to see what I build as time progresses.

The following images are alternative designs that did not make the cut:

According to a few people, the guy on the right did not fit in well so this menu did not make it.
Although my overall theme was red and black, I did not think thiss would benefit the aesthetics of the overall story.
This was a test I did when I tried integrating mock-up text with my overall design.

 

 

 

 

MidTerm Project: Going Through It

Inspiration:

I’ve always had a fear of snakes. Overcoming it seemed impossible until I started working on my game. Inspired by how snakes consume food whole, I created “Going Through It”.  An obstacle course game where the obstacle course is designed in the shape of a snake. The player controls a small stick character trying to escape the snake as fast as possible. Adding my own unique twist to the game, the player cannot directly control the jumping ability of the stick figure, instead the stick is more akin to a ‘pogo stick’ where it bounces off with every obstacle it collides with and the player only controls the rotation of the stick figure using their keyboard.

Challenges Faced:

Developing “Going Through It” presented me with several challenges

  • Collision Detection: One of the primary difficulties was implementing an effective collision detection system that could handle the stick’s rotation and interactions with obstacles at various angles. Ensuring that the stick responds correctly to collisions, including bouncing off surfaces at appropriate angles, required careful calculation and testing. This is one of the primary features of the game and needed to be perfect although I am always sorting out minor issues with the collision detection mechanism.
  • Physics and Movement: Balancing the physics of gravity, friction, and rotational speed to create a challenging yet fun experience was another challenge. The stick couldn’t be too fast, the gravity couldn’t be too strong and the rotation had to be just right to be responsive yet precise. Fixing these problems involved a significant amount of play testing the game.
  • User Interface and Feedback: Designing an intuitive user interface that provides clear feedback to players was an essential feature of the game for me. This included displaying elapsed time, providing instructions, and ensuring that game states (such as starting or ending the game) were communicated effectively. In the end I decided to go with a very minimal layout that fits with the aesthetic of the game but I do believe that it is still intuitive and someone could understand how to play and win with minimal effort.

Final Project:
Conclusions and Reflections:
Reflecting back on this project, developing this game has been both a creative and highly technical journey.
Looking ahead, I hope to improve many aspects of this project as it is an idea that I haven’t seen before. The following are some ideas I have for future improvements to this game.
Level Design: Expanding “Going Through It” with more levels featuring diverse obstacle layouts and increasing difficulty. Moving obstacles are also a problem I hope to tackle in the future.
Multiplayer Mode: Due to the speed-run nature of the game, a mode where players can compete in real time would greatly add to the immersion and entertaining nature of the gameOverall, this project has laid a strong foundation for further development, I am genuinely excited about the game I have created and I hope to keep working on it in the future. Plus the amount of trigonometry implemented for collisions has made me a better mathematician which is always a welcome side effect.

 

MIDTERM PROJECT: SUPERMAN SAVES

INTRODUCTION

For my midterm project, I decided to build upon an earlier project concept, evolving it into a full-fledged interactive game called “Superman Saves”. This project incorporates the concepts and techniques I’ve learned in class so far.

CONCEPT

The concept of the game is simple: Superman has to rescue a person from danger by navigating obstacles such as clouds and birds. The player uses arrow keys to control Superman’s movements, helping him avoid the obstacles while trying to rescue the person in time. As the player progresses through different levels, the game increases in difficulty by speeding up the obstacles, making it more challenging to achieve the objective. 

RESOURCES

For the background image, i used DALL.E AI to generate it. I got the sounds from freesound.org

HIGHLIGHTS

One of the more challenging aspects of the project was implementing accurate collision detection between Superman and the moving obstacles (clouds and birds). The collision detection logic ensures that when Superman gets too close to an obstacle, he loses a life, and his position is reset. The difficulty lies in precisely calculating the distance between Superman and the obstacles, accounting for the different speeds and movements of the clouds and birds.

The code snippet below handles collision detection for both clouds and birds, resetting Superman’s position and decreasing his lives if a collision occurs:

function checkCollision() {
  // Check collisions with clouds
  if (dist(supermanX, supermanY, cloudX1, cloudY1) < 50 ||
      dist(supermanX, supermanY, cloudX2, cloudY2) < 50 ||
      dist(supermanX, supermanY, cloudX3, cloudY3) < 50) {
    supermanX = width / 2;
    supermanY = height - 100; // Reset Superman's position
    lives -= 1; // Lose a life
    return true;
  }

  // Check collisions with birds
  if (dist(supermanX, supermanY, birdX, birdY) < 50) {
    supermanX = width / 2;
    supermanY = height - 100; // Reset Superman's position
    lives -= 1; // Lose a life
    return true;
  }
  return false;
}

 

CHALLENGES AND IMPROVEMENTS

Creating the dynamic background and ensuring smooth movement was initially challenging. Managing multiple moving elements (clouds, birds, stars) required a balance between performance and visual appeal. Looking ahead, I plan to add more features such as power-ups for Superman, different types of obstacles, and possibly multiplayer options.

 

EMBEDDED CODE

 

 

LINK TO FULL SCREEN

 

 

Mid Term Project: Animal Sounds Trivia

CONCEPT

The game “Animal Sounds Trivia” is an interactive trivia game designed to educate players about animals. My goal is to help people identify animals by their sounds. The idea came from the realisation that, while walking in a jungle, the ability to recognise an animal by its sound could help determine whether the animal is dangerous or not.

The Game play.

The game begins with a screen containing instructions and that allow user to start the Game. The starting screen can be seen below:

From the main menu, when the player clicks a button to begin playing, the game starts. The player controls a character who walks through a forest. As the character moves forward, the distance to a destination gradually decreases (Also indicated in the progress bar).  At specific intervals (based on the distance), the player hears different animal sounds, triggering a trivia question. A pop-up window appears with multiple-choice options for identifying the animal that made the sound. The player selects an answer, and if correct, they earn 10 points for the animal. After answering, the player can continue their journey. The cycle of walking, hearing an animal sound, and answering trivia repeats until the player reaches the destination and completes the trivia challenge. Throughout the game, the background scrolls to simulate movement, and the character’s walking animation plays, giving the illusion of progress. At the end the Player is presented with their total score and can choose to restart the game.

Interesting Piece of Code

In my implementation, I added a function that type message on the screen. I find the code for the function definition interesting because of the experience it adds to the game. By typing words for instructions it adds a sense of activeness of the game.

// Function to display a typed message with a typing effect
   displayTypedMessage(x, y)
  {
    let typingSpeed = 50;  
    let lineHeight = 32;  
    let margin = 10;  
    let currentTime = millis();

    if (currentTime - lastCharTime > typingSpeed && currentCharIndex < this.instructionText.length) 
    {
      currentCharIndex++; 
      lastCharTime = currentTime;  
    }
    
// Create a substring of the instruction text to display
    let displayedText = this.instructionText.slice(0, currentCharIndex);

    let lines = [];
    let currentLine = "";
    
// Handle line breaks
    for (let i = 0; i < displayedText.length; i++) 
    {
      let nextChar = displayedText[i];
      let potentialLine = currentLine + nextChar;

// Check if the potential line exceeds the available width
      if (textWidth(potentialLine) > width*0.9 - margin * 2 - x) 
      {
        lines.push(currentLine);  
        currentLine = nextChar;   
      } else 
      {
        currentLine = potentialLine;
      }
    }
    lines.push(currentLine);  
    fill(0);
    textAlign(LEFT, TOP);
    for (let i = 0; i < lines.length; i++) 
    {
      text(lines[i], x, y + i * lineHeight);
    }

    if (currentCharIndex >= this.instructionText.length)
    {
      if (currentTime - lastCharTime > 2000) 
      {  
        currentCharIndex = 0;  
      }
    }
  }
Problems I Faced and Solutions

In the development of this game I faced several challenges. Some  of them includes the following:

  1.  Synchronising the Animal sounds so that they do not overlap: After exploring several options, I was able to solve this by  creating a function that would pause all the sounds when not in use.
  2. Managing the game Flow: This was a problem as I was designing my game, I did not have a clear structure in mind. However as I began the implementation  I was able to figure out the pieces and manage the flow smoothy.
Reflections  

As I look forward to future works, I hope to maximise the use of OOP. While I have used OOP in the implementation of my game, I think I would even use it more to create a more easily manageable code for my game than it currently is. However, I am generally proud of how the game has turned out and I look forward to having people play it.

Midterm Project – Motion Ship

#####LINK TO THE GAME#####
#####CODE OF THE GAME#####
(Unfortunately my p5 seems down for no reason;
Project is temporarily hosted on GitHub)

intro

First of all, as the game design documentation in my progress report has included the essence of my project at large, I would try to focus more on the improvements and specificities I made in this second stage of developing my NEXT-GEN-SOMATIC-MOTION-SENSING-SPACE-SHOOTING-ACTION game, Motion Ship.

tech and concept DEVELOPMENT

When it comes to the development of the project, I would say that there are two parts to the story: 1. To realize and polish my initial vision; 2. To make decisions in terms of removing elements from the plan or adding flavors to it (e.g., removing the audio level & mouse control to reduce the complexity of commands to only head motion and keyboard inputs).

1. Interactive Experience

As the centerpiece of the game, the realization of the concept of ‘controlling the spaceship with the player’s head motion’ was my primary objective. Although at the end of the first stage, I had achieved the basic mapping relationship between the head position detected by the ML model and the displayed position of the player spaceship in the game, there were still several awkward shortcomings, including:

  1. The ship respawns itself every frame in the head position directly instead of moving towards it smoothly. This was later tackled by introducing the smooth-approaching logic I used in my first project.
  2. The ship’s motion responsiveness to the head motion was too ‘authentic’, leading to the player’s having to literally move drastically in order to control the ship instead of intuitively directing the ship with slight movements. This was tackled by adding factors of motion sensitivity to the mapping relationship.
  3. The ship appeared to be ‘translating’ in space (although in terms of programming, it is), instead of reflecting the aerodynamic behavior of real aircraft. Thus, rotations in all three axes were introduced to simulate such effects.
update(headPos) {
  if (this.toDestroy === false) {
    // Update position based on head movement (-1 to 1 mapped to screen space)
    let targetX = map(headPos.x, -1, 1, -width, width);
    let targetY = map(headPos.y, -1, 1, -height * 1.5, height * 1.5);
    this.x += (targetX - this.x) * 0.15;
    this.y += (targetY - this.y) * 0.15;
    
    // Constrain the postion within the gaming zone (2.87 approx. 3 calculated from triangular perspective: fovy = 0.5, camZ = 800, shipZ = 280)
    this.x = constrain(this.x, -gamingZone.width / 3, gamingZone.width / 3);
    this.y = constrain(this.y, -gamingZone.height / 3, gamingZone.height / 3);
    
    // Update rotation based on head movement
    this.rotationX = map(-headPos.y, -1, 1, -PI / 3, PI / 3);
    this.rotationY = map(headPos.x, -1, 1, -PI / 10, PI / 10);
    this.rotationZ = map(headPos.x, -1, 1, -PI / 1.25, PI / 1.25);
    
    // Tactic engine reset
    if (this.tacticEngineOn === true) {
      let currentTime = millis();
      if (this.model === assets.models.playerShip1) {
        this.health = 100;
      } else {
        this.energy = 100;
      }
      if (currentTime - this.tacticEngineStart > 15000) {
        this.tacticEngineOn = false;
        if (this.model === assets.models.playerShip1) {
          this.health = 100;
        } else {
          this.energy = 100;
        }
      }
    }
2. Gameplay Aspect & UI

One major awkwardness I spotted then was that when the canvas aspect followed the window, the 3D spatial relationships between the objects and the visual distortion tended to be uncontrollable – for example, an enemyship could seem on the laser trajectory of the player when in the distance, but in fact it was an illusion introduced by perspectives. As a result, I devised several mechanisms to smooth out the experience, including:

  1. Define a definite gaming zone with constant aspect (1:1) on the window (regardless of whether the window is in vertical or landscape aspects).
  2. Trigonometrically calculate and confine the objects in the 3D space in relation to the camera position.
  3. Enlarge the collision box & the speed of the lasers fired to reduce difficulty when hitting moving enemies.

On top of that, other improvements besides the gaming zone include allowing enemy ships to launch lasers, incorporating different meteoroid models, displaying pilot logs and other info on the margin out of the gaming zone, displaying laser and health bar within the gaming zone, etc.

3. Visual Effects

To further improve the immersiveness of the gameplay, I made four major changes:

  1. Space dust randomly generates and flies towards the player’s ship, creating a sense of speed (compared to the insufficient indication of speed when there were only enemies and obstacles flying slowly towards the player).
    class SpaceDust {
      constructor(maxParticles = 50) {
        this.maxParticles = maxParticles;
        this.particles = [];
        this.spawnRate = 2; // Number of particles to spawn each frame
        this.initParticles();
      }
    
      // Initializes the particles array with empty particles.
      initParticles() {
        for (let i = 0; i < this.maxParticles; i++) {
          this.particles.push(this.createParticle());
        }
      }
    
      /*
      Creates a single dust particle with random properties.
      @returns {Object} A particle with position, velocity, size, and lifespan.
      */
      createParticle() {
        return {
          pos: createVector(random(-gamingZone.width / 2, gamingZone.width / 2), random(-gamingZone.height / 2, gamingZone.height / 2), -random(1000, 1500)),
          vel: createVector(0, 0, random(80, 100)), // random Z speed
          size: random(2, 4),
          lifespan: random(50, 200) // Frames the particle will live
        };
      }
    
      // Updates all particles: moves them forward and resets them if necessary.
      update() {
        for (let i = 0; i < this.maxParticles; i++) {
          let p = this.particles[i];
          p.pos.add(p.vel);
          p.lifespan --;
    
          // If the particle has passed the player or its lifespan ended, reset it
          if (p.pos.z > 300 || p.lifespan <= 0) {
            this.particles[i] = this.createParticle();
          }
        }
      }
    
       // Renders all particles onto the screen.
      render() {
        push();
        // Enable additive blending for a glowing effect
        blendMode(ADD);
        for (let p of this.particles) {
          push();
          translate(p.pos.x, p.pos.y, p.pos.z);
          noStroke();
          fill(255, 255, 255, map(p.lifespan, 0, 200, 50, 255)); // Fade out based on lifespan
          sphere(p.size);
          pop();
        }
        blendMode(BLEND); // Reset to default blending
        pop();
      }
    }
  2. Vignette effect in the background to create depth instead of having all the objects floating on a plane.
    loadBackgroundWithVignette(key, path) {
      loadImage(path, (img) => {
        const vignettedImg = this.applyVignette(img);
        this.textures[key] = vignettedImg;
      });
    }
    
    applyVignette(img) {
      // Create a graphics buffer the same size as the image
      let gfx = createGraphics(img.width, img.height);
      gfx.clear();
    
      // Parameters for the vignette
      let centerX = img.width / 2;
      let centerY = img.height / 2;
      let maxDiameter = max(img.width, img.height) * 1.25;
    
      gfx.noFill();
      gfx.background(0, 0, 0, 0); // Ensure transparency
    
      gfx.blendMode(BLEND);
    
      // Draw multiple concentric ellipses to create a radial gradient
      for (let r = maxDiameter / 2; r > 0; r -= 20) {
        // Adjust alpha based on radius
        let alpha = map(r, 0, maxDiameter / 2, 40, 0); // intensity: darkest part = 50, larger the darker
        gfx.noStroke();
        gfx.fill(0, 0, 0, alpha);
        gfx.ellipse(centerX, centerY, r, r);
      }
    
      // Convert gfx (p5.Graphics) to p5.Image
      let vignetteImage = gfx.get();
    
      // Create a copy of the original image to avoid modifying it directly
      let processedImg = img.get();
    
      // Blend the vignette image onto the processed image using MULTIPLY mode
      processedImg.blend(vignetteImage, 0, 0, vignetteImage.width, vignetteImage.height, 0, 0, processedImg.width, processedImg.height, MULTIPLY);
    
      return processedImg;
    }
  3. Parallax effect of the background to increase the responsiveness of environment to the player’s motion.
    class Background {
      constructor(texture) {
        this.texture = texture;
        this.xOffset = 0;
        this.yOffset = 0;
        this.playerPreviousX = null;
        this.playerPreviousY = null;
        this.parallaxFactor = 250; // Adjust for parallax strength
      }
    
      update(playerX, playerY) {
        let playerMovementX = playerX - this.playerPreviousX;
        let playerMovementY = playerY - this.playerPreviousY;
        
        // Calculate the background offset
        this.xOffset += playerMovementX * this.parallaxFactor;
        this.yOffset += playerMovementY * this.parallaxFactor;
        
        this.playerPreviousX = playerX;
        this.playerPreviousY = playerY; 
      }
    
      render() {
        push();
        translate(-this.xOffset, -this.yOffset, -5000); // Positioned far in the background
        noStroke();
        texture(this.texture);
        // Render a large plane to cover the background area
        plane(width * 7.5, height * 7.5);
        pop();
      }
    }
  4. The windshield (although only frames) around the gaming zone to enhance the sense of an FPP piloting experience.

4. Game Flow

After hearing feedback from several friends, I decided to add an instruction page before entering the gameplay to make life easier for the players.

In addition, I also enabled the player to restart the game immediately instead of having to restart from scratch or reconfigure the game.

5. Storytelling

Last but not least, one of the most illuminating takeaways from developing this project is to recognize and accommodate the gap between a developer’s understanding/assumption and the players’ ‘infinite’ possibilities to approach the product. For example, displaying the variable names on the screen or using them in the instructions seems to be clear enough for me during the development, while a player may not have enough experience or interest to distinguish and follow.

Therefore, I replaced the variable names with terms of more meaning within the space action worldview to create more intuitive guidelines for the player with the aid of visual indications.

SOme words, in hindsight

It is true that there is no ‘perfection’ in terms of finishing a project – at this point, I still have many ideas to add to the game if regarding it as a game to publish or so, including level design, more value balance, more storytelling, enemy and obstacle varieties, bosses, more tactic engines (special skills of each ship), more consistent aesthetics, and so on. On the other hand, I found myself quite satisfied with this current presentation – in terms of me utilizing wheels and knowledge learned in the process, trying to think not only from a developer perspective, and establishing a coherent storytelling through a product, etc. And it made me more excited to get into the physical programming.

Midterm Project – Winter Wonderland

As I have mentioned in my Midterm Progress Report, my goal was to create not just a game, but rather an interactive environment for which I took inspiration from the game I played a long time ago with my friends.

Fortnite Winterfest Presents - How to Get Free Daily Gifts

It was a long journey from the beginning to the end of my work that included not much but, still, some challenging parts that I will describe later in this blogpost.

Concept

Initially, I wanted to create an interactive game related to Christmas Eve, and simply work on good-quality visuals and add certain interactions, e.g. opening the gifts. However, after getting feedback from the professor and thinking about other potential ideas, I realized that it would not be enough for me to be fully satisfied with the results. That is why I decided to add the game that would also include good-quality animations and sprites, and would incorporate the collision detection and OOP principles.

As it needed to also be related to Christmas, I decided to make the Santa the main character of the game. Initially, I wanted to do something with the landscape orientation as my game is pretty wide. However, I decided to reject that idea because of the potential problems with the screen movement control. (For my laptop, for example, as it has a small screen, the game exceeds the boundaries of the monitor, so I need to scroll through the screen).

I decided to implement the game with the basic concept of ‘something falling from the sky, and you either need to dodge it or catch it’. Nothing special, but I strived to make it as fun as possible. In my game, the background poster inside the house tells that there is a shortage of gifts. The goal of the user is to help Santa to catch the gifts while dodging the icicles falling from the sky. The user can control the character by using the arrow keys.

Talking about the broader picture outside of the mini-game that I just described, my game consists of two main stages, outside the house (serves mainly aesthetic purposes with very nice background music that I received multiple compliments about haha), and inside the house (platform for interactions and ‘entrance’ to the mini-game).

PLAY THE GAME IN THE FULLSCREEN (PRESS ‘F’ FOR THE FULL FULLSCREEN)

Problems and Solutions

Although there were not too many problems, there were enough difficulties and struggles that it will not be possible to fit here, so I will focus mainly on those that I’m actually proud of resolving.

1) By my mistake, the biggest struggle was to connect the games together. Yes, you read it right. Connect the games.

For some reason, I was naive enough to think that p5.js has a function that can magically embed one sketch into another. Don’t get me wrong, p5.js is an amazing platform with many functionality and features to offer, but I expected such a concept of unification to exist. Exactly why, I decided, to make things easier and write the code for my mini-game in the separate file. It was to my big surprise to realize, after an hour of trying, that it is impossible to just straight up integrate the mini-game into my main file. So I needed to put a lot of work and precision into the transfer of the code, and, at some points, to hardcode, to plug the mini-game into the screen of the main game as you can see playing it now.

} else if (currentScene === 4) {
  // Game scene
  playGameSceneMusic();
  image(blurred_house_interior, 0, 0, 1800, 900);
  fill(0, 147, 255);
  rect((width - sceneGameWidth) / 2, (height - sceneGameHeight) / 2, sceneGameWidth, sceneGameHeight); 
  
  isMouseOnArrow = checkIfMouseOnArrow();
  drawArrow(); // to be able to exit back to scene 2
  
  if (!gameStarted) {
    displayRulesScreen();  // rules
  } else if (gameOver) {
    displayGameOverScreen(); //game over
  } else if (gameWon) {
    displayWinScreen(); // win
  } else {
    playGame(); // game state

2) Music and Sound effects were not behaving as intended. The main problem was the repetitiveness and infinite looping of the sounds during the interactions with the object. For example, a knock on the door would repeat itself from the start to the finish all the time while the mouse is pointing at it. I needed to fix it using the limiting boolean conditions (true/false). I hope you get what I mean by this. If you don’t, perhaps it will be easier when you’ll look into the code.

if (mouseX > doorX && mouseX < doorX + doorWidth && mouseY > doorY && mouseY < doorY + doorHeight) {
  doorTrigger = true;
  
  if (doorKnockCharged === true) { // to play sound once per mouse pointing 
    doorKnock.play(); 
    doorKnockCharged = false;
  }
} else {
  doorTrigger = false; 
  doorKnockCharged = true; 
}

3) Screen scrolling with the arrow keys did not allow to make the experience of playing the mini-game enjoyable. The Santa movements are bonded to the Left Arrow and Right Arrow. At the same time, as I have mentioned above, my screen, as I expect to be for most users as well, was too short, so the picture of the game is scrollable in the fullscreen mode. In p5.js, arrow keys trigger the screen scrolling, which interrupted the mini-game flow as the screen waas constantly moving left and right with the key clicks. I even posted the question in the discord channel, but after the internet research I found the solution.

// blocking arrow key default behavior using window event listener
window.addEventListener("keydown", function(event) {
  if (event.key === "ArrowUp" || event.key === "ArrowDown" || event.key === "ArrowLeft" || event.key === "ArrowRight") {
    event.preventDefault();  // Prevent default scrolling behavior
  }
});

 

4) When opening the gifts and the mini-game, I needed to come up with the background to be used. As looking for new backgrounds would take too much time, I decided to make it in a similar way many games do – blur the background to make the effect of focus on the chosen item. Initially, I tried to implement it using the filter(BLUR, ...) function in p5.js. Although the effect looked nice, for some reason, my game started lagging and freezing during the scenes with using the blur (probably has something to do with the constant update of pixels on the screen). So I decided to make a little smart move – instead of blurring the picture inside the game, I blurred the picture using the blurring tool on the internet and simply plugged it into my sketch.

PLAY THE GAME IN THE FULLSCREEN (PRESS ‘F’ FOR THE FULL FULLSCREEN)

Conclusion

I am very proud of my work on this midterm project. I accomplished more than I initially planned to, and I managed to maintain a more or less high standard of gaming. I found good quality images, sounds, and sprites, I successfully handled the unexpected difficulties, and managed to build the game without significant bugs (hopefully there are no bugs at all haha). Most importantly, I achieved my initial goal of creating an art piece of nostalgia for myself, and I did it by putting in a lot of hard work and thinking processes. Of course, there are a lot of things that I could add to the game or implement in a better way, for example using less hardcoding and following a more logical and organized approach. I could make more high-level effects or create better-quality textures, etc.. Unfortunately, it was not enough time to accomplish all that I could.

Nevertheless, I am happy with how the course is going so far. I have a lot to learn and grasp, and it keeps me excited and motivated for the second half of the course. Looking forward to working with Arduino!

Midterm Project – ICEY IMPACT


Concept:

The game is an arena-style, two-player game where each player controls a snowball. The goal is to either knock the opponent’s snowball (represented by an emoji image) out of the arena or collect three coins. Which ever player gets to three points first wins. A scoreboard is displayed on screen to show each player’s current score and coin count.

Sketch:
User Interaction: 
  • The game begins with an instruction screen, requiring user input to proceed.
  • Player 1 controls their snowball using the WASD keys, while Player 2 uses the arrow keys.
  • Players navigate the arena, collecting coins or attempting to knock their opponent out of bounds.
  • The first player to collect three coins or knock their opponent out three times wins.
  • Players can choose to start a new session after completing the game.
    Implementation: 
  • The game has several classes, each of which contributes to different aspects of the experience:

    • Game: This class manages the overall state of the game, including showing screens, handling instructions, tracking game progress, and determining when a round or the game ends.
    • Player: The Player class handles all of the attributes and behavior of each player. This includes movement (based on keyboard input), collision detection, score tracking, and boundary constraints.
    • Arena: The Arena class defines the boundary within which players compete. It is used to detect if a player has fallen out of bounds.
    • Item: The Item class is used to create collectible items (coins). When a player collects an item, it increases their coin count.

    Key features incorporated into the game:

    • Object-Oriented Programming: The game is structured using classes such as Game, Player, Arena, and Item to ensure modular and reusable code.
    • Visuals: The game uses images for players, the background, and items (coins).
    • Sound: Sounds are used for collecting coins and when the game ends.
    • User Input: Player controls are managed using keyboard inputs (WASD and arrow keys).
    • Collision and Boundary Detection: Players can collide with each other, and boundary detection is used to check if players fall out of the arena.
      Key Challenges Faced: 

      One of the primary challenges faced during development was handling the collision physics between the players. The snowball collisions had to feel dynamic, but it was difficult to achieve perfect elasticity. The physics calculations were complex, especially considering that each player’s position and velocity had to be updated to reflect an elastic collision.

      Another challenge was optimizing the game so that it could run smoothly in p5.js. Initially, I wanted to incorporate more images and sounds, but p5.js struggled to handle all of the resources, leading to long loading times. Ultimately, I had to simplify the assets to ensure smooth gameplay.

Code That I Am Proud Of: 

I am particularly proud of the resolveCollision() function in the Player class, which handles the collision between players. The function calculates the angle and then applies new velocities to the players to simulate the effect of a collision. This code makes the game feel more dynamic and fun, as players can push each other around the arena.

resolveCollision(other) {
  let dx = other.x - this.x;
  let dy = other.y - this.y;
  let distance = Math.sqrt(dx * dx + dy * dy);

  if (distance < this.radius + other.radius) {
    let angle = Math.atan2(dy, dx);
    let v1 = Math.sqrt(this.vx * this.vx + this.vy * this.vy);
    let v2 = Math.sqrt(other.vx * other.vx + other.vy * other.vy);

    let newVx1 = v2 * Math.cos(angle);
    let newVy1 = v2 * Math.sin(angle);
    let newVx2 = v1 * Math.cos(angle);
    let newVy2 = v1 * Math.sin(angle);

    this.vx = newVx1;
    this.vy = newVy1;
    other.vx = newVx2;
    other.vy = newVy2;

    let overlap = (this.radius + other.radius) - distance;
    let moveX = overlap * Math.cos(angle) / 2;
    let moveY = overlap * Math.sin(angle) / 2;
    this.x = this.x - moveX;
    this.y = this.y -moveY;
    other.x = other.x + moveX;
    other.y = other.y + moveY;
  }
}

This function might not achieve perfect elasticity, but it gives a satisfying collision effect, enhancing the overall player experience.

Reflection: 

Looking back, I am mostly satisfied with how the project turned out, especially given the challenges with collision detection and resource optimization. One thing I wish I could improve is the collision physics—ideally, the collisions would be perfectly elastic, allowing for even more realistic interactions between the players. However, achieving this in a way that felt right with p5.js was more challenging than expected. Despite these challenges, the game is enjoyable and provides a satisfying experience for both players.