Week 6: Pong² (Midterm Project)

Overall Concept:

This is Pong². It’s called Pong² because this isn’t just a spiritual successor to the first ever video game, it’s a dimension beyond. If regular pong only moved you in one axis, it only has one dimension; this game lets you move left to right AND back and forth– creating a very amusing experience with much more depth than the original.

Pong² features two modes. Let’s start with Versus. Versus is a continuation of what I worked on all the way back in Week 3. In Versus, you and another player play a 1 v 1 duel of up to 9 rounds, best of 5. It’s the same win conditions as classic pong but with a lot more strategic depth. Moving your paddle in the opposite direction towards the ball will “smash” or “spike” it into your opponent’s side while moving in the same direction as you receive the ball will help you “control” or “trap” it. 

Pong²’s second mode is its best: co-op. In Co-Op mode, you and another player work together to guard the bottom goal against multiple balls that speed up with every return. You have 3 lives and each ball that slips past you takes a life from your shared life total. You have to coordinate with your teammate to make sure you keep a watch on every ball and not just respond as fast as you can to each one, because that’s only going to take you so far (my bet is on 50 seconds).

 

Feedback: Color Scheme Consistency

On our Monday checkup, Professor Ang told me to have consistent colors for highlighting the gameplay instructions; when I use different colors to highlight I risk player confusion. I also proceeded to keep the game mode colors consistent with the menu instead of having the menu remain exclusively white and purple.

CO-OP Mode Design

I originally planned to make a player vs AI mode but realized that I really didn’t know how to make it respond similarly to how a real player would move the paddle. I had received feedback from Professor Ang to make the concept more interesting than just the usual 1v1 pong, and that’s when it hit me: what if I made 2 Player survival pong?

I had a lot of fun designing the co-op mode on paper, but I made my code a huge mess by duplicating my versus game mode javascript file instead of working from scratch. I THOUGHT that would make the process faster since I would be working with my previous framework but I ended up having to modify so many things it ended up convoluting it.

I originally had the game start with just one ball and you wouldn’t get the second ball until the 15th hit on the top side; however,I realized I wanted players to naturally have the idea to coordinate with each other. For instance, player one might tell his teammate “I’ll take the red ball you take the blue one” to strategize. So what I decided was to make the 15th hit spawn the third ball and the 2nd  top bounce spawn the second ball which presents the challenge to players at a good pace.

I was very proud of this for loop I built to manage the many colorful balls that you had to defend against. Between this week and last week, I also had to add a canHitPaddle() method and timer to make sure you wouldn’t accidentally double hit the same ball as you tried to move in the same direction as it.

for (let b of coopBalls){ //runs thru all the balls stored in the array
    b.display();
    b.move();
    b.scoreDetection();
    
    if (coopBallCollidePaddle(b, player1paddle1, "Top")&& b.canHitPaddle()) {
      paddleBounceNeutralSFX.play();
      b.lastHitTime = millis() //records hit time using canHitPaddle()
      b.ballYspeed *= -1;
      b.yPos = player1paddle1.yPos - player1paddle1.paddleHeight / 2 - b.radius - 5;
      
      if (keyIsDown(83)) { // 'S'key 
        paddleBounceControlSFX.play();
        b.ballYspeed *= COOPbackControlFactor;
      } else {
        paddleBounceNeutralSFX.play();
      }
    }

    if (coopBallCollidePaddle(b, player2paddle1, "Top")&& b.canHitPaddle()) {
      paddleBounceNeutralSFX.play();
      b.lastHitTime = millis()//records hit time using canHitPaddle()
      b.ballYspeed *= -1; 
      b.yPos = player2paddle1.yPos - player2paddle1.paddleHeight / 2 - b.radius - 5; //the 5 helps make a microadjustment to prevent funky collisions
      
      if (keyIsDown(DOWN_ARROW) || keyIsDown(75)) { // '↓' or 'K' key 
        paddleBounceControlSFX.play();
        b.ballYspeed *= COOPbackControlFactor;
      } else {
        paddleBounceNeutralSFX.play();
      }
      
    }
    
  }//closes for b loop  

I think the co-op mode overall turned out very well. It was surprisingly challenging but also a good amount of fun for the friends that I asked to test it.

 

VERSUS Mode Improvements

I had the idea to add direction input with forward and backward velocity. Basically say you’re on the bottom side, if you receive the ball with “down arrow” then you slow it down because you pressed the same key the ball was headed, but if you received it with the “Up arrow” key you would add momentum to it.

I had so much fun implementing this layer of complexity to the game. A main criticism I received at first was that the extra dimension of movement for the paddles didn’t offer a significant difference to the usual pong gameplay loop. 

This was my response to that– adding a much needed strategic depth to the game. You weren’t just competing on who was better at defense now, you were seeing who had the better attack strategy.

In fact I originally didn’t plan to add this forward and backward velocity effect to the co-op mode but I loved how it felt so much that I knew I had to add it in some way. This posed a balancing challenge as well; if we were to keep the backControlFactor from the versus mode then it would prove far too easy. 

 

Sound Design

I originally wanted to implement the sounds into my versus mode but then I realized building the infrastructure for all the menus first would make my life a lot easier. So sound design ended up as the last thing on the to-do list.

I usually got sound effects from silly button websites when I needed to edit videos so I never knew where to get actual licensed SFX for indie games. I eventually found one called pixabay that had a good list of sound effects.

Most of the menu button SFX and game over SFX were pretty straightforward, but I really wanted the SFX for the gameplay loop itself to sound very satisfying. My favorite ones were definitely the ball control SFX and the countdown SFX. For the ball control SFX, I was inspired by the sound design in the Japanese anime Blue Lock– which has some incredible sound effects like the bass-y effect when one of the characters controls the ball midair. I found this super nice sounding bass sound and that’s how the idea of using acoustic instrument sounds stuck. The other two ended up being a snare and a kickdrum sound.

 

Difficulties Endured

The Great Screen Changing Mystery

There was this incredibly confusing bug that I never truly figured out but the gist was that it would randomly switch screens specifically to the versus game mode SOMETIMES when I clicked into Co-Op. 

I dug and I dug through the code endlessly until I saw that I left one of the screen-related if-statements to give an else-statement that defaulted the screen to versus. This was here for earlier tests but this seriously still doesn’t make any sense to me. There was never a call to the function that would switch to versus and there was nothing that would leave the screen state invalid for any of the available screens to take over.

This was a frustrating gap in my knowledge that I never fully understood, but I did fix it.

 

Hit “K” to Let the Ball Through (and look stupid)

For some reason, the bottom paddle kept letting the ball straight through whenever I blocked it while holding “K” or the down arrow. This took hours of looking for the problem, going “I’ll deal with that later” and then running into it again and wondering what the heck is causing this?

It was very specifically the bottom paddle and it HAD to be related to backward movement input detection. Then I realized that it was ONLY the bottom paddle because I had set the ball speed to minSpeed, which is always positive. So all I had to do was to convert it to a negative value equivalent to minSpeed.

if (keyIsDown(UP_ARROW) || keyIsDown(73)) { // '↑' or 'I' key 
  paddleBounceSmashSFX.play();
  ballforVS.ballYspeed *= fwdVelocityFactor;
} else if (keyIsDown(DOWN_ARROW) || keyIsDown(75)) { // '↓' or 'K' key 
  paddleBounceControlSFX.play();
  ballforVS.ballXspeed = minSpeed;
  ballforVS.ballYspeed = minSpeed;
} else {
  paddleBounceNeutralSFX.play();
}
Cursor Changing

When I was developing the buttons, I realized that they didn’t really feel clickable. So I wanted to add some code to change the cursor to the hand whenever it hovered over a button. This proved surprisingly complicated for what is seemingly an easy task. The most efficient way I found was to append all the buttons to an array for each menu and have it checked in a for loop everytime– then closing off with a simple if-statement.

function updateCursorFor(buttonArray) {
  let hovering = false;
  
  for (let btn of buttonArray) {
    if (btn.isHovered(mouseX, mouseY)){
      hovering = true;
    } 
  }
  if (hovering) {
    cursor(HAND);
  } else {
    cursor(ARROW);
  }
}
Conclusion & Areas for Improvement

As for areas of improvement, I would probably make the ball-paddle collision logic even more stable and consistent. It usually behaves predictably and as intended but it sometimes shows the way I coded it has a lot of flaws with colliding on non-intended surfaces.

However, despite all this, I am really proud of my project– both from the UI design standpoint and the replayability of the gameplay loop. Once my other friends are done with their midterms I’m gonna have a much better opportunity to see flaws in the design.

 

 

Week 5: Pong² (Midterm Progress Report)

Concept:

Finding the concept for this project was rather difficult– I initially thought about creating a rhythm game that would use the WASD keys and the arrow keys similar to a 2D Beat Saber where the direction you cut mattered as much as your timing. I was inspired by Mandy’s project from Fall 2024 where she made a two player dance game where you have to input a specific pattern of keys in combination with your partner. I thought her project had a lot of charm to it; however, I imagine syncing the rhythm to the onscreen inputs would prove challenging so I scrapped that idea early on.

Then I revisited the project I made for week 3 where we had to use loops. Back then I just wanted to follow Professor Aya’s advice (when she visited on Monday of week 3) and use object-oriented programming to add manually controlled collision physics to the project I made for week 2. That’s how I accidentally made Pong but this week I considered seriously turning it into a fully-functioning project.

I realized that my code was going to be very messy and a long scroll from top to bottom if I kept it all in one .js file, so I looked up how to split up the files. After I created new js files I realized that setup() and draw() could only be run in the main sketch.js file so I had to find a way to work around that for the gameLogic js file which is just an imported week 3 js file. I basically made new functions and named them initiateGame() and initiateMenu() and called them in the main sketch.js setup() function; I also had to add two lines of code into the html file to have the project access these new js files.

<script src="menu.js"></script>
<script src="gameLogic.js"></script>

Updating the “PING” Game

Besides the obvious requirements to add a menu screen and a restart button, there were plenty of personal touches I wanted to add to what I made two weeks ago.

The first of which was to implement a more visually appealing score tracking system. Last time I had the game continuously repeat so I made a very minimalistic but number-based scoring system to fit with the rest of the minimalist aesthetic. Since I was adding a set number of rounds now, I wanted to use a more interesting way of representing each point. There would be a rectangle in the middle that would either flick upwards or downwards slightly based on which side took the point (kind of like a lightswitch in a way).

The next idea was to have the ability for an options menu to change the in-game volume mixer and maybe even keybinds.

Since there was also the need to reset the game without restarting the whole sketch now, I made sure to add an ESC function where you can back out to the menu from the game and also subsequently reset the scores and start a new game the next time the user clicks the button to enter the game.

//RESET SCORE
function resetGame(){ //resets the score
  roundNumber = 0;
  topScore = 0;
  topCounter = [];
  bottomScore = 0;
  bottomCounter = [];
}
...
function menuMousePressed(){
  twoPlayerButton.handleClick(mouseX, mouseY);
  resetGame();
}

I also made each button with its own class while passing functions as parameters to call when clicked. I have only test one button so far– the twoPlayerButton that leads you into the normal mode – but it works great and I’ve “scaffolded” an area for more buttons to be added the same way.

allMenuButtons.push(
  twoPlayerButton = new button(
    tempButton, //image 
    width/2 + 120, //xPos
    height/2, //yPos
    tempButton.width, //sets to uploaded file dimensions
    tempButton.height, 
    switchToGame //which function to call if clicked?
  )
);

User Interface Planning

To plan out the UI, I quickly made a Canva file that matched the dimensions of my project and made a quick sketch of what I wanted the menu to look like. I’m going for a rather

This is also how I came up with a better name for the game: Pong2  

It’s not quite Pong 2, but since the paddles are no longer restricted to one dimension along each edge and now have true 2D movement, I wanted to call it something representative of that.

For the font choice I chose Gotham for its geometric feel. Since p5js didn’t have access to Gotham, I downloaded an otf file online and placed it in my sketch folder.

For the final version of the sketch,  I want to add a faint bouncing ball in the background to make the menu screen feel more dynamic and alive too.

A detail I was quite proud about was setting up a hover checker that would turn my cursor into a hand if I was hovering over something that was clickable.

for (let btn of allMenuButtons) { 
  btn.display(); //displays the button
  
  if (btn.isHovered(mouseX, mouseY)) {
    hovering = true; //check if hovering any buttons
  }
}

if (hovering) {
  cursor(HAND);
} else {
  cursor(ARROW);
}
Identify the Most Frightening Aspect: 

The most frightening part of this project is most certainly the one player mode I want to make where you can play against an AI. Theoretically it’s just some simple math to have it sort of predict the ball’s trajectory but I imagine it would consume a considerable amount of effort to figure out how that would work.

I might drop this aspect altogether if it’s too challenging to make before the deadline but I really would like to make it work.

 

 

Week 5 – Reading Reflection

The moment I started reading the article I immediately recognized Myron Krueger’s Videoplace from my first week in Understanding IM; I remember it because Professor Shiloh explained that Kreuger was actually manually adjusting the project in the background but making it appear to audiences like an automatic feedback loop. At the time, only computer specialists and engineers had access to complex computer vision technologies; this story is a reminder to me that the development tools that we now take for granted have only become accessible to the majority of the population in the past decade.

How does computer vision differ from human vision?
In the simplest sense, I believe computer vision lacks perspective and has an innate lack of context. Where humans lack in high processing speeds, they make up for in their innate flexible perception of reality of what is in front of them. They ask questions or make comparisons to what may not necessarily be the objectively closest comparison.

When it comes to perspective in AI– artificial intelligence didn’t grow up with an innate curiosity about the world no matter how many “Hello, World!”s it says. A human can look at a boy and a girl who always hang out together and assume romantic context but an AI wouldn’t know that innately; that’s probably why the trope of AI learning human emotions from watching their movies and media is such a common one in our fiction pieces.

Techniques to help the computer see / track what we’re interested in?
I believe the article mentions using bright lighting or at least high contrast backgrounds. However, I’m sure that image training is also very important in today’s computer vision.

Effect of tracking & surveillance in interactive art
I remember when I got my Xbox 360 as a kid and got the Kinect system bundled alongside it. It was such a revolutionary technology back then and now we can recreate the same thing on the software side with just a webcam on p5js! That is incredibly impressive to me.

I never even considered computer vision in surveillance until I read the piece on Suicide Box, which recorded real tragedies of people taking their lives at the Golden Gate bridge. What surprised me is how port authorities counted thirteen in the initial hundred days of deployment whereas the suicide box with its computer vision recorded seventeen. That’s four human lives that were tragically lost and possibly forgotten.

 

 

Week 4: Passing Moments (Data Visualization)

(higher amplitude = longer sleep; larger frequency = longer lifespan)

Conception

In this project I wanted to demonstrate the idea of passing moments and how your total lifespan affects how fast you may perceive each moment. Since I love animals, the project started with me just wanting to play around with some data regarding animals; however, I was struggling to find something interesting.

At first I had some data with brain mass and wanted to compare each animal’s brain mass to body mass ratio using circles but it ended up looking very bland so I completely scrapped that sketch.

Then I looked around online for some more databases until I found one that had both lifespan and sleep durations. I thought I could perhaps make something to visualize the impermanence of life and demonstrate the idea of “passing moments.” You could say it’s a bit of a memento mori in a way for both you and the animal mentioned on screen.

Development

I was thinking of how I could represent lifespan and I thought about the heart rate monitors beside hospital beds. I thought perhaps I could use sin waves to represent life span and have the amplitude and frequency be scaled with different things, so that’s what I went with.

I matched the frequency with their life span 

let frequency = map(chosenMammal.lifeSpan, 2, 80, 0.02, 0.08);
...
sin(x*frequency - frameCount * 0.1)

Then I matched the amplitude with hours of sleep per day

let sleepHours = chosenMammal.totalSleep;
let ampMult = 8;
...
let y = height/2 + sin(x*frequency - frameCount * 0.1) * sleepHours * ampMult;

Together, I was able to use the beginShape() and endShape() functions to create a continuous animated sine wave for the mammal. However, that felt like it lacked a frame of reference so I added another wave behind it in gray to represent humans. This way, it could really put into perspective just how fast each moment for us vs the chosen mammal was. I was quite proud of how this part turned out.

function drawSleepSpiral(chosenMammal, color){
  let sleepHours = chosenMammal.totalSleep;
  let ampMult = 8;
  let frequency = map(chosenMammal.lifeSpan, 2, 80, 0.02, 0.08);
  
  push();
  noFill();
  
  if (chosenMammal == allMammals[21]){ //21 is human
    stroke(200);
  } else{
    stroke(color);
  }
  
  strokeWeight(6);
  beginShape();
    for (let x = 0; x < width; x+=1){
      let y = height/2 + sin(x*frequency - frameCount * 0.1) * sleepHours * ampMult;
      vertex(x,y);
    }
  endShape();
  pop();
}

I wasn’t happy with how the default font looked so I loaded in a very beloved font, Helvetica, into the project with what we learned last week.

textFont("Helvetica");
textAlign(CENTER, CENTER);

I was also thinking of adding a text input for the user to put in how many hours they sleep and compare that to the mammal onscreen but I felt like that took away from the idea of lifespan and sounded more like I wanted the user to acknowledge how healthy their sleep schedule was, which I thought would greatly detract from the memento mori effect I was aiming for.

Lastly I added a mousePressed function to cycle through the database without having to hit the start button each time.

function mousePressed(){
  chosenMammal = random(allMammals); // switch to a new mammal on click
  randomColor = random(colorArray); //changes to a random color with the same click
}
Conclusion / Difficulties Endured

I didn’t even realize I could upload my own files and create folders in the sketch files until this past week so this production piece provided me with a lot more insight into what p5js is capable of.

I initially had some trouble figuring out how to get the data out from the CSV file in a natural and efficient way but then I remembered I could extract each column data as a separate characteristic of my object like this:

for (let i = 0; i < table.getRowCount(); i++) {
  let mammalObject = {
    species: table.getString(i, "Species of animal"),
    bodyMass: table.getNum(i, "Body Weight (kg)"),
    brainMass: table.getNum(i, "Brain Weight (g)"),
    totalSleep: table.getNum(i, "Total sleep (hrs/day)"),
    lifeSpan: table.getNum(i, "Maximum life span (years)")
  };
  allMammals.push(mammalObject) //now i can obtain mammalObject.bodyMass for instance
}//closes for() after appending all mammals

This made the process of finding specific attributes of the chosen mammal so much more convenient and efficient.

I also made sure credit the source of my database at the very end of my code: https://gist.github.com/yppmark/d907dc265a84cac76ba7

Week 4 – Reading Reflection

Reading the first page of chapter one, I almost thought I was doing a reading on wayfinding–essentially the study of how we navigate our environment. Afterall, the push-pull dilemma of a door is pretty much universal regardless of what background and culture you come from. However, this chapter would continue to prove much more broad and discuss real world UX beyond putting handles on one-way push doors.

Norman comes from a background in engineering, so naturally interacting with machinery is something he has a vast perspective on. Let’s talk about where Norman says “When people fail to follow these bizarre, secret rules, and the machine does the wrong thing, its operators are blamed for not understanding the machine, for not following its rigid specifications.” In Crawford’s reading last week, he compared interaction to having a conversation, which I agree with for the most part. However, I do find it interesting that when a machine fails on us we understand the burden of successful “communication” is undoubtedly on us. This takes me away from the conversation analogy as there is always realistically no one solely at fault for a misunderstanding between two humans.

One of my favorite points of the reading is that we should strive for human-centered design(HCD). As Norman says, “Good design starts with an understanding of psychology and technology.” Realistically, how can there be design without a greater understanding of human cognition? For example, we all know what an arrow has a side it’s pointing at– the way its edges align to a sharp point naturally makes our brain look at the sharp point and what follows it.

That transitions nicely to how this applies to us interactive media majors. When it comes to applying Norman’s principles of design to interactive media, I believe we need to not only invite both like-minded and nonlike-minded friends to test our projects, but consider all their perspectives equally. As Norman says, “Engineers are trained to think logically. As a result, they come to believe that all people must think this way, and they design their machines accordingly.” We ideally should engage those who aren’t also “engineers.”

As for something that drives me crazy and how it could be improved, I really want to rework certain parts of highline pathing. There are certain areas especially near the staircases down to the ground floor that have such poor pathing. You would need to either walk over the grass or detour by a good 5 meters just to follow the intended path rather than your desired path. This semester they even added a bush on the grass area to block you from cutting through the grass– talk about remedial design…

Week 3: PING (Objects & Arrays)

(up to two players! WASD for top side and IJKL/arrow keys for bottom side)

I thought about how I wanted to build upon what I had last week and I arrived at the idea of using something to interact with the bouncing ball mechanic. At first, I thought about making an air hockey game where you could drag the paddle but I didn’t know how to create an opponent that could respond to what you do. And it couldn’t be multiplayer since there’s only one mouse, which led me to using keyboard keys to control the paddles.

I began to build two separate objects that can be controlled by wasd and arrow keys respectively– a lot like the old web games I would secretly play in my computer science class with my classmates back in elementary school.

Using Classes and this.

Last week when I presented my project to professor Aya, she suggested that I learn how to use class so I could make it easier on myself to organize my code and objects. So this week I delved into classes and have a pretty good grasp on it now. Here are two classes I made– the ball and the top paddle.

class ball { //class creates a re usable template: let ball1 = new ball(x,y,r)
  constructor(xPos, yPos, radius){ //creates the initial state
    //this referes to the object currently being created
    this.xPos = xPos;
    this.yPos = yPos;
    this.radius = radius;
    
    this.ballXspeed = ballXspeed;
    this.ballYspeed = ballYspeed;
    this.ballMoving = false; //boolean to help reset ball
    
  }
  move() { //this is a method, a function that belongs to just one object
    if (!this.ballMoving) return; //if ball is still then do nothing
    
    this.xPos += constrain(ball1.ballXspeed, -maxSpeed, maxSpeed)
    this.yPos += constrain(ball1.ballYspeed, -maxSpeed, maxSpeed)
    
    //BOUNCE: CHECKS LEFT AND RIGHT EDGE
    if (this.xPos > width - this.radius || this.xPos < 0 + this.radius) { 
      this.ballXspeed *= -1;
    }  
  }
  
  reset(){
    this.xPos = width/2;
    this.yPos = height/2;
    this.ballMoving = false;
    
    //setTimeout(function, delay) delay is in milliseconds
    setTimeout(() => { //the arrow =>
      this.ballXspeed = random([-ballXspeed, ballXspeed]);
      this.ballYspeed = random([-ballYspeed, ballYspeed]);
      this.ballMoving = true;
    }, 2000);
  }
  
  scoreDetection(){
    //CHECKS TOP AND BOTTOM EDGES
    if (this.yPos > height - this.radius) {
      adjustScore(1,0); //top side scores 1
      this.reset();
    }
    if (this.yPos < 0 + this.radius) {
      adjustScore(0,1); //bottom side scores 1
      this.reset();
    }
  }//closes score detection
  
  display(){
    ellipse(this.xPos, this.yPos, this.radius *2);
  }
} //ends class ball
class topPaddle {
  constructor (xPos, yPos, paddleWidth, paddleHeight, xSpeed, ySpeed){
    this.xPos = xPos;
    this.yPos = yPos;
    this.baseY = yPos; //initial yPos 
    this.paddleWidth = paddleWidth;
    this.paddleHeight = paddleHeight;
    this.xSpeed = xSpeed;
    this.ySpeed = ySpeed;
  }
  
  display(){
    fill(255)
    rect(this.xPos,this.yPos,this.paddleWidth,this.paddleHeight);
  }
  
  move(){
    /* Keycodes
    W = 87
    A = 65
    S = 83
    D = 68
    */
    if (keyIsDown(87)){ //TOP
      this.yPos += this.ySpeed * -1; //negative because 0 counts down
    }
    if (keyIsDown(65)){ //LEFT
      this.xPos += this.xSpeed * -1;
    }
    if (keyIsDown(83)){ //BOTTOM
      this.yPos += this.ySpeed ;
    }
    if (keyIsDown(68)){ 
      this.xPos += this.xSpeed;
    }

    //RESTRICTIONS 
    this.xPos = constrain(this.xPos, xBoundary, width-xBoundary)
    this.yPos = constrain(this.yPos, this.baseY-yBoundary, this.baseY+yBoundary)
    
  } //closes move()
}

I really enjoyed using methods to help organize my code and operate on an object-oriented basis. 

 

Not too far into writing the classes for the paddles, I realized I was recreating the game pong. This is literally how pong was made– someone wanted to test bouncy ball physics and moving paddle collision and made pong.

 

Implementing Arrays

I already experimented with arrays last week when dealing with for() loops so this week I wanted to use them in a different way: to track scores for each side. I made two arrays– one called topCounter and one called bottomCounter. If the top side won round 1, they would have 1 added to their array and have it console.log’d into the console.

 

Designing with Variables

Since we learned push() and pop() in class last week, I’ve been wanting to use these techniques to make more organized stroke() and strokeWeight() functions. 

 

Beyond that, I used a lot of variables to help with the numbers so changing the variable number in the top of the code would change all of the related values throughout the code conveniently and often symmetrically. For instance, I would get the midpoints between the two ends of the lines to have the score number displayed there.

//LEFT CENTER LINE
    line(
      0,height/2, 
      0+sideLineMargin,height/2
    )
    //CENTER LINE
    line(
      (width/2)-centerLineMargin,height/2, 
      (width/2)+centerLineMargin,height/2
    )

//lots of code inbetween

// TOP SIDE SCORE
    text(topScore, 
        ((width/2)-centerLineMargin + 0+sideLineMargin)/2,
        height/2
        )

I also found the rectMode() and textAllign() functions to be incredibly helpful.

 

Adjust Gameplay Parameters

Initially, the back and forth between the two players felt very predictable and un-engaging. It was slow and ran at a consistent speed, which also meant there was only so many things you could do to change the trajectory of the bounce.

 

So I did two things, I made the paddles have way more vertical movement than it did initially, and I added a speed multiplier that really shaked the game at its core. 

 

Every time a player would collide their paddle with the ball now, the ball would speed up by 4%, which is seemingly small, but builds up very quickly to overwhelm players.

//BALL ATTRIBUTE
let ballXspeed = 6;
let ballYspeed = 5;
let speedUpFactor = 1.04; //105% each time it collides with paddle
let maxSpeed = 14; //limits the speed of the ball

//lots of code inbetween

if (ballCollidePaddle(ball1,topPaddle1,"Bottom")){
    ball1.ballYspeed *= -1;
    ball1.ballXspeed *= speedUpFactor;
    ball1.ballYspeed *= speedUpFactor;
}

However, it would get to a point where p5js wouldn’t really render it at a high enough fps for it to be fair to the players, so I added a max speed cap of 14-ish into the move() method for the ball to make sure it would be fast but not too the point of absurdity.

 

Difficulties Endured

 

Thankfully p5js didn’t crash half a dozen times during the development like it did last week, but I had some technical struggles.

 

When I built the second paddle object in the bottom, I completely forgot to constrain the y-value of the paddle which created quite a humorous moment when my girlfriend wanted to test the paddle movement with me.

 

The math behind colliding a circle with a rectangle was pretty confusing to figure out, but I also didn’t want to use circles as paddles so I tried a lot of methods until I made it work by checking the x-edges overlapping.

let xOverlap = 
    (ball.xPos + ball.radius > paddle.xPos - paddle.paddleWidth/2) &&
    (ball.xPos - ball.radius < paddle.xPos + paddle.paddleWidth/2);
  
  if (!xOverlap) return false; //checks if ball collided with paddle early

When I tried to embed it onto wordpress, the arrow keys kept trying to scroll the embed for me so I had to add another set of controls on IJKL.

Gameplay Strategy & Technique

In testing my friends and I found a few strategies and techniques that help with winning rounds. One of the strategies is to back up as the ball approaches max speed; this really helps by giving you more time to react and save the ball. 

 

Another technique we accidentally found out was double-tapping the ball to have it gain the speed multiplier several times on the same receive.

 

My friends and I had jolly fun playing against each other even if they thought the game was incredibly difficult.

 

Conclusion

I had a lot of fun making this project and learning how to use more tools in JavaScript. Making classes felt like an essential part of JS coding that I finally understood today. I can really feel my progress growing every week with every assignment.

 

I really hope I can repurpose the work I did in this project sometime in the future or even just play it with my friends whenever I open my p5js account.

Week 3 – Reading Reflection

I was very surprised by the opening sentence of this week’s reading as I had an Understanding IM class with Professor Shiloh just this Thursday discussing how the word “interactivity” loses so much meaning when applied to works that are at most immersive and not at all actually interactive.

“Here’s a key point about the interactive process: There are two actors, not one” was a quote from page 5 that I noted down. Some of my fellow classmates in UIM defended the popular claim that the Mona Lisa is interactive because her eyes “follow you” as you admire the artwork; they defended it because they themselves feel the magic of their favorite paintings. However, I think interactivity as a term often gets confused with “immersion” yet seems to almost be used interchangeably for so many people.

Another thing I noted down from the reading was “We tend to think of interactivity as a Boolean like virginity. But why not think of interactivity as a continuous variable with relative measures?” Although the phrasing of this comparison was a little bizarre on my first read, I actually found myself agreeing with it a lot. Crawford’s three emphasis on three characteristics of Listening, Thinking, and Speaking may naturally apply to human conversation more, but could apply to our artworks as well.

I really liked this next section on the thirteenth page– where Crawford essentially provokes the reader on the topic of books as an interactive medium. Crawford says that if you think books should be considered an interactive medium then say the word, although he’s possibly thousands of miles away and unable to ever perceive your opinion, oops! “…if you’re frustrated, you are welcome to throw this across the room, but even then, there still won’t be anybody listening to your frustrations or thinking about them.”

Overall, I thought Crawford had some good points with great ways of expressing how frustrating the nothingness burger the word “interactivity” has become and will become. And to answer his question listed at the end– “Are rugs interactive? Explain in your own words why or why not?” – I believe that rugs fall near low-interactivity at best on the interactivity spectrum. This is because some party games, such as twister, could use a rug with complex designs to form a set of rules and a gameplay loop.

In response to “What do you consider to be the characteristics of a strongly interactive system? What ideas do you have for improving the degree of user interaction in your p5 sketches?”, I think the strongest characteristic of an interactive system is a feedback loop that rewards curiosity and engagement. As for ideas for improving the degree of user interaction in my sketches, I believe variability is vitally important to the repeatability of an experience. 10 people could interact with the same interactive sketch and each of them would experience something noticeably different.

Week 2: Corner Contact (Production)

 

(You can wait until it hits the corner naturally or hold Mouse1 to Speed it Up)

Before what I have here, this project started with me really curious to experiment with 3D objects, but the WEBGL system got terribly confusing with all sorts of new functions and ways I had to approach drawing shapes. I gave up on that and decided to first focus on 2D for this assignment.

I had a real tough time coming up with an idea after that. I thought about it over the weekend and I landed on the idea that I wanted whatever I made to feel satisfying, so I started thinking of things that are visually satisfying. This reminded me of a YouTube short I recently saw about how the DVD screensaver can ever only touch two opposite corners and NEVER the corner adjacent to them. This fact really lived in my head rent-free for a bit but it also made me wonder if I could create simple physics in p5js to recreate a DVD screensaver with the little JavaScript that I understood.

I started with just a ball and a simple if statement that detected whether the x position of the ball was greater than the width minus the radius, essentially allowing me to cause things to happen when the borders of the ball touched the edge. Once I had both the X and Y movements done, I needed to check for corner contacts.

function edgeBounce(i) {
  if (xPos[i] > width - radius[i] || xPos[i] < radius[i]) {
    xSpeed[i] *= -1;
  }
  if (yPos[i] > height - radius[i] || yPos[i] < radius[i]) {
    ySpeed[i] *= -1;
  }
}

Also, I know these are two dimensional circles but I will continue to refer to them as balls.

The time it would take for the ball to hit one of the corners took a really long time so I wanted to add the ability to speed up the ball animation! At first I tried to use the while() to detect when mouseIsPressed but after a lot of confusion I realized it had to be and if() function instead. However, I learned two important things today: the first was that while-else() was not a thing like if-else() is, and how to return variables from a function. It was such a natural way of learning through trial and error and it felt quite satisfying.

I eventually figured out how to do it by assigning a function, speedUp(), to return a value straight into the multiplier variable.

//somewhere in draw()
 for (let i = 0; i < xPos.length; i++) {
    // move ball
    multiplier = speedUp(i);

//..lots of code inbetween

function speedUp(i){
  if (mouseIsPressed){
    return timeMultiplier;
  } else{
    return 1;
  }
}

While I was here, I felt like the background looked a little bland at first so I decided to have a faint text in the background that would say the current speed multiplier. I made sure to have it drawn first so it’s always layered underneath the circles.

//MULTIPLIER TEXT 
textSize(400);
textAlign(CENTER,CENTER);
noStroke();
fill(multiplierColor);
text(speedUp() + "x", width/2, height/2);

And of course the main highlight of this assignment was to use loops. I was actually quite proud of how quickly I got the hang of the for loops and using arrays to take advantage of each loop.

for (let i = 0; i < xPos.length; i++) {
  // move ball
  multiplier = speedUp(i);

  
  xPos[i] += xSpeed[i] * multiplier;
  yPos[i] += ySpeed[i] * multiplier;

  edgeBounce(i); //checks for edge touch
  cornerBounce(i) //checks for corner touch, creates a new ball if successful

  // draw ball
  let c = ballColors[i];

  fill(c[0], c[1], c[2]); //RGB values of c
  stroke(c[0]-20, c[1]-20, c[2]-20); //-20 shifts it a little darker
  strokeWeight(8);
  ellipse(xPos[i], yPos[i], radius[i] * 2);
}//end of for loop
Difficulties Endured

A lot of tutorials I followed would introduce things I was not familiar with at all so I did workarounds that were probably a lot less efficient.

But most importantly, I crashed so many times and had to redo so much of my work so many times.

I originally wanted to have the new balls come out from the corner that the “motherball” contacted but when I tested my code it ended up spawning an infinite number of balls and caused my tab to completely crash and required me to redo almost 20 minutes of work.

I remember I had to write and rewrite the cornerBounce() function twice and it was really frustrating especially because of how long the if() statement was.

function cornerBounce(i){
    if ((xPos[i] <= radius[i] || xPos[i] >= width - radius[i]) &&
    (yPos[i] <= radius[i] || yPos[i] >= height - radius[i])) {
      console.log("Corner Contact Confirmed")
      if(ballCount < maxBallCount){ 
        console.log("Ball #" + xPos.length + " was Created")
        xPos.push(width/2); //PUSH adds to an array
        yPos.push(height/2); //if spawn in the corner it creates infinite balls
        xSpeed.push(random(-8, 8));
        ySpeed.push(random(-8, 8));
        
        //assigns a random color
        let newColor = random(colorOptions); 
        ballColors.push(newColor);
        
        radius.push(50);
        ballCount += 1;
      } else if (ballCount == maxBallCount){
        console.log("Max Ball Count Reached") 
      }//ends the ballcount if()
        
    }//ends the if()
}
Conclusion

Even thought my web editor crashed half a dozen times and disheartened me throughout the process, the trial and error felt like a satisfying loop of learning that will certainly help me navigate JavaScript a little more easily with each passing week.

Week 2 – Reading Reflection

Many of the featured patterns and algorithms Casey Reas made are very elaborate and very satisfying to see the construction of. Some of them looked like something you might find in the default wallpaper collection you get from purchasing a new touchscreen device. From my perspective, a lot of these pieces could be implemented as a compliment to another primary work to elevate it. For instance with the colorful cancer cell visualization in the video– if I was a cancer cell researcher and I needed to present my research in a presentation I could use visualizations of my data as a design motif throughout my presentation template.

Halfway through the video he talks about chance in art and I was very surprised to see it appear as early as 1916. I think the most notable part of this section is Duchamp’s woodwork involving randomly dropping a piece of string to determine the line of hit cuts and ultimately the shape of the wooden planks.

I find it incredibly fascinating that before the random number game was an accessible computer-generated concept, it was first printed onto paper as a collection of deviatives. The fact that there was a whole book for random strands of numbers really changed my perspective on random numbers. Before watching this video, random numbers were entirely synonymous with random number generation(RNG) through computing. I think this is largely due to my exposure to RNG in video games, which can take many forms in video games from determining the chance of landing a critical hit on an enemy in an RPG to opening a fancy cosmetic in a live service first-person shooter.

Speaking of video games, I thought the featured example using “Fractal Invaders” was really cool and kinda shows how symmetry can turn nonsense into something that you would think had a deeper purpose. It looked like a really interesting idea with the mirroring so it made me wonder what results I could achieve if I did a similar coin toss black/white color decider for a 4×4 grid and mirrored it both vertically and horizontally. I imagine this would probably come up with some really interesting pixel art that could even inspire a more intricate hand drawn illustration based on that pattern.

Overall, I was really impressed with how people would obtain random numbers without computing them– from Duchamp dropping string to decide the line of his cuts to using a flipping through a page filled with random numbers. I think my perspective on chance operations has greatly broadened.

Week 1: Made this While I was Hungry (Self-Portrait)

(Try Clicking on the Mouth when it’s Open!)

I had a few ideas of how I wanted to depict myself, none of which included a real depiction of my face, but more or less characters that I resonated with. I quickly scrapped that idea when I felt like it wouldn’t really be challenging me if I drew a depiction of an already existing illustration.

With my limited coding experience it would be certainly difficult to go from concept into actualization using just my keyboard and the funny colored words on screen, but I was committed to making something worth looking at.

One of the first things I tried to understand coming from a long photoshop/graphic design background was how layers would work on p5.js. Understanding this early gave me a lot of confidence that I wasn’t going to make something uncanny-looking. I made each body segment its own function and called it in the order from the lowest layer to the highest layer in draw()

function draw() { //draw runs on loop 60/sec
  background(110,40,80);//change later?
  
  //Draws Body Parts in Layers
  drawHair();
  drawTorso(90);
  drawHead();
  drawLeftEye(6.7);//parameter is for gap between lines
  drawRightEye(6.7)
  drawMouth();
  drawHands();
  drawBangs();
}

As I learned more about p5.js through tutorials, I wrote down lots of notes and reminders to myself. One tip that really helped me out initially was putting fill() before the shape I wanted to draw. When I was listening to the demonstration in class I thought it would make more sense for fill() to have parameters for what you wanted to fill, but after working with the shapes today I realized that would’ve caused a big confusing wall of text very quickly. Putting notes next to how many parameters and what each parameter did for each function was also really helpful in my learning process.

I wasn’t quite sure how to depict the face at first with just simple geometry but by removing the outline “strokes” and blending shapes I started sculpting some much nicer shapes.

I really wanted my avatar’s arms to be interactable with the user’s mouse values so I initially allowed the top Y-positions of the arm segments to be controlled using the mouse. I obviously didn’t want it to go out of bounds so I looked through the p5js reference until I found the constrain() function. This was a huge lifesaver for both this aspect of the self-portrait and some other aspects I worked on after this.

let armY = constrain(mouseY, 430, 500); //restricted value, min, max
  let armX = constrain(mouseX, 200, 350); 
  
  //LEFT
  quad( 
    armX-60, armY, //top left 220~
    armX-10, armY+20, //top right 280~
    220, 600, //bottom right
    100, 600  //bottom left
  );
  //RIGHT ARM
  quad( 
    armX+110, armY, //top left 380~
    armX+60, armY+20, //top right 320~
    width-220, 600, //bottom right
    width-100, 600  //bottom left
  );

This snippet was taken as I was working out the arm animation and tracking. Mini-me was very deprived of any type of hair back then. He was also very gray.

Fig 1. Snapshot Right After Figuring Out Arm Animations

I feel like the hand movements made the avatar feel so much more alive and responsive, making good use of p5.js in ways beyond just turning code into still images. This would go a step beyond when I added the cookie into mini-me’s hands then another step beyond when the mouth would respond to having a cookie in front of it.

//COOKIE
fill(210, 180, 140);
stroke(190, 160, 120);
cookieX = armX+25
//main cookie area
if (cookieEaten) {
  arc(cookieX,armY,150,75,5.7,3.5) //cookie missing the top corner
  console.log("Cookie was Eaten")
} else {
  arc(cookieX,armY,150,75,0,2*PI) //a full cookie
}
//chocolate chips
fill(90, 60, 40);
noStroke();
//top left to bottom right
ellipse(cookieX-50,armY-10,19,14)
ellipse(cookieX-21,armY-2,16,10)
ellipse(cookieX-37,armY+10,12,7)
ellipse(cookieX+3,armY+8,17,12)
ellipse(cookieX+22,armY-6,12,8)
ellipse(cookieX+29,armY+20,15,10)
ellipse(cookieX+38,armY+2,18,13)

At first I didn’t have any chocolate chips on the cookie and my friend thought it was holding an empty plate so I decided to add some chocolate chips even though the way I did it was probably not very efficient. I considered using random variables like I’ve seen some previous projects utilize for their backgrounds but the choco chips needed to avoid a very specific area of the cookie that would be “bitten” into while looking well balanced.

One of my goals with the cookie in hand was to let the user be able to eat it by clicking on the mouth of mini-me, but I didn’t know how to program a bite mark into the ellipse so I decided to change it into an arc() halfway through and use the arc’s unique properties to cut out a portion of it near the mouth, making it appear like it had been bitten into. I was pretty proud of how I worked with my limited knowledge here.

cookieEaten = false
function mouseClicked() {
    console.log(mouseX,mouseY)
    if (mouseX > 280 && mouseX < 320 && mouseY > 330 && mouseY < 400){
      cookieEaten = true; 
      console.log("clicked mouth");
     }  
  }

//lots of stuff here in between in the real code...

if (cookieEaten) {
    arc(cookieX,armY,150,75,5.7,3.5) //cookie missing the top corner
    console.log("Cookie was Eaten")
  } else {
    arc(cookieX,armY,150,75,0,2*PI) //a full cookie
  }

By far the most unsatisfying part to me was definitely the way I did my hair. There were just so little polygons to work with to properly express what type of hairstyle I wanted for mini-me. However, a lot of my friends said they actually really liked how I depicted the hair so perhaps I’m fixating on it too much.

My biggest regret was that I only learned after I was nearly done with my self-portrait that I could’ve changed the angleMode() to degrees the whole time… I spent so much time trying to remember middle school math just to work out the PI angles for certain arcs.

//the upper lip
arc(cenX - 210/4, faceCenY, 150, 150, PI/4, PI/2); //left side
arc(cenX + 210/4, faceCenY, 150, 150, PI/2, 3*PI/4);//right side

If I were to do this exercise again, I would probably create more variables to make certain parameters look a little more organized and efficient. I changed a few parts to stuff like “width-X” rather than calculating it myself but there was definitely more I could’ve done to make everything look cleaner.

Overall for my first experience with p5.js and my limited experience with JavaScript , I think I would be proud of myself even later down the line.