(CGP) 11장 Snake 게임 툴 만들기

Snake 게임 고전 게임중 하나이며 맵을 제작하여 적용할 수 있는 게임에 해당이 된다.

게임을 제작하기 이전에 맵 툴을 만들고 툴의 데이터를 게임에 적용하여 보자.

 

분노2 유튜브 동영상 강의 주소

 

(1) http://youtu.be/d6ktsi78C-o

(2) http://youtu.be/_iM9YXlTd5M

(3) http://youtu.be/RfCxSff2fls

(4) http://youtu.be/rGbWKRq4-Vc

 

 


11.1 툴 제작 개요

 

Snake 게임을 만들기 전에 Snake 게임의 각 스테이지를 디자인할 수 있는 툴부터 제작해 보자. 여기서는 다음 장에서 제작하게 되는 Snake 게임에 필요한 맵 디자인부터 몹과 먹이 그리고 뱀의 위치가 지정되도록 툴을 디자인하고 제작하게 된다.

 

툴을 처음 제작하게 되면 게임에 필요한 데이터를 다 파악하지 못하는 경우가 대부분이다.

그러므로 툴은 한 번에 만들어지기 보다는 여러 번의 수정 과정을 통해 업그레이드되므로

이전 버전에서 저장한 데이터를 현재 툴에도 적용할 수 있게 툴 설계를 해야 한다.

 

 


11.2 기획

 

■ 스토리

 

적 뱀을 피해 다니면서 제한 시간 안에 사방에 떨어져 있는 먹이를 모두 먹어라.

 

■ 게임 방식

 

주인공 뱀은 다른 방향키를 누르기까지 현재 설정된 방향으로 계속 이동한다.

주인공 뱀과 적 캐릭터가 부딪히면 주인공 뱀의 꼬리는 하나씩 줄어들지만 먹이를 먹으면 하나씩 늘어난다.

 

■ 제한 사항

 

각 스테이지마다 제한된 시간이 있으며 제한 시간 안에 먹이를 전부 먹어야만 다음 스테이지로 넘어간다. 기본 꼬리는 3개로 하며 꼬리가 없는 상태에서 적 뱀과 충돌하면 게임은 종료된다.

 

■ 기획 화면

 

[그림 11-1] 게임 기획 화면

 

 


11.3 실행 화면 

 

[그림 11-2] 메인 메뉴 화면

 

[그림 11-3] 파일 열기 화면

 

[그림 11-4] 맵 편집 화면

 

[그림 11-5] 스테이지 정보 입력 화면

 

 


11.4 툴 제작 로드맵

 

Snake 맵 툴 제작은 다음과 같이 5단계로 나눌 수 있다.

 

1단계는 기본적인 구조와 데이터를 결정하는 부분으로 이 단계가 전체 툴의 기능을 정의하는 부분에 해당된다.

 

2단계는 맵을 쉽게 편집할 수 있도록 하는 단계이다. 물론 윈도우 프로그램일 경우에는 마우스와 같은 인터페이스를 사용할 수 있겠지만, 지금 제작하는 프로그램 환경은 콘솔형이므로 키 입력만으로 편집이 가능하도록 해야 한다. 또한 맵마다 적 캐릭터와 먹이, 주인공의 위치를 정하여 스테이지를 구성할 수 있도록 해야 한다.

 

3단계는 스테이지 정보 중에서 제한 시간을 입력하거나 수정하는 부분이다.

 

4단계는 지금까지 입력된 내용을 저장하거나 읽는 단계이다.

 

5단계는 제작된 툴을 테스트하고 저장된 데이터를 확인하여 툴의 기능이 제대로 동작하는지를 테스트해 보는 단계이다.

 

여기까지의 여러 단계를 제작 로드맵으로 나타내면 아래 [그림 11-6]과 같다.

 

[STEP 01]

 

[STEP 02]

 

[STEP 03]

 

[STEP 04]

 

[STEP 05]

 

[그림 11-6] 툴 제작 로드맵

 

 


11.5 단계별 프로그래밍

 

[STEP 01]

  

[그림 11-7] 1단계 툴 제작 로드맵

 

■ 데이터 분석

 

앞장에서 제작한 하트 게임 툴은 하나의 데이터 파일 안에 여러 개의 스테이지 정보를 한꺼번에 입력하는 방식이었다. 그러나 Snake 맵 툴의 경우는 하나의 파일 안에 하나의 스테이지 정보만 저장되므로 데이터를 관리하는 링크드 리스트가 필요 없다.

 

Snake 게임 툴에서 저장하는 데이터는 크게 두 가지가 있다.

 

첫째, 스테이지 정보이다.

 

 

① 적 캐릭터 개수

② 스테이지의 제한 시간

③ 먹이 개수

 

[표 11-1] 스테이지 정보

 

위의 스테이지 정보에서 먹이 개수는 스테이지를 완료하게 하는 속성이 되지만 스테이지의 제한 시간은 게임을 실패하게 만드는 속성이다.

즉 제한 시간 안에 먹이를 다 먹지 못한다면 현재의 스테이지는 실패하게 되는 것을 말한다. 

 

둘째, 맵 정보가 있다.

맵은 게임 전체 화면을 구성하는 정보가 되며 맵 위에 적 캐릭터와 먹이등을 설정하게 된다.

 

 

① 맵 정보

② 적 캐릭터의 위치 설정

③ 주인공의 위치 설정

④ 먹이 및 위치 설정

 

[표 11-2] 맵 정보

 

이제까지 살펴본 내용을 정의하면 아래 [소스 11-1]과 같다.

 

 

#define MAP_COL  29

#define MAP_ROW  22

 

typedef struct _STAGE_INFO

{

       int nEnemyCount;                 // Note: 적 캐릭터의 개수

       clock_t LimitTime;                // Note: 스테이지의 제한 시간

       int nEatCount;                     // Note: 먹이 개수

       int nMap[MAP_ROW][MAP_COL]; // Note: 맵 정보

} STAGE_INFO;

 

[소스 11-1] 스테이지의 전체 정보 정의

 

위의 구조체에서 nMap[MAP_ROW][MAP_COL] 변수는 실제 맵의 정보를 저장하는 변수이다. 전체 맵이 가로 세로의 개념을 가지므로 이차원 배열의 행과 열을 적용할 수 있다.

따라서 맵 (0, 0)은 nMap[0][0]과 같으며 맵 데이터를 설정한다는 것은 이차원 배열에 값을 설정하는 것과 같다. 이때 설정되는 데이터에 따라 맵 데이터가 될 것인지 아니면 적 캐릭터가 될 것인지는 아래 [표 11-3]에 따라 결정된다.

 

 

 

맵 정보

0

1

2

3

의미

블록

먹이

적 

주인공

출력 캐릭터

 

[표 11-3] 맵 데이터

 

■ 메인 메뉴

 

기본적인 메인 메뉴는 파일 열기와 파일 저장, 그리고 종료가 있으며 데이터를 설정하거나 편집하기 위한 메뉴로는 맵 정보 편집과 스테이지 정보 편집이 있다.

이와 같은 사항을 정리하면 아래 [표 11-4]와 같다.

 

 

① 파일 열기

② 파일 저장

③ Map 정보 편집

④ 스테이지의 정보 편집

⑤ 종료

 

[표 11-4] 메인 메뉴 항목

 

■ 메인 메뉴 화면

 

[그림 11-8] 메인 메뉴 화면

 

[그림 11-8]을 보면 메인 메뉴에서 서브 메뉴들을 실행시킬 수 있으며 선택된 서브 메뉴는 실제적인 데이터를 입력, 수정, 삭제, 출력한 후에 다시 메인 메뉴로 제어가 옮겨진다.

아래 [그림 11-9]는 메인 메뉴와 서브 메뉴의 제어 흐름을 나타낸 것이다.

 

[그림 11-9] 메인 메뉴와 서브 메뉴와의 제어 흐름도

 

[실습 예제 11-1]

 

위의 [그림 11-8]과 같이 메인 메뉴가 출력될 수 있도록 프로그래밍해 보자.

 

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

#include <stdio.h>

#include <windows.h>

#include <malloc.h>

#include <conio.h>

#include <time.h>

 

void gotoxy( int x, int y )

{

     COORD CursorPosition = { x, y };

     SetConsoleCursorPosition( GetStdHandle( STD_OUTPUT_HANDLE ), CursorPosition );

}

 

int MainMenu()

{

    int nMenu;

    gotoxy( 22, 5 );  printf( "뱀 게임 월드 툴" );

    gotoxy( 24, 8 );  printf( "1. 열기" );

    gotoxy( 24, 10 ); printf( "2. 저장" );

    gotoxy( 24, 12 ); printf( "3. 스테이지 정보 편집" );

    gotoxy( 24, 14 ); printf( "4. Map 정보 편집" );

    gotoxy( 24, 16 ); printf( "5. 종료" );

    gotoxy( 27, 18 ); printf( "메뉴를 선택하세요 [   ]" );

    gotoxy( 47, 18 ); scanf( "%d", &nMenu ); fflush( stdin );

    return nMenu;

}

 

void ScreenLine()

{       

     gotoxy( 0, 0 );

     printf("┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n");

     printf("┃                                                          ┃\n");

     printf("┃                                                          ┃\n");

     printf("┃                                                          ┃\n");

     printf("┃                                                          ┃\n");

     printf("┃                                                          ┃\n");

     printf("┃                                                          ┃\n");

     printf("┃                                                          ┃\n");

     printf("┃                                                          ┃\n");

     printf("┃                                                          ┃\n");

     printf("┃                                                          ┃\n");

     printf("┃                                                          ┃\n");

     printf("┃                                                          ┃\n");

     printf("┃                                                          ┃\n");

     printf("┃                                                          ┃\n");

     printf("┃                                                          ┃\n");

     printf("┃                                                          ┃\n");

     printf("┃                                                          ┃\n");

     printf("┃                                                          ┃\n");

     printf("┃                                                          ┃\n");

     printf("┃                                                          ┃\n");

     printf("┃                                                          ┃\n");

     printf("┃                                                          ┃\n");

     printf("┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n");                

}

 

int main(void)

{

    int nMainMenu;

    

    while( 1 )

    {   

        ScreenLine();

        nMainMenu = MainMenu();

 

        switch( nMainMenu )

        {

        case 1 :                                        

                break;

        case 2 :                         

                break;

        case 3 :                         

                break;

        case 4 :                         

                break;

        case 5 :                         

                break;

        }

     }  

     return 0;

}

 

[소스 11-2] 메인 메뉴 화면 소스

 

 

 


STEP 02

  

[그림 11-10] 2단계 제작 로드맵

 

■ 맵(Map)

 

맵 정보를 저장하기 위해서는 메모리가 필요하다. 그래서 1단계에서는 맵 정보를 저장하기 위한 nMap[MAP_ROW][MAP_COL] 변수를 선언했으며, 이 변수의 행과 열 인덱스는 화면의 가로, 세로 컬럼과 일대일 대응된다.

다음의 [그림 11-11]은 화면 (0, 0)을 맵 시작점으로 하고, 맵 배열과 화면 좌표간의 관계를 그림으로 나타낸 것이다.

[그림 11-11] 맵과 화면 좌표 관계

 

맵의 정보 입력, 수정, 삭제

 

맵 정보를 입력하는 것은 맵 배열에 [표 11-3]에서 정의한 인덱스를 넣어주는 것이며 수정은 수정하고자 하는 위치의 맵 배열에 값을 다시 입력하는 것을 말한다. 그리고 삭제는 출력과 상관없는 값을 맵 배열에 넣어 출력에서 제외시키는 것을 말한다.

그러므로 맵 전체를 지운다는 것은 출력과 상관없는 값을 전체 맵 배열에 채우는 것을 의미한다.

 

[그림 11-12] 맵 편집의 초기 실행 화면

 

[그림 11-13] 맵의 설정 화면

 

메뉴는 [그림 11-12] 맵 편집 초기 화면의 오른쪽이 해당되며 이 메뉴를 통해 맵과 캐릭터가 선택된다. 먼저 1에서부터 4까지의 키는 맵을 구성하기 위한 것으로 키 입력이 있으면 커서는 맵 화면으로 이동한다. 이때 방향키를 이용하여 커서를 상하좌우로 이동하고 s키를 입력하여 현재 선택된 캐릭터의 인덱스가 맵 배열에 저장이 되게 한다.

 

q 키는 현재 맵의 편집 상태에서 빠져나와 오른쪽 편집 메뉴에서 키 입력을 받는 상태로 전환한다.

 

[그림 11-14] 1키를 입력한 후에 방향키로 이동하면서 s키로 블록을 설정하는 화면

 

[실습 예제 11-2]

 

위의 [그림 11-14]와 같이 맵을 편집할 수 있는 프로그램을 작성해 보자. 1에서부터 4까지는 맵에 입력할 대상을 선택하는 키이며 방향키는 맵 편집 상태에서 상하좌우로 커서를 이동시키는 역할을 한다. 만약 맵 편집 상태에서 캐릭터를 설정하려면 s키를 입력하면 된다. 그리고 k 키는 맵의 편집 상태에서 맵 데이터를 지우는 역할을 한다.

 

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

경축! 아무것도 안하여 에스천사게임즈가 새로운 모습으로 재오픈 하였습니다.
어린이용이며, 설치가 필요없는 브라우저 게임입니다.
https://s1004games.com

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

#include <stdio.h>

#include <windows.h>

#include <malloc.h>

#include <conio.h>

#include <time.h>

 

#define MAP_COL  29

#define MAP_ROW  22

 

typedef struct _STAGE_INFO

{

        int nEnemyCount;   // 적 캐릭터 개수

        clock_t LimitTime; // 스테이지 제한 시간

        int nEatCount; // 먹이 개수

        int nMap[MAP_ROW][MAP_COL];

} STAGE_INFO;

 

int g_EditState = 0; // 캐릭터 설정 번호

char g_strEditMode[4][10] = { "블럭  ", "먹이  ", "적    ", "주인공" };

int g_nCol = 14, g_nRow = 10; // 초기 커서 위치

char g_StateShape[4][3] = { "■", "♥", "⊙", "●" };

STAGE_INFO g_StageInfo;

 

void gotoxy( int x, int y )

{

     COORD CursorPosition = { x, y };

     SetConsoleCursorPosition( GetStdHandle( STD_OUTPUT_HANDLE ), CursorPosition );

}

 

int MainMenu()

{

int nMenu;

gotoxy( 22, 5 );  printf( "뱀 게임 월드 툴" );

gotoxy( 24, 8 );  printf( "1. 열기" );

gotoxy( 24, 10 ); printf( "2. 저장" );

gotoxy( 24, 12 ); printf( "3. Map 정보 편집" );

gotoxy( 24, 14 ); printf( "4. 스테이지 정보 편집" );

gotoxy( 24, 16 ); printf( "5. 종료" );

gotoxy( 27, 18 ); printf( "메뉴를 선택하세요 [   ]" );

gotoxy( 47, 18 ); scanf( "%d", &nMenu ); fflush( stdin );

return nMenu;

}

 

void ScreenLine()

{       

gotoxy( 0, 0 );

printf("┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n");

printf("┃                                                          ┃\n");

printf("┃                                                          ┃\n");

printf("┃                                                          ┃\n");

printf("┃                                                          ┃\n");

printf("┃                                                          ┃\n");

printf("┃                                                          ┃\n");

printf("┃                                                          ┃\n");

printf("┃                                                          ┃\n");

printf("┃                                                          ┃\n");

printf("┃                                                          ┃\n");

printf("┃                                                          ┃\n");

printf("┃                                                          ┃\n");

printf("┃                                                          ┃\n");

printf("┃                                                          ┃\n");

printf("┃                                                          ┃\n");

printf("┃                                                          ┃\n");

printf("┃                                                          ┃\n");

printf("┃                                                          ┃\n");

printf("┃                                                          ┃\n");

printf("┃                                                          ┃\n");

printf("┃                                                          ┃\n");

printf("┃                                                          ┃\n");

printf("┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n");     

}

 

void EditInfo()

{       

gotoxy( 62, 2 );

printf( "편집 상태: %s", &g_strEditMode[g_EditState] );

gotoxy( 62, 4 );

printf( "공백키 : k" );

gotoxy( 62, 6 );

printf( "설정키 : s" );

gotoxy( 62, 8 );

printf( "1. 블럭(■)");

gotoxy( 62, 10 );

printf( "2. 먹이(♥)");

gotoxy( 62, 12 );

printf( "3. 적(⊙)");

gotoxy( 62, 14 );

printf( "4. 주인공(●)");

gotoxy( 62, 16 );

printf( "5. 맵 전체 지우기" );

gotoxy( 62, 18 );

printf( "6. 메인 메뉴로" );   

gotoxy( 62, 20 );

printf( "선택 : [      ]" );  

}

 

void Map()

{

    int i, j;

     for( i = 0 ; i < MAP_ROW ; i++ )

     {

        for(  j = 0 ; j < MAP_COL ; j++ )

        {

             gotoxy( (j*2) + 2, i + 1 );

             if( g_StageInfo.nMap[i][j] == -1 )

                printf("  ");

             else

                printf( "%s", g_StateShape[ g_StageInfo.nMap[i][j] ] );

        }

      } 

}

 

int main(void)

{

    int nMainMenu;       

    int nKey;

 

    memset( g_StageInfo.nMap, -1, sizeof( g_StageInfo.nMap ) );

   

    while( 1 )

    {   

        ScreenLine();

        nMainMenu = MainMenu();

        switch( nMainMenu )

        {

        case 1 :        // 열기                         

                break;

        case 2 : // 저장                 

                break;  

        case 3 : // 맵 정보 편집

                 while( 1 )

                 {

                   ScreenLine();

                   Map();

                   EditInfo();

                                        

                   gotoxy( 72, 20 ); // 모드 입력 상태를 기다림

                   scanf( "%d", &nKey );  

                   fflush( stdin );

 

                   if( nKey == 6 ) // 메인 메뉴로 나가기 최선 조건이 된다.

                        break;

 

                   if( nKey == 5 ) // 맵 메모리에서 지우기

                   {

                       memset( g_StageInfo.nMap, -1, sizeof( g_StageInfo.nMap ) );

                       continue;

                    }

                       

                    if( nKey >= 1 && nKey <= 4 )

                    {

                        g_EditState = nKey-1;

                        gotoxy( 62, 2 );

                        printf( "편집 상태: %s", &g_strEditMode[g_EditState] );

                        g_nCol = 14; // Note: 커서 초기 위치

                        g_nRow = 10;

                        gotoxy( (g_nCol*2) + 2, g_nRow + 1);

                        while( 1 )

                        {                                               

                            nKey = _getch();

if( nKey == 'q' )

    break;

switch( nKey )

{

  case 'k' : 

    // 각 맵 구성 객체 개수 설정

    if( g_StageInfo.nMap[g_nRow][g_nCol] == 1 )

    {

       if( g_StageInfo.nEatCount - 1 > 0 )

           g_StageInfo.nEatCount--;

    }

    if( g_StageInfo.nMap[g_nRow][g_nCol] == 2 )

    {

       if( g_StageInfo.nEnemyCount - 1 > 0 )

           g_StageInfo.nEnemyCount--;

    }                                           g_StageInfo.nMap[g_nRow][g_nCol] = -1;

    gotoxy( (g_nCol*2) + 2, g_nRow+1 );

      printf( "  " );                             break;

case 's' :

     g_StageInfo.nMap[g_nRow][g_nCol]= g_EditState;

     gotoxy( (g_nCol*2) + 2, g_nRow+1 );

     printf( "%s", g_StateShape[g_EditState] );             // 각 맵 구성 객체 개수 설정

    if( g_EditState == 1 )

     g_StageInfo.nEatCount++;

    if( g_EditState == 2 )

     g_StageInfo.nEnemyCount++;

    break;

case 75: // 좌

    g_nCol--;

    gotoxy( (g_nCol*2) + 2, g_nRow + 1);

    break;

case 77: // 우

    g_nCol++;                                   gotoxy( (g_nCol*2) + 2, g_nRow + 1);                      break;

case 72: // 위

    g_nRow--;                                   gotoxy( (g_nCol*2) + 2, g_nRow + 1);

    break;

case 80: // 아래

    g_nRow++;                                   gotoxy( (g_nCol*2) + 2, g_nRow + 1);                   break;

}                                           

                        }

                     }                                  

                 }

                break;

          case 4 :       // 스테이지 정보 편집                     

                break;

        case 5 :                         

                break;

        }

    }   

    return 0;

}

 

[소스 11-3] 맵 편집 소스

 

21행은 4개의 특수 문자를 저장하는 배열을 선언한 것이다.

특수 문자는 2 byte 크기를 가지며 문자열은 맨 끝에는 항상 널문자(0, ‘

(CGP) 11장 Snake 게임 툴 만들기

Snake 게임 고전 게임중 하나이며 맵을 제작하여 적용할 수 있는 게임에 해당이 된다.

게임을 제작하기 이전에 맵 툴을 만들고 툴의 데이터를 게임에 적용하여 보자.

 

분노2 유튜브 동영상 강의 주소

 

(1) http://youtu.be/d6ktsi78C-o

(2) http://youtu.be/_iM9YXlTd5M

(3) http://youtu.be/RfCxSff2fls

(4) http://youtu.be/rGbWKRq4-Vc

 

 


11.1 툴 제작 개요

 

Snake 게임을 만들기 전에 Snake 게임의 각 스테이지를 디자인할 수 있는 툴부터 제작해 보자. 여기서는 다음 장에서 제작하게 되는 Snake 게임에 필요한 맵 디자인부터 몹과 먹이 그리고 뱀의 위치가 지정되도록 툴을 디자인하고 제작하게 된다.

 

툴을 처음 제작하게 되면 게임에 필요한 데이터를 다 파악하지 못하는 경우가 대부분이다.

그러므로 툴은 한 번에 만들어지기 보다는 여러 번의 수정 과정을 통해 업그레이드되므로

이전 버전에서 저장한 데이터를 현재 툴에도 적용할 수 있게 툴 설계를 해야 한다.

 

 


11.2 기획

 

■ 스토리

 

적 뱀을 피해 다니면서 제한 시간 안에 사방에 떨어져 있는 먹이를 모두 먹어라.

 

■ 게임 방식

 

주인공 뱀은 다른 방향키를 누르기까지 현재 설정된 방향으로 계속 이동한다.

주인공 뱀과 적 캐릭터가 부딪히면 주인공 뱀의 꼬리는 하나씩 줄어들지만 먹이를 먹으면 하나씩 늘어난다.

 

■ 제한 사항

 

각 스테이지마다 제한된 시간이 있으며 제한 시간 안에 먹이를 전부 먹어야만 다음 스테이지로 넘어간다. 기본 꼬리는 3개로 하며 꼬리가 없는 상태에서 적 뱀과 충돌하면 게임은 종료된다.

 

■ 기획 화면

 

[그림 11-1] 게임 기획 화면

 

 


11.3 실행 화면 

 

[그림 11-2] 메인 메뉴 화면

 

[그림 11-3] 파일 열기 화면

 

[그림 11-4] 맵 편집 화면

 

[그림 11-5] 스테이지 정보 입력 화면

 

 


11.4 툴 제작 로드맵

 

Snake 맵 툴 제작은 다음과 같이 5단계로 나눌 수 있다.

 

1단계는 기본적인 구조와 데이터를 결정하는 부분으로 이 단계가 전체 툴의 기능을 정의하는 부분에 해당된다.

 

2단계는 맵을 쉽게 편집할 수 있도록 하는 단계이다. 물론 윈도우 프로그램일 경우에는 마우스와 같은 인터페이스를 사용할 수 있겠지만, 지금 제작하는 프로그램 환경은 콘솔형이므로 키 입력만으로 편집이 가능하도록 해야 한다. 또한 맵마다 적 캐릭터와 먹이, 주인공의 위치를 정하여 스테이지를 구성할 수 있도록 해야 한다.

 

3단계는 스테이지 정보 중에서 제한 시간을 입력하거나 수정하는 부분이다.

 

4단계는 지금까지 입력된 내용을 저장하거나 읽는 단계이다.

 

5단계는 제작된 툴을 테스트하고 저장된 데이터를 확인하여 툴의 기능이 제대로 동작하는지를 테스트해 보는 단계이다.

 

여기까지의 여러 단계를 제작 로드맵으로 나타내면 아래 [그림 11-6]과 같다.

 

[STEP 01]

 

[STEP 02]

 

[STEP 03]

 

[STEP 04]

 

[STEP 05]

 

[그림 11-6] 툴 제작 로드맵

 

 


11.5 단계별 프로그래밍

 

[STEP 01]

  

[그림 11-7] 1단계 툴 제작 로드맵

 

■ 데이터 분석

 

앞장에서 제작한 하트 게임 툴은 하나의 데이터 파일 안에 여러 개의 스테이지 정보를 한꺼번에 입력하는 방식이었다. 그러나 Snake 맵 툴의 경우는 하나의 파일 안에 하나의 스테이지 정보만 저장되므로 데이터를 관리하는 링크드 리스트가 필요 없다.

 

Snake 게임 툴에서 저장하는 데이터는 크게 두 가지가 있다.

 

첫째, 스테이지 정보이다.

 

 

① 적 캐릭터 개수

② 스테이지의 제한 시간

③ 먹이 개수

 

[표 11-1] 스테이지 정보

 

위의 스테이지 정보에서 먹이 개수는 스테이지를 완료하게 하는 속성이 되지만 스테이지의 제한 시간은 게임을 실패하게 만드는 속성이다.

즉 제한 시간 안에 먹이를 다 먹지 못한다면 현재의 스테이지는 실패하게 되는 것을 말한다. 

 

둘째, 맵 정보가 있다.

맵은 게임 전체 화면을 구성하는 정보가 되며 맵 위에 적 캐릭터와 먹이등을 설정하게 된다.

 

 

① 맵 정보

② 적 캐릭터의 위치 설정

③ 주인공의 위치 설정

④ 먹이 및 위치 설정

 

[표 11-2] 맵 정보

 

이제까지 살펴본 내용을 정의하면 아래 [소스 11-1]과 같다.

 

 

#define MAP_COL  29

#define MAP_ROW  22

 

typedef struct _STAGE_INFO

{

       int nEnemyCount;                 // Note: 적 캐릭터의 개수

       clock_t LimitTime;                // Note: 스테이지의 제한 시간

       int nEatCount;                     // Note: 먹이 개수

       int nMap[MAP_ROW][MAP_COL]; // Note: 맵 정보

} STAGE_INFO;

 

[소스 11-1] 스테이지의 전체 정보 정의

 

위의 구조체에서 nMap[MAP_ROW][MAP_COL] 변수는 실제 맵의 정보를 저장하는 변수이다. 전체 맵이 가로 세로의 개념을 가지므로 이차원 배열의 행과 열을 적용할 수 있다.

따라서 맵 (0, 0)은 nMap[0][0]과 같으며 맵 데이터를 설정한다는 것은 이차원 배열에 값을 설정하는 것과 같다. 이때 설정되는 데이터에 따라 맵 데이터가 될 것인지 아니면 적 캐릭터가 될 것인지는 아래 [표 11-3]에 따라 결정된다.

 

 

 

맵 정보

0

1

2

3

의미

블록

먹이

적 

주인공

출력 캐릭터

 

[표 11-3] 맵 데이터

 

■ 메인 메뉴

 

기본적인 메인 메뉴는 파일 열기와 파일 저장, 그리고 종료가 있으며 데이터를 설정하거나 편집하기 위한 메뉴로는 맵 정보 편집과 스테이지 정보 편집이 있다.

이와 같은 사항을 정리하면 아래 [표 11-4]와 같다.

 

 

① 파일 열기

② 파일 저장

③ Map 정보 편집

④ 스테이지의 정보 편집

⑤ 종료

 

[표 11-4] 메인 메뉴 항목

 

■ 메인 메뉴 화면

 

[그림 11-8] 메인 메뉴 화면

 

[그림 11-8]을 보면 메인 메뉴에서 서브 메뉴들을 실행시킬 수 있으며 선택된 서브 메뉴는 실제적인 데이터를 입력, 수정, 삭제, 출력한 후에 다시 메인 메뉴로 제어가 옮겨진다.

아래 [그림 11-9]는 메인 메뉴와 서브 메뉴의 제어 흐름을 나타낸 것이다.

 

[그림 11-9] 메인 메뉴와 서브 메뉴와의 제어 흐름도

 

[실습 예제 11-1]

 

위의 [그림 11-8]과 같이 메인 메뉴가 출력될 수 있도록 프로그래밍해 보자.

 

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

#include <stdio.h>

#include <windows.h>

#include <malloc.h>

#include <conio.h>

#include <time.h>

 

void gotoxy( int x, int y )

{

     COORD CursorPosition = { x, y };

     SetConsoleCursorPosition( GetStdHandle( STD_OUTPUT_HANDLE ), CursorPosition );

}

 

int MainMenu()

{

    int nMenu;

    gotoxy( 22, 5 );  printf( "뱀 게임 월드 툴" );

    gotoxy( 24, 8 );  printf( "1. 열기" );

    gotoxy( 24, 10 ); printf( "2. 저장" );

    gotoxy( 24, 12 ); printf( "3. 스테이지 정보 편집" );

    gotoxy( 24, 14 ); printf( "4. Map 정보 편집" );

    gotoxy( 24, 16 ); printf( "5. 종료" );

    gotoxy( 27, 18 ); printf( "메뉴를 선택하세요 [   ]" );

    gotoxy( 47, 18 ); scanf( "%d", &nMenu ); fflush( stdin );

    return nMenu;

}

 

void ScreenLine()

{       

     gotoxy( 0, 0 );

     printf("┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n");

     printf("┃                                                          ┃\n");

     printf("┃                                                          ┃\n");

     printf("┃                                                          ┃\n");

     printf("┃                                                          ┃\n");

     printf("┃                                                          ┃\n");

     printf("┃                                                          ┃\n");

     printf("┃                                                          ┃\n");

     printf("┃                                                          ┃\n");

     printf("┃                                                          ┃\n");

     printf("┃                                                          ┃\n");

     printf("┃                                                          ┃\n");

     printf("┃                                                          ┃\n");

     printf("┃                                                          ┃\n");

     printf("┃                                                          ┃\n");

     printf("┃                                                          ┃\n");

     printf("┃                                                          ┃\n");

     printf("┃                                                          ┃\n");

     printf("┃                                                          ┃\n");

     printf("┃                                                          ┃\n");

     printf("┃                                                          ┃\n");

     printf("┃                                                          ┃\n");

     printf("┃                                                          ┃\n");

     printf("┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n");                

}

 

int main(void)

{

    int nMainMenu;

    

    while( 1 )

    {   

        ScreenLine();

        nMainMenu = MainMenu();

 

        switch( nMainMenu )

        {

        case 1 :                                        

                break;

        case 2 :                         

                break;

        case 3 :                         

                break;

        case 4 :                         

                break;

        case 5 :                         

                break;

        }

     }  

     return 0;

}

 

[소스 11-2] 메인 메뉴 화면 소스

 

 

 


STEP 02

  

[그림 11-10] 2단계 제작 로드맵

 

■ 맵(Map)

 

맵 정보를 저장하기 위해서는 메모리가 필요하다. 그래서 1단계에서는 맵 정보를 저장하기 위한 nMap[MAP_ROW][MAP_COL] 변수를 선언했으며, 이 변수의 행과 열 인덱스는 화면의 가로, 세로 컬럼과 일대일 대응된다.

다음의 [그림 11-11]은 화면 (0, 0)을 맵 시작점으로 하고, 맵 배열과 화면 좌표간의 관계를 그림으로 나타낸 것이다.

[그림 11-11] 맵과 화면 좌표 관계

 

맵의 정보 입력, 수정, 삭제

 

맵 정보를 입력하는 것은 맵 배열에 [표 11-3]에서 정의한 인덱스를 넣어주는 것이며 수정은 수정하고자 하는 위치의 맵 배열에 값을 다시 입력하는 것을 말한다. 그리고 삭제는 출력과 상관없는 값을 맵 배열에 넣어 출력에서 제외시키는 것을 말한다.

그러므로 맵 전체를 지운다는 것은 출력과 상관없는 값을 전체 맵 배열에 채우는 것을 의미한다.

 

[그림 11-12] 맵 편집의 초기 실행 화면

 

[그림 11-13] 맵의 설정 화면

 

메뉴는 [그림 11-12] 맵 편집 초기 화면의 오른쪽이 해당되며 이 메뉴를 통해 맵과 캐릭터가 선택된다. 먼저 1에서부터 4까지의 키는 맵을 구성하기 위한 것으로 키 입력이 있으면 커서는 맵 화면으로 이동한다. 이때 방향키를 이용하여 커서를 상하좌우로 이동하고 s키를 입력하여 현재 선택된 캐릭터의 인덱스가 맵 배열에 저장이 되게 한다.

 

q 키는 현재 맵의 편집 상태에서 빠져나와 오른쪽 편집 메뉴에서 키 입력을 받는 상태로 전환한다.

 

[그림 11-14] 1키를 입력한 후에 방향키로 이동하면서 s키로 블록을 설정하는 화면

 

[실습 예제 11-2]

 

위의 [그림 11-14]와 같이 맵을 편집할 수 있는 프로그램을 작성해 보자. 1에서부터 4까지는 맵에 입력할 대상을 선택하는 키이며 방향키는 맵 편집 상태에서 상하좌우로 커서를 이동시키는 역할을 한다. 만약 맵 편집 상태에서 캐릭터를 설정하려면 s키를 입력하면 된다. 그리고 k 키는 맵의 편집 상태에서 맵 데이터를 지우는 역할을 한다.

 

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

#include <stdio.h>

#include <windows.h>

#include <malloc.h>

#include <conio.h>

#include <time.h>

 

#define MAP_COL  29

#define MAP_ROW  22

 

typedef struct _STAGE_INFO

{

        int nEnemyCount;   // 적 캐릭터 개수

        clock_t LimitTime; // 스테이지 제한 시간

        int nEatCount; // 먹이 개수

        int nMap[MAP_ROW][MAP_COL];

} STAGE_INFO;

 

int g_EditState = 0; // 캐릭터 설정 번호

char g_strEditMode[4][10] = { "블럭  ", "먹이  ", "적    ", "주인공" };

int g_nCol = 14, g_nRow = 10; // 초기 커서 위치

char g_StateShape[4][3] = { "■", "♥", "⊙", "●" };

STAGE_INFO g_StageInfo;

 

void gotoxy( int x, int y )

{

     COORD CursorPosition = { x, y };

     SetConsoleCursorPosition( GetStdHandle( STD_OUTPUT_HANDLE ), CursorPosition );

}

 

int MainMenu()

{

int nMenu;

gotoxy( 22, 5 );  printf( "뱀 게임 월드 툴" );

gotoxy( 24, 8 );  printf( "1. 열기" );

gotoxy( 24, 10 ); printf( "2. 저장" );

gotoxy( 24, 12 ); printf( "3. Map 정보 편집" );

gotoxy( 24, 14 ); printf( "4. 스테이지 정보 편집" );

gotoxy( 24, 16 ); printf( "5. 종료" );

gotoxy( 27, 18 ); printf( "메뉴를 선택하세요 [   ]" );

gotoxy( 47, 18 ); scanf( "%d", &nMenu ); fflush( stdin );

return nMenu;

}

 

void ScreenLine()

{       

gotoxy( 0, 0 );

printf("┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n");

printf("┃                                                          ┃\n");

printf("┃                                                          ┃\n");

printf("┃                                                          ┃\n");

printf("┃                                                          ┃\n");

printf("┃                                                          ┃\n");

printf("┃                                                          ┃\n");

printf("┃                                                          ┃\n");

printf("┃                                                          ┃\n");

printf("┃                                                          ┃\n");

printf("┃                                                          ┃\n");

printf("┃                                                          ┃\n");

printf("┃                                                          ┃\n");

printf("┃                                                          ┃\n");

printf("┃                                                          ┃\n");

printf("┃                                                          ┃\n");

printf("┃                                                          ┃\n");

printf("┃                                                          ┃\n");

printf("┃                                                          ┃\n");

printf("┃                                                          ┃\n");

printf("┃                                                          ┃\n");

printf("┃                                                          ┃\n");

printf("┃                                                          ┃\n");

printf("┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n");     

}

 

void EditInfo()

{       

gotoxy( 62, 2 );

printf( "편집 상태: %s", &g_strEditMode[g_EditState] );

gotoxy( 62, 4 );

printf( "공백키 : k" );

gotoxy( 62, 6 );

printf( "설정키 : s" );

gotoxy( 62, 8 );

printf( "1. 블럭(■)");

gotoxy( 62, 10 );

printf( "2. 먹이(♥)");

gotoxy( 62, 12 );

printf( "3. 적(⊙)");

gotoxy( 62, 14 );

printf( "4. 주인공(●)");

gotoxy( 62, 16 );

printf( "5. 맵 전체 지우기" );

gotoxy( 62, 18 );

printf( "6. 메인 메뉴로" );   

gotoxy( 62, 20 );

printf( "선택 : [      ]" );  

}

 

void Map()

{

    int i, j;

     for( i = 0 ; i < MAP_ROW ; i++ )

     {

        for(  j = 0 ; j < MAP_COL ; j++ )

        {

             gotoxy( (j*2) + 2, i + 1 );

             if( g_StageInfo.nMap[i][j] == -1 )

                printf("  ");

             else

                printf( "%s", g_StateShape[ g_StageInfo.nMap[i][j] ] );

        }

      } 

}

 

int main(void)

{

    int nMainMenu;       

    int nKey;

 

    memset( g_StageInfo.nMap, -1, sizeof( g_StageInfo.nMap ) );

   

    while( 1 )

    {   

        ScreenLine();

        nMainMenu = MainMenu();

        switch( nMainMenu )

        {

        case 1 :        // 열기                         

                break;

        case 2 : // 저장                 

                break;  

        case 3 : // 맵 정보 편집

                 while( 1 )

                 {

                   ScreenLine();

                   Map();

                   EditInfo();

                                        

                   gotoxy( 72, 20 ); // 모드 입력 상태를 기다림

                   scanf( "%d", &nKey );  

                   fflush( stdin );

 

                   if( nKey == 6 ) // 메인 메뉴로 나가기 최선 조건이 된다.

                        break;

 

                   if( nKey == 5 ) // 맵 메모리에서 지우기

                   {

                       memset( g_StageInfo.nMap, -1, sizeof( g_StageInfo.nMap ) );

                       continue;

                    }

                       

                    if( nKey >= 1 && nKey <= 4 )

                    {

                        g_EditState = nKey-1;

                        gotoxy( 62, 2 );

                        printf( "편집 상태: %s", &g_strEditMode[g_EditState] );

                        g_nCol = 14; // Note: 커서 초기 위치

                        g_nRow = 10;

                        gotoxy( (g_nCol*2) + 2, g_nRow + 1);

                        while( 1 )

                        {                                               

                            nKey = _getch();

if( nKey == 'q' )

    break;

switch( nKey )

{

  case 'k' : 

    // 각 맵 구성 객체 개수 설정

    if( g_StageInfo.nMap[g_nRow][g_nCol] == 1 )

    {

       if( g_StageInfo.nEatCount - 1 > 0 )

           g_StageInfo.nEatCount--;

    }

    if( g_StageInfo.nMap[g_nRow][g_nCol] == 2 )

    {

       if( g_StageInfo.nEnemyCount - 1 > 0 )

           g_StageInfo.nEnemyCount--;

    }                                           g_StageInfo.nMap[g_nRow][g_nCol] = -1;

    gotoxy( (g_nCol*2) + 2, g_nRow+1 );

      printf( "  " );                             break;

case 's' :

     g_StageInfo.nMap[g_nRow][g_nCol]= g_EditState;

     gotoxy( (g_nCol*2) + 2, g_nRow+1 );

     printf( "%s", g_StateShape[g_EditState] );             // 각 맵 구성 객체 개수 설정

    if( g_EditState == 1 )

     g_StageInfo.nEatCount++;

    if( g_EditState == 2 )

     g_StageInfo.nEnemyCount++;

    break;

case 75: // 좌

    g_nCol--;

    gotoxy( (g_nCol*2) + 2, g_nRow + 1);

    break;

case 77: // 우

    g_nCol++;                                   gotoxy( (g_nCol*2) + 2, g_nRow + 1);                      break;

case 72: // 위

    g_nRow--;                                   gotoxy( (g_nCol*2) + 2, g_nRow + 1);

    break;

case 80: // 아래

    g_nRow++;                                   gotoxy( (g_nCol*2) + 2, g_nRow + 1);                   break;

}                                           

                        }

                     }                                  

                 }

                break;

          case 4 :       // 스테이지 정보 편집                     

                break;

        case 5 :                         

                break;

        }

    }   

    return 0;

}

 

[소스 11-3] 맵 편집 소스

 

21행은 4개의 특수 문자를 저장하는 배열을 선언한 것이다.

특수 문자는 2 byte 크기를 가지며 문자열은 맨 끝에는 항상 널문자(0, ‘\0’)가 있어야 하므로 하나의 특수 문자를 저장하기 위해서는 최소 3 byte 메모리가 필요하다.

21행과 같이 선언했을 때 출력은 행 인덱스만 있으면 된다. 예를 들면 아래 [소스 11-4]와 같다.

 

 

1

2

3

4

5

6

7

8

9

10

#include <stdio.h>

char g_StateShape[4][3] = { "■", "♥", "⊙", "●" };

 

int main(void)

{

    int i;

    for( i = 0 ; i < 4 ; i++ )

         printf( "%s  ", g_StateShape[i] );

    return 0;

}

 

[소스 11-4] 이차원 배열을 이용한 특수 문자 출력

 

[그림 11-15] 특수 문자 출력 화면

 

97행의 Map()은 맵을 출력하는 함수이다.

g_StageInfo.nMap[22][29] 안에는 21행에서 선언한 캐릭터 인덱스가 들어 있다.

105행에서 -1은 삭제를 의미하므로 106행에서 공백으로 출력하고 있다.

 

131행은 서브 메뉴가 종료될 때까지 무한 반복을 한다.

이와 같이 하는 이유는 [그림 11-9]와 같은 제어 흐름을 가지기 때문이다.

 

141행과 142행은 서브 메뉴를 빠져나기 위한 최우선 키이다.

142행의 break 문은 131행의 while 문을 빠져 나가게 한다.

 

191행부터 206행까지는 방향키에 따라 커서를 이동시키는 부분이다.

 

 


[STEP 03]

  

 

 

[그림 11-16] 3단계 제작 로드맵

 

■ 스테이지의 정보 설정

 

스테이지 정보는 적 캐릭터 개수와 스테이지의 제한 시간, 그리고 먹이 개수가 있다. 특히 적 캐릭터와 먹이 개수는 맵 편집을 할 때 자동으로 계산되어야 한다. 왜냐하면, 맵 수정을 하다 보면 적 캐릭터 개수와 먹이 개수가 유동적으로 증감할 수 있는데 그 때마다 편집 상태에서 나와 메인 메뉴에서 다시 개수를 설정하는 것은 상당히 번거로울 뿐만 아니라 툴을 만드는 의미를 저하시키는 요인이 되기 때문이다. 그래서 이와 같은 부분은 툴에서 처리하도록 해야 한다.

 

- 제한 시간 입력 및 수정

 

스테이지에 관한 제한 시간의 단위는 밀리세컨드이지만 툴 사용자 입장에서 보면 밀리세컨드 단위보다는 초 단위의 입력이 쉽기 때문에 사용자로부터는 초로 입력 받고, 소스 코드상에서는 밀리세컨드로 변환하여 설정한다.

아래 [소스 11-6]은 3단계 [소스 11-3] 212행의 ‘case 4 : ’ 부분에 들어가는 코드이다.

 

 

1

2

3

4

5

6

7

8

9

10

11

12

13

void StageInfo()

{      

     gotoxy( 22, 5 );

     printf( "스테이지 정보" );

     gotoxy( 5, 7 );

     printf( "제한 시간 : %d 초", g_StageInfo.LimitTime );   

     gotoxy( 5, 9 ); 

     printf( "적 캐릭터수 : %d", g_StageInfo.nEnemyCount );

     gotoxy( 5, 11 );

     printf( "먹이 수 : %d", g_StageInfo.nEatCount );

     gotoxy( 5, 13 );

     printf( "제한 시간 입력 또는 상위 메뉴로(q): [        ]" );

}

 

[소스 11-5] 스테이지 정보 입력

 

 

1

2

3

4

5

6

7

8

9

case 4 : 

       ScreenLine();

       StageInfo();

       gotoxy( 43 ,13 );

       scanf( "%d"ㄹ, &LimitTime );

       fflush( stdin );

       if( LimitTime != 'q' )

          g_StageInfo.LimitTime = LimitTime * 1000;  // 초를 밀리세컨드로 변환

       break;

 

[소스 11-6] 스테이지 정보 입력

 

 


STEP 04

 

[그림 11-17] 4단계 제작 로드맵

 

파일을 입출력하는 방법은 앞서 제작한 하트 게임 툴과 같다. 차이가 있다면 저장하려는 정보가 바뀌었을 뿐이다.

이 툴에서 저장하려는 데이터는 1단계에서 정의한 STAGE_INFO라는 구조체 데이터이며 읽으려는 데이터도 STAGE_INFO 구조체로 단위로 읽으면 된다.

 

파일 관련 프로그래밍을 할 때는 저장하는 부분부터 먼저 제작하고 파일 읽기에 관한 프로그래밍을 하면 잘못된 데이터를 읽어서 발생하는 버그를 줄일 수 있다.

 

■ 파일 저장

 

파일을 저장하기 전에 저장하려는 파일명을 먼저 입력 받아야 한다. 또한 입력 받은 내용을 쉽게 확인할 수 있도록 텍스트 파일(*.txt)로 저장한다.

텍스트 파일은 메모장과 같은 일반적인 에디터에서도 내용을 볼 수 있으므로 데이터가 정상적으로 저장되었는지를 쉽게 확인할 수 있다.

 

[그림 11-18] 저장할 파일명 입력 화면

 

아래 [소스 11-7]은 파일명을 입력 받은 후에 데이터를 저장하는 부분이다.

 

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

case 2 :

        ScreenLine();

        SaveFile();

        gotoxy( 17, 10 );

        scanf( "%s", strFileName );

        fflush( stdin );                         

        if( fp != NULL )

           fclose( fp );

        fp = fopen( strFileName, "w" );  // FILE *fp;

        fprintf( fp, "%d %d %d\n", g_StageInfo.LimitTime, g_StageInfo.nEatCount,

                  g_StageInfo.nEnemyCount );

        fwrite( g_StageInfo.nMap, sizeof( int ), MAP_COL*MAP_ROW, fp );

        fclose( fp );

        fp = NULL;

        break;

 

[소스 11-7] 저장

 

■ 파일 읽기

 

이미 파일 저장 부분에서 데이터가 맞게 저장되었는지를 확인했으므로 파일 읽기는 저장의 반대로 프로그래밍하면 된다. 읽기 함수인 fread()를 이용하여 구조체 단위로 한꺼번에 읽어 낸다.

 

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

case 1 :

        ScreenLine();

        OpenFile();

        gotoxy( 17, 10 );

        scanf( "%s", strFileName );

        fflush( stdin );                         

        if( fp != NULL )

            fclose( fp );

        fp = fopen( strFileName, "r" );  // FILE *fp;

        fscanf( fp, "%d %d %d\n", &g_StageInfo.LimitTime, &g_StageInfo.nEatCount,

                                     &g_StageInfo.nEnemyCount );

        fread( g_StageInfo.nMap, sizeof( int ), MAP_COL*MAP_ROW, fp );

        fclose( fp );

        fp = NULL;                              

        break;

 

[소스 11-8] 읽기

 

여기까지 다음 장의 Snake 게임에 적용할 툴을 단계별로 제작해 보았다.

이번 Snake 게임 툴을 살펴보면서 툴의 구조와 코딩 방법이 유사하다는 것을 알게 되었을 것이다. 이렇게 툴을 한번 제작해보면 그 다음의 툴 제작은 조금 더 쉽게 제작할 수 있으며 이 부분은 게임 제작에도 똑같이 적용된다. 그러면 이제 전체 프로그램을 완성해 보자.

 

 

좋은하루강의가 도움이 되셨습니까? 손가락 꾸욱 눌러주는 센스 ~~ 

[출처] https://nowcampus.tistory.com/entry/11%EC%9E%A5-Snake-%EA%B2%8C%EC%9E%84-%ED%88%B4-%EB%A7%8C%EB%93%A4%EA%B8%B0?category=655340

 

 

 

’)가 있어야 하므로 하나의 특수 문자를 저장하기 위해서는 최소 3 byte 메모리가 필요하다.

21행과 같이 선언했을 때 출력은 행 인덱스만 있으면 된다. 예를 들면 아래 [소스 11-4]와 같다.

 

 

1

2

3

4

5

6

7

8

9

10

#include <stdio.h>

char g_StateShape[4][3] = { "■", "♥", "⊙", "●" };

 

int main(void)

{

    int i;

    for( i = 0 ; i < 4 ; i++ )

         printf( "%s  ", g_StateShape[i] );

    return 0;

}

 

[소스 11-4] 이차원 배열을 이용한 특수 문자 출력

 

[그림 11-15] 특수 문자 출력 화면

 

97행의 Map()은 맵을 출력하는 함수이다.

g_StageInfo.nMap[22][29] 안에는 21행에서 선언한 캐릭터 인덱스가 들어 있다.

105행에서 -1은 삭제를 의미하므로 106행에서 공백으로 출력하고 있다.

 

131행은 서브 메뉴가 종료될 때까지 무한 반복을 한다.

이와 같이 하는 이유는 [그림 11-9]와 같은 제어 흐름을 가지기 때문이다.

 

141행과 142행은 서브 메뉴를 빠져나기 위한 최우선 키이다.

142행의 break 문은 131행의 while 문을 빠져 나가게 한다.

 

191행부터 206행까지는 방향키에 따라 커서를 이동시키는 부분이다.

 

 


[STEP 03]

  

 

 

[그림 11-16] 3단계 제작 로드맵

 

■ 스테이지의 정보 설정

 

스테이지 정보는 적 캐릭터 개수와 스테이지의 제한 시간, 그리고 먹이 개수가 있다. 특히 적 캐릭터와 먹이 개수는 맵 편집을 할 때 자동으로 계산되어야 한다. 왜냐하면, 맵 수정을 하다 보면 적 캐릭터 개수와 먹이 개수가 유동적으로 증감할 수 있는데 그 때마다 편집 상태에서 나와 메인 메뉴에서 다시 개수를 설정하는 것은 상당히 번거로울 뿐만 아니라 툴을 만드는 의미를 저하시키는 요인이 되기 때문이다. 그래서 이와 같은 부분은 툴에서 처리하도록 해야 한다.

 

- 제한 시간 입력 및 수정

 

스테이지에 관한 제한 시간의 단위는 밀리세컨드이지만 툴 사용자 입장에서 보면 밀리세컨드 단위보다는 초 단위의 입력이 쉽기 때문에 사용자로부터는 초로 입력 받고, 소스 코드상에서는 밀리세컨드로 변환하여 설정한다.

아래 [소스 11-6]은 3단계 [소스 11-3] 212행의 ‘case 4 : ’ 부분에 들어가는 코드이다.

 

 

1

2

3

4

5

6

7

8

9

10

11

12

13

void StageInfo()

{      

     gotoxy( 22, 5 );

     printf( "스테이지 정보" );

     gotoxy( 5, 7 );

     printf( "제한 시간 : %d 초", g_StageInfo.LimitTime );   

     gotoxy( 5, 9 ); 

     printf( "적 캐릭터수 : %d", g_StageInfo.nEnemyCount );

     gotoxy( 5, 11 );

     printf( "먹이 수 : %d", g_StageInfo.nEatCount );

     gotoxy( 5, 13 );

     printf( "제한 시간 입력 또는 상위 메뉴로(q): [        ]" );

}

 

[소스 11-5] 스테이지 정보 입력

 

 

1

2

3

4

5

6

7

8

9

case 4 : 

       ScreenLine();

       StageInfo();

       gotoxy( 43 ,13 );

       scanf( "%d"ㄹ, &LimitTime );

       fflush( stdin );

       if( LimitTime != 'q' )

          g_StageInfo.LimitTime = LimitTime * 1000;  // 초를 밀리세컨드로 변환

       break;

 

[소스 11-6] 스테이지 정보 입력

 

 


STEP 04

 

[그림 11-17] 4단계 제작 로드맵

 

파일을 입출력하는 방법은 앞서 제작한 하트 게임 툴과 같다. 차이가 있다면 저장하려는 정보가 바뀌었을 뿐이다.

이 툴에서 저장하려는 데이터는 1단계에서 정의한 STAGE_INFO라는 구조체 데이터이며 읽으려는 데이터도 STAGE_INFO 구조체로 단위로 읽으면 된다.

 

파일 관련 프로그래밍을 할 때는 저장하는 부분부터 먼저 제작하고 파일 읽기에 관한 프로그래밍을 하면 잘못된 데이터를 읽어서 발생하는 버그를 줄일 수 있다.

 

■ 파일 저장

 

파일을 저장하기 전에 저장하려는 파일명을 먼저 입력 받아야 한다. 또한 입력 받은 내용을 쉽게 확인할 수 있도록 텍스트 파일(*.txt)로 저장한다.

텍스트 파일은 메모장과 같은 일반적인 에디터에서도 내용을 볼 수 있으므로 데이터가 정상적으로 저장되었는지를 쉽게 확인할 수 있다.

 

[그림 11-18] 저장할 파일명 입력 화면

 

아래 [소스 11-7]은 파일명을 입력 받은 후에 데이터를 저장하는 부분이다.

 

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

case 2 :

        ScreenLine();

        SaveFile();

        gotoxy( 17, 10 );

        scanf( "%s", strFileName );

        fflush( stdin );                         

        if( fp != NULL )

           fclose( fp );

        fp = fopen( strFileName, "w" );  // FILE *fp;

        fprintf( fp, "%d %d %d\n", g_StageInfo.LimitTime, g_StageInfo.nEatCount,

                  g_StageInfo.nEnemyCount );

        fwrite( g_StageInfo.nMap, sizeof( int ), MAP_COL*MAP_ROW, fp );

        fclose( fp );

        fp = NULL;

        break;

 

[소스 11-7] 저장

 

■ 파일 읽기

 

이미 파일 저장 부분에서 데이터가 맞게 저장되었는지를 확인했으므로 파일 읽기는 저장의 반대로 프로그래밍하면 된다. 읽기 함수인 fread()를 이용하여 구조체 단위로 한꺼번에 읽어 낸다.

 

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

case 1 :

        ScreenLine();

        OpenFile();

        gotoxy( 17, 10 );

        scanf( "%s", strFileName );

        fflush( stdin );                         

        if( fp != NULL )

            fclose( fp );

        fp = fopen( strFileName, "r" );  // FILE *fp;

        fscanf( fp, "%d %d %d\n", &g_StageInfo.LimitTime, &g_StageInfo.nEatCount,

                                     &g_StageInfo.nEnemyCount );

        fread( g_StageInfo.nMap, sizeof( int ), MAP_COL*MAP_ROW, fp );

        fclose( fp );

        fp = NULL;                              

        break;

 

[소스 11-8] 읽기

 

여기까지 다음 장의 Snake 게임에 적용할 툴을 단계별로 제작해 보았다.

이번 Snake 게임 툴을 살펴보면서 툴의 구조와 코딩 방법이 유사하다는 것을 알게 되었을 것이다. 이렇게 툴을 한번 제작해보면 그 다음의 툴 제작은 조금 더 쉽게 제작할 수 있으며 이 부분은 게임 제작에도 똑같이 적용된다. 그러면 이제 전체 프로그램을 완성해 보자.

 

 

좋은하루강의가 도움이 되셨습니까? 손가락 꾸욱 눌러주는 센스 ~~ 

[출처] https://nowcampus.tistory.com/entry/11%EC%9E%A5-Snake-%EA%B2%8C%EC%9E%84-%ED%88%B4-%EB%A7%8C%EB%93%A4%EA%B8%B0?category=655340

 

 

 

본 웹사이트는 광고를 포함하고 있습니다.
광고 클릭에서 발생하는 수익금은 모두 웹사이트 서버의 유지 및 관리, 그리고 기술 콘텐츠 향상을 위해 쓰여집니다.
번호 제목 글쓴이 날짜 조회 수
108 [Xamarin] Xamarin.Forms. Android 실행/ 디버깅시에 에뮬리이터 배포오류 Why am I getting this error in Xamarin.Forms using Visual Studio? file 졸리운_곰 2021.12.01 22
107 [Xamarin] Visual Studio 2019 를 설치하고 Xmarin.forms 빌드시 에러 : I am just download and start Visual Studio (Xamarin Project). But there is an ERROR NU1101. file 졸리운_곰 2021.12.01 53
106 [게임개발] How to Create Smarter NPCs in Games file 졸리운_곰 2021.08.31 23
105 (CGP)16장. 탱크 게임 file 졸리운_곰 2021.06.28 802
104 (CGP) 15장. 탱크 맵툴 만들기 file 졸리운_곰 2021.06.28 225
103 (CGP) 14장 Sogo 게임 file 졸리운_곰 2021.06.28 21
102 (CGP)13장. 패턴 뷰어 file 졸리운_곰 2021.06.28 19
101 (CGP) 12장 Snake 게임 file 졸리운_곰 2021.06.28 23
» (CGP) 11장 Snake 게임 툴 만들기 file 졸리운_곰 2021.06.28 51
99 (CGP) 10장. 하트담기 게임 file 졸리운_곰 2021.06.28 25
98 (CGP) 9장. 하트 툴 만들기 file 졸리운_곰 2021.06.28 24
97 (CGP) 8장. 벽돌깨기 게임 file 졸리운_곰 2021.06.28 48
96 (CGP)7장. 짝 맞추기 게임 file 졸리운_곰 2021.06.28 131
95 (CGP) 6장. 두더지 잡기 게임 file 졸리운_곰 2021.06.28 113
94 (CGP) 5장. 슛골인 게임 file 졸리운_곰 2021.06.27 151
93 (CGP) 4장. 사운드 file 졸리운_곰 2021.06.27 78
92 (CGP) 3장. 게임의 기본 구조 file 졸리운_곰 2021.06.27 126
91 (CGP) 2장. 함수 file 졸리운_곰 2021.06.27 36
90 (CGP) 1장. C언어 file 졸리운_곰 2021.06.27 60
89 (CGP) 0장. C를 이용한 게임프로그래밍 강좌를 시작하기 전에 file 졸리운_곰 2021.06.27 173
대표 김성준 주소 : 경기 용인 분당수지 U타워 등록번호 : 142-07-27414
통신판매업 신고 : 제2012-용인수지-0185호 출판업 신고 : 수지구청 제 123호 개인정보보호최고책임자 : 김성준 sjkim70@stechstar.com
대표전화 : 010-4589-2193 [fax] 02-6280-1294 COPYRIGHT(C) stechstar.com ALL RIGHTS RESERVED