4-2-나.문자 움직이기

루프라는 개념을 이해하면 아주 많은 것들을 할 수 있게 된다. 실무 프로그래밍을 해 보면 알겠지만 프로그램은 순전히 루프 투성이인데 프로그램이 하는 일이라는 것이 사람의 명령을 반복적으로 처리하는 것이기 때문이다. 반복이라는 것도 완전히 똑같은 작업을 단순히 되풀이하기만 하는 것이 아니라 매 반복때마다 제어 변수의 값을 참조하여 조금씩 다른 작업을 할 수 있어 응용할 수 있는 여지가 아주 많다.

for문을 배웠으니 이제 약간은 흥미로운 예제를 분석해 보자. 이 예제도 무척 간단하기는 하지만 지금까지의 예제에 비해서는 상대적으로 재미있는 동작을 보여준다. 여기서 만들 예제는 # 문자를 화면의 왼쪽에서 오른쪽으로 움직인다. 다음 예제를 실행해 보자.

 

: movechar

#include <Turboc.h>

 

void main()

{

     int i;

     clrscr();

 

     for (i=1;i<=80;i=i+1) {

          gotoxy(i,10);

          putch('#');

          gotoxy(i-1,10);

          putch(' ');

          delay(100);

     }

}

 

프로그램을 시작하자 마자 clrscr 함수를 호출하여 화면을 깨끗하게 지운다. 그리고 제어 변수 i를 1부터 시작해서 80까지 1씩 증가시키면서 #과 공백을 번갈아가며 출력한다. 실행해 보면 #문자가 최초 화면 왼쪽에 나타났다가 오른쪽 끝으로 한칸씩 이동할 것이다.

문자 하나만 움직이지만 이것도 일종의 애니메이션이라고 할 수 있다. 어떻게 해서 #이 움직이는지 스스로 분석해 보아라. 모두 앞에서 배운 것들이므로 스스로 분석이 가능할 것이다. 루프를 돌 때 제어 변수가 어떻게 변하고 루프 내에서 제어 변수를 어떻게 사용하는지 차근 차근히 분석해 보아라. 다음은 이 프로그램의 동작을 실행 순서별로 해설해 놓은 것인데 자신이 분석한 결과와 맞는지 비교해 보자.

 

정수형 변수 i를 선언하고 clrscr 함수를 호출하여 화면을 깔끔하게 지웠다. 화면에 아무 것도 출력되어 있지 않아야 #문자가 움직이는 것을 제대로 살펴볼 수 있을 것이다. 여기까지는 프로그램이 실행을 준비하는 단계이다.

for 루프가 시작되면 초기식에 의해 제어 변수 i는 1이 된다. 조건식은 i가 80보다 작거나 같다이고 증감식은 i를 1씩 증가하도록 되어 있으므로 i는 1부터 2,3,4,....80까지 변할 것이다. 루프안에는 #문자를 출력하는 명령, 공백을 출력하는 명령, 그리고 0.1초간 시간을 끄는 명령이 포함되어 있다.

최초 루프가 시작될 때 i는 1의 값을 가지고 있다. 그래서 (1,10)의 위치에 #이 출력되고 (0,10)의 위치에 공백이 출력된다. 이때 화면에는 (1,10)에 #문자 하나만 출력되어 있을 것이다. 0.1초간 대기한 후 증감식에 의해 i는 2가 되고 다음 루프가 실행된다.

i가 2가 되었을 때는 (2,10)에 #이 출력되고 그 바로 왼쪽인 (1,10)에 공백이 출력된다. 공백이 출력되는 좌표는 제어 변수 i의 바로 왼쪽 위치인 (i-1,10)인데 이 좌표는 바로 직전의 루프에서 #을 출력한 좌표이다. 이 좌표에 공백을 출력한다는 것은 바로 앞에서 출력했던 #을 삭제하라는 명령이다. 두 번째 루프에 의해 (1,10)의 #은 지워지고 (2,10)에 #이 새로 출력되었으므로 마치 #이 한칸 오른쪽으로 이동한 것처럼 보인다.

i가 3이 되면 (3,10)에 새로운 #이 출력되고 바로 직전 루프에서 출력해 놓은 (2,10)의 #은 공백으로 덮여져 삭제된다. 이런 식으로 루프가 진행되면 1씩 증가하는 i위치에 #이 새로 출력되므로 마치 #이 오른쪽으로 이동하는 것처럼 보이게 된다.

for문의 조건식이 i가 80보다 작거나 같을 때까지로 되어 있으므로 i는 계속 증가하여 80까지 갈 것이며 그래서 #은 (80,10)의 위치까지 반복적으로 계속 이동하게 된다. 화면의 오른쪽 끝 좌표는 79이므로 (80,10)은 실제로 존재하지 않으며 #은 왼쪽에서 오른쪽으로 이동하여 오른쪽 끝에서 사라지게 될 것이다.

제어 변수 i가 81이 되었을 때 조건식이 거짓이 되므로 이때 루프를 탈출하며 프로그램이 종료된다.

 

이렇게 단계를 설명해도 컴퓨터의 실행 속도가 너무 빠르기 때문에 실행 흐름이 잘 파악되지 않는데 이럴 때는 각 출력문 다음에 getch();를 삽입하여 진행 과정을 느리게 관찰할 수 있다. # 문자가 이동하는 것처럼 보이는 것은 현재 자리 출력, 바로 앞자리 삭제를 반복적으로 처리하는 간단한 원리로 이루어진다. 게임에서 총알이 날아가고 캐릭터가 움직이는 기본 원리도 이 예제와 동일하다. 즉 헌 것 지우고 새 것 그리기의 연속인 것이다.

이전 자리를 지우고 다시 출력하는 대신 puts(" #")으로 앞쪽에 공백을 하나 출력하여 왼쪽을 지움과 동시에 오른쪽에 '#'을 출력할 수도 있다. 그러나 이는 일반적이지 않은데 그래픽 환경에서는 지움과 출력의 방법이 완전히 달라질 수 있기 때문이다. 가령 배경에 아주 복잡한 그림이 있을 경우는 지우는 동작이 단순히 공백을 출력하는 정도가 아니라 굉장히 어려워질 수 있다. 그래서 원론적으로 물체를 움직이는 방법은 헌 것 지우고 새 것 그리기여야만 하며 이 예제는 원칙대로 작성한 것이다. 반복문을 작성할 때는 다음 두 가지 사항을 잘 결정해야 한다.

첫 번째로 한 번 반복할 명령들의 단위를 잘 파악해야 한다. 어떤 명령들의 묶음이 한 번 반복할 대상인지를 잘 관찰하는 것이 중요하다. 이 예제의 루프에는 다섯 개의 명령이 포함되어 있지만 gotoxy와 putch는 한 쌍으로 볼 수 있으므로 다음 세 개의 명령이 반복 단위이다. 루프를 한 번 돌 때마다 이 세가지 명령을 순서대로 실행하여 출력, 지움, 시간끌기를 하며 이 세 동작의 조합에 의해 글자가 한칸 오른쪽으로 이동한다.

 

for (i=1;i<=80;i=i+1) {

     gotoxy(i,10);putch('#');                    // 현재 위치에 # 출력

     gotoxy(i-1,10);putch(' ');                 // 이전 위치의 # 지움

     delay(100);                                // 0.1초간 시간을 끈다.

}

 

명령들간의 순서에도 의미가 있는데 이 예제처럼 새 위치에 그리기, 이전 위치 지우기, 시간끌기 순으로 되어 있어야 애니메이션 결과를 제대로 보여줄 수가 있다. 만약 delay(100); 이 두 출력문의 사이에 위치하고 있다면 대기하는 0.1초동안 이전 출력이 지워지지 않은 상태가 되므로 두 개의 #이 같이 이동하는 것처럼 보일 것이다.

두 번째로 각 반복 단위에서 변화를 줄 수 있는 부분을 잘 파악해야 한다. 매번 같은 명령만 반복한다면 똑같은 명령만 의미없이 계속 실행될 뿐이므로 루프내의 각 명령문에서 제어 변수를 적절하게 참조하여 매 반복마다 조금씩 다른 동작을 하도록 해야 한다. 이 예제의 출력 명령은 i위치에 #을 출력하며 삭제 명령은 i-1위치를 지움으로써 제어 변수값을 참조하고 있다. 이 명령들을 감싸는 루프에서 제어 변수 i를 1~80까지 돌리므로써 #이 1~80까지 이동하는 것이다.

사실 이 예제는 도저히 이해하지 못할만큼 어렵지도 않을 뿐더러 프로그램이 동작하는 결과를 화면으로 확인해볼 수 있기 때문에 나름대로 재미도 있다. 이 예제를 이해했다면 약간의 응용을 해 보도록 하자. #을 좌우로가 아닌 상하로도 움직일 수 있고 대각선으로도 움직여 볼 수 있을 것이며 루프를 여러 개 사용하면 화면을 한바퀴 돌도록 할 수도 있다.