(CGP)13장. 패턴 뷰어

패턴 뷰어는 각종 캐릭터들의 이동경로를 지정하기 위한 프로그램을 말한다.

이장에서는 패턴 데이터 구조를 파악하고 설계 및 정의, 파일 저장, 로딩까지 살펴보게 된다.

 

토닥토닥튜브 동영상 강의 주소

(1) http://youtu.be/IfC7niUSM6A

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

(3) http://youtu.be/ocxeB4j9A-Y

 

  

 

 

 

 

 


13.1 툴 제작 개요

 

패턴툴은 게임에 출현하는 캐릭터의 이동 경로를 지정하기 위한 툴이다.

툴에서 제작한 이동 경로는 사소한 캐릭터부터 보스 캐릭터까지 적용되며 스테이지마다 다양한 캐릭터의 이동을 제공한다.

물론 패턴 데이터를 코드로 직접 적용할 수도 있겠지만 패턴을 편집하기가 쉽지가 않으며 매번 게임을 실행시켜 캐릭터 패턴을 일일이 확인하는 것은 더욱 힘들다.

그러므로 패턴 데이터는 게임 코드와 분리시켜 툴에서 다양하게 편집하게 해야 한다.

툴에 따라 하나의 게임이 얼마나 다양화될 수 있는지를 툴 제작을 통해 살펴보도록 하자.

  

 


13.2 툴 기획

 

다음 장에서 제작하게 되는 Sogo 게임은 일반적인 슈팅 게임이다.

슈팅 게임의 기본 캐릭터는 주인공, 무기, 적 캐릭터, 보스 캐릭터가 있으며 슈팅 게임의 게임성은 다양한 적 캐릭터의 이동과 무기에 있다.

 

12장에서 제작한 Snake 게임의 적 캐릭터를 보면 패턴이라기보다는 임의의 방향을 적용한 형태이다. 그러므로 게임에서 적 캐릭터의 이동 경로를 예상할 수 없으며 다양한 이동을 디자인할 수 없다. 이러한 부분은 패턴툴을 통해 해결이 되는데 패턴툴은 하나의 패턴만 저장하며 패턴의 조합은 게임 코드에서 캐릭터와 연결하여 적용한다.

 

툴은 기본적으로 입력, 수정, 삭제, 추가 기능을 가지고 있지만 여기서는 간단히 패턴 읽기와 보기, 실행 기능만을 가지도록 한다.

그렇다면 입력, 수정, 삭제, 추가, 저장 기능은 무엇을 통해 할 것인가? 이것은 메모장의 편집 기능을 활용하도록 한다. 우리가 저장하는 패턴 데이터는 텍스트(text) 파일 형식으로 저장되므로 얼마든지 메모장과 같은 에디터에서 편집할 수 있다.

그리고 이와 같은 기능이 툴에서 제외됨으로써 툴 코드와 툴 제작 시간을 많이 줄이게 된다.

 

 


13.3 툴 제작 가이드

 

툴을 제작하기 전에 가장 먼저 해야 하는 것은 데이터 구조를 정의하는 것이다.

그 다음 툴의 기능을 정의하고 이에 따른 메인 메뉴와 서브 메뉴를 결정하면 툴의 외형적인 구조는 완성된다.

 

현재 제작하려는 프로그램에 사용되는 메뉴와 기능에 대해서 살펴보면 다음과 같다.

 

첫째, 파일 읽기 메뉴이다.

 

이 메뉴는 패턴 데이터를 단순히 읽는 기능으로 패턴 데이터 안에는 여러 이동 경로가 있다. 이 데이터를 읽기 위해서는 그 크기만큼의 메모리가 반드시 필요하므로 이 메뉴에서는  이동 경로 개수만큼 해당 메모리를 생성하고 데이터를 읽은 후에 메모리에 저장한다.

 

둘째, 패턴 보기 메뉴이다.

 

이 메뉴는 읽은 패턴 데이터를 실제 화면에 출력해 주어 메모장에서 입력한 경로가 어느 위치인지를 출력해주는 메뉴이다. 이 메뉴에는 실행이라는 서브 메뉴가 있는데 현재 출력된 이동 경로를 실제 게임 프레임워크에 적용하여 캐릭터를 출력해 준다.

이를 통해 현재 이동 경로를 따라 캐릭터가 어떻게 이동하는지를 알 수 있게 되며 패턴 속성인 이동 시간 간격과 이동 거리를 즉시 수정할 수 있게 해준다.

 

셋째, 종료 메뉴이다.

 

 


13.4 실행 화면

  

 

 

 

[그림 13-1] 패턴 뷰어 프로그램의 메인 화면

 

 

[그림 13-2] 패턴 파일 읽기 화면

 

[그림 13-3] 패턴 데이터 출력 화면

 

[그림 13-4] 게임 프레임워크를 활용한 이동 경로 출력

 

 


13.5 툴 제작 로드맵

 

프로그램 구조는 앞서 제작한 툴과 유사한 구조를 가진다.

현재 제작하려는 패턴 뷰어에는 입력, 수정, 삭제, 저장, 추가 기능이 없으므로 이전 제작 툴에 비해 오히려 간단한 구조와 흐름을 가진다.

특이한 부분은 프로그램 구조 안에 게임 프레임워크를 도입하여 실제 게임과 동일한 환경에서 이동 경로를 출력해 주는 부분이다.

 

[STEP 01]

 

 

[STEP 02]

 

[STEP 03]

 

[STEP 04]

[그림 13-5] 패턴 뷰어 제작 로드맵

 

 

 


13.6 단계별 프로그래밍

  

[STEP 01]

 

 

[그림 13-6] 1단계 제작 로드맵

 

■ 패턴

 

패턴이란 일정한 형태 또는 형식을 반복하는 것을 말하며 다양한 이동 패턴은 게임을 흥미롭게 만든다.

 

- 패턴 종류

 

이 게임에 적용하려는 패턴은 아래 [그림 13-7]과 같이 단순 패턴부터 조합된 패턴까지  적용하려고 한다. 특히 패턴의 조합은 게임 특성에 따라 소스 내에서 설정하도록 하고 여기서는 다양한 패턴이 만들어질 수 있는 환경을 제공하는데 주안점을 둔다.

 

 

[그림 13-7] 이동 패턴

 

①에서 ③까지는 한 가지 이동 형태가 적용된 것이며, ④와 ⑤는 두 가지 이상의 이동 형태가 적용된 것이다.

 

- 패턴 속성

 

이동 패턴에서 좌표는 가장 중요한 요소 중 하나이다.

좌표를 계산하는 방법은 다양하지만 여기서는 앞서 제작한 8장 벽돌깨기 게임의 공 이동 방법을 사용하여 이동 좌표를 계산한다.

벽돌깨기 게임에서 공의 이동 방향은 아래 [그림 13-8]과 같이 여섯 방향이었지만 여기서는 두 개의 방향을 더 추가하여 전부 여덟 방향으로 이동 좌표를 계산한다.

 

 

벽돌깨기 게임의 공 이동 방향

8 방향

 

[그림 13-8] 이동 방향

 

만약 더 다양한 이동 경로를 만들고 싶다면 위의 이동 방향을 많이 나누면 된다.

이동 좌표는 이동 방향에 따른 (+, -)증감과 이동 단계인 스텝(step)에 따라 결정된다.

이동 방향에 따른 좌표의 증감은 아래 [표 13-1]과 같으며 이 부분은 8장 벽돌깨기 게임에서도 살펴본 내용이다.

 

 

이동 방향

좌표 증감

0

Y--

1

X++, Y--

2

X++

3

X++, Y++

4

Y++

5

X--, Y++

6

X--

7

X--, Y--

 

[표 13-1] 이동 방향에 따른 증감

 

스텝(step)은 이 방향으로 [표 13-1]에 정의된 증감을 몇 번 적용할 것인가를 나타낸다.

이동 방향이 0일 때 좌표 증감이 Y-- 의미는 현재 y 좌표에만 이동 거리인 1 만큼을 감소하라는 의미이다. 만약 이동 거리가 3이라면 한번 이동할 때마다 이동 거리를 3만큼 감소하면 된다. 이때 이동 거리만큼을 증가할 것인지 감소할 것인지는 이동 거리의 부호로 결정할 수 있다. 즉 이동 방향이 0이고 이동 거리가 3이라면 y 좌표 증감은 -1 × 3 의 결과값으로 현재 y좌표에 더해주면 된다. 이때 -1은 크기에 영향을 주지 않고 방향만 바꾸는 역할을 한다.

이것을 식으로 나타내면 아래 [식 13-1]과 같다.

  

 

목표 좌표 = 현재 좌표 + ( 좌표 증감 부호× 이동 거리 ) × 스텝

 

[식 13-1] 목표 좌표

 

예로 현재 좌표가 ( 10, 10 )이고 스텝이 3이며 이동 방향이 1이라면 최종 이동 좌표는 [그림 13-9]와 같이 ( 13, 7 )이 된다.

 

[그림 13-9] 스텝(step)이 적용된 좌표

 

위의 [그림 13-9]와 같은 구간을 일정한 속도로 이동하기 위해서는 이동 시간 간격과 이동 거리가 있어야 한다.

스텝의 증가는 이동하는 횟수와 동일하므로 현재 좌표에서 목표 좌표까지 도달하는 시간은 아래 [식 13-2]와 같다

 

 

목표 좌표에 도달하는 시간 = 이동 시간 간격 × 스텝

 

[식 13-2] 시간 계산

 

이와 같이 시작 좌표로부터 이동 방향과 스텝, 그리고 이동 거리만 있으면 목표 좌표는 얼마든지 구할 수 있으며 이동 시간 간격과 이동 거리로 이동 속도를 다양하게 조절할 수 있다. 이제까지 살펴본 패턴 속성을 정리하면 [표 13-2]와 같고 이것은 파일로 저장하거나 읽기 위한 데이터 구조가 된다.

 

 

① 이동 방향

② 이동 스텝

③ 이동 시간 간격

④ 이동 거리

 

[표 13-2] 패턴 속성

 

위의 패턴 속성은 하나의 이동 패턴에 관한 속성이다.

이동 패턴은 이와 같은 패턴 여러 개가 연결되어 만들어지므로 아래 [표 13-3]과 같은 속성이 있어야 한다.

 

 

 

 

① 패턴 개수

② 시작 좌표

 

[표 13-3] 이동 패턴 속성  

 

위의 이동 패턴 속성에서 패턴 개수는 [표 13-2] 패턴 속성을 생성할 개수를 의미한다.

이와 같은 속성간의 관계를 구조체로 정의하면 아래 [소스 13-1]과 같다.

 

 

typedef enum _DIRECT { UP, UP_RIGHT, RIGHT, DOWN_RIGHT, DOWN, DOWN_LEFT, LEFT, UP_LEFT } DIRECT;

typedef struct _PAT

{

        DIRECT nDirect;  // 이동 방향

        int nStep;         // 스텝

        clock_t MoveTime; // 이동 시간 간격

        int nDist;          // 이동 거리

} PAT;

 

typedef struct _PAT_INFO

{

        int nCount;      // 패턴 개수

        int nX0, nY0;    // 시작 좌표       

        PAT *pPat;      // 패턴

} PAT_INFO;

 

PAT_INFO g_PatInfo;

 

[소스 13-1] 이동 패턴 속성 정의

 

[실습 예제 13-1]

 

[소스 13-1]에서 정의한 이동 패턴 속성에 의해 ( 30, 0 )를 시작 좌표로 하고 아래 [그림 13-10]과 같이 이동하는 패턴을 게임 프레임워크 안에서 프로그래밍해 보자. 단, A 구간의 이동 시간 간격은 1000, 스텝은 10이며 B 구간의 이동 시간 간격은 500, 스텝은 7이다.

[그림 13-10] 이동 패턴

 

[그림 13-11] A 구간 이동

 

[그림 13-12] B 구간 이동

 

 

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

#include <stdio.h>

#include <windows.h>

#include <conio.h>

#include <time.h>

#include <malloc.h>

#include "Screen.h"

 

typedef enum _DIRECT { UP, UP_RIGHT, RIGHT, DOWN_RIGHT, DOWN, DOWN_LEFT,

                          LEFT, UP_LEFT } DIRECT;

// 패턴 속성

typedef struct _PAT

{

        DIRECT nDirect;   // 이동 방향

        int nStep;          // 스텝

        clock_t MoveTime;  // 이동 시간 간격

        int nDist;           // 이동 거리

} PAT;

 

// 이동 패턴 속성

typedef struct _PAT_INFO

{

        int nCount;       // 패턴 개수

        int nX0, nY0;     // 시작 좌표         

        PAT *pPat;      

} PAT_INFO;

 

PAT_INFO g_PatInfo;

 

int g_nX, g_nY;

int g_nIndex, g_nStep;

int g_nExit;

 

void Init()

{

     g_PatInfo.nCount = 2;

     g_PatInfo.nX0 = 30;

     g_PatInfo.nY0 = 0;

     g_PatInfo.pPat = (PAT*)malloc( sizeof(PAT)*2 );

    

     g_PatInfo.pPat[0].MoveTime = 1000;

     g_PatInfo.pPat[0].nDist = 1;

     g_PatInfo.pPat[0].nStep = 10;

     g_PatInfo.pPat[0].nDirect = DOWN;

 

     g_PatInfo.pPat[1].MoveTime = 500;

     g_PatInfo.pPat[1].nDist = 1;

     g_PatInfo.pPat[1].nStep = 7;

     g_PatInfo.pPat[1].nDirect = DOWN_LEFT;

 

     g_nX = g_PatInfo.nX0;

     g_nY = g_PatInfo.nY0;

 

     g_nIndex = 0;

     g_nStep = -1;

}

 

void Update()

{       

     int nSignX, nSignY;

     static clock_t OldTime;

     clock_t CurTime = clock();

 

     if( g_nExit == 1 )

        return ;

 

     if( CurTime - OldTime > g_PatInfo.pPat[ g_nIndex ].MoveTime )

     {

        OldTime = CurTime;

        g_nStep++;

 

        if( g_nStep == g_PatInfo.pPat[ g_nIndex ].nStep )

        {

           g_nIndex++;

             if( g_nIndex == g_PatInfo.nCount )

           {

              g_nExit = 1;

              return ;

           }else{

              g_nStep = 0;

           }

          }

 

        switch( g_PatInfo.pPat[g_nIndex].nDirect )

        {

        case UP:

                nSignX = 0;

                nSignY = -1;

                break;

        case UP_RIGHT:

                nSignX = 1;

                nSignY = -1;

                break;

        case RIGHT:

                nSignX = 1;

                nSignY = 0;

                break;

        case DOWN_RIGHT:

                nSignX = 1;

                nSignY = 1;

                break;

        case DOWN:

                nSignX = 0;

                nSignY = 1;

                break;

        case DOWN_LEFT:

                nSignX = -1;

                nSignY = 1;

                break;

        case LEFT:

                nSignX = -1;

                nSignY = 0;

                break;

        case UP_LEFT:

                nSignX = -1;

                nSignY = -1;

                break;

        }

 

          g_nX = g_nX + nSignX*g_PatInfo.pPat[g_nIndex].nDist;

        g_nY = g_nY + nSignY*g_PatInfo.pPat[g_nIndex].nDist;

     }

}

 

void Render()

{

     ScreenClear();

 

     if( g_nExit == 1 )

         return ;

 

     ScreenPrint( g_nX, g_nY, "@" );

 

     ScreenFlipping();

}

 

void Release()

{

     if( g_PatInfo.pPat != NULL )

        free( g_PatInfo.pPat );

}

 

int main(void)

{

    ScreenInit();

    Init();

 

    while( 1 )

    {

        if( g_nExit == 1 )

            break;

 

        Update();

        Render();       

    }

 

    Release();

    ScreenRelease();     

    return 0;

}

 

[소스 13-2] 이동 패턴

 

38행에서 패턴 정보를 저장할 메모리를 malloc() 함수로 생성했으므로 재할당 또는 프로그램이 종료될 때에는 138행, 139행과 같은 메모리 해제 부분이 반드시 있어야 한다.

 

스텝이 증가하는 때는 경과 시간이 이동 시간 간격보다 크거나 같을 때이고 패턴 배열의 인덱스가 증가하는 때는 스텝의 증가가 제한 스텝과 같을 때이다.

이동 패턴이 종료하는 때는 패턴 배열의 인덱스인 g_nIndex와 패턴 개수가 같을 때이다.

이와 같은 내용을 코드로 옮긴 부분이 66행부터 81행까지이다.

패턴에 의한 최종적인 목표 좌표는 119행과 120행을 통해 구하게 된다.

 

■ 메인 메뉴

  

제작하려는 패턴 뷰어는 기존의 툴과는 다르게 입력, 수정, 삭제, 추가, 저장 기능 없이 메모장의 기능을 활용하는 프로그램이다. 그래서 파일 읽기와 패턴 보기, 그리고 나열된 여러 패턴을 실제 게임 환경에 출력하도록 하고 메모장에서 이들 데이터를 편집하도록 한다.

메인 메뉴를 정리하면 아래 [표 13-4]와 같다.

 

 

① 파일 읽기

② 패턴 보기

③ 종료

 

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

 

‘② 패턴 보기’의 서브 메뉴로 패턴을 실제 게임 환경에서 출력하는 ‘실행’ 서브 메뉴가 있다.

 

■ 메인 화면 제작

 

메뉴 선택 방식을 가진 프로그램들은 대부분 아래 [소스 13-3]과 같은 구조를 가진다.

 

 

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

#include <stdio.h>

#include <windows.h>

 

void gotoxy( int x, int y )

{

     COORD CursorPosition = { x, y };

     SetConsoleCursorPosition( GetStdHandle( STD_OUTPUT_HANDLE), CursorPosition );

}

 

int Menu()

{

    int nMenu;

 

    gotoxy( 20, 2 );  printf( " 패턴 뷰어 프로그램" );

    gotoxy( 10, 8 );  printf( "1. 파일 열기" );

    gotoxy( 10, 10);  printf( "2. 패턴 보기" );

    gotoxy( 10, 12 ); printf( "3. 종료" );

    gotoxy( 10, 17 ); printf( "메뉴 선택 [    ]" );

    gotoxy( 22, 17 ); scanf( "%d", &nMenu );

 

    return nMenu;

}

 

void InputBox()

{

     gotoxy( 10, 5 ); printf( "┌──────────────────────┐" );

     gotoxy( 10, 6 ); printf( "│                                            │" );

     gotoxy( 10, 7 ); printf( "│ 파일명 :                                   │" );

     gotoxy( 10, 8 ); printf( "│                                            │" );

     gotoxy( 10, 9 ); printf( "└──────────────────────┘" );

}

 

int main(void)

{

    char string[100];

    int nKey, nMenu, nLoop1 = 1;

 

    while( 1 )

    {

        system( "cls" );

        nMenu = Menu();

        if( nMenu == 3 )

            break;

 

        switch( nMenu )

        {

        case 1 :

                system( "cls" );

                InputBox();

                gotoxy( 22, 7 );

                scanf( "%s", string );

                break;

        case 2 :

                nLoop1 = 1;

                while( nLoop1 )

                {

                     system( "cls" );

                         gotoxy( 10, 10 );

                     printf( "이동 경로 출력" );

                     gotoxy( 0, 23 );

                       printf( "실행: s 메뉴로: q  [     ]" );

                     gotoxy( 22, 23 );

                                        

                     while( 1 )

                     {

                        nKey = _getch();

                        if( nKey == 'q' )

                        {

                            nLoop1 = 0;

                            break;

                        }else if( nKey == 's' ){

                                system( "cls" );

                                gotoxy( 10, 10 );

                                printf( "게임 환경에서 이동 경로 실행" );

 

                                _getch();

                                break;

                        }

                       }

                   }

                   break;

        }

     }

     return 0;

}

 

[소스 13-4] 툴 기본 구조

 

대부분 메뉴를 선택하는 구조는 위의 소스와 같은 switch()문과 같은 분기문을 통해 선택된다. 69행의 nLoop1 = 0; 은 55행의 while문을 빠져나가기 위한 부분이며 72행부터 77행까지는 이동 패턴을 실제 게임 환경에서 출력하는 부분이다.

 

 


[STEP 02]

 

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

 

■ 파일 읽기

 

파일 읽기를 하기 위해서는 저장된 파일이 있어야 하므로 저장된 파일 형식은 읽기 위한 형식과 같다. 현재 파일로부터 읽은 데이터는 [소스 13-1] 이동 패턴 속성이며 그 형식대로 아래 [그림 13-14]와 같이 메모장에 입력하여 파일로 저장하고 읽어 보면 아래와 같다.

 

[그림 13-14] 패턴을 메모장에 입력 ( pat1.txt )

 

[그림 13-15] 패턴 데이터 출력

 

 

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

#include <stdio.h>

#include <conio.h>

#include <time.h>

#include <malloc.h>

 

typedef enum _DIRECT { UP, UP_RIGHT, RIGHT, DOWN_RIGHT, DOWN, DOWN_LEFT,

                          LEFT, UP_LEFT } DIRECT;

 

typedef struct _PAT

{

        DIRECT nDirect;

        int nStep;

        clock_t MoveTime;

        int nDist;

} PAT;

 

typedef struct _PAT_INFO

{

        int nCount;

        int nX0, nY0;    

        PAT *pPat;

} PAT_INFO;

 

PAT_INFO g_PatInfo;

 

int main(void)

{

    int i;

    FILE *fp;

 

    fp = fopen( "pat1.txt", "r" );

 

    fscanf( fp, "%d\n", &g_PatInfo.nCount );

    fscanf( fp, "%d %d\n", &g_PatInfo.nX0, &g_PatInfo.nY0 );

 

    g_PatInfo.pPat = (PAT*)malloc( sizeof( PAT ) * g_PatInfo.nCount );

 

    for( i = 0 ; i < g_PatInfo.nCount ; i++ )

         fscanf( fp, "%d %d %d %d\n", &g_PatInfo.pPat[i].nDirect,

                                      &g_PatInfo.pPat[i].nStep,

                                      &g_PatInfo.pPat[i].MoveTime,

                                      &g_PatInfo.pPat[i].nDist );

    fclose( fp );

 

    printf( "%d\n", g_PatInfo.nCount );

    printf( "%d %d\n", g_PatInfo.nX0, g_PatInfo.nY0 );

        

    for( i = 0 ; i < g_PatInfo.nCount ; i++ )

    {

        printf( "%d %d %d %d\n", g_PatInfo.pPat[i].nDirect,

                                  g_PatInfo.pPat[i].nStep,

                                  g_PatInfo.pPat[i].MoveTime,

                                  g_PatInfo.pPat[i].nDist );

    }

 

    free( g_PatInfo.pPat );

    return 0;

}

 

[소스 13-4] 패턴 데이터 읽기

 

 


[STEP 03]

 

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

 

■ 패턴 보기

 

패턴 보기는 파일로 읽은 패턴 데이터의 구간 정보를 화면에 출력해서 메모장에서 입력한 좌표를 확인하기 위한 부분이다.

사용자는 패턴 보기에서 출력한 이동 경로를 보면서 메모장으로 입력한 데이터를 편집하면 된다.

 

- 최종 경로 출력

 

패턴 보기 메뉴를 실행하게 되면 최초 화면에는 패턴의 목표 좌표가 출력된다.

각 구간의 목표 좌표는 시작 위치를 기준으로 스텝만큼 이동 방향으로 이동한 좌표가 된다. 예를 들어 [그림 13-14]를 보면 ( 20, 0 )은 시작 좌표이고 첫 번째 패턴인 4 방향으로 10 스텝만큼 1 이동 거리를 가지고 이동하면 아래 [식 13-3]에 의해 ( 20, 10 )이 된다. 이 식은 1단계 패턴 속성에서 설명한 내용이다.

 

 

목표 좌표 = 현재 좌표 + ( 좌표 증감 부호× 이동 거리 ) × 스텝

 

[식 13-3] 목표 좌표

 

- 이동 경로 렌더링

 

실제 게임과 동일한 환경에서 패턴을 출력하기 위해서는 게임 프레임워크를 적용해야 한다.

이 부분은 실제 [소스 13-3]의 72행에 들어가야 하는 내용이다.

게임 프레임워크에서 프로그래밍할 때는 Init()와 Update(), Render(), Release() 함수로 구분하여 프로그래밍했지만 여기서는 구조를 함수단위가 아닌 코드 단위로 작성하면 아래 [소스 13-5]와 같다.

 

 

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

if( nKey == 's' ){

   //게임 화면 전환 및 초기화

   ScreenInit();

   nIndex = 0;

   nX = g_PatInfo.nX0;  // 시작 좌표

   nY = g_PatInfo.nY0;  // 시작 좌표

   nStep = -1;

   PatOldTime = clock();

   

   while( 1 )

   {                                                            

        // Note: Update()

        CurTime = clock();

        if( CurTime - PatOldTime > g_PatInfo.pPat[nIndex].MoveTime )

        {

            PatOldTime = CurTime;

            nStep++;

            if( nStep == g_PatInfo.pPat[nIndex].nStep )

            {

                nIndex++;

                

                  if( nIndex == g_PatInfo.nCount ) // 종료 조건                                       {

                  nLoop2 = 0;     

                  break;             // 10행 while문 탈출

                }else{

                  nStep = 0;

                }

            }

            switch( g_PatInfo.pPat[nIndex].nDirect )

            {

              case UP:

                    nSignX = 0;

                    nSignY = -1;

                    break;

                case UP_RIGHT:

                    nSignX = 1;

                    nSignY = -1;

                    break;

                case RIGHT:

                    nSignX = 1;

                    nSignY = 0;

                    break;

                case DOWN_RIGHT:

                    nSignX = 1;

                    nSignY = 1;

                    break;

                case DOWN:

                    nSignX = 0;

                    nSignY = 1;

                    break;

                case DOWN_LEFT:

                    nSignX = -1;

                    nSignY = 1;

                    break;

                case LEFT:

                    nSignX = -1;

                    nSignY = 0;

                    break;

                case UP_LEFT:

                    nSignX = -1;

                    nSignY = -1;

                    break;

            }

            nX = nX + nSignX*g_PatInfo.pPat[nIndex].nDist;

            nY = nY + nSignY*g_PatInfo.pPat[nIndex].nDist;

        }

        // Note: Render()

        ScreenClear();

        ScreenPrint( nX, nY, "@" );

        ScreenFlipping();

    }                                                   

    ScreenRelease();

}               

 

[소스 13-5] 게임 프레임워크를 적용한 패턴 렌더링

 

65행과 66행은 프레임마다 일정한 시간 간격으로 이동한 좌표를 구하는 부분이다.

 

여기까지 패턴 뷰어 프로그램을 제작하기 위한 모든 사항을 살펴보았다. 이를 기반으로 전체 프로그램을 만들어 다음 장에서 제작하는 슈팅 게임에 적용해 보자.

 

 

 

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

[출처] https://nowcampus.tistory.com/entry/CGP13%EC%9E%A5-%ED%8C%A8%ED%84%B4%EB%B7%B0%EC%96%B4?category=655340

 

 

 

 

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

 

 

(CGP)13장. 패턴 뷰어

패턴 뷰어는 각종 캐릭터들의 이동경로를 지정하기 위한 프로그램을 말한다.

이장에서는 패턴 데이터 구조를 파악하고 설계 및 정의, 파일 저장, 로딩까지 살펴보게 된다.

 

토닥토닥튜브 동영상 강의 주소

(1) http://youtu.be/IfC7niUSM6A

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

(3) http://youtu.be/ocxeB4j9A-Y

 

  

 

 

 

 

 


13.1 툴 제작 개요

 

패턴툴은 게임에 출현하는 캐릭터의 이동 경로를 지정하기 위한 툴이다.

툴에서 제작한 이동 경로는 사소한 캐릭터부터 보스 캐릭터까지 적용되며 스테이지마다 다양한 캐릭터의 이동을 제공한다.

물론 패턴 데이터를 코드로 직접 적용할 수도 있겠지만 패턴을 편집하기가 쉽지가 않으며 매번 게임을 실행시켜 캐릭터 패턴을 일일이 확인하는 것은 더욱 힘들다.

그러므로 패턴 데이터는 게임 코드와 분리시켜 툴에서 다양하게 편집하게 해야 한다.

툴에 따라 하나의 게임이 얼마나 다양화될 수 있는지를 툴 제작을 통해 살펴보도록 하자.

  

 


13.2 툴 기획

 

다음 장에서 제작하게 되는 Sogo 게임은 일반적인 슈팅 게임이다.

슈팅 게임의 기본 캐릭터는 주인공, 무기, 적 캐릭터, 보스 캐릭터가 있으며 슈팅 게임의 게임성은 다양한 적 캐릭터의 이동과 무기에 있다.

 

12장에서 제작한 Snake 게임의 적 캐릭터를 보면 패턴이라기보다는 임의의 방향을 적용한 형태이다. 그러므로 게임에서 적 캐릭터의 이동 경로를 예상할 수 없으며 다양한 이동을 디자인할 수 없다. 이러한 부분은 패턴툴을 통해 해결이 되는데 패턴툴은 하나의 패턴만 저장하며 패턴의 조합은 게임 코드에서 캐릭터와 연결하여 적용한다.

 

툴은 기본적으로 입력, 수정, 삭제, 추가 기능을 가지고 있지만 여기서는 간단히 패턴 읽기와 보기, 실행 기능만을 가지도록 한다.

그렇다면 입력, 수정, 삭제, 추가, 저장 기능은 무엇을 통해 할 것인가? 이것은 메모장의 편집 기능을 활용하도록 한다. 우리가 저장하는 패턴 데이터는 텍스트(text) 파일 형식으로 저장되므로 얼마든지 메모장과 같은 에디터에서 편집할 수 있다.

그리고 이와 같은 기능이 툴에서 제외됨으로써 툴 코드와 툴 제작 시간을 많이 줄이게 된다.

 

 


13.3 툴 제작 가이드

 

툴을 제작하기 전에 가장 먼저 해야 하는 것은 데이터 구조를 정의하는 것이다.

그 다음 툴의 기능을 정의하고 이에 따른 메인 메뉴와 서브 메뉴를 결정하면 툴의 외형적인 구조는 완성된다.

 

현재 제작하려는 프로그램에 사용되는 메뉴와 기능에 대해서 살펴보면 다음과 같다.

 

첫째, 파일 읽기 메뉴이다.

 

이 메뉴는 패턴 데이터를 단순히 읽는 기능으로 패턴 데이터 안에는 여러 이동 경로가 있다. 이 데이터를 읽기 위해서는 그 크기만큼의 메모리가 반드시 필요하므로 이 메뉴에서는  이동 경로 개수만큼 해당 메모리를 생성하고 데이터를 읽은 후에 메모리에 저장한다.

 

둘째, 패턴 보기 메뉴이다.

 

이 메뉴는 읽은 패턴 데이터를 실제 화면에 출력해 주어 메모장에서 입력한 경로가 어느 위치인지를 출력해주는 메뉴이다. 이 메뉴에는 실행이라는 서브 메뉴가 있는데 현재 출력된 이동 경로를 실제 게임 프레임워크에 적용하여 캐릭터를 출력해 준다.

이를 통해 현재 이동 경로를 따라 캐릭터가 어떻게 이동하는지를 알 수 있게 되며 패턴 속성인 이동 시간 간격과 이동 거리를 즉시 수정할 수 있게 해준다.

 

셋째, 종료 메뉴이다.

 

 


13.4 실행 화면

  

 

 

 

[그림 13-1] 패턴 뷰어 프로그램의 메인 화면

 

 

[그림 13-2] 패턴 파일 읽기 화면

 

[그림 13-3] 패턴 데이터 출력 화면

 

[그림 13-4] 게임 프레임워크를 활용한 이동 경로 출력

 

 


13.5 툴 제작 로드맵

 

프로그램 구조는 앞서 제작한 툴과 유사한 구조를 가진다.

현재 제작하려는 패턴 뷰어에는 입력, 수정, 삭제, 저장, 추가 기능이 없으므로 이전 제작 툴에 비해 오히려 간단한 구조와 흐름을 가진다.

특이한 부분은 프로그램 구조 안에 게임 프레임워크를 도입하여 실제 게임과 동일한 환경에서 이동 경로를 출력해 주는 부분이다.

 

[STEP 01]

 

 

[STEP 02]

 

[STEP 03]

 

[STEP 04]

[그림 13-5] 패턴 뷰어 제작 로드맵

 

 

 


13.6 단계별 프로그래밍

  

[STEP 01]

 

 

[그림 13-6] 1단계 제작 로드맵

 

■ 패턴

 

패턴이란 일정한 형태 또는 형식을 반복하는 것을 말하며 다양한 이동 패턴은 게임을 흥미롭게 만든다.

 

- 패턴 종류

 

이 게임에 적용하려는 패턴은 아래 [그림 13-7]과 같이 단순 패턴부터 조합된 패턴까지  적용하려고 한다. 특히 패턴의 조합은 게임 특성에 따라 소스 내에서 설정하도록 하고 여기서는 다양한 패턴이 만들어질 수 있는 환경을 제공하는데 주안점을 둔다.

 

 

[그림 13-7] 이동 패턴

 

①에서 ③까지는 한 가지 이동 형태가 적용된 것이며, ④와 ⑤는 두 가지 이상의 이동 형태가 적용된 것이다.

 

- 패턴 속성

 

이동 패턴에서 좌표는 가장 중요한 요소 중 하나이다.

좌표를 계산하는 방법은 다양하지만 여기서는 앞서 제작한 8장 벽돌깨기 게임의 공 이동 방법을 사용하여 이동 좌표를 계산한다.

벽돌깨기 게임에서 공의 이동 방향은 아래 [그림 13-8]과 같이 여섯 방향이었지만 여기서는 두 개의 방향을 더 추가하여 전부 여덟 방향으로 이동 좌표를 계산한다.

 

 

벽돌깨기 게임의 공 이동 방향

8 방향

 

[그림 13-8] 이동 방향

 

만약 더 다양한 이동 경로를 만들고 싶다면 위의 이동 방향을 많이 나누면 된다.

이동 좌표는 이동 방향에 따른 (+, -)증감과 이동 단계인 스텝(step)에 따라 결정된다.

이동 방향에 따른 좌표의 증감은 아래 [표 13-1]과 같으며 이 부분은 8장 벽돌깨기 게임에서도 살펴본 내용이다.

 

 

이동 방향

좌표 증감

0

Y--

1

X++, Y--

2

X++

3

X++, Y++

4

Y++

5

X--, Y++

6

X--

7

X--, Y--

 

[표 13-1] 이동 방향에 따른 증감

 

스텝(step)은 이 방향으로 [표 13-1]에 정의된 증감을 몇 번 적용할 것인가를 나타낸다.

이동 방향이 0일 때 좌표 증감이 Y-- 의미는 현재 y 좌표에만 이동 거리인 1 만큼을 감소하라는 의미이다. 만약 이동 거리가 3이라면 한번 이동할 때마다 이동 거리를 3만큼 감소하면 된다. 이때 이동 거리만큼을 증가할 것인지 감소할 것인지는 이동 거리의 부호로 결정할 수 있다. 즉 이동 방향이 0이고 이동 거리가 3이라면 y 좌표 증감은 -1 × 3 의 결과값으로 현재 y좌표에 더해주면 된다. 이때 -1은 크기에 영향을 주지 않고 방향만 바꾸는 역할을 한다.

이것을 식으로 나타내면 아래 [식 13-1]과 같다.

  

 

목표 좌표 = 현재 좌표 + ( 좌표 증감 부호× 이동 거리 ) × 스텝

 

[식 13-1] 목표 좌표

 

예로 현재 좌표가 ( 10, 10 )이고 스텝이 3이며 이동 방향이 1이라면 최종 이동 좌표는 [그림 13-9]와 같이 ( 13, 7 )이 된다.

 

[그림 13-9] 스텝(step)이 적용된 좌표

 

위의 [그림 13-9]와 같은 구간을 일정한 속도로 이동하기 위해서는 이동 시간 간격과 이동 거리가 있어야 한다.

스텝의 증가는 이동하는 횟수와 동일하므로 현재 좌표에서 목표 좌표까지 도달하는 시간은 아래 [식 13-2]와 같다

 

 

목표 좌표에 도달하는 시간 = 이동 시간 간격 × 스텝

 

[식 13-2] 시간 계산

 

이와 같이 시작 좌표로부터 이동 방향과 스텝, 그리고 이동 거리만 있으면 목표 좌표는 얼마든지 구할 수 있으며 이동 시간 간격과 이동 거리로 이동 속도를 다양하게 조절할 수 있다. 이제까지 살펴본 패턴 속성을 정리하면 [표 13-2]와 같고 이것은 파일로 저장하거나 읽기 위한 데이터 구조가 된다.

 

 

① 이동 방향

② 이동 스텝

③ 이동 시간 간격

④ 이동 거리

 

[표 13-2] 패턴 속성

 

위의 패턴 속성은 하나의 이동 패턴에 관한 속성이다.

이동 패턴은 이와 같은 패턴 여러 개가 연결되어 만들어지므로 아래 [표 13-3]과 같은 속성이 있어야 한다.

 

 

 

 

① 패턴 개수

② 시작 좌표

 

[표 13-3] 이동 패턴 속성  

 

위의 이동 패턴 속성에서 패턴 개수는 [표 13-2] 패턴 속성을 생성할 개수를 의미한다.

이와 같은 속성간의 관계를 구조체로 정의하면 아래 [소스 13-1]과 같다.

 

 

typedef enum _DIRECT { UP, UP_RIGHT, RIGHT, DOWN_RIGHT, DOWN, DOWN_LEFT, LEFT, UP_LEFT } DIRECT;

typedef struct _PAT

{

        DIRECT nDirect;  // 이동 방향

        int nStep;         // 스텝

        clock_t MoveTime; // 이동 시간 간격

        int nDist;          // 이동 거리

} PAT;

 

typedef struct _PAT_INFO

{

        int nCount;      // 패턴 개수

        int nX0, nY0;    // 시작 좌표       

        PAT *pPat;      // 패턴

} PAT_INFO;

 

PAT_INFO g_PatInfo;

 

[소스 13-1] 이동 패턴 속성 정의

 

[실습 예제 13-1]

 

[소스 13-1]에서 정의한 이동 패턴 속성에 의해 ( 30, 0 )를 시작 좌표로 하고 아래 [그림 13-10]과 같이 이동하는 패턴을 게임 프레임워크 안에서 프로그래밍해 보자. 단, A 구간의 이동 시간 간격은 1000, 스텝은 10이며 B 구간의 이동 시간 간격은 500, 스텝은 7이다.

[그림 13-10] 이동 패턴

 

[그림 13-11] A 구간 이동

 

[그림 13-12] B 구간 이동

 

 

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

#include <stdio.h>

#include <windows.h>

#include <conio.h>

#include <time.h>

#include <malloc.h>

#include "Screen.h"

 

typedef enum _DIRECT { UP, UP_RIGHT, RIGHT, DOWN_RIGHT, DOWN, DOWN_LEFT,

                          LEFT, UP_LEFT } DIRECT;

// 패턴 속성

typedef struct _PAT

{

        DIRECT nDirect;   // 이동 방향

        int nStep;          // 스텝

        clock_t MoveTime;  // 이동 시간 간격

        int nDist;           // 이동 거리

} PAT;

 

// 이동 패턴 속성

typedef struct _PAT_INFO

{

        int nCount;       // 패턴 개수

        int nX0, nY0;     // 시작 좌표         

        PAT *pPat;      

} PAT_INFO;

 

PAT_INFO g_PatInfo;

 

int g_nX, g_nY;

int g_nIndex, g_nStep;

int g_nExit;

 

void Init()

{

     g_PatInfo.nCount = 2;

     g_PatInfo.nX0 = 30;

     g_PatInfo.nY0 = 0;

     g_PatInfo.pPat = (PAT*)malloc( sizeof(PAT)*2 );

    

     g_PatInfo.pPat[0].MoveTime = 1000;

     g_PatInfo.pPat[0].nDist = 1;

     g_PatInfo.pPat[0].nStep = 10;

     g_PatInfo.pPat[0].nDirect = DOWN;

 

     g_PatInfo.pPat[1].MoveTime = 500;

     g_PatInfo.pPat[1].nDist = 1;

     g_PatInfo.pPat[1].nStep = 7;

     g_PatInfo.pPat[1].nDirect = DOWN_LEFT;

 

     g_nX = g_PatInfo.nX0;

     g_nY = g_PatInfo.nY0;

 

     g_nIndex = 0;

     g_nStep = -1;

}

 

void Update()

{       

     int nSignX, nSignY;

     static clock_t OldTime;

     clock_t CurTime = clock();

 

     if( g_nExit == 1 )

        return ;

 

     if( CurTime - OldTime > g_PatInfo.pPat[ g_nIndex ].MoveTime )

     {

        OldTime = CurTime;

        g_nStep++;

 

        if( g_nStep == g_PatInfo.pPat[ g_nIndex ].nStep )

        {

           g_nIndex++;

             if( g_nIndex == g_PatInfo.nCount )

           {

              g_nExit = 1;

              return ;

           }else{

              g_nStep = 0;

           }

          }

 

        switch( g_PatInfo.pPat[g_nIndex].nDirect )

        {

        case UP:

                nSignX = 0;

                nSignY = -1;

                break;

        case UP_RIGHT:

                nSignX = 1;

                nSignY = -1;

                break;

        case RIGHT:

                nSignX = 1;

                nSignY = 0;

                break;

        case DOWN_RIGHT:

                nSignX = 1;

                nSignY = 1;

                break;

        case DOWN:

                nSignX = 0;

                nSignY = 1;

                break;

        case DOWN_LEFT:

                nSignX = -1;

                nSignY = 1;

                break;

        case LEFT:

                nSignX = -1;

                nSignY = 0;

                break;

        case UP_LEFT:

                nSignX = -1;

                nSignY = -1;

                break;

        }

 

          g_nX = g_nX + nSignX*g_PatInfo.pPat[g_nIndex].nDist;

        g_nY = g_nY + nSignY*g_PatInfo.pPat[g_nIndex].nDist;

     }

}

 

void Render()

{

     ScreenClear();

 

     if( g_nExit == 1 )

         return ;

 

     ScreenPrint( g_nX, g_nY, "@" );

 

     ScreenFlipping();

}

 

void Release()

{

     if( g_PatInfo.pPat != NULL )

        free( g_PatInfo.pPat );

}

 

int main(void)

{

    ScreenInit();

    Init();

 

    while( 1 )

    {

        if( g_nExit == 1 )

            break;

 

        Update();

        Render();       

    }

 

    Release();

    ScreenRelease();     

    return 0;

}

 

[소스 13-2] 이동 패턴

 

38행에서 패턴 정보를 저장할 메모리를 malloc() 함수로 생성했으므로 재할당 또는 프로그램이 종료될 때에는 138행, 139행과 같은 메모리 해제 부분이 반드시 있어야 한다.

 

스텝이 증가하는 때는 경과 시간이 이동 시간 간격보다 크거나 같을 때이고 패턴 배열의 인덱스가 증가하는 때는 스텝의 증가가 제한 스텝과 같을 때이다.

이동 패턴이 종료하는 때는 패턴 배열의 인덱스인 g_nIndex와 패턴 개수가 같을 때이다.

이와 같은 내용을 코드로 옮긴 부분이 66행부터 81행까지이다.

패턴에 의한 최종적인 목표 좌표는 119행과 120행을 통해 구하게 된다.

 

■ 메인 메뉴

  

제작하려는 패턴 뷰어는 기존의 툴과는 다르게 입력, 수정, 삭제, 추가, 저장 기능 없이 메모장의 기능을 활용하는 프로그램이다. 그래서 파일 읽기와 패턴 보기, 그리고 나열된 여러 패턴을 실제 게임 환경에 출력하도록 하고 메모장에서 이들 데이터를 편집하도록 한다.

메인 메뉴를 정리하면 아래 [표 13-4]와 같다.

 

 

① 파일 읽기

② 패턴 보기

③ 종료

 

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

 

‘② 패턴 보기’의 서브 메뉴로 패턴을 실제 게임 환경에서 출력하는 ‘실행’ 서브 메뉴가 있다.

 

■ 메인 화면 제작

 

메뉴 선택 방식을 가진 프로그램들은 대부분 아래 [소스 13-3]과 같은 구조를 가진다.

 

 

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

#include <stdio.h>

#include <windows.h>

 

void gotoxy( int x, int y )

{

     COORD CursorPosition = { x, y };

     SetConsoleCursorPosition( GetStdHandle( STD_OUTPUT_HANDLE), CursorPosition );

}

 

int Menu()

{

    int nMenu;

 

    gotoxy( 20, 2 );  printf( " 패턴 뷰어 프로그램" );

    gotoxy( 10, 8 );  printf( "1. 파일 열기" );

    gotoxy( 10, 10);  printf( "2. 패턴 보기" );

    gotoxy( 10, 12 ); printf( "3. 종료" );

    gotoxy( 10, 17 ); printf( "메뉴 선택 [    ]" );

    gotoxy( 22, 17 ); scanf( "%d", &nMenu );

 

    return nMenu;

}

 

void InputBox()

{

     gotoxy( 10, 5 ); printf( "┌──────────────────────┐" );

     gotoxy( 10, 6 ); printf( "│                                            │" );

     gotoxy( 10, 7 ); printf( "│ 파일명 :                                   │" );

     gotoxy( 10, 8 ); printf( "│                                            │" );

     gotoxy( 10, 9 ); printf( "└──────────────────────┘" );

}

 

int main(void)

{

    char string[100];

    int nKey, nMenu, nLoop1 = 1;

 

    while( 1 )

    {

        system( "cls" );

        nMenu = Menu();

        if( nMenu == 3 )

            break;

 

        switch( nMenu )

        {

        case 1 :

                system( "cls" );

                InputBox();

                gotoxy( 22, 7 );

                scanf( "%s", string );

                break;

        case 2 :

                nLoop1 = 1;

                while( nLoop1 )

                {

                     system( "cls" );

                         gotoxy( 10, 10 );

                     printf( "이동 경로 출력" );

                     gotoxy( 0, 23 );

                       printf( "실행: s 메뉴로: q  [     ]" );

                     gotoxy( 22, 23 );

                                        

                     while( 1 )

                     {

                        nKey = _getch();

                        if( nKey == 'q' )

                        {

                            nLoop1 = 0;

                            break;

                        }else if( nKey == 's' ){

                                system( "cls" );

                                gotoxy( 10, 10 );

                                printf( "게임 환경에서 이동 경로 실행" );

 

                                _getch();

                                break;

                        }

                       }

                   }

                   break;

        }

     }

     return 0;

}

 

[소스 13-4] 툴 기본 구조

 

대부분 메뉴를 선택하는 구조는 위의 소스와 같은 switch()문과 같은 분기문을 통해 선택된다. 69행의 nLoop1 = 0; 은 55행의 while문을 빠져나가기 위한 부분이며 72행부터 77행까지는 이동 패턴을 실제 게임 환경에서 출력하는 부분이다.

 

 


[STEP 02]

 

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

 

■ 파일 읽기

 

파일 읽기를 하기 위해서는 저장된 파일이 있어야 하므로 저장된 파일 형식은 읽기 위한 형식과 같다. 현재 파일로부터 읽은 데이터는 [소스 13-1] 이동 패턴 속성이며 그 형식대로 아래 [그림 13-14]와 같이 메모장에 입력하여 파일로 저장하고 읽어 보면 아래와 같다.

 

[그림 13-14] 패턴을 메모장에 입력 ( pat1.txt )

 

[그림 13-15] 패턴 데이터 출력

 

 

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

#include <stdio.h>

#include <conio.h>

#include <time.h>

#include <malloc.h>

 

typedef enum _DIRECT { UP, UP_RIGHT, RIGHT, DOWN_RIGHT, DOWN, DOWN_LEFT,

                          LEFT, UP_LEFT } DIRECT;

 

typedef struct _PAT

{

        DIRECT nDirect;

        int nStep;

        clock_t MoveTime;

        int nDist;

} PAT;

 

typedef struct _PAT_INFO

{

        int nCount;

        int nX0, nY0;    

        PAT *pPat;

} PAT_INFO;

 

PAT_INFO g_PatInfo;

 

int main(void)

{

    int i;

    FILE *fp;

 

    fp = fopen( "pat1.txt", "r" );

 

    fscanf( fp, "%d\n", &g_PatInfo.nCount );

    fscanf( fp, "%d %d\n", &g_PatInfo.nX0, &g_PatInfo.nY0 );

 

    g_PatInfo.pPat = (PAT*)malloc( sizeof( PAT ) * g_PatInfo.nCount );

 

    for( i = 0 ; i < g_PatInfo.nCount ; i++ )

         fscanf( fp, "%d %d %d %d\n", &g_PatInfo.pPat[i].nDirect,

                                      &g_PatInfo.pPat[i].nStep,

                                      &g_PatInfo.pPat[i].MoveTime,

                                      &g_PatInfo.pPat[i].nDist );

    fclose( fp );

 

    printf( "%d\n", g_PatInfo.nCount );

    printf( "%d %d\n", g_PatInfo.nX0, g_PatInfo.nY0 );

        

    for( i = 0 ; i < g_PatInfo.nCount ; i++ )

    {

        printf( "%d %d %d %d\n", g_PatInfo.pPat[i].nDirect,

                                  g_PatInfo.pPat[i].nStep,

                                  g_PatInfo.pPat[i].MoveTime,

                                  g_PatInfo.pPat[i].nDist );

    }

 

    free( g_PatInfo.pPat );

    return 0;

}

 

[소스 13-4] 패턴 데이터 읽기

 

 


[STEP 03]

 

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

 

■ 패턴 보기

 

패턴 보기는 파일로 읽은 패턴 데이터의 구간 정보를 화면에 출력해서 메모장에서 입력한 좌표를 확인하기 위한 부분이다.

사용자는 패턴 보기에서 출력한 이동 경로를 보면서 메모장으로 입력한 데이터를 편집하면 된다.

 

- 최종 경로 출력

 

패턴 보기 메뉴를 실행하게 되면 최초 화면에는 패턴의 목표 좌표가 출력된다.

각 구간의 목표 좌표는 시작 위치를 기준으로 스텝만큼 이동 방향으로 이동한 좌표가 된다. 예를 들어 [그림 13-14]를 보면 ( 20, 0 )은 시작 좌표이고 첫 번째 패턴인 4 방향으로 10 스텝만큼 1 이동 거리를 가지고 이동하면 아래 [식 13-3]에 의해 ( 20, 10 )이 된다. 이 식은 1단계 패턴 속성에서 설명한 내용이다.

 

 

목표 좌표 = 현재 좌표 + ( 좌표 증감 부호× 이동 거리 ) × 스텝

 

[식 13-3] 목표 좌표

 

- 이동 경로 렌더링

 

실제 게임과 동일한 환경에서 패턴을 출력하기 위해서는 게임 프레임워크를 적용해야 한다.

이 부분은 실제 [소스 13-3]의 72행에 들어가야 하는 내용이다.

게임 프레임워크에서 프로그래밍할 때는 Init()와 Update(), Render(), Release() 함수로 구분하여 프로그래밍했지만 여기서는 구조를 함수단위가 아닌 코드 단위로 작성하면 아래 [소스 13-5]와 같다.

 

 

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

if( nKey == 's' ){

   //게임 화면 전환 및 초기화

   ScreenInit();

   nIndex = 0;

   nX = g_PatInfo.nX0;  // 시작 좌표

   nY = g_PatInfo.nY0;  // 시작 좌표

   nStep = -1;

   PatOldTime = clock();

   

   while( 1 )

   {                                                            

        // Note: Update()

        CurTime = clock();

        if( CurTime - PatOldTime > g_PatInfo.pPat[nIndex].MoveTime )

        {

            PatOldTime = CurTime;

            nStep++;

            if( nStep == g_PatInfo.pPat[nIndex].nStep )

            {

                nIndex++;

                

                  if( nIndex == g_PatInfo.nCount ) // 종료 조건                                       {

                  nLoop2 = 0;     

                  break;             // 10행 while문 탈출

                }else{

                  nStep = 0;

                }

            }

            switch( g_PatInfo.pPat[nIndex].nDirect )

            {

              case UP:

                    nSignX = 0;

                    nSignY = -1;

                    break;

                case UP_RIGHT:

                    nSignX = 1;

                    nSignY = -1;

                    break;

                case RIGHT:

                    nSignX = 1;

                    nSignY = 0;

                    break;

                case DOWN_RIGHT:

                    nSignX = 1;

                    nSignY = 1;

                    break;

                case DOWN:

                    nSignX = 0;

                    nSignY = 1;

                    break;

                case DOWN_LEFT:

                    nSignX = -1;

                    nSignY = 1;

                    break;

                case LEFT:

                    nSignX = -1;

                    nSignY = 0;

                    break;

                case UP_LEFT:

                    nSignX = -1;

                    nSignY = -1;

                    break;

            }

            nX = nX + nSignX*g_PatInfo.pPat[nIndex].nDist;

            nY = nY + nSignY*g_PatInfo.pPat[nIndex].nDist;

        }

        // Note: Render()

        ScreenClear();

        ScreenPrint( nX, nY, "@" );

        ScreenFlipping();

    }                                                   

    ScreenRelease();

}               

 

[소스 13-5] 게임 프레임워크를 적용한 패턴 렌더링

 

65행과 66행은 프레임마다 일정한 시간 간격으로 이동한 좌표를 구하는 부분이다.

 

여기까지 패턴 뷰어 프로그램을 제작하기 위한 모든 사항을 살펴보았다. 이를 기반으로 전체 프로그램을 만들어 다음 장에서 제작하는 슈팅 게임에 적용해 보자.

 

 

 

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

[출처] https://nowcampus.tistory.com/entry/CGP13%EC%9E%A5-%ED%8C%A8%ED%84%B4%EB%B7%B0%EC%96%B4?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
» (CGP)13장. 패턴 뷰어 file 졸리운_곰 2021.06.28 19
101 (CGP) 12장 Snake 게임 file 졸리운_곰 2021.06.28 23
100 (CGP) 11장 Snake 게임 툴 만들기 file 졸리운_곰 2021.06.28 50
99 (CGP) 10장. 하트담기 게임 file 졸리운_곰 2021.06.28 25
98 (CGP) 9장. 하트 툴 만들기 file 졸리운_곰 2021.06.28 22
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