Midterm Project

Concept

The code for the game can be found here.

The game that I wanted to create was a version of my favorite classic breakout game but actually without the bricks!

So how does the game work?

The game has 3 phases:
  1. The start screen – this screen shows the instructions on how to play the game as well as a button to start the game the first time user is playing. The instructions are fairly simple:

– Move the paddle with the A and D keys to left and right respectively.
– Don’t let the ball fall!
– Catching UFOs rapidly increases score while catching Xs rapidly decreases your score.
– Beware: hitting the paddle at the edges or Xs increases the speed of the ball!
– Overall, the further you go in the sky the higher the score you get.
– Compete with your friends to beat the highest score on your laptop!

2. The game phase – once the user clicks on “Start Game”, the ball flies up in the air and the game begins. The user can start moving the paddle using the A and D keys to save the ball and hit it at specific angles to prevent the ball from speeding up.
The score, UFOs collected and the highscore appears at the top of the game.

3. Game over phase – if the user fails to catch the ball, game is over. This screen displays the user’s final scores and gives and option to replay the game.

Implementation

There were a number of parts about the assignment that proved to be challenging for me.

Ball and paddle collisions

When the game begins the ball is assigned a random speed between 2 and 5. Initially, I tried to keep the variable to change the direction of the ball separate than the speed of the ball. However, this was becoming immensely messy and created further complications when I tried to include the collision with paddle. To cater to this problem, I used negative numbers for ball’s left speed and positive numbers for ball’s right speed and similarly for ball’s top and bottom speed as well. However, because of the number of times the ball’s speed changes throughout the game, it was even more difficult to manage this because it could lead to the ball’s speed becoming 0 in which case the ball would get stuck in the air. So the if conditions had to be used very carefully throughout the code to avoid such problems.

A very simple example of this issue is that when the game starts, I choose a random direction for the ball to start off. If I simply chose a random number between -5 and 5, the ball could start off extremely slow or not even start off if the speed is 0. Therefore, to avoid this problem I first generated a random number between 0 and 1. Then based on the result of this, I chose the speed of the ball. If the random number was between 0 and 0.5, I randomly choose the ball’s speed from -5 to -3 in the left direction. On the other hand, if the first random number is greater than 0.5 than I choose a random number again for the ball’s speed between 3 to 5 in the right direction.

Below is the code for this problem:

let PoNX = random(0, 1);
let PoNY = random(0, 1);

if (PoNX < 0.5) {
  ballSpeedX = random(-5, -3);
} 
else {
  ballSpeedX = random(3, 5);
}
ballSpeedY = random(-5, -3);

The Y direction remains the same in the top direction to allow the user some time to adjust to the game screen. Though this problem is a very short one, it required navigating a lot of error statements to finally reach this conclusion. This is a very small example but I ran into similar problems throughout my code for when all ball collisions, with the wall, paddle, UFO and cross.

In particular, detecting the collisions with the corners of the paddle or from the sides of the paddle was the most difficult challenge costing me hours of hard work which I still doubt if it has been resolved fully. Adding on to the challenge, I had to play the game for a couple of minutes before I could bring the paddle in the exact position of contact with the ball which was causing an issue to be able to detect and test the problem, which made this challenge even more time consuming.

Mapping the paddle speed

Similarly, because of the above stated reasons, to come to a fully working code for mapping the paddle speed was a tough challenge. Below is the code for this:

//if ball hits the paddle
  else if (ballY >= 465 && ballX >= paddleX + 5 && ballX <= paddleX + 95) {
       ballSpeedY *= -1;
       bounces += 1;
       spaceBounce.play();
       ballY -= 3;

       //map ball speed at left of paddle
       if (ballX >= paddleX && ballX < paddleX + 50) {
         newSpeed = map(ballX, paddleX + 49, paddleX, 2, 5);
         if (ballSpeedX < 0) {
           ballSpeedX = -newSpeed;
         }
         if (ballSpeedX > 0) {
           ballSpeedX = newSpeed;
         }
         console.log(ballSpeedX);
   }

   //map ball speed at right of paddle
   else if (ballX >= paddleX + 50 && ballX < paddleX + 100) {
         newSpeed = map(ballX, paddleX + 100, paddleX + 50, 2, 5);
         if (ballSpeedX < 0) {
           ballSpeedX = -newSpeed;
         }
         if (ballSpeedX > 0) {
           ballSpeedX = newSpeed;
         }
         console.log(ballSpeedX);
       }
     }

Scrolling background

To give the idea to the user of a moving background, so they feel the are going upwards not only adds greater user engagement in the game, but also makes it slightly more difficult to judge the speed of the ball. To make it more difficult for the user as the game progresses, every time the user score increases by 500, the background speed increases to make it more challenging for the user and make thes feel as if the game is speeding up even though it’s not.

The following is the code for this effect:

//increase background speed
if (score % 500 == 0){
  bgSpeed += 0.5
}

//starry background
image(artwork, 0, bgY);
image(artwork, 0, bgY2);

//scroll background
bgY += bgSpeed;
bgY2 += bgSpeed;

if (bgY >= 1000) {
  bgY = bgY2 - 1000;
}

if (bgY2 >= 1000) {
  bgY2 = bgY - 1000;
}

Saving high score in local storage

To allow the users to compete with each other and prevent the game from only being a one-off game, every time a user plays the game, the score gets saved in the local storage of the browser. So regardless of the screen being closed or refreshed, the score is saved and pulled from the local storage for each game. This feature motivates the users to compete with each other and beat the highest score.

If time allowed, I could have improved this feature further by storing the high score in a file and then reading from the file each time so regardless of the high score being limited to the browser or laptop that the game is being played on, it would be more accessible to everyone who plays the game.

Changing ball & border colors

Finally, for aesthetic purposes and to prevent the game from becoming boring, I set the border and the ball colors to continuously change over the course of the game. This not only adds color to the game but also make it a little challenging for the user to easily keep track of the ball. This aspect of the game was also particularly challenging to write so I browsed some online sources to write the code below:

// cycle ball colors
   r += rs;
   g += gs;
   b += bs;

   //ball color cycle
   if (r > 255 || r < 50) {
     rs *= -1;
   }
   if (g > 255 || b < 50) {
     gs *= -1;
   }
   if (b > 255 || b < 50) {
     bs *= -1;
   }

Overall, despite all the challenges, I thoroughly enjoyed creating the game, (even though by now I have played it hundreds of time and no longer love it as much as I used to), I learned multiple features of p5.js as well as some logical aspects of the coding which I did not initially consider myself either. The game stimulated me logically as well as creatively as I was continuously think of what new features I could implement which are not commonly found in such games to give my project a unique feel. Because of the different features such as the speeds of the ball and the background, changing colors, UFOs, crosses, and high scores, I believe I was successful in giving this project a new and unique feel which I am very proud of!

Beach Cat! – Midterm Project

 

Final Product

Project Concept

I used to have a cat simulation game on my laptop’s control strip that I found very enjoyable to play with. The game served as an inspiration, along with my love for the beach, to create a similar game featuring a cat on the beach. The game’s concept is to take care of a cat and attend to its many needs (it might not have a lot of needs but requires a lot of close attention). To give this project a more game-simulator-like feel, I went for pixelated aesthetics and I am very happy with how it turned out.

Project Description/Parts I am Proud of

The user selects a cat and moves around the beach to feed the cat, give it water, and use the litter box. If the cat does not receive the needed attention, it dies and the user has to start over 🙁 The game is controlled by the arrow keys and asdw keys and the user is free to walk around all the beach areas.

One technique that I am proud of and which helped me a lot in visualizing the game and putting the building blocks together is that when building the game, I set up a grid on the screen with x and y indexes. I then used this grid to locate my objects and find intersections with them.

Background Terrain with the grid

Another aspect that I am proud of is another helper tool – a helper function to split a sprite sheet according to the global variable tileSize. This function takes a unidimensional sprite sheet image and returns an array of tiles. I wish I came up with this function at the beginning of the coding journey as opposed to the end because it would’ve saved me a lot of time but I am glad I incorporated it still.

//function to get a uni-dimensional spritesheet and return an array of the elements
function splitSheet(imgName){
  let arrayName = []
  for(let i=0; i<imgName.width/tileSize; i++){
    arrayName[i] = imgName.get(i*tileSize, 0, tileSize, tileSize);
  }
  return arrayName;
}

I also tried my best not to hard-code any indexes, locations, etc, which is a good practice for when I am going to come back to the game and built on top of it.

Problems

I really wanted this game to have background music, but because I uploaded too many images (the sprite sheets I had were not convenient to work with so I had to split them into many smaller ones that had the same dimensions)  there was no memory left for a sound that is lengthy enough to play on the background. When I did, the game basically never loaded, which is why I had to give up that idea.

Another problem that I ran into and that took me a while to figure out is storing my 4 different cat sprite sheets for 4 different movements (up, down, left, right), AND 3 different “stages” of each movement. This is when I came up with the function mentioned above and I ended up storing things in a 3d matrix.

Overall, I am really happy with the way the game turned out given the short time limit. There are many more features that I would love to add to the game but for now, this is the final game 🙂

MIDTERM: “Grannys Lovely Maze”

TO PLAY THE GAME CLICK HERE!

If you are a professor and you want to see the code click here!

GAME CONCEPT

Grannys Lovely Maze is a 2-player maze chase game. Kitty (PLAYER 1) is a royal cat who enjoys spending time in the outdoor maze of his castle. He needs to find a way back to his grandma before the Bear (PLAYER 2) turns him into a dinner. The maze is set in the nighttime, meaning that neither the Kitty nor the Bear can see the full maze. Each player is spotlighted to only see the closest surrounding. This feature is the distinguishing point of this game, which adds some uniqueness as well as makes the game more challenging.

The game itself consists of 3 maps. If one of the players scores 2 consecutive wins, while the other player scores 0, the third map is not played and the game ends. Scores are not displayed during the game because I feel like with how short the game is there is no real need for a visual element on the screen that could potentially distract the players from the game.

Below are the screenshots from each of the screens.

IMPLEMENTATION

My project consists of three screens: start (instructions), game, and end. In designing each screen I have tried to be consistent with the sizes, shapes, and colors that I use. The overall visual concept can be probably described as an 8-bit pixel-style arcade. This is a style that I adore and used previously in personal and class projects. I have done some images myself (ground and bushes textures in the maze) as I couldn’t find what I was looking for online. I have sourced the sprites, the music, and the font to match online.

To create some order and organization, I have created 5 classes: Game (connects every class and controls the game logic), Cell (map display), Player (players display), Door (granny display), and Confetti (confetti display). I have used the gameState variable inspired by professor Mang’s example to switch between game screens. There is also a separate file that stores three available maps as arrays.

To create the spotlight effect and display only a part of the maze surrounding the players I have created a function inside of the Player class that uses an if statement to check if the cells around the player position are within the boundaries of the spotlight. I am no mathematician so I didn’t figure out how to make so that the spotlight round instead of boxy. However, I feel like it fits the pixel aesthetics of the game and doesn’t take away from the experience.

inSpot(other) {
    if (
      other.x < this.x * cellWidth + this.spotlight * cellWidth &&
      other.x > this.x * cellWidth - this.spotlight * cellWidth &&
      other.y > this.y * cellWidth - this.spotlight * cellWidth &&
      other.y < this.y * cellWidth + this.spotlight * cellWidth
    ) {
      return true;
    }
    return false;
  }

To add some aspect of celebration to the end screen and make it less plain I have added the Confetti class. It creates a list with randomly colored rectangles that both rotate and move vertically at random speeds. If the position of a rectangle is beyond the height of the screen, the rectangle is deleted from the list using the splice function. I am proud of this little feature. I think it adds some character and adds to the experience.

class Confetti {
  constructor() {
    this.x = random(width);
    this.y = -10;
    this.diameter = random(10, 20);
    this.color = color(random(255), random(255), random(255));
    this.speed = random(1, 5);
    this.rotation = 0;
    this.rotationSpeed = random(-0.05, 0.05);
  }
  
  update() {
    this.y += this.speed;
    this.rotation += this.rotationSpeed;
  }
  
  display() {
    push();
    translate(this.x, this.y);
    rotate(this.rotation);
    rectMode(CENTER);
    noStroke();
    fill(this.color);
    rect(0, 0, this.diameter, this.diameter/2);
    pop();
  }
  
  isOffScreen() {
    return this.y > height + this.diameter;
  }
}

Another part of the implementation I like is the display of the granny (exit point). I found a beautiful spritesheet online. To make her more dynamic, she is looking around before Kitty reaches her. She inherits her spotlight function from the Player class.

class Door extends Player {
  constructor(i, j, type) {
    super(i, j, type);
    this.spotlight = 3;
  }

  // MAKE THE SPRITE CHANGE EVERY SECOND
  update() {
    if (frameCount % 60 == 0) {
      this.sprite++;
      this.sprite %= 6;
    }
  }
  // DISPLAY
  draw() {
    this.update();
    image(
      oldladyImg,
      this.x * cellWidth,
      this.y * cellWidth,
      cellWidth,
      cellWidth,
      40 * this.sprite,
      0,
      40,
      54
    );
  }
}

CHALLENGES

In the implementation of my project, I have run into some problems, most of which were resolved. Main problems that I have tackled include:

        • Maze Building – initially I planned to implement an algorithm that would build random solvable mazes. I did have an algorithm, however, I then realized that I wouldn’t be able to correctly assign player and maze exit positions. In such a way that players can each reach one another and exit using multiple ways, and player 1 is as close to the exit as player 2 is close to player 1 (to make it equally possible for players to win in each round). A solution I found was building the maze from pre-existing maps. As a full maze is not shown to the players anyways, there is not as much need in generating new maze maps each time players want to play the game as they will have a harder time memorizing the map that they can’t fully see. I used the algorithm to show me possible map designs, which I slightly modified to increase the number of possible passages to the exit. For each of the maps, I store a list of starting positions for each player and the exit point.
        • Button Font – I use a custom font loaded as a file for my game. Initially, I loaded it and declared it as a variable in the javascript file. I used textFont("retroGaming"); the setup function to set the font of the body to the custom font, however, it didn’t work for the buttons. I have tried using button.style("font-family","retroGaming"); but it didn’t work either. After lots of thinking and searching, I realized that I can set button properties and load font in CSS, which was the solution to my problem.
        • Players Display – the sprites, though proportional to each other and their surroundings looked tiny in the 47*37 maze. I have recognized that this is an issue only at the very end of developing my project as I left sprite sheets for the very last. Changing canvas size didn’t make the players more visible and also made other screens look disproportionate, so I decided to find another solution, which was decreasing the size of the maze. I believe that this has made the maze less complicated to go through, which I am not happy about. On the other hand, with bigger-sized maps I didn’t use the entirety of the map useful for going through a level, meaning that usually a corner or 2 would be left untouched. If I had a chance to redo this project, I’d try making it so that the size of the map that each player sees is scaled up, so they don’t actually know if they are close to each other or to the exit until there are very few cells left between them and the two viewports merge. I think it would make the game more exciting but at the moment I have no idea how I would implement that.

Midterm Project | Survive the Semester Game

Hello everyone! I would share my ideas and implementation of my midterm game in this post.

Sleep, rest, study

Sketch Links

You may check out the code and game overall by clicking this link: https://editor.p5js.org/ds6058/sketches/Wdb9MMOe6

Concept

I wanted to create a game related to academics and our daily life for this midterm project. The narrative behind the game is quite simple and intuitive: the main character is a girl named Arti and her goal in the game is to catch falling objects – namely, sleep, rest (I choose to party with friends as the image), and study sessions to ‘survive the semester’. The game has two bars that measure the main character’s joy and health level. The player’s goal to win the game is to keep them as high as possible till time runs out.

Quality is somewhat poor. Above you may see the main interface of the game above. The simplest way to win is to stay active throughout the game. Skipping catching some objects may get side effects on health and joy, as well as catching some objects may actually harm your character if certain levels are already getting low. Specifically, game logic penalizes skipping sleeping and completing a study session with low health harms both health and joy, while partying is the best way to fastly recover from low joy levels.

Speed of falling changes over time and reaches certain peak periods before dropping to lower speeds. Yet, similarly to how the semester progresses, we start with low speed, and then at some periods, our workload gets heavy before dropping to lower speeds. Though, it does not get back to the initial speeds in the first weeks.

Technical Design

Objects [sleep, rest, study] are predefined in the setup() function and are chosen randomly. So, there is no particular algorithm to select, yet with time allocated and speed it is possible to win the game in different random scenarios.

I use two classes Game and ProgressBar to implement the main functionality of my game. They define the main variables and methods used. Below, is a code snippet from the play_game() method in the Game class. Each frame it runs, it checks if the object is within the screen, whether the player has caught the object, or whether time run out.

if (0 == this.time_left) {
  screen_on = game_won_screen;
}
if (this.y > height) { // if object goes out of screen without being catched
  this.is_caught = "no";
  this.update_bars();
  this.create_new_falling_object();
} else if ( //check if the object is caught by comparing mouseX coordinates and object's coordinates
  this.y > height - 150 &&
  this.x > mouseX - 100 &&
  this.x < mouseX + 100
) {
  this.is_caught = "yes";
  this.update_bars();
  this.create_new_falling_object();
  this.score += 1;
  // play sound of scoring if caught
  score_sound.play();
}

Below, there is a part of the code that controls how objects affect health and joy bars.

  // studying with low health is unenjoyable and unhealthy
  if (this.health_bar.level < this.health_bar.max_level / 2) {
    this.health_bar.level = max(0, this.health_bar.level - 10);
    this.joy_bar.level = max(0, this.joy_bar.level - 10);
  } else {
  // studying with ok health adds to joy
    this.joy_bar.level = min(
      this.joy_bar.max_level,
      this.joy_bar.level + 60
    );
  }
} else if (this.is_caught == "yes" && this.object_generated == "sleep") {
  // sleeping helps to increase health bar level
  this.health_bar.level = min(
    this.health_bar.max_level,
    this.health_bar.level + 100
  );
  // sleeping brings joy
  this.joy_bar.level = min(this.joy_bar.max_level, this.joy_bar.level + 30);
} else if (this.is_caught == "no" && this.object_generated == "sleep") {
// missing sleep reduces health bar level
  this.health_bar.level = max(0, this.health_bar.level - 30);

While the implementation of the code snippets above is not challenging, what I am particularly proud of about them and implementation overall is the ease to extend and make logic much more comprehensive [and personal]. The game logic, for now, is simple, yet it brings ideas about priorities, choices and their consequences. It is technically very easy to add more objects, not limiting ourselves just to sleep, rest, and studying.  Adapting new changes to game logic is also possible and is accessible.

I am also a bit proud that my progress bars are working as need them to. I had trouble understanding why changing the width of the rectangle with set x and y of starting point changes starting point as well while running. It was challenging to fix, yet I found a way to go around it.

Some areas of improvement

  • Adding more sounds. I currently have only one, though, it is used frequently throughout the game when a player catches any item.
  • Developing logic further. This one is particularly interesting for me. It is possible to develop a game from this version that would allow for much more thinking behind connections of game implementation and actual students’ lifestyles.
  • Working more on game design with particular attention to the text. The background image is nice, yet displaying of plain black/white text is unappealing and should be substituted to something more appealing to human’s eye.

 

 

Midterm

Overall Concept

Link to Sketch: https://editor.p5js.org/mk7592/full/Q3_SYFuO6

Whenever I’m traveling to someplace new, driving around my hometown, or even walking down a new street, I am always intrigued by that one small,  cozy cafe sitting at the corner of the street. The ones with soft, lo-fi music playing in the background with fairy lights hanging all around. For my midterm, I thought of replicating that thought and creating a similar experience but with one important addition: Interaction. I decided to sway away from making games for once and to try something new that I would enjoy just as much. So I came to the conclusion of making a small but interactive cafe experience in p5.

The whole concept behind my idea is to let the user explore the artwork on their own and immerse themselves in the calm environment of the cafe. The way I would make this work is to add small interactable elements in the cafe like books that the user can read, some wall art that they can view, or a boombox that they can use to change the music. All of these elements will be subtle but noticeable enough to make the user want to interact with them. This way, the user will be able to experience something new in every corner.

I decided to keep the design simple but intuitive. The color scheme would be soft and pastel-like and the overall design of the objects in the canvas would replicate SVG images. The cafe would have a calm and relaxing feel to it with soothing music and close-to-real-life sound effects, such as the sound of opening a door when it is clicked.

Initial wireframes I drew for my project:

Workflow/ Parts that Went Well

  • Switching the user perspective from scene-to-scene. For example, displaying the outside of the cafe at first, and then changing the perspective to view the inside of the cafe when the user clicks on the cafe’s entrance. Creating a separate SceneManager class to handle user interactions and changing the layers of the scene proved to be quite useful in organizing my code because if there was a bug with the layers I knew the problem was somewhere inside the SceneManager class. Updating layer booleans upon mouseclicks and updating the scenes accordingly worked side-by-side:
function mouseClicked() {
  // checking which object the user has clicked on and then displaying the relevant layer

  if (scene.onDoor) {
    console.log("Door clicked!");
    // if yes move inside the cafe
    scene.layer1 = false;
    scene.layer2 = true;

    // start playing the music
    dooropen.play();
    if (canStartMusic) {
      sounds[0].play();
      canStartMusic = false; // to prtevent music playing on top whenever the door is clicked
    }
    currentsound = sounds[0];
    currentsoundIndex = 0;
  }
  if (scene.onSmallBoombox) {
    console.log("boombox clicked!");
    // if yes zoom into boombox
    // layer 1 is already false as we're in layer 2
    scene.layer2 = false;
    scene.layer3 = true;
  }
  • Drawing! Utilizing vector images, Canva, and Procreate on my iPad to design the stuff I wanted. Drawing on the same size as my p5 canvas (100x600px) worked very nicely because I didn’t have to worry a lot about positioning the individual interactive pieces inside the p5 canvas. I did have to create a lot of the same images with small tweaks in them, like highlighting a certain wall art frame with the same background. Had I made separate images for small interactables and then positioned them on the canvas, it would have taken a lot of time.
  • Creating a working boombox. The forward and back buttons were tricky to implement because at first, I didn’t realize I had to first check if the music was playing or not and pause it if it was otherwise the next track would play on top of the previous one. Also, simply modding over the list of songs wasn’t enough to switch back and forth between the tracks. There was this little bug that I spent a lot of time on and it happened when we were at the first song in the list and pressed the back button. It should have switched to the last song on the list but instead, it was playing another song in the list. Turns out, it was going on a negative index which was then throwing an out-of-bounds error or playing the wrong song. Separating out the zero index condition solved this issue:
if (scene.onBackButton) {
  // play the previous song
  console.log("back clicked!");
  // same as in next sound but the only difference is we have to take care of index 0
  if (currentsound.isPlaying()) {
    currentsound.pause(); // if the current sound is playing, pause it first
    if (currentsoundIndex == 0) {
      sounds[sounds.length - 1].play(); // if we're at index 0, play the last song in the list
      currentsoundIndex = sounds.length - 1;
      currentsound = sounds[currentsoundIndex];
    } else {
      sounds[(currentsoundIndex - 1) % sounds.length].play(); // otherwise play the previous one
      currentsoundIndex = (currentsoundIndex - 1) % sounds.length;
      currentsound = sounds[currentsoundIndex];
    }
  } else if (currentsound.isPaused) {
    // if it's paused already, repaet the steps without the pausing step
    if (currentsoundIndex == 0) {
      sounds[sounds.length - 1].play();
      currentsoundIndex = sounds.length - 1;
      currentsound = sounds[currentsoundIndex];
    } else {
      sounds[(currentsoundIndex - 1) % sounds.length].play();
      currentsoundIndex = (currentsoundIndex - 1) % sounds.length;
      currentsound = sounds[currentsoundIndex];
    }
  }
}
  • Embedding a previous project (my Japanese tree) inside this project. I did this as a little surprise when the user clicks on one of the frames that looks like a purple tree. It takes the user to a Japanese tree that I made for another project. While doing user testing, everyone really admired this little surprise.
  • I tried to pay special attention to small things like hovering over the door creates a knocking sound but it doesn’t repeat as you’re hovering over it. It will only knock again if you move the cursor out of the door and back on it again. Only playing the initial music when the first time the door is opened and not every time since that would play the music on top of the one that’s already playing: another thing I found on user testing.
let canKnock = true; // to prevent continuous knocking while on door
let canStartMusic = true; // to prevent music playing on top whenever we enter
...
// inside the display() function of the SceneManager class
    //  playing knocking sound when mouse is over the door
    if (this.onDoor) {
      if (!knocking.isPlaying() && canKnock) {
        console.log("inside if");
        knocking.play();
        canKnock = false; // so that we don't repeatedly knock while the mouse hovers over the door
      }
    } else if (!this.onDoor) {
      // making it true back again since we moved the mouse away from the door
      canKnock = true;
    }
  • Switching content inside the books on the shelf. Positioning the text inside the book’s area was somehow a real hassle, eventually, I had to tweak the position till it fit the area of the book I wanted. Especially since the quotes were of varying lengths. I made the quote file myself using my favorite ones and separated them from author names using semicolons. Although this was a bit time-consuming because I had to find enough quotes to make it seem randomized and too repetitive. This was so that I could make the experience more personalized and so that I could add more quotes in the future.

Areas for Improvement

  • I thought about adding an instruction set to let the user know what they’re supposed to do. I’m still a bit conflicted on this idea because the project was meant to be explored by the user as an interactive art piece that would be filled with little surprises. However, having a guide does prove useful to some users. I could make the cafe menu interactable and turn it into an instruction guide inside the cafe, but I think the experience is simple enough for the user to understand for now.
  • A better way to detect clicking on images? I compared mousePos with the coordinates of my objects which was a hassle, but I couldn’t find anything concrete that would help me detect hovers/mouseclicks on images very easily on p5. Then again, some of the images have a transparent background around them which I don’t want to include while detecting mouse interactions, so maybe working with coordinates was in fact the way to go?
  • Adding more interactions with coffee, having the user brew some using a machine. What’s a cafe experience without coffee? I wished to add some features that would let the user play with a coffee machine, but I was going off on a tangent by starting to implement a game using that, which I decided to call off at the end due to time constraints. I would love to add this to my cafe in the future though.

Midterm

Sketch link

Try the experience out for yourself here.

It’s highly recommended to play it through once or twice before reading onwards.

Concept

I wanted to create a short retro game inspired by the original The Legend of Zelda game. Simultaneously I wanted to make the player question what it means to attack and kill digital characters in a game. In the majority of games, the player has to or can choose to kill other characters or enemies in the game, and these deaths are very simple, in that the killed character disappears or stops moving, after which the player can completely forget about them forever. My game is a subversion of that.

The game is intended to be played through in two main ways: without killing anyone, or by killing everyone. In order to not kill anyone, the player can try to lure the enemies and run around them. Also, the dash mechanic (activated by pressing E) should make this easier. In order to kill everyone, the player has to attack them with their sword. After the death, a few things happen which can be described as unsettling: the screen flashes in black and white for a second, the music tones down in pitch, a pool of blood appears where the enemy died, and a ghost of the enemy starts following the player. These things are meant to encourage the player to continue playing in one of two ways: either avoiding to kill any future enemies, or killing everything, pushed by their curiosity of what would happen next.

Also, the ending screen changes depending on how many kills the player has, and subtly encourages the player to do another play-through.

High-level project overview

The project uses the Entity-Component system to structure things in a way that allows easily adding new features and content. My GameEngine class manages several “scenes”, with one currently active scene at any moment. At game start, the Menu scene is active, which just shows the title screen and waits for user input to start the game. Afterwards there is a Play scene which loads the level and plays it. Finally, once the level is completed, an End scene shows the end screen and allows going back to the menu.

At each frame, the GameEngines’s update function is called, which calls the currently active scene’s update function, and then draws things to the screen.

// GameEngine update function
update() {
  if (!this.running || this.sceneMap.size == 0) return;
  const currentScene = this.sceneMap.get(this.currentSceneName);
  currentScene.update();
  currentScene.sRender();
}

// ...
// bunch of code
// ...

// Scene_Play update function
update() {
  this.entityManager.update();

  if (this.paused) {
  } else {
    this.sAI();
    this.sDash();
    this.sMovement();
    this.sStatus();
    this.sAttack();
    this.sCollision();
    this.sOwnership();
    this.sScriptedEvents();
    this.sAnimation();
    this.sCamera();
  }

  this.currentFrame++;
}

Here we can see the main concepts of the Entity-Component system. The logic of the game is divided into several “systems”, which are just functions that do all the work.

What does Entity mean? I do not create separate classes for each different “thing” in the game. In other words, there is no Player class, Enemy class, Tile class, etc. Instead, I have a generic Entity class:

class Entity {
  constructor(id, tag) {
    this.id = id;
    this.tag = tag;
    this.active = true;
    this.c = new ComponentList();
  }

  destroy() {
    this.active = false;
  }
}

Here is the definition of ComponentList:

class ComponentList {
  // A list of components that every entity can have, although they can be null.
  constructor() {
    this.animation = null;
    this.transform = null;
    this.bbox = null;
    this.input = null;
    this.state = null;
    this.lifespan = null;
    this.followPlayer = null;
    this.patrol = null;
    this.health = null;
    this.damage = null;
    this.invincibility = null;
    this.keys = null;
    this.owner = null;
    this.message = null;
    this.trigger = null;
  }
}

To each entity, I add components as I see fit, and the systems above handle things based on which components an entity has. For example: everything that I plan to draw to the screen (the player, enemies, tiles) has an Animation component, which is explained in my Midterm progress blog post. Everything that will have a position, velocity, etc., will have a transform component. Everything that will interact with other things (i.e. collision) will have a bounding box component. Everything that is supposed to have health has a Health component. Everything that is supposed to do damage has a Damage component. And so on.

I’m really happy with my decision to structure things in this way, because after I set things up, it was very straightforward to make changes and add new content. However, this was one of the biggest challenges of the project, and it took a long time to implement.

Another big challenging part for me included figuring out how to create, save, and load the level, which led to me to creating a python script which probably saved me several hours of menial work.

Another thing I’m happy about are the effects that happen when an enemy is killed. I experimented with various ways to manipulate the pixels on the screen. I turn the screen to black and white immediately after an enemy’s death. I also wanted to add a greyscale/desaturation effect which increases in intensity as more enemies are killed, and it looked really good, but unfortunately Javascript is very slow and the game became unplayable, even by just scanning the pixels array from p5 once a frame. In terms of sound, after a kill, the pitch goes up for a second, and then goes down to lower than before.

Bonus screenshots

Room 2, normal view.
Room 2, debug view. Collision boxes and enemy line of sight are visible.

To enable debug view, uncomment the line:

else if (key == "c") this.drawCollision = !this.drawCollision;

And press “c” while in-game.

Reflection

Areas of improvement include the content of the level and the unsettling/horror effects. Details of the level can be improved, such as adding more rooms, more enemy types, more decorations, and so on. The more this looks like a real game, the better the delivery of the second, hidden part of it. Also, many more horror effects can be added. Although outside of the scope of this project, one could add a hidden final boss or obstacle that only appears if the player has chosen to kill all enemies they encounter. This could further distinguish and solidify the two routes the game is intended to be played through.

The Martian – Midterm Project

The Martian

Sketch link

To play the game in fullscreen click this link -> Click Here to Play the Game

The project code -> Code

Overall Concept

For this Midterm Project I created a game using everything I have learned so far in class. The game is be called “The Martian” and the objective of the game is to guide the Mars Rover through the treacherous terrain of Mars, collecting valuable resources and avoiding hostile alien spaceships and rocks on the surface. The player will have to navigate using keyboard and mouse as they progress through the game. In “The Martian” the player will experience the thrill of driving a Mars Rover on the surface of the red planet. With challenging terrain, hostile aliens, and valuable resources to collect, the game will offer hours of exciting gameplay. The use of p5.js will enable the creation of a visually stunning and engaging game.

The overall concept of this game is a space-themed adventure where the player takes on the role of a NASA satellite specialist who is tasked with rescuing the PERSEVERANCE probe from an alien threat. The game has two modes: collection mode and exploration mode.  Your primary objective is to protect it from any danger by avoiding all the obstacles and enemy UFOs. In pause mode, the obstacles and UFOs will not harm you.

Implementation and Design

How this project works

In the game you are a satellite specialist at NASA, and the probe PERSEVERANCE, is being chased by an alien! It’s your task to save it! Avoid the stones and enemy UFO’s to save it! You will have to use the Space and Down keys to control the rover. I also decided to implement two different modes to the game:

  1. In collection mode- Collecting a Uranium atom is worth 5 points and collecting a water is worth 10 points. While collecting points, remember to dodge the UFO’s and stones on the surface of the red planet.
  2. In exploration mode crossing certain distance automatically gives 1 point. All you have to do is to dodge the UFO’s and stones on the surface of the red planet.

Technical Design/Good technical decisions

The starting screen of the game has three buttons:

  1. Mode button in which you can choose between Collection and Exploration modes according to your preference.
  2. Start button to start the game.
  3. Rules button which gives precise instructions on how to play and control the game.

This creates a user friendly experience where users can easily manage the different aspects of the game from the main page.

The technical decision I am particularly proud of is implementing the two different modes of the game. I initially thought that it would be only Collection mode where you can earn points by collecting really scientifically valuable resources like Uranium atom and Water in Mars. But after I asked my friends for user testing one of them gave me a feedback that not every user wants to collect something to play the game. Considering this feedback I also realized that in the games like Geometry Dash the main goal was to survive not to collect the resources. And this fits the logic because whats the point of your resources if you are dead?! That way the Exploration mode was made.

Collection mode:

Exploration mode:

Code for the two modes:

switch (mode) {
      case "exploration" : score+=0.01;
                      score$=Math.round(score);
                      water.visible=false;
                      atom.visible=false;
                      break;
      case "collection" : water.velocityX=-4;
                          atom.velocityX=-4;
                          if(rover.isTouching(water)) {
                              score+=10;
                              score$=score;
                              water.x=width+Math.round(random(1000,1400));
                              water.y=Math.round(random(rover.y,height-300));
                              jump.play();
                          }
                          if(rover.isTouching(atom)) {
                              score+=5;
                              score$=score;
                              atom.x=width+Math.round(random(299,499));
                              atom.y=Math.round(random(rover.y,height-300));
                              jump.play();
                          }
                          water.visible=true;
                          atom.visible=true;
                          break;
  }

Another technical design I am proud of is the collision detection algorithm/logic. The idea is to detect the collision between Mars Rover and other objects like valuable resources and hostile alien spaceships or rocks. I  came up with an algorithm to distinguish between these various collisions with different objects. Because if the Mars Rover collides with valuable resources the Score should increase and if it collides with the rock or alien spaceship it should end the game and display the restart button.

case "collection" : water.velocityX=-4;
                    atom.velocityX=-4;
                    if(rover.isTouching(water)) {
                        score+=10;
                        score$=score;
                        water.x=width+Math.round(random(1000,1400));
                        water.y=Math.round(random(rover.y,height-300));
                        jump.play();
                    }
                    if(rover.isTouching(atom)) {
                        score+=5;
                        score$=score;
                        atom.x=width+Math.round(random(299,499));
                        atom.y=Math.round(random(rover.y,height-300));
                        jump.play();
                    }
                    water.visible=true;
                    atom.visible=true;
                    break;

else if(touches[0].y<restart.position.y+50&&touches[0].y>restart.position.y-50) {
    if(touches[0].x<restart.position.x+50&&touches[0].x>restart.position.x-50) {
        state="start";
        rover.visible=true;

        water.x=width+Math.round(random(1000,1400));
        water.y=Math.round(random(rover.y,height-300));

        atom.x=width+Math.round(random(299,499));
        atom.y=Math.round(random(rover.y,height-300));

        score=0;

        switchPBtn.visible=false;
        restart.visible=false;
        ufo$.destroyEach();

        click.play();

        frameCount=0;
    }
}
if(water.x==0) {
           water.x=width+Math.round(random(1000,1400));
           water.y=Math.round(random(rover.y,height-300));
       }
if(atom.x==0) {
           atom.x=width+Math.round(random(299,499));
           atom.y=Math.round(random(rover.y,height-300));
       }

Good Game Design

I paid particular attention to my game design elements and UI interface to create the best possible user experience.

Uranium Atom Resource
Water Resouce

 

Mars Rock
Alien Spaceship

For the User Interface elements and buttons I utilized internet and created some of them using Photoshop tool.

Rules Button
Pause Button
Selection Circle

I am proud of my Pause functionality which not a lot of games of these type have. I think it is essential for the user to have an access to Pause button as he can be busy with other important matter and it would be pity to lose your progress just because of that. Code for Pause Logis:

else if(state=="pause") {

       sd=0;

       rover.velocityY=0;

       atom.velocityX=0;
       water.velocityX=0;
       stone.velocityX=0;
       ufo$.setVelocityXEach(0);

       textSize(20);
       textFont(font);
       fill(rgb(40,99, 235));
       text("Score- " + score$,width/2-30,100);

       restart.visible=true;
       switchPBtn.visible=true;
       switchBtn.visible=false;

       block[0].velocityX=0;
       block[1].velocityX=0;
       block[2].velocityX=0;
       block[3].velocityX=0;
       block[4].velocityX=0;

       block$.velocityXEach=0;
       block$.destroyEach();

       ufo$.velocityXEach=0;
       
       if(touches.length>0) {
           if(touches[0].y<switchPBtn.position.y+50&&touches[0].y>switchPBtn.position.y-50) {
               if(touches[0].x<switchPBtn.position.x+50&&touches[0].x>switchPBtn.position.x-50) {
                   switchPBtn.visible=!true;
                   switchBtn.visible=false;
                   restart.visible=false;

                   block[0].velocityX=-4;
                   block[1].velocityX=-4;
                   block[2].velocityX=-4;
                   block[3].velocityX=-4;
                   block[4].velocityX=-4;

                   click.play();

                   block$.velocityXEach=-4;

                   sd=2;
                   //alert("UP");
               }
           }
           else if(touches[0].y<restart.position.y+50&&touches[0].y>restart.position.y-50) {
               if(touches[0].x<restart.position.x+50&&touches[0].x>restart.position.x-50) {
                   state="start";
                   rover.visible=true;

                   water.x=width+Math.round(random(1000,1400));
                   water.y=Math.round(random(rover.y,height-300));

                   atom.x=width+Math.round(random(299,499));
                   atom.y=Math.round(random(rover.y,height-300));

                   score=0;

                   switchPBtn.visible=false;
                   restart.visible=false;
                   ufo$.destroyEach();

                   click.play();

                   frameCount=0;
               }
           }
       }

Potential Improvements and Challenges

Possible future improvements/ideas enhancing the game’s overall experience:

  • Power-ups can be added to the game to give the player temporary advantages such as invincibility or increased speed. This will make the game more engaging and provide players with a sense of accomplishment.
  • Multiple levels can be included in the game to provide players with different challenges as they progress. Each level can have its own unique obstacles and enemy UFOs, making the game more varied and exciting.
  • Including a high score system in the game can add a sense of competition, encouraging players to try and beat each other’s scores. This will also give players a reason to keep playing and trying to improve their skills.

Challenges

  •  Because I didn’t have any previous background in using sound and animation libraries I have spent quite some time figuring out ways of implementation and tested them extensively.
  • As mentioned in Midterm Progress post  for me the most frightening  or complex part of this midterm project was to implement a collision detection algorithm. I also have to somehow come up with an algorithm to distinguish between these various collisions with different objects. Because if the Mars Rover collides with valuable resources the Score should increase and if it collides with the rock or alien spaceship it should end the game and display the restart button. Luckily it was easier than I expected.
  • Putting different screen views into action and moving between them was a bit challenging for me. Particularly, finding my way back to the Main Page from The End screen without refreshing the sketch took some time to figure out. Eventually, I created a straightforward system that relied on flags to accomplish this.

 

Midterm Project – Pong (Blindspot Version)

Sketch

Sketch Link: https://editor.p5js.org/swostikpati/full/ShPga9v6_

Inspiration

As a lover of classic arcade games, I have always been fascinated by the simple yet addictive gameplay of Pong. Originally developed in the 1970s by Atari, Pong started as a simple slot machine game before becoming the first commercially successful video game. Its simplicity, accessibility, and fun factor quickly made it a favorite among gamers of all ages.

My inspiration for creating a Pong game on p5.js stemmed from my desire to learn more about game development and to challenge myself to create a game that was both simplistic, visually appealing, and fun to play. Even while doing so, I wanted to add my own creative touch to the game while still retaining its classic feel.

Concept

The concept of the project is simple – to implement the Pong game. But I had to add some unique elements to the game to make it a different experience from the existing pong video games. I thought of revving up the difficulty and consequently the adrenaline rush of the game by adding a “blindspot” in the middle of the screen where the ball disappears and continues to travel in the same trajectory until it appears again from the other side of the blindspot. As the players continue through each rally, the blindspot region keeps increasing and so does the speed of the moving ball making it more and more difficult for both players.

Implementation

The game implementation has two parts to it – the creative process and the technical implementation.

Creative Design

The creative design part of the implementation focuses on the visual aspects of the game, including the graphics, sound effects, animation, and user interface (color choices, font choices, etc).

The game’s colors are kept minimal with almost all the elements switching between absolute black and white.

There are three screens in total – the Start Screen, the Game Screen, and the End (Game Over) Screen.

The Start screen displays the rules of the game with the least bit of information possible as players generally skip the instructions. The controls are shown on a separate half of the page in order for the players to at least get the most essential information required (as the other parts can soon be figured out intuitively). This screen also features a ball moving around the screen just to give a feel of the game before the user starts playing.

The game screen has a background board with two rectangular paddles, one on each side of the screen, and a ball in the center. For the animation, I added smooth movement to the paddles and the ball to give the game a more polished feel. The ball moves in a straight line until it hits a paddle or wall (the top and bottom walls only), at which point it bounces off at an angle based on where it made contact. When the ball hits either the right or left wall, a point is scored, and the ball resets to the center of the screen. I have also made sure that at the start of every rally, the blindspot is temporarily disabled so that the players are able to view the ball at the center of the screen. As soon as the ball makes contact with a paddle, the blindspot is reactivated and the usual gameplay continues. I added a score tracker on the top of the screen for each player. The score is displayed in a large, bold font that is easy to read.

The final screen is the Game Over screen. This screen displays the player who won with a trophy. It also contains a button that gives the user the option to restart the game. The user doesn’t need to refresh the page for this.

I included several sound effects throughout the game to enrich the user experience. There were different sound effects used for a ball tap against the wall or a paddle, a point score, and game over. The fonts that were used at  different places were carefully thought about. The background images that have been imported into the sketch and used in different screens were designed by me on Canva.

The game was tested several times, with several paddle speeds, ball speeds, ball speed increments, blindspots and blindspot increments to make sure there were no glitches or bugs. All in all, every design decision throughout the game was carefully thought about to enhance the overall user experience of the game.

Technical Design

The technical implementation of the game included designing the switch between game screens, implementing the mechanics of the pong game itself with the enhancements as proposed that included paddle and ball movements, collision detection and response, adding sound effects at particular times, etc.

The entire code was written in an object-oriented fashion with classes of ball and paddles, and several functions were used to increase code reusability and modularity.

The code snippets below are used to implement important functionalities in the game.

Paddle Class

// Paddle class
class Paddle {
  constructor(x, y) {
    //     Stores the position of the paddles
    this.x = x;
    this.y = y;

    //     Flags to define whether the paddles are currently moving or not
    this.up = false;
    this.down = false;
  }

  //   displays the paddles(as rects) based on their x and y positions
  draw() {
    rectMode(CENTER);
    fill(255);
    noStroke();
    rect(this.x, this.y, paddleWidth, paddleHeight);
  }

  //   updates the position of paddles based on their movement
  update() {
    //     paddles move up or down by 10 units based on which key is clicked
    if (this.up) {
      this.y -= paddleSpeed;
    }
    if (this.down) {
      this.y += paddleSpeed;
    }
    this.y = constrain(this.y, paddleHeight / 2, height - paddleHeight / 2);
  }
}

Snippets from Ball Class

update() {
    //game over condition
    if (rightScore >= maxScore || leftScore >= maxScore) {
      gameWinSound.play();
      //       storing the winner
      if (rightScore == maxScore) {
        winner = 1; //Player A won
      } else {
        winner = 0; //Player B won
      }
      // print(winner);
      //       transitioning to the gameover screen
      screenView = 2;
    }

    //     updating current position of the ball
    this.x += this.speedX;
    this.y += this.speedY;

    // check for collisions with paddles
    if (
      this.x - ballSize / 2 <= paddleWidth &&
      this.y >= leftPaddle.y - paddleHeight / 2 &&
      this.y <= leftPaddle.y + paddleHeight / 2
    ) {
      //       plays collision sound
      ballHitSound.play();
      //       increments ball speed in a particular rally
      this.speedX = abs(this.speedX) + ballSpeedInr;
      //       increments the blindspot region - there is an upper limit set as well to prevent it from covering too much of the screen
      blindSpot = Math.min(blindSpotInr + blindSpot, maxBlindSpot);
    }
    
}

 

// check for scoring - one side
    if (this.x - ballSize / 2 <= 0) {
      //       sound effect when a player scores
      scoreSound.play();

      //       ball is brought back to the center of the screen
      this.x = width / 2;
      this.y = height / 2;

      //       speedX and speedY are reset to the original ball speed
      this.speedX = ballSpeed;
      this.speedY = ballSpeed;

      //       score of the corresponding player is incremented
      rightScore++;

      //       to switch off blindspot just till the game starts
      ballFlag = false;

      //       resetting the blindspot region
      blindSpot = 50;
    }

Screen Switching

function draw() {
  // Switches between game screens based on the variable screenView
  if (0 === screenView) {
    startScreen();
  }
  if (1 === screenView) {
    playScreen();
  }
  if (2 === screenView) {
    endScreen();
  }
}

Event Listeners

// listens for key presses to start motion of paddles
function keyPressed() {
  //   listens for up and down movements by player A using up down arrow keys
  if (keyCode === UP_ARROW) {
    rightPaddle.up = true;
  } else if (keyCode === DOWN_ARROW) {
    rightPaddle.down = true;
  }
  //   listening for up and down movements by player B using "W" and "S" keys
  else if (keyCode === 87) {
    leftPaddle.up = true;
  } else if (keyCode === 83) {
    leftPaddle.down = true;
  }
}

// listens for when the keys are released to stop motion of paddles
function keyReleased() {
  if (keyCode === UP_ARROW) {
    rightPaddle.up = false;
  } else if (keyCode === DOWN_ARROW) {
    rightPaddle.down = false;
  } else if (keyCode === 87) {
    leftPaddle.up = false;
  } else if (keyCode === 83) {
    leftPaddle.down = false;
  }
}

// listens for mouse clicks
function mouseClicked() {
  //   transitions from Start Screen to Game Screen and starts the game
  if (0 === screenView) {
    leftScore = 0;
    rightScore = 0;
    ball.x = width / 2;
    ball.y = height / 2;
    screenView = 1;
  }

  //   transitions from End Screen to Start Screen to help user to restart a game without reloading. This happens on clicking the button on the end screen
  if (
    2 === screenView &&
    mouseX > width / 2 - buttonWidth / 2 &&
    mouseX < width / 2 + buttonWidth / 2 &&
    mouseY > height - 100 - buttonHeight / 2 &&
    mouseY < height - 100 + buttonHeight / 2
  ) {
    screenView = 0;
  }
}

Key Challenges and Solutions

  • Implementing different screen views and navigating around them – It took some time for me to figure out the navigation across different screen views – especially returning back to the “Start Screen” from the “Game over” screen without refreshing the sketch. In the end, I implemented a simple flag-based mechanism to do so.
  • Identifying the paddle and ball speeds, increments, and blindspot regions – If even one among the paddles or the ball moved too slow or too fast, it would take away a lot from the overall user experience of the game. Even the increments in speed and the blindspot region had to be decided on carefully. The game had to be the right amount of “challenging” and stimulating for it to hook the users. Too easy and the users get bored. Too difficult and the users get frustrated. The game had to be the right mix of challenging and rewarding. This might feel like a simple thing of deciding a few numbers but it does have a huge impact on the overall gameplay and user experience. I spent quite some time playing around and extensively testing several mixes of numbers before arriving at the ones used in the final code.
  • Figuring out the Game Mechanics and the collision detection algorithm (the most frightening part) – I had talked about this in my previous post where I shared my progress in the midterm project. This was the most frightening part initially as the game was nothing without these two elements figured out and working perfectly.
  • Integrating more features while still keeping the minimalistic feel intact – It was a challenge to maintain the simple and minimalistic feel to the game intact and at the same time integrate more features into it. The balance was key as I wanted to preserve the game as it has been for ages and still make the experience a completely new one.

Potential Developments

  • Giving the option to the players to add their names in the Start screen rather than going by “Player A” and “Player B”.
  • Integrating a database to store high scores and user accounts. This would allow me to also have a leaderboard in the end that could be fetched based on the data from the database.
  • Adding more elements to the game including game animations during scoring and powerups like freezing the opponent’s paddle for a while or combo points for a rally, etc.
  • Making the blindspot region generate at any random position throughout the screen and making it move will add a new layer of challenge to the game.

Reflections

Throughout the development of this project, I have learned a lot about the process of game design and the intricacies that go into creating a simple yet engaging game. I had to experiment with different approaches and techniques to achieve the desired functionality. I was also able to create an algorithm that worked effectively and provided a satisfying gameplay experience. I made sure to focus both on the technical implementation and the creative design process of developing the game. The experience has been both challenging and rewarding, and I am proud of the final product that I have created.

Screens

Start Screen
Game Screen
Game Over Screen

Midterm Project – Zen Garden

Welcome to Zen Garden, a slow-paced experience where you are fully in charge of planting and growing each plant in the garden. Take a deep breath, enjoy the relaxing music, make butterfly friends and take pride in growing a beautiful garden.

Enjoy the experience for yourself here.

The Concept:

I wanted to create an experience rather than a game, since there is no winning or losing. That goes well with the intention of it being a relaxing experience, one which I and many of my peers need during weeks such as the midterms week. I wanted to create an experience where the user has control of what they plant, where they plant it, how many or few plants they want and at what stages they want to leave the plants at. The user controls how much the plants grow – with there being a final fully grown stage (which the user will be made aware by a celebratory sound).

Implementation:

I mainly used Object Oriented Programming to create this game, with my classes being Plant, Water and Butterfly. Within the Plant class are the different stages of a plant, the Water class is used to create individual droplets of water for watering the plant – and it utilizes a gravity() function to get them moving, and the Butterfly class has sprites and keeps track of where in the screen it is.

As for the other code, I had a few clever implementations which needed to be changed to simpler ones to optimize the code. I have a lot of get() functions and even more display() functions, since each item has its own methods for both of these.

In terms of user interaction, I only utilized mouse pressing and clicking. For all of the gameplay I used mouse pressing – regardless of if the user was pressing and holding (such as when they pick up the seed and hold onto it until they get to the planter), I just found that it worked better that way, and I only started utilizing mouse clicks to add sounds (which came at the end of my coding journey).

I use a lot of images for the graphics, but there are things which are drawn on P5, such as the play button, the clock and the white rectangles.

For the planters, I determined their locations and based on just coordinates I determine how many planters there are and where they are – I keep track of these using arrays. The graphics themselves are just a singular image for the whole thing – I initially coded in each block using loops but decided to optimize the code and removed the loops.

Challenges:

A lot of the challenges I faced was with how heavy my program was getting. I found myself needing to optimize my code and remove any loops which could somehow be avoided (such as the individual plots of land being replaced by one image).

The program is unfortunately still quite heavy because I wanted to utilize so many images and clicks, but other than the computer getting loud, it seems there are no problems reflected on the experience itself. I see it as a good compromise to have an aesthetically pleasing program with a lot of engaging sounds.

Another challenge which I realized too late is that the dimensions of the canvas might be a bit small to look at. A bandaid solution I would recommend if the dimensions bother you is to just zoom in on your browser – since the game graphics are intentionally pixelated, the quality will not be compromised much.

In terms of coding, it was quite challenging balancing events which happen at the same time. The draw() function can be quite tricky, since it loops so fast, I found myself jumping between the draw() function and other functions and utilizing a lot of boolean variables to set conditions and different states. After a lot of trial and error, as well as debugging (my most common line before cleaning up my code was probably console.log()), it all works at the end!

The Outer Experience

Click  “The Outer Experience Here” to experience in fullscreen.

The central concept of this project was derived from galaxy features(rotation about a center point, rotation about an object’s axis, etc.).

The main goal is to achieve an experience for anyone to experience how space feels like. The project includes certain extraterrestrial bodies such as the sun, Jupiter and its moons, and other creative gas liked spherical structures.

How it Works

The project works by moving around using the mouse together with W,S,A,D  keys on the user front. Technically, this is achieved by implementing the cam feature in p5.js with sophisticated math only @ “morejpeg” on youtube could help me with.

The project also features Jupiter and its moons, the sun, and other spherical galaxy planet-liked structures. These implementations were made possible using “sphere,” “rotate,” “texture,” and “translate” keywords in p5.js. The technical achievement I am most proud of is randomly distributing star-liked structures in space.

Also, I think the decision to create a big sphere to house all the stars and other planetary bodies was a smart decision. The choice of music also makes the experience a calming one I believe.

Problems

Generally, working in 3D involves so much math and analysis, and that took an enormous amount of my time. Thus, the main problem I encountered was using math to implement the camera in 3D.

That aside, randomly distributing stars in the void was a challenging task because they constantly kept randomly rotating in the void. Finding the exact position to place a text for User control proved difficult because I simply couldn’t locate the default camera location in the void.

 

References

morejpeg, (2023, Jan)”Coding First Person Camera Controls in 10 Minutes.” Youtube:https://youtu.be/0b9WPrc0H2w