2018년 9월 디자인과 코딩을 함께 다루는 유튜브 채널을 열었습니다. "타입과 미디어" 사이트는 당분간 유지할 계획이지만, 새로운 정보는 유튜브 비디오를 통해서 소개하겠습니다. 많은 관심 부탁드립니다! — 정대인

조건문과 사용자 인풋

세부 목차

if 조건문의 이해

맑은 날엔 선글라스를, 비오는 날엔 우산을

우리가 일상생활을 할 때에, 상황과 조건에 따라서 다른 행동을 합니다. 날씨가 맑다면, 선글라스가 필요합니다. 프로세싱 스케치 또한 조건에 따라서 다른 행동을 하고 결과를 만들 수 있습니다.

if

만약, 날씨가 맑으면:
  선글라스를 준비한다.

위의 내용을 좀 더 코드에 가깝게 표현해보겠습니다.

boolean sunny = true;

if (sunny == true) {
  getSunglasses();
}

위의 if 다음에 따라오는 소괄호 안에는 참인지 거짓인지, 혹은, true인지 false인지 평가할 수 있는 내용이 들어갑니다. true인 경우에는, getSunglasses()를 실행합니다. 첫 줄에 sunny = true, 즉, 오늘의 날씨는 맑으니 getSunglasses()를 실행하게 됩니다. 만약, sunny = false인 경우는 어떻게 될까요? if 구문 내의 내용은 무시되고, 건너뛰게 됩니다.

if/else

날씨가 맑으면 선글라스를 준비하는 것까지는 좋았습니다. 하지만, 날씨가 좋지 않은 날에는 어떡할까요? 우산이 필요하지 않을까요? 이에 대응할 수 있는 else 구문을 넣어보겠습니다.

boolean sunny = false;

if (sunny == true) {
  getSunglasses();
} else {
  getUmbrella();
}

위의 경우 sunny = false, 즉, 날씨가 맑지 않으므로 getUmbrella()가 실행됩니다.

if/else if/else

위와 같이 언제나 두가지 상황만 존재하는 것은 아닙니다. 경우의 수가 더 많을 때는, else if를 사용해서 각각의 상황에 대응할 수 있습니다.

int numGuests = 3;

if (numGuests == 0) {
  goToSleep();
} else if (numGuests == 1) {
  goToIceCreamShop();
} else if (numGuests == 2) {
  goToCoffeeShop();
} else {
  goToRestaurant();
}

여러 조건을 충족시키기

여러 조건을 동시에 충족하는지 테스트할 때에는, if 구문 안에 또다른 if 구문을 넣어줄 수 있습니다.

// 만약 날씨가 맑고, 온도가 24도 미만이라면,
if (sunny) {
  if (temperature < 24) {
    wearTShirts();
  }
}

또는 &&연산자를 사용해줄 수 있습니다.

// 만약 날씨가 맑고, 온도가 24도 미만이라면,
if (sunny && temperature < 24) {
  wearTShirts();
}

여러 조건들 중, 한가지만 만족시켜도 되는 경우에는 || 연산자를 사용해줄 수 있습니다.

// 만약 날씨가 맑거나 주말이라면 (한가지 조건만 충족시켜도 됨)
if (sunny || weekend) {
  goToBeach();
}

부정적인 조건

~하지 않다면, 과 같이 부정적인 조건을 만족시키는 경우는 !=를 사용해줄 수 있습니다.

//만약 아프지 않다면
if (sick != false) {
  goToSchool();
}

이를 줄여서 boolean 변수 앞에 !를 붙여줄 수 있습니다.

if (!sick) {
  goToSchool();
}

위의 예제들은 설명을 위해서 만들었을 뿐, 실제로 실행되는 코드는 아닙니다.

switch 조건문

switch 조건문은 if 구문과 비슷합니다. if, else if 를 계속해서 늘어놓는 것보다 간편하게 조건문을 만들 수 있습니다. 예를 들어서 true, false로 딱 떨어지는 상황이 아니라 월별로 다른 결과를 만들어야한다면 switch문을 사용하는 것이 보기에 깔끔합니다. switch()의 소괄호 안에 들어가는 표현은 int로 변환가능한 데이터이어야 합니다. 예를 들어, int, char 등을 사용할 수 있습니다. 여러 개의 case를 지정해주어서 다양한 인풋에 대처할 수 있습니다.

case로 지정되지 않은 인풋에 대해서는 default를 이용하여 한꺼번에 처리할 수 있습니다. 각 case의 마지막에는 break; 구문을 꼭 넣어줘야 합니다. 그렇지 않으면 다음 case까지 연달아 실행되게 됩니다.

PFont font;

int currentMonth = 2;

void setup() {
  size(600, 200);  
  font = createFont("SansSerif", 48);
  textFont(font);
  fill(0);
}

void draw() {
  background(255);
  
  switch (currentMonth) {
    case 12:
      text("12월은 춥습니다", 20, 50);
      break;
    case 1:
      text("1월도 춥습니다.", 20, 50);
      break;
    case 2:
      text("2월도 조금 춥네요.", 20, 50);
      break;
    default:
      text("안 춥습니다.", 20, 50);
      break;
  }
}

마우스 인풋

조건문을 가장 흔하게 쓰는 경우는 사용자의 인풋을 받는 경우입니다. 사용자의 마우스의 위치와 클릭에 대응하도록 조건문을 만들어보겠습니다.

마우스 현재 위치

프로세싱은 마우스의 현재 위치를 저장하고 있는 시스템 변수 mouseXmouseY를 제공합니다. 시스템에서 제공하는 변수이므로, 우리가 따로 선언할 필요 없이 바로 사용할 수 있습니다. 아래의 예제에서 마우스를 움직여보면, 그 좌표가 화면에 표시됩니다.

void setup() {
  size(600, 200);
  stroke(0);
  fill(0);
}

void draw() {
  background(255);

  line(mouseX, 0, mouseX, height);
  line(0, mouseY, width, mouseY);
  
  ellipse(mouseX, mouseY, 10, 10);
  text("(" + mouseX + ", " + mouseY + ")", mouseX+5, mouseY-5);
}
Your browser does not support canvas.

마우스로 그림 그리기

background() 함수를 setup()에 넣고 처음에 색상을 설정한 뒤, 리프레시하지 않으면, 마우스가 그리는 궤적이 화면에 남게 되어 붓의 효과를 낼 수 있습니다. 아래의 예제에서 마우스를 움직이면 원이 그려집니다. 마우스를 클릭하면 화면이 리셋됩니다.

void setup() {
  size(600, 200);
  background(255);
  noFill();
}

void draw() {
  stroke(200);
  ellipse(mouseX, mouseY, 20, 20);
}

void mousePressed() {
  background(255);
}
Your browser does not support canvas.

마우스 직전 위치

pmouseXpmouseY 변수는 마우스의 직전 위치를 담고 있습니다.

PFont font;

float x;
float y;
float px;
float py;

void setup() {
  size(600, 200);
  font = createFont("Serif", 24);
  textFont(font);
  textAlign(CENTER, CENTER);
  fill(0);
  
  x = 40;
  y = 100;
  px = 560;
  py = 100;
}

void draw() {
  background(255);
  
  stroke(100, 255, 100);
  line(px, py, x, y);
  noStroke();
  fill(200, 255, 200);
  ellipse(px, py, 30, 30);
  fill(0);
  text("과거", px, py);
  fill(100, 255, 100);
  ellipse(x, y, 30, 30);
  fill(0);
  text("현재", x, y);
} 

void mousePressed() {
  px = x;
  py = y;
  x = mouseX;
  y = mouseY;
}
Your browser does not support canvas.

마우스 위치를 이용한 조건문

float xpos;
float ypos;
float cSize; // circle size
color c;

void setup() {
  size(600, 200);

  xpos = width/2;
  ypos = height/2;
  cSize = 120;
}

void draw() {
  if (dist(mouseX, mouseY, xpos, ypos) <= cSize/2) {
    background(100, 200, 100);
    c = color(0);
  } else {
    background(0);
    c = color(255);
  }
  noStroke();
  fill(c);
  ellipse(xpos, ypos, cSize, cSize);
  stroke(c);
  strokeWeight(10);
  noFill();
  ellipse(xpos, ypos, cSize+50, cSize+50);
} 
Your browser does not support canvas.

mousePressed() 또는 mousePressed

마우스의 클릭을 감지하는 데에는 두가지 방법이 있습니다. mousePressed()는 함수를 이용한 것이고 mousePressedboolean 변수를 이용한 것입니다. mousePressed() 함수는 마우스의 클릭이 감지되었을 때, 자동으로 실행됩니다. mousePressed 변수는 클릭했을 때 true 값을 돌려주므로, 조건문에서 사용할 수 있습니다.

아래는 mousePressed() 함수를 이용한 예제입니다.

void setup() {
  size(600, 200);
  background(255);
  noFill();
}

void draw() {
} 

void mousePressed() {
  rect(mouseX, mouseY, 20, 20);
}
Your browser does not support canvas.

아래는 mousePressed 변수를 이용한 예제입니다.

void setup() {
  size(600, 200);
  background(255);
  noFill();
}

void draw() {
  if (mousePressed) {
    rect(mouseX, mouseY, 40, 40);
  }
} 
Your browser does not support canvas.

위의 두 예제 모두 마우스 클릭에 반응합니다. 하지만, 마우스를 클릭하고 드래그하면 그 차이를 알 수 있습니다. mousePressed() 함수를 이용한 경우, 클릭에 반응해서 함수는 한 번 호출됩니다. mousePressed 변수의 경우, draw() 함수 내에 위치하고 있기 때문에, 매 프레임 mousePressed 변수의 값을 읽게 되고, 연속해서 도형을 그릴 수 있습니다.

어떤 버튼을 클릭했지?

기본적으로 mousePressed()mousePressed는 마우스의 왼쪽 클릭에 반응합니다. 하지만 마우스의 여러 버튼에 각각 다른 대응을 하고 싶다면 마우스 버튼을 감지하는 조건문을 추가할 수 있습니다.

void setup() {
  size(600, 200);
  background(255);
  noFill();
}

void draw() {
} 

void mousePressed() {
  if (mouseButton == LEFT) {
    background(255);
    for (int i = 0; i < width; i += 10) {
      line(i, 0, i, height);
    }
  } else if (mouseButton == RIGHT) {
    background(255);
    for (int i = 0; i < height; i += 10) {
      line(0, i, width, i);
    }
  }
}
Your browser does not support canvas.

키보드 인풋

사용자가 마지막으로 누른 키보드의 키는 key라는 시스템 변수에 저장되어 있습니다. 이를 이용해서, 키보드 인풋에 대응하는 스케치를 만들 수 있습니다.

일반 키

PFont font;

void setup() {
  size(600, 200);
  font = createFont("SansSerif", 72);
  textFont(font);
  background(255);
}

void draw() {
} 

void keyPressed() {
  // if spacebar pressed
  if (key == ' ') {
    noStroke();
    fill(0, 255, 0);
    ellipse(random(width), random(height), 30, 30);
  }
  fill( random(255) );
  text(key, random(width), random(height));
}

void mousePressed() {
  background(255);
}
Your browser does not support canvas.

특수 키

ALT, CONTROL, SHIFT, 화살표 키와 같은 특수 키 등은 따로 처리할 수 있습니다. 아래 예제는 키보드의 방향키를 이용해서 사각형의 궤적을 남기고 있습니다. draw() 함수 내에 background()가 없기 때문에 매 프레임의 사각형이 쌓여서 보여집니다.

PFont font;

int xpos;
int ypos;
int rectSize;

void setup() {
  size(600, 200);
  font = createFont("SansSerif", 72);
  textFont(font);
  textAlign(CENTER, CENTER);

  xpos = width/2;
  ypos = height/2;
  rectSize = 40;

  background(255);
  stroke(0);
  fill(255);
  rectMode(CENTER);
}

void draw() {
  rect(xpos, ypos, rectSize, rectSize);
} 

void keyPressed() {
  if (key == CODED) {
    if (keyCode == UP) {
      ypos -= 5;
    } else if (keyCode == DOWN) {
      ypos += 5;
    } else if (keyCode == LEFT) {
      xpos -= 5;
    } else if (keyCode == RIGHT) {
      xpos += 5;
    } else if (keyCode == SHIFT) {
      rectSize = 20;
    } else if (keyCode == CONTROL) {
      rectSize = 40;
    }
  }
}

void mousePressed() {
  background(255);
}
Your browser does not support canvas.

keyPressed() 또는 keyPressed

키보드 인풋을 받는 방법에도 마우스와 마찬가지로 두가지 방법이 있습니다. keyPressed()는 키가 눌렸을 때 한 번 호출되므로, 키를 계속해서 누르고 있어도 한 번만 실행되고, keyPressed 변수를 draw() 안에 위치시킨 경우, 키를 누르고 있으면 매 프레임마다 실행됩니다.

두 개 이상의 키를 동시에 입력

키보드의 각 키에 이미지를 세이브한다던지 각종 기능을 할당하는 것은 좋은 방법입니다. 하지만 사용자로부터 한글, 알파벳 등의 키 입력을 받아야할 때는 그 키들이 의도치않게 겹치게 되는 상황이 발생할 수도 있습니다. 일반적으로 우리가 사용하는 소프트웨어들 또한 이를 방지하기 위해서 각종 메뉴에 접근할 때는 두 개 이상의 키들을 동시에 누르도록 되어있습니다. 프로세싱에서 이를 적용하는 예제를 살펴봅시다. 아레 예제는 이곳을 참고하여 작성하였습니다.

boolean isCtrlPressed = false;
boolean isRPressed = false;
boolean isSPressed =false;

float greyVal;

void setup() {
  size(600, 100);
  background(0);
}

void draw() {
  background(greyVal);
  ellipse(width/2, height/2, 70, 70);
}

void keyPressed() {
  if (keyCode == CONTROL && isCtrlPressed == false) isCtrlPressed = true;
  if (char(keyCode) == 'R') isRPressed = true;
  if (char(keyCode) == 'S') isSPressed = true;

  if (isCtrlPressed) {
    if (isRPressed) greyVal = random(255);
    if (isSPressed) save("image.gif");
  }
}

void keyReleased() {
  if (keyCode == CONTROL) isCtrlPressed = false;
  if (char(keyCode) == 'R') isRPressed = false;
  if (char(keyCode) == 'S') isSPressed = false;
}
Your browser does not support canvas.

위와 같이, 각각의 키 조합에 해당하는 booleanif 조건문을 추가해줌으로써, 원하는 기능을 도입할 수 있습니다.