BLOG ARTICLE 변수 | 3 ARTICLE FOUND

  1. 2008.03.21 4.1 [중급] 대전게임 케릭터 만들기
  2. 2008.02.12 3.7 [초급] 다트 게임
  3. 2007.06.12 4. 변수 (2)

아래의 이미지들을 가지고 대전게임에서 나오는 캐릭터와 같은 에니메이션을 간단하게 만들어 보겠습니다. 좌측을 클릭하시면 이미지들을 압축한 파일만 다운로드 받으실 수 있습니다.

사용자 삽입 이미지

사용되는 키는 아래와 같습니다.
  • 위 방향키 - 점프
  • 아래 방향키 - 방어
  • 좌우 방향키 - 이동
  • a, s - 공격

1. 이미지(Costume) 준비

사용자 삽입 이미지
좌측과 같이 각 동작에 필요한 이미지들을 준비합니다. [Paint] 버튼을 눌러 직접 그리거나 [Import] 버튼을 클릭하여 준비된 이미지를 가져 옵니다. 제가 사용한 아래의 파일을 다운로드 받으셔서 사용하셔도 됩니다.




act1, act2는 평상시 동작, act3은 손공격, act4는 발공격, act5는 점프, act6은 방어 동작입니다.


사용자 삽입 이미지
주의 하실 점은 크기가 다른 각 이미지 마다 중심점을 맞추기 위해 모든 코스튬에서 [Edit]를 클릭하여 Paint Editor에서 [+] Set rotation center를 클릭하여 좌측과 같이 좌측 하단에 중심점을 맞추어 줍니다.



2. 변수 생성
변수(Variable)는 말 그대로 변할 수 있는 수입니다. 스크래치에서는 여러 유동적인 값을 저장하기 위해 사용합니다.

사용자 삽입 이미지
이번 장에서는 현재동작과 현재 프레임을 저장하기 위해 'curAction'과 'curFrame' 두개의 변수를 생성합니다. 변수를 생성하기 위해서는 좌측과 같이 Variables 를 클릭하고 [Make a variable]을 클릭합니다.



사용자 삽입 이미지
입력창에 좌측과 같이 curAction을 입력한 후에 하단의 [OK] 버튼을 클릭합니다.

완료되면 다시 [Make a variable]을 클릭해서 curFrame을 입력하고 [OK]버튼을 클릭합니다.





3. 사용자 키입력 처리
각 동작들은 한두개의 차이를 제외하고는 모두 비슷하기 때문에, 여기서는 가장 복잡한 점프 동작만 설명하겠습니다. 다른 동작들은 아래의 전체 스크립트를 참조하시기 바랍니다.

사용자 삽입 이미지
좌측은 위 방향키가 눌러졌을 때, 동작을 지정하는 블록들입니다.

[if <[curAction] = (0)>] 은 현재 동작(curAction)이 공격이나 점프가 아닌 일반 동작(0)일 경우에 실행되도록 합니다.

[set curAction to (1)] 현재 동작(curAction)을 1로 설정해 이제 현재 동작이 일반 동작이 아님을 나타냅니다.

[set curFrame to (20)] 현재 프레임을 20으로 설정합니다. 모든 동작은 40 프레임이 되면 초기화 되는데 20 프레임 동안 점프 동작을 보여주기 위해  (40-20=)20으로 설정합니다.

[change y by (30)] 다른 동작과 달리 점프 동작은 일반 동작 보다 위에 출력해야 하기 때문에 y 값에 30을 더해 30pixel 만큼 위로 올립니다.

[switch to costume [act5]] 현재 동작을 점프 동작(act5)으로 설정합니다.


4. 에니메이션
[forever]로 실행되는 동안 반복되면서 에니메이션을 출력하는 핵심 부분입니다. 한번 반복될 때 마다 프레임을 증가 시키고 현재 상태(curAction, curFrame)에 따른 필요한 작업을 수행합니다.

사용자 삽입 이미지
[if <[curAction] = (0)>] 블록은 현재 동작이 사용자가 동작 키를 입력하지 않은 일반동작(0)일 경우에만 실행됩니다.

현재 프레임이 0~19면 모양을 act1로 설정하고 20 이상이면 act2로 설정합니다. act1과 act2를 반복적으로 보여줌으로서 일반상태의 움직임을 표현합니다.

[change curFrame by (1)] 현재 프레임을 1 증가 시킵니다.

[if <[curFrame] > (40)>] 블록은 현재 프레임이 40보다 클경우 실행되며 각종 변수들과 동작을 초기화 합니다.

[if <(costume #)=(3)>] 현재 이미지가 점프 동작이면
[change y by (-30)]으로 점프시에 30 증가했던 값을 본래 위치로 설정합니다. (저는 코스튬을 몇번 삭제하고 다시 올렸기 때문에 3이지만 등록 순서에 따라 틀릴 수 있습니다. 점프 커스튬의 좌측 상단 숫자를 입력하시면 됩니다.)

[set curAction to (0)] 현재 동작을 일반동작(0)으로 설정합니다.

[set curFrame to (0)] 현재 프레임을(curFrame)을 0으로 만들어 프레임 값이 항상 0~39로 한정되게 합니다.

5. 완료

전체 스크립트는 아래와 같습니다. 조금 복잡하지만 이전 내용과 비슷한 부분이 많고 반복되는 부분을 제외하면 이해할 부분은 많지 않습니다.
사용자 삽입 이미지

웹에서 실행되는 모습과 프로젝트 파일 다운로드는 스크레치 사이트의 제 프로젝트에서 확인하실 수 있습니다.

이번 장에서는 빙글빙글 회전하는 표적에 마우스를 클릭하여 표적의 1, 2, 3, 4의 번호 중 하나를 선택하는 게임을 만들어 보겠습니다.
사용자 삽입 이미지
1. 스프라이트 추가

사용자 삽입 이미지
기존 스프라이트를 삭제하고 좌측의 스프라이트 추가 버튼을 클릭하여 표적과 화살 이미지를 선택하여 아래와 같이 두개의 스프라이트를 추가합니다. 표적과 화살 이미지는 페인트 툴에서 직접 그리거나 아래의 이미지 파일을 다운로드 받아 사용합니다.


2. 표적 스크립트

1) 변수 생성
변수(Variables)는 스크립트에서 특정 상태나 값을 저장하기 위해서 사용합니다. 변수는 말 그대로 변할 수 있는 수(값)이기 때문에 원하는 값을 설정하고 참조할 수 있습니다. 이 프로그램에서는 표적이 회전해야 하는지 화살이 발사되어 멈추어야 하는지를 판별하기 위해 isEnd란 변수를 생성하여 사용합니다.

isEnd가 1일 경우에는 사용자가 마우스를 클릭하여 화살이 발사된 경우며, 0일 경우에는 아직 발사하지 않은 상태입니다.

사용자 삽입 이미지
변수를 만들기 위해 Variables를 항목에서 [Make a variable] 항목을 클릭합니다.

아래와 같이 변수를 생성하는 창이 열리면, 생성될 변수의 이름을 입력한 후 [OK]를 클릭합니다.

For all sprites는 모든 스프라이트가 사용하도록 생성됩니다. For this sprite only는 해당 스프라이트에서만 사용하는 변수입니다. 여기서는 표적 스프라이트와 화살 스프라이트에서 모두 참조 하므로 아래와 같이 For all sprites를 선택합니다.

사용자 삽입 이미지

사용자 삽입 이미지
변수 생성이 완료되면 좌측과 같이 세개의 블럭이 추가됩니다. 각 블럭들의 의미는 다음과 같습니다.




[change 변수 by (값)]
변수에 (값)에 있는 수만큼 더 합니다.

[set 변수명 to (값)]
변수를 (값)에 입력된 값을 대입합니다.

[변수명]
변수를 if와 같은 조건 블럭이나 Numbers의 여러 대입, 비교등의 블럭내에 드래그 하여 가져다 놓을 수 있습니다.

사용자 삽입 이미지
앞의 체크박스를 클릭하면 좌측과 같이 실행화면에서 좌측과 같이 변수의 값을 확인할 수 있습니다.

2) 스프라이트
표적을 회전시키기 위해 해당 블럭들을 가져와서 아래와 같이 배치합니다.

사용자 삽입 이미지

[set isEnd to (0)]
시작시에 isEnd 값을 0으로 설정합니다.

[forever if <isEnd = (0)>]
포함된 명령어 블럭들이 isEnd가 0인 동안 무한으로 반복됩니다. 녹색의 <() = ()> 블럭은 Numbers에 위치합니다.

[turn (15) degrees]
표적을 한번 반복될 때마다 15도씩 시계방향으로 회전시킵니다.

3. 화살 스크립트

마우스가 클릭되었을 때 화살을 마우스가 눌려진 위치에 출력하고 표적을 정지 시키기 위해 아래와 같이 명령어 블럭들을 배치 합니다.

사용자 삽입 이미지

[hide]
시작시에는 화살의 모습을 숨깁니다.

if <<isEnd = 0> AND <mouse down?>>]
isEnd 변수가 0(표적이 회전 중)이고 사용자의 마우스가 눌러 졌을 경우에만 화살을 발사하도록 합니다. <> and <> 블록은 좌측과 우측의 값이 모두 참(1)일 경우에만 참(1)이 됩니다.

[set isEnd to (1)]
화살이 발사 되었으므로 isEnd를 1로 설정합니다. isEnd가 1이므로 표적이 회전하지 않습니다. (상단 표적 스프라이트 참조)

[play sound [pop]]
스프라이트가 기본으로 가지고 있는 소리(pop)를 출력합니다.

[go to [mouse-pointer]]
마우스 포인터가 위치한 곳으로 스프라이트의 위치를 이동합니다.

[show]
이제 화살을 출력합니다.

이제 테스트를 위해 시작버튼을 클릭합니다. 회전하는 표적에 마우스를 가져가서 클릭하면 해당 위치에 화살이 출력되며 표적이 멈춥니다.



C에서 변수는 말 그대로 변할 수 있는 값을 의미하며, 변수는 메모리에서 값을 저장할 수 있는 저장소 입니다.  "[변수타입] [변수명];"와 같이 변수를 선언하고 사용할 수 있습니다.

int a;

위에는 a라는 int(정수)형 변수를 선언한 예 입니다.  int가 변수타입 ,  a가 변수 명입니다.

4.1 변수 타입
변수 타입은 문자, 정수, 부동소수점 등 변수에서 저장할 값의 종류를 의미합니다.  C에서는 아래와 같은 변수타입을 제공합니다.
사용자 삽입 이미지

1) char

char 는 1바이트 크기를 가지고 있으며, ASCII 코드의 한 문자 또는 0~255(-128~127)의 숫자를 저장할 수 있습니다. C에서 char의 한 문자는 홑따움표(')를 사용하여 표현하고, 문자열은 쌍따움표(")를 사용하여 표현합니다.


2) short, int , long

일반적인 정수를 표현하기 위한 변수형입니다. 흔히 int는 해당 시스템이 가장 빠르게 처리할 수 있는 크기라고 하며, 일반적으로 2 또는 4Byte의 크기를 가집니다.


3) float, double

float과 double은 부동소수점을 표현하는데 사용됩니다. 모든 데이터를 2진수로 인식하는 컴퓨터에서는 부동소수점을 처리할 때, 약간의 문제가 있습니다. 이 부분에 대해서는 후에 기회가 되면 다시 언급하겠습니다.


4) signed/unsigned

각 변수 타입 앞에는 signed 또는 unsigned 를 선언할 수 있습니다. 생략된 경우에는 signed로 간주합니다. signed와 unsigned는 음수를 포함할 지, 포함하지 않을지를 결정합니다.

1byte인  char를 예로 들면 256가지 경우의 수를 표현할 수 있습니다. 이는 양수만 사용할 경우에는 0~255까지 표현할 수 있지만, 음수도 포함할 경우에는 -128~127까지 표현할 수 있습니다.

4바이트인 int형일 경우에는 비트로 환산하면 4X8=32Bit, 즉 232 까지 표현할 수 있습니다.

참고로 기계가 인식할 수 있는 단위를 비트(bit)라고 합니다. 1bit는 0과 1 (On/Off)의 두가지 경우의 값을 나타낼 수 있습니다. 1 Byte는 8개의 bit로 이루어져 있습니다. 그러므로 2에 8승, 즉 256가지 값을 표현할 수 있습니다.

소스에서 char c = 9; 라는 코드는 실행 시 메모리에 변수 c를 위해 1Byte(8bit)를 할당하고 값은 9로 저장되는데 , 이 9는 컴퓨터에서는 이진수 00001001로 아래와 같이 메모리에 저장됩니다.
사용자 삽입 이미지
위의 2진수는 아래와 같이 10진수로 변환할 수 있습니다.
(0 X 27) + (0 X 26) + (0 X 25) + (0 X 24) + (1 X 23) + (0 X 22) + (0 X 21) + (1 X 20)

첫번째와 네번째만 1이므로 우측으로 부터 첫번째와 두번째 연산인 (1 X 23) + (1 X 20)만 유효하므로 9로 표현됩니다. 2진수를 이해하고 있어야 C에서 사용하는 쉬프트 나 비트 연산을 이해할 수 있습니다.


5) 변수 타입 크기

각 변수타입들의 크기는 일반적으로 아래의 출력내용과 비슷하나, 시스템에 따라서 약간 틀릴 경우도 있습니다. 아래의 코드로 사용하는 시스템에서 변수 타입별로 크기를 확인하실 수 있습니다.

#include <stdio.h>

int main(int argc, char* argv[])
{
    char c;
    short s;
    int n; 
    long l;
    float f;
    double d;

    printf("char: %d\nshort: %d\nint: %d\nlong: %d\nfloat: %d\ndouble: %d\n",      sizeof(c), sizeof(s), sizeof(n), sizeof(l), sizeof(f), sizeof(d));
   
return 0;
}

위의 소스를 컴파일 하고 실행보면, 아래와 같이 각 변수타입 별 바이트 수를 확인하 실 수 있습니다. 아래는 제 맥의 터미널에서 실행해 본 화면 입니다.
사용자 삽입 이미지


4.2 변수명

1) 작성 규칙

변수명은 용도에 따라서 아래의 규칙에 따라 개발자의 마음대로 정할 수 있습니다.
  • 알파벳 대소문자([A-Z][a-z]), 숫자([0-9]), 언더바(_)를 사용할 수 있습니다.
  • 첫 글자에 숫자가 올 수 없습니다.
  • 대소문자를 구별합니다.

>> 아래는 유효한 변수명의 예 입니다.
int myData; int my_data; int mydata_1;

>> 아래는 사용할 수 없는 변수명의 예 입니다.
int my data; int 3mydata;

>> 아래의 temp와 Temp는 서로 다른 변수로 인식 합니다.
int temp; int Temp;


2) 명명법

함수도 마찬가지지만 변수명은 소스를 이해하기 쉽게 만드는데 많은 영향을 줍니다.

윈도우 API 함수와 MFC에서 주로 사용되는 헝가리안 표기법이 있습니다. 변수의 타입과 용도를 알기쉽게 만들어 주는 표기법입니다. 간단하게 예를 들면 아래와 같습니다.

char*  g_pszAppName;
g_ 는 전역변수를 의미하며, p는 포인터, sz은 NULL로 끝나는 문자를 의미 합니다. 그뒤의 이름은 의미별로 대문자로 구별합니다. 이런식의 표기법은 소스 중간에서 g_pszAppName을 보더라도 쉽게 타입과 용도를 추측할 수 있습니다.

하지만 일반적으로 unix C 프로그래머들은 대문자를 define, 전역변수나 특수한 용도가 아니면, 많이 사용하지 않았습니다. 대소문자 구분 대신 "_"를 주로 사용하였습니다. 위의 예를 들면 아래와 같습니다.
char* app_name;

그외 java나 objective-c에선 아래와 같은 명명법을 선호합니다.
char* appName;

어떤 것이 좋은지는 저도 알 수 없고,  윈도우 프로그래머라고 해서 꼭 헝가리안 표기법을 사용하거나 unix 나 java 프로그래머라고 해서 꼭 위와 같이 하지는 않습니다.

하지만 한가지 확실한 점은 일관된 규칙으로 의미있는 변수명을 사용해야 소스의 이해 및 관리가 쉽습니다. 간단히 예를 들면 만약 남녀의 수를 합산하는 코드가 있다면, 1)의 소스 보다 2)의 소스가 이해하기가 더욱 쉽습니다.

1) a = b + c;
2) total_count = man_count + woman_count;


4.3 변수 선언 위치
C에서 변수는 함수의 처음 위치에서 선언되어야 합니다. 하지만 C++ 부터 변수를 함수내에 사용하는 곳에서 자유롭게 선언할 수 있게 되었습니다.

오래된 C 컴파일러는 아래의 좌측 이미지와 같이 함수에서 사용하는 모든 변수들은 함수가 시작되면서 선언되어야 합니다.  하지만 C++의 장점을 포용한 현재의 C 컴파일러들은 아래의 우측과 같이 실행코드 중간에 int j; 와 같이 변수를 선언하는 것을 허용합니다.
사용자 삽입 이미지사용자 삽입 이미지


4.4 변수 유효 범위
변수는 선언되는 위치에 따라서 전역(global)변수와 지역(local)변수로 나누어 집니다.

#include <stdio.h>

int temp = 5;

int main(int argc, char* argv[])
{
    int temp = 3;

    printf("%d", temp);

    return 0;
}

위에서 int temp = 10; 으로 함수 밖에서 선언된 변수는 전역변수이며, 어느 곳에서나 사용될 수 있습니다.

main 함수 내의 int temp = 3; 으로 선언된 변수는 지역변수이며, main 함수 내에서만 사용할 수 있습니다. 

함수 내에서는 지역변수가 우선됨으로 위의 소스를 실행하면, 지역변수 temp의 3이 출력됩니다. main함수 내의 int temp =3; 라인을 삭제 후 컴파일 하면 전역변수 temp의 5가 출력됩니다.

전역변수와 지역변수의 큰 차이점은 전역변수는 어플리케이션 메모리 에서 힙(heap)이라 불리우는 곳에, 지역변수는 지역변수와 더불어 함수인자 등에 사용되는 스택(stack)에 위치 합니다.

스택은 시스템에 따라 제한된 일정한 크기를 가지고 있습니다. 그렇기 때문에 큰 배열 등의 대용량의 메모리가 필요한 경우에는 전역변수나 malloc등을 이용하여 힙영역에 위치 시키거나, 인자로 전달할 때는 포인터를 사용하여야 합니다.


4.5 static 변수
변수타입 앞에 static 이란 키워드를 사용할 수 있습니다. static 변수는 함수내에 지역변수로 선언 되더라도 스택이 아닌 힙 영역에 위치합니다. 그래서 한번 사용하고 값이 사라지는 일반 지역변수와는 달리 계속 그 값을 유지하고 있습니다.

#include <stdio.h>

void test1()
{
    int n = 0;
    printf("test1: %d\n", n);

    n = n + 1;
}

void test2()
{
    static int n = 0;
    printf("test2: %d\n", n);
   
    n = n + 1;
}

int main(int argc, char* argv[])
{
    test1();
    test1();
    test1();

    test2();
    test2();
    test2();

    return 0;
}

위의 소스코드를 컴파일하고 실행하면, 아래와 같은 결과를 확인할 수 있습니다.
사용자 삽입 이미지
test1 함수의 지역변수 n은 함수가 호출될 때마다 스택에 새로 생성되며, 0으로 초기화 됩니다. 그래서 함수 마지막에 1을 더 해주어도 항상 0으로 출력됩니다.

test2 함수의 static 변수 n은  어플리케이션 실행 시에 0으로 초기화되어 힙영역에 위치하기 때문에, 함수가 재호출되어도 항상 그 값을 유지하고 있습니다.

이상 변수에 관해서 알아 보았는데, 몇 가지 언급 되지 않은 내용이 있습니다. 전역 변수에서 static 사용, extern 키워드 등이 있습니다. 이는 여러 소스 파일을 사용할 경우에 사용되며, 추후에 다시 언급하겠습니다.

'프로그래밍 강좌 > C 언어 기초' 카테고리의 다른 글

6. 제어문  (0) 2007.06.14
5. 연산자  (0) 2007.06.13
4. 변수  (2) 2007.06.12
3. C 기초문법  (0) 2007.06.05
2. 소스코드, 컴파일, 링크  (6) 2007.06.04
1. C언어 공부를 위한 준비  (9) 2007.06.03