4-4-나.# 움직이기

다음 예제는 커서 이동키로 화면 중앙에 있는 #문자를 이동시킨다. 상하좌우 커서 이동키를 누르면 #문자가 이동하며 스페이스키는 화면을 지우고 Q를 누르면 프로그램이 종료된다. 사용자가 키조작을 통해 직접 프로그램을 움직여 볼 수 있는 재미있는 예제이다.

 

: movesharp

#include <Turboc.h>

 

void main()

{

     int x,y;

     int ch;

 

     x=40;

     y=10;

     clrscr();

 

     for(;;) {

          gotoxy(x,y);

          putch('#');

          ch=getch();

          if (ch == 0xE0 || ch == 0) {

              ch=getch();

              switch (ch) {

              case 75:

                   x=x-1;

                   break;

              case 77:

                   x=x+1;

                   break;

              case 72:

                   y=y-1;

                   break;

              case 80:

                   y=y+1;

                   break;

              }

          } else {

              switch (ch) {

              case ' ':

                   clrscr();

                   break;

              case 'q':

              case 'Q':

                    exit(0);

              }

          }

     }

}

 

예제를 입력한 후 실행해 보자. 커서키를 누르고 있으면 누른 키의 방향으로 #문자가 움직일 것이다. 실행중의 모습은 다음과 같다.

세 개의 변수를 사용하는데 x, y는 #의 현재 좌표이며 (40,10)으로 초기화했으므로 최초 #은 화면의 중앙쯤에 출력된다. ch는 getch 함수로 입력받은 키 코드값을 가지는데 이 값에 따라 #의 이동 방향이 결정된다. getch 함수는 A, B, 1, 2 같은 문자키가 눌러졌을 때 문자를 바로 돌려 주지만 커서 이동키나 펑션키같이 문자가 할당되지 않은 확장키에 대해서는 0xE0 또는 0의 특이값을 리턴하도록 되어 있다. 이 때는 getch를 한 번 더 호출해야 확장키의 코드를 구할 수 있다. 0xE0, 0중 어떤 특이값이 리턴되는가는 키에 따라 다른데 굳이 구분할 필요없이 둘 중 하나가 리턴되면 getch를 한 번 더 호출하여 확장키값을 얻으면 된다.

getch가 리턴하는 0xE0값은 일반적인 문자가 아닌 확장키라는 특이값인데 이 값을 ch로 입력받기 위해서 ch는 반드시 int형으로 선언해야 한다. char 타입은 0xE0라는 값을 저장할 수 있는 크기를 가지지 못하기 때문이다. 커서 이동키는 getch 함수를 두 번 호출해서 입력받는데 이때 getch 함수가 읽는 커서 이동키의 확장키 코드는 다음과 같다. ch 변수에 저장된 키값을 이 키코드들과 비교해 보면 사용자가 어떤 방향으로 이동하고 싶은지를 알 수 있다.

프로그램 전체는 for 무한루프로 싸여져 있어 Q를 입력할 때까지 같은 루틴을 계속 반복한다. 각 루프에서 현재 위치 (x, y)에 #문자를 출력하며 키 입력을 받아 이 키값에 따라 x, y 변수의 좌표를 조정한다. 왼쪽 이동키인 75를 누르면 x를 1 감소시켜 #을 한칸 왼쪽으로 이동시키며 아래쪽 이동키인 80을 누르면 y를 1증가시켜 #이 아래쪽으로 이동하도록 한다. 커서 이동키 외에 두 개의 키를 더 처리하고 있는데 스페이스 키를 누르면 clrscr 함수를 호출하여 화면을 깔끔하게 지우며 Q 키(또는 q키)를 누르면 프로그램을 종료한다.

ch에 입력받은 키값에 따라 여섯 가지 다른 처리를 각각 하고 있으므로 다중 선택문인 switch문이 사용되었다. switch문은 입력받은 키 값에 따라 좌표를 옮기거나 화면을 지우거나 프로그램을 종료하는 다중 분기 처리를 하며 이 코드가 for 무한 루프로 둘러 싸여 있으므로 사용자가 Q키를 누를 때까지 무한히 반복할 것이다.

지금까지 작성했던 짧은 예제들에 비해 길이가 조금 길기는 하지만 구조가 단순하기 때문에 어렵지는 않다. 이 예제를 완전히 이해한 후 다음 실습을 해 보자. 일종의 패턴 연습이다. 이 중 몇 가지는 스스로 코드를 작성할 수 있으며 이런 문제를 가급적 많이 풀어 봐야 한다.

 

 커서 이동키나 공백키, Q가 아닌 키를 누르면 잘못 눌렀다는 의미로 삑삑 소리를 내 보자. 확장열 \a를 출력하면 스피커로 삑 소리가 난다. switch 문의 case에 지정된 여섯 가지 외의 다른 키에 대한 처리를 default로 처리하면 된다. 두 개의 switch문 끝에 다음 코드를 작성하자.

 

if (ch == 0xE0 || ch == 0) {

     ch=getch();

     switch (ch) {

     ....

    default:

        putch('\a');

        break;

     }

} else {

     switch (ch) {

     ....

    default:

        putch('\a');

        break;

     }

}

 

ch가 case에 없는 키값을 가질 경우, 예를 들어 A나 3같은 키를 누르면 default case가 선택되어 삑 소리를 내며 투덜거릴 것이다. 제일 끝에 있는 break문은 뒤쪽에 다른 case가 없기 때문에 사실 굳이 필요하지는 않다. 그러나 일관성과 확장성을 위해 모든 case에 break문을 넣는 것이 좋은데 default 다음에 다른 case가 필요해질 수도 있기 때문이다. 예제에서 case 'Q'에는 break가 없는데 여기에도 break를 넣는 것이 원칙이기는 하다. 그러나 exit 함수는 프로그램을 즉시 종료하는 워낙 특수한 명령이라 break를 넣지 않았다.

 커서 이동키를 누름에 따라 x, y 좌표를 조정함으로써 #문자가 이동하는데 이 변수들의 값이 무조건 증가, 감소하도록 되어 있어 왼쪽 끝에서 더 왼쪽으로 갈 수 있고 아래쪽보다 더 밑으로 내려갈 수도 있다. 이렇게 되면 #이 화면에서 사라지는 경우도 있는데 x, y 좌표가 화면을 벗어나지 않도록 해보자.

화면의 왼쪽 위 좌표는 (0,0)이고 오른쪽 아래 좌표는 (79,24)이므로 x, y변수가 이 영역을 벗어나지 않도록 하면 된다. 각 커서 이동키를 처리하는 case에 if 조건을 넣어 해당 방향으로 이동 가능할 때만 x, y 변수값을 조정하도록 하자.

 

switch (ch) {

case 75:

     if (x > 0) x=x-1;

     break;

case 77:

     if (x < 79) x=x+1;

     break;

case 72:

     if (y > 0) y=y-1;

     break;

case 80:

     if (y < 24) y=y+1;

     break;

     ....

 

왼쪽으로 이동하기 전에 x가 0보다 큰지를 점검해 보고 0보다 큰 경우에만 x를 1 감소시켰다. 만약 x가 0이라면 즉 왼쪽 벽에 닿아 있다면 x값을 그대로 유지하여 #이 화면 왼쪽 밖으로 나가지 않도록 했다. 나머지 방향도 비교하는 값만 다를 뿐 논리는 동일하다.

 이번에는 화면 밖을 벗어나면 반대쪽에 # 문자가 나타나도록 해 보자. 즉, 왼쪽 끝에서 더 왼쪽으로 가면 오른쪽 끝에 #이 나타나도록 하는 것이다. 이것도 조건에 따라 좌표만 조작하면 된다. 대표적으로 왼쪽 이동의 경우만 보자.

 

switch (ch) {

case 75:

     if (x > 0) {

          x=x-1;

     } else {

          x=79;

     }

     break;

 

코드 자체가 설명적이므로 더 이상의 잔소리는 불필요할 것이다. 4방향 모두 이런 코드를 작성하면 #문자가 화면 끝을 지나 마음대로 이동할 수 있다.

 이동하는 문자가 #으로 고정되어 있는데 사용자의 기호에 따라 이 문자를 변경할 수 있도록 해 보자. 1을 누르면 $, 2는 @ , 3은 #,  4는 공백으로 문자를 변경하도록 한다. 이렇게 하려면 putch('#')문에 사용된 '#' 문자 상수를 변수로 바꾸어야 한다.

 

char munja='#';

 

for(;;) {

     gotoxy(x,y);

     putch(munja);

 

munja라는 이름으로 문자형 변수를 선언하고 '#'으로 초기화한 후 putch 함수로 이 문자를 출력하도록 했다. 여기까지는 앞의 예제와 동일하지만 putch가 출력하는 대상이 문자 상수에서 문자 변수로 바뀌었으므로 이 변수값만 바꾸면 출력되는 문자가 달라질 것이다. 문자를 처리하는 아래쪽 switch 문에 다음 case를 추가한다.

 

} else {

     switch (ch) {

     case ' ':

          clrscr();

          break;

     case 'q':

     case 'Q':

          exit(0);

    case '1':

        munja='$';

        break;

    case '2':

        munja='@';

        break;

    case '3':

        munja='#';

        break;

    case '4':

        munja=' ';

        break;

     }

}

 

1, 2, 3, 4 키를 누를 때 munja 변수를 적절한 다른 문자로 바꾸기만 하면 된다. 무엇인가 변할 수 있는 정보를 저장할 때 쓰는 것이 바로 변수이다.

커서 이동키를 입력받기 위해 확장키에 대해 간략하게 소개했는데 그렇다면 기능키나 Ins, Del 같은 나머지 확장키의 코드는 어디를 보면 알 수 있을까? 확장키는 키보드의 스캔 코드로 정의되는데 이 정보를 알고 싶으면 스캔 코드표를 보면 된다. 그러나 지금은 구하기도 힘든 스캔 코드표를 보는 것보다 더 좋은 방법은 프로그램을 짜서 실행해 보는 것이다.

 

: scancode

#include <Turboc.h>

 

void main()

{

     int ch;

     for(;;) {

          ch=getch();

          if (ch == 0xE0 || ch == 0) {

              ch=getch();

              printf("확장 키 입력, 코드 = %d\n",ch);

          } else {

              printf("일반 문자 입력, 문자 = %c, 코드 = %d\n",ch,ch);

              if (ch == 'q') exit(0);

          }

     }

}

 

이 예제는 누르는 키의 종류를 판별해서 문자, 확장키 코드를 출력한다. 이 프로그램을 실행해 놓고 알고 싶은 확장키를 눌러보면 확장키 번호를 쉽게 알 수 있다.