ICM Week 2

 

This week’s programming assignment had to include those details listed below:

  • One element controlled by the mouse.
  • One element that changes over time, independently of the mouse.
  • One element that is different every time you run the sketch.

I could have either refactored my first drawing or created completely different one and I chose the latter.

So first, I thought about the scenario of the animation that I was going to program this time. I wrote down some ideas that came into my mind for each requirement.

For the thing that controlled by the mouse, I came up with the idea of moving characters that responses to the mouse position or the click event. It would be great if it is not just moving this point to another point, but also able to move its body. The ideas for the second and third conditions were like these: lightening and darkening sky, the rising and setting sun or moon, turning clock hands, randomly appearing stars, etc.  Finally, I chose a scenario with a character moving around a field day and night.

Next thing was to create the character. The character had better be a simple shape for having body motion, or the calculation would be a real disaster. I sketched some candidates.

I quite liked the bird, and above all, it was the simplest one. So I decided to implement a scene, where a bird flies to the mouse clicked x-point, the sky gets bright and dark on a regular cycle and clouds or stars turn up irregularly.

On my sketch, I first draw the body of the bird using arcs, an ellipse, and a triangle. To find a targeting position more easily, I draw a grid on the background and made the console keep printing the x and y position of the current mouse cursor.


The code for the grid and the cursor position printing part is respectively like below:

function drawGrid() {
  
  let i, j;
  
  stroke(200);
  
  for(i=10; i<width; i+=10) {
    line(0, i, width, i);
  }
  for(i=10; i<height; i+=10) {
    line(i, 0, i, height);
  }
}

The wing is a tricky one. It has its own translated and rotated coordinate and also flutters on a regular cycle, so I  separated the wing drawing function from the body drawing part. Furthermore, it has two status for the fluttering animation: upward and downward.

function westWingUp() {

  push();
    translate(20, 20);
  
  // // current (0, 0)
  // stroke(0);
  // strokeWeight(5);
  // point(0,0);
  // noStroke();
  
  // wing up
  rotate(PI/4);
  fill(cWing);
  arc(-50, 0, 100, 100, 0, PI);
  pop();
}

function westWingDown() {
  push();
  translate(0, 20);
  
  // // current (0, 0)
  // stroke(0);
  // strokeWeight(5);
  // point(0,0);
  // noStroke();
  
  // wing down
  rotate(PI/4);
  fill(cWing);
  arc(50, 0, 100, 100, 0, PI);
  pop();
}

But the whole body and wing of the bird have to move together when the mouse is clicked. Therefore I applied translate() function to the body as well instead of directly put the variable mouseX to the shape functions that consist of the bird.

push();
  	translate(birdX, birdY);
  	westNoWings();
  
  	// move until the bird reach the destination   
    if(birdX < mouseX) birdX += dX*speed;
    
    // fluttering wings
    if(flyCnt>=0 && flyCnt<10) {  // upwing for 10f
  	  westWingUp();  
    	flyCnt++;
    } else if(flyCnt>=10 && flyCnt <20) {	//downwing for 10f
    	westWingDown();
    	flyCnt++;
    	if(flyCnt==20) flyCnt = 0;
    }
    
    // bouncing up and down while flying
    if(birdY == birdMaxY) up = false;
    else if(birdY == birdMinY) up = true;
    if(up) {
      birdY++;
    } else if(!up) {
      birdY--;
    }
  	
  	pop();
function westNoWings() {
  
  noStroke();
  
  // beak
  fill(cBeak);
  triangle(150, 20, 80, 15, 80, 30);
  
  // head
  fill(cBody);
  ellipse(70, 25, 60, 50);
 
  // body
  arc(0, 0, 150, 150, 0, PI);
  
  // eye
  fill(255);
  arc(75, 15, 25, 25, 0, PI);
  
  // iris
  fill(0);
  arc(75, 15, 16, 16, 0, PI);
  
}
function mouseClicked() {     

    fly = true;

    destX = mouseX;
    dX = mouseX - birdX;
    speed = map(dX, 0, width, 0, 0.05);
    if(dX>0) west = true;
    if(dX<0) west = false;
}

In the code above, the “//fluttering wings” part makes the bird keeps its wing upward for 10 frames and downward for another 10 frames. And one of the new discovery is that if I use mouseIsPressed variable or mousePressed() function instead of mouseClicked(), it deosn’t work properly. It seems because  no matter how a user quickly presses and releases the mouse button, the processor recognize it a series of multiple mouse pressed events.

Next, I developed the darkening and brightening sky.

function draw() {

    background(skyR, skyG, skyB);
    …

    // if it gets totally bright, begin to get dark and vice versa
    if(((skyR >= dayR) && (skyG >= dayG) && (skyB >= dayB)) || ((skyR <= ngtR) && (skyG <=ngtG) && (skyB <= ngtB)))
        daySpd *= -1;

    if( ((daySpd<0) && (skyR!=ngtR)) || ((daySpd>0) && (skyR!=dayR)) ) skyR += daySpd;
    if( ((daySpd<0) && (skyG!=ngtG)) || ((daySpd>0) && (skyG!=dayG)) ) skyG += daySpd;
    if( ((daySpd<0) && (skyB!=ngtB)) || ((daySpd>0) && (skyB!=dayB)) ) skyB += daySpd;
}

Here, the initial value of each variable is like below:

dayR = 235;
dayG = 192;
dayB = 215
ngtR = 104;
ngtG = 21;
ngtB = 66;
skyR = dayR;
skyG = dayG;
skyB = dayB
dimSpd = 50;

I chose a random light blue for the brightest background color and also a random dark blue for the darkest sky. Then, I encountered an unexpected problem. The transition between two colors didn’t look that natural.

So I changed them. This time, I chose the colors diagonally contrasting on the color picker.

And the second result obviously looks better than the first one.

Lastly, I worked on the random cloud thing. I was going to show the random number of clouds those have different arbitrary positions, colors, and alphas. I needed arrays to save those values and get the draw() function to show them for some frames.

// random cloud
if((frameCount%30) == 0 ) {
    rdmNum = random(5, 20);
    rdmAlp = [];
    rdmX = [];
    rdmY = [];
    rdmRad = [];
    rdmR = [];
    rdmG = [];
    rdmB = [];
    for(let i=0; i<rdmNum; i++) {
        rdmAlp.push(random(20, 100));
        rdmX.push(random(0, width));
        rdmY.push(random(0, height));
        rdmRad.push(random(30, 250));
        rdmR.push(random(0, 255));
        rdmG.push(random(0, 255));
        rdmB.push(random(0, 255));
    }
}

I tested this algorithm with different colors and update speeds.

The actual result (Click on the canvas!):

My original plan was to have the bird move only when the mouse is clicked, but it turned out to be a quite hassling job! So I changed the code to make this bird always move following the mouse cursor. In addition, I added a bouncing effect to it.

push();
translate(birdX, birdY);
westNoWings();

// move until the bird reach the destination
if(birdX < mouseX) birdX += dX*speed;

// fluttering wings

…

// bouncing up and down while flying
if(birdY == birdMaxY) up = false;
else if(birdY == birdMinY) up = true;
if(up) {
    birdY++;
} else if(!up) {
    birdY--;
}

pop();


// tracking the mouse pointer and calculating bird's speed
dX = mouseX - birdX;
speed = map(dX, 0, width, 0, 0.05);
if(dX > 0) west = true;
if(dX < 0) west = false;

“// bouncing up and down while flying” part gets the bird to go up by one-pixel per frame until it reaches the maximum height — numerically though, it is the minimum y-position — that I defined. On the contrary to this, it goes down again from the peak by one pixel per frame until reaches the minimum height. These actions are repeated again and again during the running time.

I also touched up the appearing and disappearing animation of the clouds — or bubbles — smoother by giving them fade in/out effect.

// generating random positions, colours and alphas
localF = frameCount%dimSpd;
if(localF == 0 ) {
    rdmNum = random(5, 20);
    rdmAlp = [];
    rdmX = [];
    rdmY = [];
    rdmRad = [];
    rdmR = [];
    rdmG = [];
    rdmB = [];
    for(let i=0; i<rdmNum; i++) {
        rdmAlp.push(random(20, 100));
        rdmX.push(random(0, width));
        rdmY.push(random(0, height));
        rdmRad.push(random(30, 250));
        rdmR.push(random(0, 255));
        rdmG.push(random(0, 255));
        rdmB.push(random(0, 255));
    }
}

// make the bubbles fade in and out during dimSpd(frames)
for(let i=0; i<rdmNum; i++) {
    if(localF<(dimSpd/2)) tempAlp = map(localF, 0, (dimSpd/2-1), 0, rdmAlp[i]); else if(localF>=(dimSpd/2)) tempAlp = map(localF, (dimSpd/2), (dimSpd-1), rdmAlp[i], 0);
    noStroke();
    fill(rdmR[i], rdmG[i], rdmB[i], tempAlp);
    ellipse(rdmX[i], rdmY[i], rdmRad[i], rdmRad[i]);
}

And this is the final result:

The full source code is available via:
http://alpha.editor.p5js.org/yeony102/sketches/BkYVKYw5b

Posted in ICM

One thought on “ICM Week 2

Leave a Reply

Your email address will not be published. Required fields are marked *