As soon as I saw the instructions for this assignment that I have to utilize object-oriented programming and arrays, I thought of depicting balloons. More specifically, I reminded myself of the flying house of the Pixar movie called “Up” released in 2009. So, inspired by the flying house in this movie, I decided to recreate the flying house in this assignment. Attached below is the image of a flying house from “Up” which I was inspired by.
For this assignment, I have created 3 classes, which were Balloon, House, and Cloud class. Each class contains attributes and functions needed for each object to be created.
Balloon Class
class Balloon {
constructor(x, y, height, colors) {
this.balloonPosition = createVector(x, y); //position of the balloon saved as a vector for convinience
this.balloonHeight = height; // height of the balloon
this.balloonWidth = height * 0.8; //width of hte balloon; maintaining the balloon shape through making height is greater than the width
this.balloonLift = createVector(0, -random(0.5, 1.5)); // random variable in between 0.5 to 1.5 for lifting the balloon
this.balloonColor = random(colors); //picking the color of the balloon randomly from the color array
//determining the tilt factor of the ballon based on the x position of the balloon
if (x < 180) {
this.tilt = random(-PI / 8, -PI / 18); // tilting towards left
} else if (x > 200) {
this.tilt = random(PI / 18, PI / 8); //tilting towards right
} else {
this.tilt = 0;// does not tilt
}
}
drawString(chimneyY){
stroke(220);// color of the balloon string
strokeWeight(1); //thickness of the string
//setting up a reference point for rotation (bottom center)
let x0=0; //x-coordinate of the bottom center relative to the (not tilted)balloon's center
let y0=this.balloonHeight/2;// y-coordinate of the bottom center below the (not tilted) balloon's center
//getting the rotated x and y coordinates of the center of the tilted balloon by applying the rotate formula
let rotatedX = x0 * cos(this.tilt) - y0 * sin(this.tilt);
let rotatedY = x0 * sin(this.tilt) + y0 * cos(this.tilt);
//Adding the rotatedX value to the original position of the balloon to obtain where to start the balloon's string
let stringStartX= this.balloonPosition.x+rotatedX
let stringStartY= this. balloonPosition.y+rotatedY
//drawing the string
line(stringStartX, stringStartY, 174,chimneyY-100)// the end of the string will be the same to show that the string is connected to the chimney
// variables for drawing the knots(triangle)
let baseSize= 50 // base size for the largest balloon
let adjustment = (this.balloonHeight/baseSize)*5// changing the adjustments based on the size of the relative size of the balloon
//calculating the triangle vertices by the balloon size
let vertexX1 = stringStartX - adjustment;
let vertexY1 = stringStartY + adjustment;
let vertexX2 = stringStartX + adjustment;
let vertexY2 = stringStartY + adjustment;
let vertexX0 = stringStartX; // Top vertex (string start point)
let vertexY0 = stringStartY;
// rotating vertices around the top vertex
let rotatedVertexX1 = vertexX0 + (vertexX1 - vertexX0) * cos(this.tilt) - (vertexY1 - vertexY0) * sin(this.tilt);
let rotatedVertexY1 = vertexY0 + (vertexX1 - vertexX0) * sin(this.tilt) + (vertexY1 - vertexY0) * cos(this.tilt);
let rotatedVertexX2 = vertexX0 + (vertexX2 - vertexX0) * cos(this.tilt) - (vertexY2 - vertexY0) * sin(this.tilt);
let rotatedVertexY2 = vertexY0 + (vertexX2 - vertexX0) * sin(this.tilt) + (vertexY2 - vertexY0) * cos(this.tilt);
// drawing the rotated triangle (knot)
noStroke();
fill(this.balloonColor); // setting fill same color with the balloon for the triangle
triangle(rotatedVertexX1, rotatedVertexY1, rotatedVertexX2, rotatedVertexY2, vertexX0, vertexY0-3);
}
drawBalloon() {
push();//saving the current drawinf style settings and transformations
translate(this.balloonPosition.x,this.balloonPosition.y);// moving the origin to the balloon's position
rotate(this.tilt);//rotating the canvas by the balloon's tilt angle
fill(this.balloonColor);//setting the color to fill the balloon
ellipse(0,0,this.balloonWidth,this.balloonHeight)//drawing the balloon at the new origin
pop(); //restoring the previous drawing style settings and transformations
}
update(){this.balloonPosition.add(this.balloonLift);// adding the lift vector to the balloon's position vector
}
}
As attached above, within the Balloon class, there are three functions: drawString(), drawBalloon(), and update(). The drawString() and drawBalloon() functions are functions that are responsible for drawing the string attached to the balloon and the balloon itself. I originally planned to draw a string within the drawBalloon() function as well. However, whenever I tried to do so, due to the order of the execution, the code’s output was not what I expected to see. Hence. I made the drawString to draw the string and the know of the balloon separately.
For the drawBalloon() function, it was not particularly hard for me to code this part, as I just had to implement the ideas of rotating a shape and drawing a shape which we learned during one of our first lectures to code.
However, for the drawString() function, it was quite challenging. It took me quite a while to figure out to ensure that the string and the knot of the balloon were located correctly on the bottom center of the balloon even after tilting the balloon itself. After struggling for a few minutes, I realized that I could use the sin() function and cos() function, which are built-in functions of p5, to figure out the “changed” bottom center of the balloon. More specifically, I was reminded of the coordination rotation formula, which I learned back in high school. Since we know the angle of rotation, through implementing coordination rotation formulas, I was able to easily figure out what is the changed x and y positions of the bottom center of the rotated balloon.
For the update() function, which is responsible for raising the balloons, I have simply added the balloon lift vector to the balloon’s position vector. The update() function was one of the reasons why I decided to utilize the createVector() in the first part of the code. I wanted to make mine as concise as possible. Hence, to prevent making repetitive lengthy code, I decided to create the createVector(). As shown in the code snippet above, the lift factor is determined randomly between 0.5 and 1.5. I made the lift factor to be randomized because I wanted the balloons to have different speeds so that it would look more natural and it would help look like the balloons dragging the house above.
Cloud Class
drawCloud(){
fill(255);//white color for the cloud
noStroke();
//creating several ellipses to form a cloud shape
ellipse(this.cloudPosition.x, this.cloudPosition.y-10,70,50);
ellipse(this.cloudPosition.x, this.cloudPosition.y-10, 70, 50);
ellipse(this.cloudPosition.x + 10, this.cloudPosition.y + 10, 70, 50);
ellipse(this.cloudPosition.x - 20, this.cloudPosition.y + 10, 70, 50);
ellipse(this.cloudPosition.x+40, this.cloudPosition.y+10, 70,50);
}
Within the cloud class, I have created the drawCloud() function to draw a cloud. As shown in the snippet above, I have illustrated the cloud by combining 4 ellipses. I have made the ellipse’s position to be random. So, every time we replay the code, the position of the cloud changes.
House Class
update() {
this.houseY -= 0.5; //liftingthe house
}
drawHouse() {
// house body
fill("#64b9dd")
rectMode(CENTER);
rect(this.houseX, this.houseY, 120, 60);
// roof and other details
fill("#84797b")
quad(140, this.houseY - 90, 257, this.houseY - 90, 278, this.houseY - 30, 119, this.houseY - 30);
fill("#fccc65")
triangle(215, this.houseY - 110, 185, this.houseY- 30, 255, this.houseY - 30);
// mini house body
rectMode(CORNER);
fill("#f3a07f")
rect(200, this.houseY - 30, 40, 60);
//door
fill(255)
rect(154, this.houseY-10,30,40)
stroke("#84797b")
strokeWeight(8)
point(175,this.houseY+10)
strokeWeight(4)
// windows
noStroke()
fill(255)
rect(208,this.houseY-70,20,25)
rect(207, this.houseY - 20, 25, 35);
stroke( "#84797b")
line(209,this.houseY-60,227,this.houseY-60)
line(219, this.houseY - 19, 219, this.houseY + 14);
line(207, this.houseY - 3, 232, this.houseY - 3);
// chimney
noStroke()
fill("#84797b")
rect(166, this.houseY-100, 20, 10);
}
There are 2 functions: drawHouse() and update(). As the names of the functions suggest, the drawHouse() function is responsible for drawing the house itself while the update() function is responsible for moving the house upward. For drawHouse(), I have utilized simple shape functions such as quad(), triangle(), rect(), and line() to draw the house. For the update() function, similar to the mechanism I used for the update() function of the Balloon class. I have deducted the lift factor of 0.5 to the y position of the instance of this class.
In Main Sketch
In the main sketch, I have created the objects of the Balloon class, House class, and Cloud class. For Cloud class and Balloon Class, in which I had to create multiple objects, I created empty arrays and stored each object of the class to the respective array using the for loops. For the Balloon objects, I randomized the x and y positions of the object, the size of the balloon, and also the color of the balloon. But I’ve set a range when randomizing the x and y positions to ensure that the balloon is always created above the house. Also, for the Cloud class, I have randomized the x and y positions of the object and the size of the balloon.
//function adding a new balloon when the mouse is pressed
function mousePressed() {
//Checking if the mouse is pressed above a certain height relative to the house
if (mouseY < house.houseY - 110) {
// Add a new balloon at the mouse position with a random size and color
balloons.push(new Balloon(mouseX, mouseY, random(20, 50), colors));
}
One of the key parts of the main sketch-which I am particularly proud of- is the mousePressed. I have let a new Balloon be generated when the mouse is clicked. However, a new Balloon will be generated if and only if the y position of the mouse is above the house. I have set the initial number of balloons generated when the code is played to 90 balloons. After a few clicks of the mouse, when the number of balloons is more than 100, then the balloon and house will start rising.
The Final Output
Mouse click above the house to add more balloons! Once there are more than 100 balloons, the house and balloons start rising 🙂
Reflection and Areas of Improvement.
Overall, I enjoyed working on this assignment. Although it was quite challenging to code as there were lots of things that I had to consider, it was very fun and interesting to apply arrays and classes together to create objects. I also loved how the usage of classes and arrays could make the code way simpler and more concise. For the areas of improvement, I think I can perhaps make the balloons pop when they reach the end and make the house fall when the number of balloons decreases. Also, I could make the house and balloons not only move up but also left and right using the keyboard arrows.