Sun & Moon Created in P5.js (with tutorial)
Posted on
by Kevin Foong"Sun & Moon" is an original sketch created using P5.js, the Javascript version of Processing. It is a simple sketch based on trigonometry to calculate the closest "sun" then moving towards it and finally rotating around it.
To play click anywhere on the screen to spawn a "moon".
Code Explanation
Here is a simple walkthrough of the sketch. P5.js has two predefined functions, setup
and draw
. setup
is called once and once only when the program starts. It is where you initialise all your variables. draw
is called repeatedly in an endless loop and it is where you put all the code for your animation.
Here we are using an array to store our Sun objects and another array to store our Moon objects. Clicking the mouse on the screen will create a new Moon object at the x,y location of the mouse.
let suns = [];
let num_suns = 6;
let moons = [];
function setup() {
createCanvas(windowWidth,windowHeight);
for (let i = 0; i < num_suns; i++) {
suns.push(new Sun(random(0,width), random(0,height)));
}
noStroke();
fill(0);
}
function draw() {
background(175,215,255);
for (let i = 0; i < suns.length; i++) {
suns[i].display();
}
for(let i=0; i<moons.length; i++) {
moons[i].display();
}
}
function mouseClicked() {
moons.push(new Moon(mouseX,mouseY));
}
The Sun class is fairly simple. The size of the sun is randomly generated, and display
just draws an ellipse (circle) in a random x,y position on the screen.
class Sun {
constructor(x, y) {
this.x=x;
this.y=y;
//size of sun
this.rad=random(8,50);
}
display() {
fill(255,0,0,200);
ellipse(this.x,this.y,this.rad*2,this.rad*2);
}
getx () {
return this.x;
}
gety() {
return this.y;
}
getrad() {
return this.rad;
}
}
The Moon class is where we do all the mathematics. We calculate all our variables in the constructor
.
Firstly we use pythagoras theorem to calculate the distance between the moon objects and each sun to identify which is the closest sun. Pythagoras theorem states that 'c' ('c' in our case is the distance between moon and sun) is the square root of 'a' to the power of 2 plus 'b' to the power of 2. We can easily calculate 'a' and 'b' via the x,y position of the sun and moon.
With the closest sun identified we next calculate theta (Θ) using trigonometry. Theta is the angle between our sun and moon. We will use theta later to animate the moon orbitting.
Finally the display
function is used to render the moon. We use P5.js lerp
function to help us animate the moon gravitating towards the sun. Once the moon reaches the sun we then use trigonometry to render the moon orbiting the sun. Here we make use of the theta value we calculated earlier.
The formula for animating an object in a circle is:
x = sin(Θ) * speed
y = cos(Θ) * speed
class Moon {
constructor(x,y) {
this.x=x;
this.y=y;
this.rad=4;
this.loccount=0; //for the lerp
this.startspin=false;
// calculate distance to each sun
this.adj = [];
this.opp = [];
this.dist = [];
for (let i=0; i<num_suns; i++) {
this.adj[i] = this.x-suns[i].getx();
this.opp[i] = suns[i].gety()-this.y;
this.dist[i] = sqrt(pow(this.adj[i],2)+pow(this.opp[i],2));
}
// for the closest sun, get the index and the distance
this.closestsun_index = 0;
this.closestsun_dist = 99999999;
for (let i=0; i<num_suns; i++) {
if (this.dist[i] < this.closestsun_dist) {
this.closestsun_index = i;
this.closestsun_dist = this.dist[i];
}
}
// get theta
this.theta = atan2(this.opp[this.closestsun_index],this.adj[this.closestsun_index])+PI/2;
// get the sun object
this.sun = suns[this.closestsun_index];
// get sun radius
this.radmotion = this.sun.getrad() + 8;
// calculate distance to sun
this.distspin = this.closestsun_dist - this.radmotion;
// calculate spin speed
this.spinspeed = random(-0.04,0.04);
}
display() {
fill(0,80,80,200);
if (!this.startspin) {
this.locx = lerp(this.x, this.sun.getx(), this.loccount/this.closestsun_dist);
this.locy = lerp(this.y, this.sun.gety(), this.loccount/this.closestsun_dist);
ellipse(this.locx,this.locy,this.rad*2,this.rad*2);
this.loccount+=1.6;
} else {
this.locx = this.sun.getx() + sin(this.theta)*this.radmotion;
this.locy = this.sun.gety() + cos(this.theta)*this.radmotion;
ellipse(this.locx,this.locy,this.rad*2,this.rad*2);
this.theta += this.spinspeed;
}
if(this.loccount>=this.distspin){
this.startspin=true;
}
}
}
See full source code on Github
Tags: processing